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

Fix DC implementation #131

Merged
merged 1 commit into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Following the code pattern, you would want

Suggested change
return PSSWithSHA256, nil
sigAlgo = PSSWithSHA256

However I think what you have is actually more readable. Perhaps we should return the sigalg immediately when known everywher ein this function?

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