Skip to content

Commit 5ef1b90

Browse files
committed
crypto/tls: define api for delegated credentials so they are fetched using the same mechanisms used to fetch certificates #67 (#69)
Refactor new API Address comments from review Address comments from review 2 Address comments from review 3
1 parent 7c96cb6 commit 5ef1b90

File tree

6 files changed

+180
-153
lines changed

6 files changed

+180
-153
lines changed

src/crypto/tls/auth.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ var rsaSignatureSchemes = []struct {
196196
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
197197
//
198198
// This function must be kept in sync with supportedSignatureAlgorithms.
199-
// NOTE: for the PQ KEM exp or using Delegated Credentials, it must be kept in
200-
// sync with supportedSignatureAlgorithmsDC.
201199
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
202200
priv, ok := cert.PrivateKey.(crypto.Signer)
203201
if !ok {

src/crypto/tls/common.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,8 @@ type ClientHelloInfo struct {
497497

498498
// SignatureSchemesDC lists the signature schemes that the client
499499
// is willing to verify when using Delegated Credentials.
500-
// This can be different from SignatureSchemes.
500+
// This is and can be different from SignatureSchemes. SignatureSchemesDC
501+
// is set only if the DelegatedCredentials Extension is being used.
501502
// If Delegated Credentials are supported, this list should not be nil.
502503
SignatureSchemesDC []SignatureScheme
503504

@@ -549,6 +550,9 @@ type CertificateRequestInfo struct {
549550

550551
// SignatureSchemesDC lists the signature schemes that the server
551552
// is willing to verify when using Delegated Credentials.
553+
// This is and can be different from SignatureSchemes. SignatureSchemesDC
554+
// is set only if the DelegatedCredentials Extension is being used.
555+
// If Delegated Credentials are supported, this list should not be nil.
552556
SignatureSchemesDC []SignatureScheme
553557

554558
// Version is the TLS version that was negotiated for this connection.
@@ -820,13 +824,6 @@ type Config struct {
820824
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts.
821825
SupportDelegatedCredential bool
822826

823-
// GetDelegatedCredential returns a DelegatedCredential for use with the
824-
// delegated credential extension based on the ClientHello. It only works
825-
// with TLS 1.3. If this is nil, then the server will not offer
826-
// a DelegatedCredential. If the call returns nil, the server is also
827-
// not offering a DelegatedCredential.
828-
GetDelegatedCredential func(*ClientHelloInfo, *CertificateRequestInfo) (*DelegatedCredential, crypto.PrivateKey, error)
829-
830827
// mutex protects sessionTicketKeys and autoSessionTicketKeys.
831828
mutex sync.RWMutex
832829
// sessionTicketKeys contains zero or more ticket keys. If set, it means the
@@ -917,7 +914,6 @@ func (c *Config) Clone() *Config {
917914
Renegotiation: c.Renegotiation,
918915
KeyLogWriter: c.KeyLogWriter,
919916
SupportDelegatedCredential: c.SupportDelegatedCredential,
920-
GetDelegatedCredential: c.GetDelegatedCredential,
921917
ECHEnabled: c.ECHEnabled,
922918
ClientECHConfigs: c.ClientECHConfigs,
923919
ServerECHProvider: c.ServerECHProvider,
@@ -1462,6 +1458,16 @@ func (c *Config) writeKeyLog(label string, clientRandom, secret []byte) error {
14621458
// and is only for debugging, so a global mutex saves space.
14631459
var writerMutex sync.Mutex
14641460

1461+
// A DelegatedCredentialPair contains a Delegated Credential and its
1462+
// associated private key.
1463+
type DelegatedCredentialPair struct {
1464+
// DC is the delegated credential.
1465+
DC *DelegatedCredential
1466+
// PrivateKey is the private key used to derive the public key of
1467+
// contained in DC. PrivateKey must implement crypto.Signer.
1468+
PrivateKey crypto.PrivateKey
1469+
}
1470+
14651471
// A Certificate is a chain of one or more certificates, leaf first.
14661472
type Certificate struct {
14671473
Certificate [][]byte
@@ -1479,9 +1485,15 @@ type Certificate struct {
14791485
// SignedCertificateTimestamps contains an optional list of Signed
14801486
// Certificate Timestamps which will be served to clients that request it.
14811487
SignedCertificateTimestamps [][]byte
1482-
// DelegatedCredential is a serialized Delegated Credential, signed by
1483-
// the leaf certificate.
1484-
// If not supported, this will be nil.
1488+
// DelegatedCredentials are a list of Delegated Credentials with their
1489+
// corresponding private keys, signed by the leaf certificate.
1490+
// If there are no delegated credentials, this field is nil.
1491+
DelegatedCredentials []DelegatedCredentialPair
1492+
// DelegatedCredential is the delegated credential to be used in the
1493+
// handshake.
1494+
// If there are no delegated credentials, this field is nil.
1495+
// NOTE: Do not fill this field, as it will be filled depending on
1496+
// the provided list of delegated credentials.
14851497
DelegatedCredential []byte
14861498
// Leaf is the parsed form of the leaf certificate, which may be initialized
14871499
// using x509.ParseCertificate to reduce per-handshake processing. If nil,

src/crypto/tls/delegated_credentials_test.go

Lines changed: 63 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@ import (
1717
"time"
1818
)
1919

20-
// dcAndPrivateKey stores a Delegated Credential and its corresponding private
21-
// key.
22-
type dcAndPrivateKey struct {
23-
*DelegatedCredential
24-
privateKey crypto.PrivateKey
25-
}
26-
2720
// These test keys were generated with the following program, available in the
2821
// crypto/tls directory:
2922
//
@@ -137,12 +130,12 @@ ZyJKvc2KqjGeZh0Or5pq6ZJb0zR7WPdz5aJIzaZ5YcxLMSv0KwaAEPH2
137130
`
138131

139132
var (
140-
dcTestConfig *Config
141-
dcTestCerts map[string]*Certificate
142-
serverDC []*dcAndPrivateKey
143-
clientDC []*dcAndPrivateKey
144-
dcNow time.Time
145-
dcTestDCScheme = []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, Ed25519}
133+
dcTestConfig *Config
134+
dcTestCerts map[string]*Certificate
135+
serverDC []DelegatedCredentialPair
136+
clientDC []DelegatedCredentialPair
137+
dcNow time.Time
138+
dcTestDCSignatureScheme = []SignatureScheme{ECDSAWithP256AndSHA256, Ed25519, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512}
146139
)
147140

148141
func init() {
@@ -232,7 +225,7 @@ func initDCTest() {
232225
}
233226
dcTestCerts["no dc"] = noDcCert
234227

235-
// The root certificates for the client.
228+
// The root certificates for the peer.
236229
dcTestConfig.RootCAs = x509.NewCertPool()
237230

238231
for _, c := range dcTestCerts {
@@ -243,20 +236,19 @@ func initDCTest() {
243236
dcTestConfig.RootCAs.AddCert(dcRoot)
244237
}
245238

246-
for i := 0; i < len(dcTestDCScheme); i++ {
247-
dc, sk, err := NewDelegatedCredential(dcCertP256, dcTestDCScheme[i], dcNow.Sub(dcCertP256.Leaf.NotBefore)+dcMaxTTL, false)
239+
for i := 0; i < len(dcTestDCSignatureScheme); i++ {
240+
dc, priv, err := NewDelegatedCredential(dcCertP256, dcTestDCSignatureScheme[i], dcNow.Sub(dcCertP256.Leaf.NotBefore)+dcMaxTTL, false)
248241
if err != nil {
249242
panic(err)
250243
}
251-
serverDC = append(serverDC, &dcAndPrivateKey{dc, sk})
244+
serverDC = append(serverDC, DelegatedCredentialPair{dc, priv})
252245

253-
dc, sk, err = NewDelegatedCredential(dcCertP256, dcTestDCScheme[i], dcNow.Sub(dcCertP256.Leaf.NotBefore)+dcMaxTTL, true)
246+
dc, priv, err = NewDelegatedCredential(dcCertP256, dcTestDCSignatureScheme[i], dcNow.Sub(dcCertP256.Leaf.NotBefore)+dcMaxTTL, true)
254247
if err != nil {
255248
panic(err)
256249
}
257-
clientDC = append(clientDC, &dcAndPrivateKey{dc, sk})
250+
clientDC = append(clientDC, DelegatedCredentialPair{dc, priv})
258251
}
259-
260252
}
261253

262254
func publicKeysEqual(publicKey, publicKey2 crypto.PublicKey, algo SignatureScheme) error {
@@ -387,7 +379,7 @@ func TestDelegatedCredentialMarshal(t *testing.T) {
387379
cert := dcTestCerts["dcEd25519"]
388380
time := dcNow.Sub(cert.Leaf.NotBefore) + dcMaxTTL
389381

390-
for _, sig := range dcTestDCScheme {
382+
for _, sig := range dcTestDCSignatureScheme {
391383
delegatedCred, _, err := NewDelegatedCredential(cert, sig, time, false)
392384
if err != nil {
393385
t.Fatal(err)
@@ -418,24 +410,44 @@ func TestDelegatedCredentialMarshal(t *testing.T) {
418410
}
419411
}
420412

421-
var dcTests = []struct {
413+
var dcServerTests = []struct {
422414
clientDCSupport bool
415+
clientMaxVers uint16
416+
serverMaxVers uint16
417+
expectSuccess bool
418+
expectDC bool
419+
name string
420+
}{
421+
{true, VersionTLS13, VersionTLS13, true, true, "tls13: DC client support"},
422+
{false, VersionTLS13, VersionTLS13, true, false, "DC not client support"},
423+
{true, VersionTLS12, VersionTLS13, true, false, "client using TLS 1.2. No DC is supported in that version."},
424+
{true, VersionTLS13, VersionTLS12, true, false, "server using TLS 1.2. No DC is supported in that version."},
425+
{true, VersionTLS11, VersionTLS13, true, false, "client using TLS 1.1. No DC is supported in that version."},
426+
{true, VersionTLS13, VersionTLS10, false, false, "server using TLS 1.0. No DC is supported in that version."},
427+
}
428+
429+
var dcClientTests = []struct {
423430
serverDCSupport bool
424431
clientMaxVers uint16
425432
serverMaxVers uint16
426433
expectSuccess bool
427434
expectDC bool
428435
name string
429436
}{
430-
{true, true, VersionTLS13, VersionTLS13, true, true, "tls13: DC server and client support"},
431-
{true, false, VersionTLS13, VersionTLS13, true, false, "DC not server support"},
432-
{false, true, VersionTLS13, VersionTLS13, true, false, "DC not client support"},
433-
{true, true, VersionTLS12, VersionTLS13, true, false, "client using TLS 1.2. No DC is supported in that version."},
434-
{true, true, VersionTLS13, VersionTLS12, true, false, "server using TLS 1.2. No DC is supported in that version."},
435-
{true, true, VersionTLS11, VersionTLS13, true, false, "client using TLS 1.1. No DC is supported in that version."},
436-
{true, true, VersionTLS13, VersionTLS10, false, false, "server using TLS 1.0. No DC is supported in that version."},
437+
{true, VersionTLS13, VersionTLS13, true, true, "tls13: DC server support"},
438+
{false, VersionTLS13, VersionTLS13, true, false, "DC not server support"},
439+
{true, VersionTLS12, VersionTLS13, true, false, "client using TLS 1.2. No DC is supported in that version."},
440+
{true, VersionTLS13, VersionTLS12, true, false, "server using TLS 1.2. No DC is supported in that version."},
441+
{true, VersionTLS11, VersionTLS13, true, false, "client using TLS 1.1. No DC is supported in that version."},
442+
{true, VersionTLS13, VersionTLS10, false, false, "server using TLS 1.0. No DC is supported in that version."},
437443
}
438444

445+
// dcCount defines the delegated credential to be used as returned by the
446+
// getCertificate or getClientCertificate callback. This allows to use
447+
// delegated credentials with different algorithms at each run of the
448+
// tests.
449+
var dcCount int
450+
439451
// Checks that the client suppports a version >= 1.3 and accepts Delegated
440452
// Credentials. If so, it returns the delegation certificate; otherwise it
441453
// returns a non-delegated certificate.
@@ -446,7 +458,9 @@ func testServerGetCertificate(ch *ClientHelloInfo) (*Certificate, error) {
446458
}
447459

448460
if versOk && ch.SupportsDelegatedCredential {
449-
return dcTestCerts["dcP256"], nil
461+
serverCert := dcTestCerts["dcP256"]
462+
serverCert.DelegatedCredentials = serverDC[dcCount:]
463+
return serverCert, nil
450464
}
451465
return dcTestCerts["no dc"], nil
452466

@@ -462,45 +476,17 @@ func testClientGetCertificate(cr *CertificateRequestInfo) (*Certificate, error)
462476
}
463477

464478
if versOk && cr.SupportsDelegatedCredential {
465-
return dcTestCerts["dcP256"], nil
479+
clientCert := dcTestCerts["dcP256"]
480+
clientCert.DelegatedCredentials = clientDC[dcCount:]
481+
return clientCert, nil
466482
}
467483
return dcTestCerts["no dc"], nil
468484

469485
}
470486

471-
// sets the dc to use for testing
472-
var dcCounter = 0
473-
474-
// Checks that the client supports the signature algorithm supported by the test
475-
// server, and that the server has a Delegated Credential.
476-
func testGetDelegatedCredential(ch *ClientHelloInfo, cr *CertificateRequestInfo) (*DelegatedCredential, crypto.PrivateKey, error) {
477-
if ch != nil {
478-
schemeOk := false
479-
for _, scheme := range ch.SignatureSchemesDC {
480-
schemeOk = schemeOk || (scheme == dcTestDCScheme[dcCounter])
481-
}
482-
483-
if schemeOk && ch.SupportsDelegatedCredential {
484-
return serverDC[dcCounter].DelegatedCredential, serverDC[dcCounter].privateKey, nil
485-
}
486-
} else if cr != nil {
487-
schemeOk := false
488-
for _, scheme := range cr.SignatureSchemesDC {
489-
schemeOk = schemeOk || (scheme == dcTestDCScheme[dcCounter])
490-
}
491-
492-
if schemeOk && cr.SupportsDelegatedCredential {
493-
return clientDC[dcCounter].DelegatedCredential, clientDC[dcCounter].privateKey, nil
494-
}
495-
}
496-
497-
return nil, nil, nil
498-
}
499-
500487
// Tests the handshake and one round of application data. Returns true if the
501488
// connection correctly used a Delegated Credential.
502489
func testConnWithDC(t *testing.T, clientMsg, serverMsg string, clientConfig, serverConfig *Config, peer string) (bool, error) {
503-
initDCTest()
504490
ln := newLocalListener(t)
505491
defer ln.Close()
506492

@@ -569,38 +555,30 @@ func TestDCHandshakeServerAuth(t *testing.T) {
569555

570556
clientConfig := dcTestConfig.Clone()
571557
serverConfig := dcTestConfig.Clone()
572-
serverConfig.GetCertificate = testServerGetCertificate
573558
clientConfig.InsecureSkipVerify = true
574559

575-
for i, test := range dcTests {
560+
for i, test := range dcServerTests {
576561
clientConfig.SupportDelegatedCredential = test.clientDCSupport
577562

578-
for dcCounter < len(dcTestDCScheme)-1 {
579-
if test.serverDCSupport {
580-
serverConfig.GetDelegatedCredential = testGetDelegatedCredential
581-
dcCounter++
582-
} else {
583-
serverConfig.GetDelegatedCredential = nil
584-
}
585-
563+
initDCTest()
564+
for dcCount = 0; dcCount < len(dcTestDCSignatureScheme); dcCount++ {
565+
serverConfig.GetCertificate = testServerGetCertificate
586566
clientConfig.MaxVersion = test.clientMaxVers
587567
serverConfig.MaxVersion = test.serverMaxVers
588568

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

591571
if err != nil && test.expectSuccess {
592-
t.Errorf("test #%d (%s) fails: %s", i+1, test.name, err.Error())
572+
t.Errorf("test #%d (%s) with signature algorithm #%d fails: %s", i, test.name, dcCount, err.Error())
593573
} else if err == nil && !test.expectSuccess {
594-
t.Errorf("test #%d (%s) succeeds; expected failure", i+1, test.name)
574+
t.Errorf("test #%d (%s) with signature algorithm #%d succeeds; expected failure", i, test.name, dcCount)
595575
}
596576

597577
if usedDC != test.expectDC {
598-
t.Errorf("test #%d (%s) usedDC = %v; expected %v", i+1, test.name, usedDC, test.expectDC)
578+
t.Errorf("test #%d (%s) with signature algorithm #%d usedDC = %v; expected %v", i, test.name, dcCount, usedDC, test.expectDC)
599579
}
600580
}
601581
}
602-
603-
dcCounter = 0
604582
}
605583

606584
// Test the client authentication with the Delegated Credential extension.
@@ -614,34 +592,27 @@ func TestDCHandshakeClientAuth(t *testing.T) {
614592
clientConfig := dcTestConfig.Clone()
615593
clientConfig.GetClientCertificate = testClientGetCertificate
616594

617-
for i, test := range dcTests {
595+
for j, test := range dcClientTests {
618596
serverConfig.SupportDelegatedCredential = test.serverDCSupport
619597

620-
for dcCounter < len(dcTestDCScheme)-1 {
621-
if test.clientDCSupport {
622-
clientConfig.GetDelegatedCredential = testGetDelegatedCredential
623-
dcCounter++
624-
} else {
625-
clientConfig.GetDelegatedCredential = nil
626-
}
627-
598+
initDCTest()
599+
for dcCount = 0; dcCount < len(dcTestDCSignatureScheme); dcCount++ {
628600
serverConfig.MaxVersion = test.serverMaxVers
629601
clientConfig.MaxVersion = test.clientMaxVers
630602

631603
usedDC, err := testConnWithDC(t, clientMsg, serverMsg, clientConfig, serverConfig, "server")
632604

633605
if err != nil && test.expectSuccess {
634-
t.Errorf("test #%d (%s) fails: %s", i+1, test.name, err.Error())
606+
t.Errorf("test #%d (%s) with signature algorithm #%d fails: %s", j, test.name, dcCount, err.Error())
635607
} else if err == nil && !test.expectSuccess {
636-
t.Errorf("test #%d (%s) succeeds; expected failure", i+1, test.name)
608+
t.Errorf("test #%d (%s) with signature algorithm #%d succeeds; expected failure", j, test.name, dcCount)
637609
}
638610

639611
if usedDC != test.expectDC {
640-
t.Errorf("test #%d (%s) usedDC = %v; expected %v", i+1, test.name, usedDC, test.expectDC)
612+
t.Errorf("test #%d (%s) with signature algorithm #%d usedDC = %v; expected %v", j, test.name, dcCount, usedDC, test.expectDC)
641613
}
642614
}
643615
}
644-
dcCounter = 0
645616
}
646617

647618
// Test server and client authentication with the Delegated Credential extension.
@@ -657,19 +628,19 @@ func TestDCHandshakeClientAndServerAuth(t *testing.T) {
657628

658629
serverConfig.SupportDelegatedCredential = true
659630
clientConfig.SupportDelegatedCredential = true
660-
clientConfig.GetDelegatedCredential = testGetDelegatedCredential
661-
serverConfig.GetDelegatedCredential = testGetDelegatedCredential
662631

663632
serverConfig.MaxVersion = VersionTLS13
664633
clientConfig.MaxVersion = VersionTLS13
665634

635+
initDCTest()
636+
666637
usedDC, err := testConnWithDC(t, clientMsg, serverMsg, clientConfig, serverConfig, "both")
667638

668639
if err != nil {
669640
t.Errorf("test server and client auth fails: %s", err.Error())
670641
}
671642

672643
if usedDC != true {
673-
t.Errorf("test server and client auth does not succed")
644+
t.Errorf("test server and client auth does not succeed")
674645
}
675646
}

0 commit comments

Comments
 (0)