Skip to content

crypto/tls: support crypto.MessageSigner #75656

@salrashid123

Description

@salrashid123

go 1.25.1 introduced crypto.MessageSigner which allows the implementation to perform the hash operation and then sign the data.

For TLS, go seems to use utilize the crypto.Singer where the data is already hashed.

The suggestion in this feature request is to also support crypto.MessageSigner.

One usecase of this is really pretty obscure: when using a restricted key inside a TPM, you first need to make sure the TPM itself hashes the raw data and only then will the TPM do the signature. This means an implementation for a TPM backed restricted key that signs TLS needs to see the actual raw data to sign.

if you have a tpm-backed key and a plain, non-restricted key, you can get away with just crypto.signer. See example go_tpm_https_embed


just as a poc, i fiddled with a tls server and addied in a selector to provide raw data to a messagesigner

  • crypto/tls/handshake_server_tls13.go
  
func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {


	var sig []byte

	if _, ok := hs.cert.PrivateKey.(crypto.MessageSigner); ok {
		fmt.Println("usign messagesigner")
		var signaturePadding = []byte{
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
			0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
		}

		var to_sign bytes.Buffer
		to_sign.Write(signaturePadding)
		to_sign.WriteString(serverSignatureContext)
		to_sign.Write(hs.transcript.Sum(nil))
		sig, err = hs.cert.PrivateKey.(crypto.MessageSigner).SignMessage(c.config.rand(), to_sign.Bytes(), signOpts)
	} else {
		fmt.Println("usign signer")
		signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
		sig, err = hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
	}

the actual http server looks like this with a customer message signer for the rsa key (yeah, i'm not using a tpm here):

package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"fmt"
	"math/big"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"
	"github.com/salrashid123/messagesigner"
	"golang.org/x/net/http2"
)

func gethandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "ok")
}

func main() {

	router := mux.NewRouter()
	router.Methods(http.MethodGet).Path("/").HandlerFunc(gethandler)

	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		fmt.Println("Error generating private key:", err)
		os.Exit(1)
	}

	/// usign crypto.Signer
	// crt, err := createServerCertFromSigner(key)
	// if err != nil {
	// 	fmt.Println(err)
	// 	os.Exit(1)
	// }

	// using crypto.MessageSigner
	msigner, err := messagesigner.NewMessageSigner(&messagesigner.MessageSignerConfig{
		Signer: key,
	})
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	crt, err := createServerCertFromMessageSigner(msigner)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	tlsConfig := &tls.Config{
		Certificates:  []tls.Certificate{*crt},
		MinVersion:    tls.VersionTLS13,
		Renegotiation: tls.RenegotiateNever,
	}

	server := &http.Server{
		Addr:      ":8081",
		Handler:   router,
		TLSConfig: tlsConfig,
	}
	http2.ConfigureServer(server, &http2.Server{})
	fmt.Println("Starting Server..")
	err = server.ListenAndServeTLS("", "")
	fmt.Printf("Unable to start Server %v", err)

}

func createServerCertFromSigner(pk crypto.Signer) (*tls.Certificate, error) {

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %s", err)
	}

	template := &x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, template, pk.Public(), pk)
	if err != nil {
		return nil, fmt.Errorf("failed to create certificate: %s", err)
	}

	return &tls.Certificate{PrivateKey: pk, Leaf: template, Certificate: [][]byte{derBytes}}, nil
}

func createServerCertFromMessageSigner(pk crypto.MessageSigner) (*tls.Certificate, error) {
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %s", err)
	}

	template := &x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, template, pk.Public(), pk)
	if err != nil {
		return nil, fmt.Errorf("failed to create certificate: %s", err)
	}

	return &tls.Certificate{PrivateKey: pk, Leaf: template, Certificate: [][]byte{derBytes}}, nil
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Accepted

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions