Skip to content

Commit

Permalink
Fix DC implementation #131
Browse files Browse the repository at this point in the history
Addresses issues #127, #128, #129, and #130.
Add tool for generating delegated credentials.
  • Loading branch information
jhoyla authored and bwesterb committed Sep 7, 2022
1 parent ca3fe71 commit abb10c1
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 118 deletions.
60 changes: 7 additions & 53 deletions src/crypto/tls/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,72 +259,26 @@ func signatureSchemesForCertificate(version uint16, cert *Certificate) []Signatu
return sigAlgs
}

// signatureSchemeForDelegatedCredential returns the list of supported
// SignatureSchemes for a given delegated credential, based on the public
// key, and optionally filtered by its explicit
// SupportedSignatureAlgorithmsDC.
//
// This function must be kept in sync with supportedSignatureAlgorithmsDC.
func signatureSchemeForDelegatedCredential(version uint16, dc *DelegatedCredential) []SignatureScheme {
pub := dc.cred.publicKey

var sigAlgs []SignatureScheme
switch pub.(type) {
case *ecdsa.PublicKey:
pk, ok := pub.(*ecdsa.PublicKey)
if !ok {
return nil
}
switch pk.Curve {
case elliptic.P256():
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
case elliptic.P384():
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
case elliptic.P521():
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
default:
return nil
}
case ed25519.PublicKey:
sigAlgs = []SignatureScheme{Ed25519}
case circlSign.PublicKey:
pk, ok := pub.(circlSign.PublicKey)
if !ok {
return nil
}
scheme := pk.Scheme()
tlsScheme, ok := scheme.(circlPki.TLSScheme)
if !ok {
return nil
}
sigAlgs = []SignatureScheme{SignatureScheme(tlsScheme.TLSIdentifier())}
default:
return nil
}

return sigAlgs
}

// selectSignatureSchemeDC picks a SignatureScheme from the peer's preference list
// that works with the selected delegated credential. It's only called for protocol
// versions that support delegated credential, so TLS 1.3.
func selectSignatureSchemeDC(vers uint16, dc *DelegatedCredential, peerAlgs []SignatureScheme) (SignatureScheme, error) {
func selectSignatureSchemeDC(vers uint16, dc *DelegatedCredential, peerAlgs []SignatureScheme, peerAlgsDC []SignatureScheme) (SignatureScheme, error) {
if vers != VersionTLS13 {
return 0, errors.New("unsupported TLS version for dc")
}

supportedAlgs := signatureSchemeForDelegatedCredential(vers, dc)
if len(supportedAlgs) == 0 {
return 0, errors.New("unsupported scheme for dc")
if !isSupportedSignatureAlgorithm(dc.algorithm, peerAlgs) {
return undefinedSignatureScheme, errors.New("tls: peer doesn't support the delegated credential's signature")
}

// Pick signature scheme in the peer's preference order, as our
// preference order is not configurable.
for _, preferredAlg := range peerAlgs {
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
for _, preferredAlg := range peerAlgsDC {
if preferredAlg == dc.cred.expCertVerfAlgo {
return preferredAlg, nil
}
}
return 0, errors.New("tls: peer doesn't support any of the delegated credential's signature algorithms")
return 0, errors.New("tls: peer doesn't support the delegated credential's signature algorithm")
}

// selectSignatureScheme picks a SignatureScheme from the peer's preference list
Expand Down
69 changes: 50 additions & 19 deletions src/crypto/tls/delegated_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/binary"
"errors"
Expand All @@ -40,6 +41,10 @@ const (
dcMaxSignatureLen = (1 << 16) - 1 // Bytes
)

const (
undefinedSignatureScheme SignatureScheme = 0x0000
)

var extensionDelegatedCredential = []int{1, 3, 6, 1, 4, 1, 44363, 44}

// isValidForDelegation returns true if a certificate can be used for Delegated
Expand All @@ -61,7 +66,6 @@ func isValidForDelegation(cert *x509.Certificate) bool {
return true
}
}

return false
}

Expand Down Expand Up @@ -133,6 +137,7 @@ func (cred *credential) marshalPublicKeyInfo() ([]byte, error) {
}

return rawPub, nil

default:
return nil, fmt.Errorf("tls: unsupported signature scheme: 0x%04x", cred.expCertVerfAlgo)
}
Expand Down Expand Up @@ -228,6 +233,12 @@ func getHash(scheme SignatureScheme) crypto.Hash {
return crypto.SHA512
case Ed25519:
return directSigning
case PKCS1WithSHA256, PSSWithSHA256:
return crypto.SHA256
case PSSWithSHA384:
return crypto.SHA384
case PSSWithSHA512:
return crypto.SHA512
default:
return 0 //Unknown hash function
}
Expand Down Expand Up @@ -291,28 +302,29 @@ func prepareDelegationSignatureInput(hash crypto.Hash, cred *credential, dCert [
// Extract the algorithm used to sign the Delegated Credential from the
// end-entity (leaf) certificate.
func getSignatureAlgorithm(cert *Certificate) (SignatureScheme, error) {
var sigAlgo SignatureScheme
switch sk := cert.PrivateKey.(type) {
case *ecdsa.PrivateKey:
pk := sk.Public().(*ecdsa.PublicKey)
curveName := pk.Curve.Params().Name
certAlg := cert.Leaf.SignatureAlgorithm
if certAlg == x509.ECDSAWithSHA256 && curveName == "P-256" {
sigAlgo = ECDSAWithP256AndSHA256
} else if certAlg == x509.ECDSAWithSHA384 && curveName == "P-384" {
sigAlgo = ECDSAWithP384AndSHA384
} else if certAlg == x509.ECDSAWithSHA512 && curveName == "P-521" {
sigAlgo = ECDSAWithP521AndSHA512
certAlg := cert.Leaf.PublicKeyAlgorithm
if certAlg == x509.ECDSA && curveName == "P-256" {
return ECDSAWithP256AndSHA256, nil
} else if certAlg == x509.ECDSA && curveName == "P-384" {
return ECDSAWithP384AndSHA384, nil
} else if certAlg == x509.ECDSA && curveName == "P-521" {
return ECDSAWithP521AndSHA512, nil
} else {
return SignatureScheme(0x00), fmt.Errorf("using curve %s for %s is not supported", curveName, cert.Leaf.SignatureAlgorithm)
return undefinedSignatureScheme, fmt.Errorf("using curve %s for %s is not supported", curveName, cert.Leaf.SignatureAlgorithm)
}
case ed25519.PrivateKey:
sigAlgo = Ed25519
return Ed25519, nil
case *rsa.PrivateKey:
// If the certificate has the RSAEncryption OID there are a number of valid signature schemes that may sign the DC.
// In the absence of better information, we make a reasonable choice.
return PSSWithSHA256, nil
default:
return SignatureScheme(0x00), fmt.Errorf("tls: unsupported algorithm for Delegated Credential")
return undefinedSignatureScheme, fmt.Errorf("tls: unsupported algorithm for signing Delegated Credential")
}

return sigAlgo, nil
}

// NewDelegatedCredential creates a new Delegated Credential using 'cert' for
Expand Down Expand Up @@ -365,7 +377,7 @@ func NewDelegatedCredential(cert *Certificate, pubAlgo SignatureScheme, validTim
return nil, nil, err
}
default:
return nil, nil, fmt.Errorf("tls: unsupported algorithm for Delegated Credential: %T", pubAlgo)
return nil, nil, fmt.Errorf("tls: unsupported algorithm for Delegated Credential: %s", pubAlgo)
}

// Prepare the credential for signing
Expand All @@ -390,6 +402,13 @@ func NewDelegatedCredential(cert *Certificate, pubAlgo SignatureScheme, validTim
if err != nil {
return nil, nil, err
}
case *rsa.PrivateKey:
opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash,
Hash: hash}
sig, err = rsa.SignPSS(rand.Reader, sk, hash, values, opts)
if err != nil {
return nil, nil, err
}
default:
return nil, nil, fmt.Errorf("tls: unsupported key type for Delegated Credential")
}
Expand Down Expand Up @@ -448,17 +467,29 @@ func (dc *DelegatedCredential) Validate(cert *x509.Certificate, isClient bool, n
}

return ed25519.Verify(pk, in, dc.signature)
case PSSWithSHA256,
PSSWithSHA384,
PSSWithSHA512:
pk, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return false
}
hash := getHash(dc.algorithm)
return rsa.VerifyPSS(pk, hash, in, dc.signature, nil) == nil
default:
return false
}
}

// marshal encodes a DelegatedCredential structure. It also sets dc.Raw to that
// Marshal encodes a DelegatedCredential structure. It also sets dc.Raw to that
// encoding.
func (dc *DelegatedCredential) marshal() ([]byte, error) {
func (dc *DelegatedCredential) Marshal() ([]byte, error) {
if len(dc.signature) > dcMaxSignatureLen {
return nil, errors.New("tls: delegated credential is not valid")
}
if len(dc.signature) == 0 {
return nil, errors.New("tls: delegated credential has no signature")
}

raw, err := dc.cred.marshal()
if err != nil {
Expand All @@ -475,8 +506,8 @@ func (dc *DelegatedCredential) marshal() ([]byte, error) {
return dc.raw, nil
}

// unmarshalDelegatedCredential decodes a DelegatedCredential structure.
func unmarshalDelegatedCredential(raw []byte) (*DelegatedCredential, error) {
// UnmarshalDelegatedCredential decodes a DelegatedCredential structure.
func UnmarshalDelegatedCredential(raw []byte) (*DelegatedCredential, error) {
rawCredentialLen, err := getCredentialLen(raw)
if err != nil {
return nil, err
Expand Down
31 changes: 19 additions & 12 deletions src/crypto/tls/delegated_credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,12 @@ func TestDelegatedCredentialMarshal(t *testing.T) {
t.Fatal(err)
}

ser, err := delegatedCred.marshal()
ser, err := delegatedCred.Marshal()
if err != nil {
t.Error(err)
}

delegatedCred2, err := unmarshalDelegatedCredential(ser)
delegatedCred2, err := UnmarshalDelegatedCredential(ser)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -466,6 +466,12 @@ func testServerGetCertificate(ch *ClientHelloInfo) (*Certificate, error) {

}

// Used when the server doesn't support DCs.
// This function always returns a non-DC cert.
func testServerGetCertificateNoDC(ch *ClientHelloInfo) (*Certificate, error) {
return dcTestCerts["no dc"], nil
}

// Checks that the client suppports a version >= 1.3 and accepts Delegated
// Credentials. If so, it returns the delegation certificate; otherwise it
// returns a non-Delegated certificate.
Expand Down Expand Up @@ -507,8 +513,8 @@ func testConnWithDC(t *testing.T, clientMsg, serverMsg string, clientConfig, ser
}
serverCh <- server
}()

client, err := Dial("tcp", ln.Addr().String(), clientConfig)

if err != nil {
return false, err
}
Expand Down Expand Up @@ -552,20 +558,22 @@ func testConnWithDC(t *testing.T, clientMsg, serverMsg string, clientConfig, ser
func TestDCHandshakeServerAuth(t *testing.T) {
serverMsg := "hello, client"
clientMsg := "hello, server"

initDCTest()
clientConfig := dcTestConfig.Clone()
serverConfig := dcTestConfig.Clone()
clientConfig.InsecureSkipVerify = true

for i, test := range dcServerTests {
clientConfig.SupportDelegatedCredential = test.clientDCSupport

initDCTest()
for dcCount = 0; dcCount < len(dcTestDCSignatureScheme); dcCount++ {
serverConfig.GetCertificate = testServerGetCertificate
if test.serverMaxVers < VersionTLS13 {
t.Logf("Server doesn't support DCs, not offering. test %d", i)
serverConfig.GetCertificate = testServerGetCertificateNoDC
} else {
serverConfig.GetCertificate = testServerGetCertificate
}

clientConfig.MaxVersion = test.clientMaxVers
serverConfig.MaxVersion = test.serverMaxVers

usedDC, err := testConnWithDC(t, clientMsg, serverMsg, clientConfig, serverConfig, "client")

if err != nil && test.expectSuccess {
Expand All @@ -586,6 +594,7 @@ func TestDCHandshakeClientAuth(t *testing.T) {
clientMsg := "hello, server"
serverMsg := "hello, client"

initDCTest()
serverConfig := dcTestConfig.Clone()
serverConfig.ClientAuth = RequestClientCert
serverConfig.GetCertificate = testServerGetCertificate
Expand All @@ -595,7 +604,6 @@ func TestDCHandshakeClientAuth(t *testing.T) {
for j, test := range dcClientTests {
serverConfig.SupportDelegatedCredential = test.serverDCSupport

initDCTest()
for dcCount = 0; dcCount < len(dcTestDCSignatureScheme); dcCount++ {
serverConfig.MaxVersion = test.serverMaxVers
clientConfig.MaxVersion = test.clientMaxVers
Expand All @@ -620,6 +628,7 @@ func TestDCHandshakeClientAndServerAuth(t *testing.T) {
clientMsg := "hello, server"
serverMsg := "hello, client"

initDCTest()
serverConfig := dcTestConfig.Clone()
serverConfig.ClientAuth = RequestClientCert
serverConfig.GetCertificate = testServerGetCertificate
Expand All @@ -632,8 +641,6 @@ func TestDCHandshakeClientAndServerAuth(t *testing.T) {
serverConfig.MaxVersion = VersionTLS13
clientConfig.MaxVersion = VersionTLS13

initDCTest()

usedDC, err := testConnWithDC(t, clientMsg, serverMsg, clientConfig, serverConfig, "both")

if err != nil {
Expand Down
Loading

0 comments on commit abb10c1

Please sign in to comment.