Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow supplying external crypto.Signer for TLS signature #524

Open
salrashid123 opened this issue Feb 1, 2023 · 4 comments · May be fixed by #681
Open

Allow supplying external crypto.Signer for TLS signature #524

salrashid123 opened this issue Feb 1, 2023 · 4 comments · May be fixed by #681
Labels
enhancement New feature or request

Comments

@salrashid123
Copy link

salrashid123 commented Feb 1, 2023

Summary

Allow users to supply a crypto.Signer implementation instead of an actual private key to dtls.v2.Config.

This would allow an abstration allowing customers to use keys embedded into hardware (TPM) or KMS systems that implement that interface.

Motivation

Right now users have to supply the raw private key to Config but with TPM, KMS or PKCS-11 systems, the key is not extractactable but is 'available' for use sometimes through a a crypto.signer interface:

eg for TPM

with this feature, a client on a device can use its embedded key for dtls connections

Additional context

some additional refernces (untested at scale)

@daenney
Copy link
Member

daenney commented Feb 2, 2023

Hiya! Thanks for raising this. I think this makes a lot of sense. I'd like to retain the "pass key directly" functionality though since that's what everyone is already using and is a very common way to use the tls library from stdlib too.

But I'm happy for something like this to be added and be tried before falling back to whatever may have been passed in the config for the raw key.

@daenney daenney added the enhancement New feature or request label Feb 2, 2023
@salrashid123
Copy link
Author

salrashid123 commented Feb 2, 2023

got it, yeah adding the Signer capability is what i meant (vs breaking change)

fwiw, if i'm not mistaken, even the privatekeys implements Singer too so there maybe a path to converge/collapse internally


and fyi, atleast in go, the tls negotations uses SignatureAlgorithm: x509.SHA256WithRSAPSS for TLS but that'll be the responsiblity of the Signer thats passed (eg, for tpm singer i have (atleast for tls1.3 which i do't know even applies here)

@Sean-Der
Copy link
Member

Sean-Der commented Jun 1, 2024

Hey @salrashid123 could I get your help on this? I am going to start working on a new major release and want to address all API breaks.

Do you have a suggested API/have any pseudo-code?

@salrashid123
Copy link
Author

hi-

i'm not at all familiar with how the API is composed is used in practice (i just used it for a pretty odd reason) but maybe able help in other ways.

i did reverse the code in the library as-is to plumb through a signer ..it just involved mashing crypto.Singer with crypto.PrivateKey and what not ...meaning what i got below is just that...a hack so please look at it for ideas on what maybe much more appropriate for the new release

the current lbirary allows you to pass a tls.Certificates through Config. the hack involved constructing a certificate where the private key is actually a crypto.signer and the cert was generated itself using that same tpm-backed signer. Basicallly the certificate is backed by a TPM.

i did an override of selfsign test application to plumb through the signer from go-tpm-tools.client.GetSigner()

the diff below are the changes

i use a software tpm to save an rsa and ecc key and then get its crypto.Signer...but you can use any signer

rm -rf /tmp/myvtpm
mkdir /tmp/myvtpm
sudo swtpm socket --tpmstate dir=/tmp/myvtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear

export TPM2TOOLS_TCTI="swtpm:port=2321"
tpm2_pcrread sha256:23

## create primary
tpm2_flushcontext -t
tpm2_createprimary -C o -c primary.ctx

### ecc
tpm2_create -G ecc256:ecdsa -g sha256 -u ecc.pub -r ecc.priv -C primary.ctx
tpm2_flushcontext -t
tpm2_load -C primary.ctx -u ecc.pub -r ecc.priv -c ecc.ctx
tpm2_evictcontrol -C o -c ecc.ctx 0x81008000
tpm2_flushcontext -t

## rsassa
tpm2_create -G rsa2048:rsassa:null -g sha256 -u rsassa.pub -r rsassa.priv -C primary.ctx
tpm2_flushcontext -t
tpm2_load -C primary.ctx -u rsassa.pub -r rsassa.priv -c rsassa.ctx
tpm2_evictcontrol -C o -c rsassa.ctx 0x81008002
tpm2_flushcontext -t
diff --git a/cipher_suite.go b/cipher_suite.go
index 6f7015c..11fde92 100644
--- a/cipher_suite.go
+++ b/cipher_suite.go
@@ -4,6 +4,7 @@
 package dtls
 
 import (
+	"crypto"
 	"crypto/ecdsa"
 	"crypto/ed25519"
 	"crypto/rsa"
@@ -264,6 +265,16 @@ func filterCipherSuitesForCertificate(cert *tls.Certificate, cipherSuites []Ciph
 		certType = clientcertificate.ECDSASign
 	case *rsa.PrivateKey:
 		certType = clientcertificate.RSASign
+	case crypto.Signer:
+		s := cert.PrivateKey.(crypto.Signer).Public()
+		switch s.(type) {
+		case ed25519.PublicKey:
+			certType = clientcertificate.ECDSASign
+		case *ecdsa.PublicKey:
+			certType = clientcertificate.ECDSASign
+		case *rsa.PublicKey:
+			certType = clientcertificate.RSASign
+		}
 	}
 
 	filtered := []CipherSuite{}
diff --git a/config.go b/config.go
index d765ecd..51ef72a 100644
--- a/config.go
+++ b/config.go
@@ -5,6 +5,7 @@ package dtls
 
 import (
 	"context"
+	"crypto"
 	"crypto/ecdsa"
 	"crypto/ed25519"
 	"crypto/rsa"
@@ -280,6 +281,7 @@ func validateConfig(config *Config) error {
 			case ed25519.PrivateKey:
 			case *ecdsa.PrivateKey:
 			case *rsa.PrivateKey:
+			case crypto.Signer:
 			default:
 				return errInvalidPrivateKey
 			}
diff --git a/crypto.go b/crypto.go
index 7b01002..1d84e37 100644
--- a/crypto.go
+++ b/crypto.go
@@ -55,6 +55,14 @@ func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCur
 	case *rsa.PrivateKey:
 		hashed := hashAlgorithm.Digest(msg)
 		return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+	case crypto.Signer:
+		hashed := hashAlgorithm.Digest(msg)
+		// if using rsapss and TPM
+		// return p.Sign(rand.Reader, hashed, &rsa.PSSOptions{
+		// 	Hash:       hashAlgorithm.CryptoHash(),
+		// 	SaltLength: rsa.PSSSaltLengthAuto,
+		// })
+		return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
 	}
 
 	return nil, errKeySignatureGenerateUnimplemented
@@ -93,6 +101,12 @@ func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.A
 		case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
 			hashed := hashAlgorithm.Digest(message)
 			return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature)
+		// case x509.SHA256WithRSAPSS:
+		// 	hashed := hashAlgorithm.Digest(message)
+		// 	//if TPM, use rsa.PSSSaltLengthAuto
+		// 	return rsa.VerifyPSS(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature, &rsa.PSSOptions{
+		// 		SaltLength: rsa.PSSSaltLengthAuto,
+		// 	})
 		default:
 			return errKeySignatureVerifyUnimplemented
 		}
@@ -123,6 +137,11 @@ func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.Private
 	case *ecdsa.PrivateKey:
 		return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
 	case *rsa.PrivateKey:
+		// if using rsapss and TPM
+		// return p.Sign(rand.Reader, hashed, &rsa.PSSOptions{
+		// 	Hash:       hashAlgorithm.CryptoHash(),
+		// 	SaltLength: rsa.PSSSaltLengthAuto,
+		// })
 		return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
 	}
 
@@ -162,6 +181,12 @@ func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hash.Algorith
 		case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
 			hash := hashAlgorithm.Digest(handshakeBodies)
 			return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hash, remoteKeySignature)
+		// case x509.SHA256WithRSAPSS:
+		// 	hashed := hashAlgorithm.Digest(handshakeBodies)
+		// 	//if using rsapss and TPM
+		// 	return rsa.VerifyPSS(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature, &rsa.PSSOptions{
+		// 		SaltLength: rsa.PSSSaltLengthAuto,
+		// 	})
 		default:
 			return errKeySignatureVerifyUnimplemented
 		}
diff --git a/examples/listen/selfsign/main.go b/examples/listen/selfsign/main.go
index 025b667..2332935 100644
--- a/examples/listen/selfsign/main.go
+++ b/examples/listen/selfsign/main.go
@@ -8,20 +8,63 @@ import (
 	"context"
 	"crypto/tls"
 	"fmt"
+	"io"
+	"log"
 	"net"
+	"slices"
 	"time"
 
+	"github.com/google/go-tpm-tools/client"
+	"github.com/google/go-tpm-tools/simulator"
+	"github.com/google/go-tpm/tpmutil"
 	"github.com/pion/dtls/v2"
 	"github.com/pion/dtls/v2/examples/util"
 	"github.com/pion/dtls/v2/pkg/crypto/selfsign"
 )
 
+var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"}
+
+func OpenTPM(path string) (io.ReadWriteCloser, error) {
+	if slices.Contains(TPMDEVICES, path) {
+		return tpmutil.OpenTPM(path)
+	} else if path == "simulator" {
+		return simulator.GetWithFixedSeedInsecure(1073741825)
+	} else {
+		return net.Dial("tcp", path)
+	}
+}
+
+const (
+	tpmPath = "127.0.0.1:2321"
+)
+
 func main() {
 	// Prepare the IP to connect to
 	addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444}
 
+	var err error
+	rwc, err := OpenTPM(tpmPath)
+	if err != nil {
+		log.Fatalf("can't open TPM %q: %v", tpmPath, err)
+	}
+	defer func() {
+		rwc.Close()
+	}()
+
+	k, err := client.LoadCachedKey(rwc, tpmutil.Handle(0x81008000), nil) // ecc
+	//k, err := client.LoadCachedKey(rwc, tpmutil.Handle(0x81008001), nil) // rsapss
+	//k, err := client.LoadCachedKey(rwc, tpmutil.Handle(0x81008002), nil) // rsassa
+	util.Check(err)
+
+	signer, err := k.GetSigner()
+	util.Check(err)
+
+	// privatekey also implements signer
+	// signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	// util.Check(err)
+
 	// Generate a certificate and private key to secure the connection
-	certificate, genErr := selfsign.GenerateSelfSigned()
+	certificate, genErr := selfsign.GenerateSelfSignedSigner(signer)
 	util.Check(genErr)
 
 	// Create parent context to cleanup handshaking connections on exit.
diff --git a/go.mod b/go.mod
index dfaff6f..e4b9eda 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,27 @@
 module github.com/pion/dtls/v2
 
 require (
+	github.com/google/go-tpm v0.9.0
+	github.com/google/go-tpm-tools v0.4.4
 	github.com/pion/logging v0.2.2
 	github.com/pion/transport/v3 v3.0.2
+	github.com/salrashid123/signer/tpm v0.0.0-20240520114507-7047274c22ce
 	golang.org/x/crypto v0.23.0
 	golang.org/x/net v0.25.0
 )
 
+require (
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/google/go-configfs-tsm v0.2.2 // indirect
+	github.com/google/go-sev-guest v0.9.3 // indirect
+	github.com/google/go-tdx-guest v0.3.1 // indirect
+	github.com/google/logger v1.1.1 // indirect
+	github.com/google/uuid v1.3.1 // indirect
+	github.com/pborman/uuid v1.2.1 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
+	google.golang.org/protobuf v1.31.0 // indirect
+)
+
 go 1.19
diff --git a/go.sum b/go.sum
index b6153ae..87c4454 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,41 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/certificate-transparency-go v1.1.2 h1:4hE0GEId6NAW28dFpC+LrRGwQX5dtmXQGDbg8+/MZOM=
+github.com/google/go-attestation v0.5.0 h1:jXtAWT2sw2Yu8mYU0BC7FDidR+ngxFPSE+pl6IUu3/0=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98=
+github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo=
+github.com/google/go-sev-guest v0.9.3 h1:GOJ+EipURdeWFl/YYdgcCxyPeMgQUWlI056iFkBD8UU=
+github.com/google/go-sev-guest v0.9.3/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs=
+github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw=
+github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE=
+github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
+github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
+github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
+github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
+github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
+github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
+github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
+github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
 github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
 github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
 github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/salrashid123/signer/tpm v0.0.0-20240520114507-7047274c22ce h1:68WgsglS7fRWMvXuEBXCvd8KJ4fZCtJlwxdmvYZA4WQ=
+github.com/salrashid123/signer/tpm v0.0.0-20240520114507-7047274c22ce/go.mod h1:D1+aHT/KM+rEbyPaAyc2j7k177ZRx26bShZqzXlALFU=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -12,8 +43,11 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
@@ -36,6 +70,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -43,6 +78,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -60,6 +97,12 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/pkg/crypto/selfsign/selfsign.go b/pkg/crypto/selfsign/selfsign.go
index 6ef0167..4227019 100644
--- a/pkg/crypto/selfsign/selfsign.go
+++ b/pkg/crypto/selfsign/selfsign.go
@@ -46,6 +46,86 @@ func SelfSign(key crypto.PrivateKey) (tls.Certificate, error) {
 	return WithDNS(key, "self-signed cert")
 }
 
+func GenerateSelfSignedSigner(priv crypto.Signer) (tls.Certificate, error) {
+	return SelfWithSigner(priv)
+}
+
+func SelfWithSigner(key crypto.Signer) (tls.Certificate, error) {
+	return WithDNSSigner(key, "self-signed cert")
+}
+
+// WithDNS creates a self-signed certificate from a elliptic curve key
+func WithDNSSigner(key crypto.Signer, cn string, sans ...string) (tls.Certificate, error) {
+	var (
+		pubKey    crypto.PublicKey
+		maxBigInt = new(big.Int) // Max random value, a 130-bits integer, i.e 2^130 - 1
+	)
+
+	k := key.Public()
+	switch k.(type) {
+	case ed25519.PublicKey:
+		pubKey = k
+	case *ecdsa.PublicKey:
+		pubKey = k
+	case *rsa.PublicKey:
+		pubKey = k
+	default:
+		return tls.Certificate{}, errInvalidPrivateKey
+	}
+
+	/* #nosec */
+	maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
+	/* #nosec */
+	serialNumber, err := rand.Int(rand.Reader, maxBigInt)
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+
+	names := []string{cn}
+	names = append(names, sans...)
+
+	keyUsage := x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign
+	if _, isRSA := key.(*rsa.PrivateKey); isRSA {
+		keyUsage |= x509.KeyUsageKeyEncipherment
+	}
+
+	template := x509.Certificate{
+		ExtKeyUsage: []x509.ExtKeyUsage{
+			x509.ExtKeyUsageClientAuth,
+			x509.ExtKeyUsageServerAuth,
+		},
+		BasicConstraintsValid: true,
+		NotBefore:             time.Now(),
+		KeyUsage:              keyUsage,
+		NotAfter:              time.Now().AddDate(0, 1, 0),
+		SerialNumber:          serialNumber,
+		Version:               2,
+		IsCA:                  true,
+		DNSNames:              names,
+		Subject: pkix.Name{
+			CommonName: cn,
+		},
+		// w/ rsapss
+		//SignatureAlgorithm: x509.SHA256WithRSAPSS,
+	}
+
+	raw, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey, key)
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+
+	leaf, err := x509.ParseCertificate(raw)
+	if err != nil {
+		return tls.Certificate{}, err
+	}
+
+	return tls.Certificate{
+		Certificate: [][]byte{raw},
+		PrivateKey:  key,
+		Leaf:        leaf,
+	}, nil
+}
+
 // WithDNS creates a self-signed certificate from a elliptic curve key
 func WithDNS(key crypto.PrivateKey, cn string, sans ...string) (tls.Certificate, error) {
 	var (
diff --git a/pkg/crypto/signaturehash/signaturehash.go b/pkg/crypto/signaturehash/signaturehash.go
index 2561acc..68e6bcb 100644
--- a/pkg/crypto/signaturehash/signaturehash.go
+++ b/pkg/crypto/signaturehash/signaturehash.go
@@ -57,6 +57,18 @@ func (a *Algorithm) isCompatible(privateKey crypto.PrivateKey) bool {
 		return a.Signature == signature.ECDSA
 	case *rsa.PrivateKey:
 		return a.Signature == signature.RSA
+	case crypto.Signer:
+		s := privateKey.(crypto.Signer).Public()
+		switch s.(type) {
+		case ed25519.PublicKey:
+			return a.Signature == signature.Ed25519
+		case *ecdsa.PublicKey:
+			return a.Signature == signature.ECDSA
+		case *rsa.PublicKey:
+			return a.Signature == signature.RSA
+		default:
+			return false
+		}
 	default:
 		return false
 	}

hoihochan added a commit to hoihochan/dtls that referenced this issue Nov 6, 2024
For certificate-based authentication, it is possible that
the private key resides in hardware such as Trusted Platform
Module (TPM) and the only way in Golang to access it via
the crypto.Signer interface.

Any code paths that deal with a private key should use the
crypto.Signer interface which comes with a function called
Public() and it can be used to determine the type of key used.

Fixes pion#524
@hoihochan hoihochan linked a pull request Nov 6, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants