Skip to content

Commit

Permalink
Update delegated credentials to the final RFC
Browse files Browse the repository at this point in the history
Although the comments say draft-03, we're currently on draft-06.
dcd6e44 forgot to update all the
comments.

The final RFC is identical to draft-06, except
expected_cert_verify_algorithm was renamed to dc_cert_verify_algorithm,
so this is just changing comment and renaming something.

While I'm here, write the codepoint in decimal instead of hex, to match
the document and how the other IANA codepoints are written out.

Change-Id: I6d1f362a21eecafeef5bba5879f4158e31c8def4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66367
Reviewed-by: Bob Beck <[email protected]>
Commit-Queue: Bob Beck <[email protected]>
Auto-Submit: David Benjamin <[email protected]>
  • Loading branch information
davidben authored and Boringssl LUCI CQ committed Feb 20, 2024
1 parent da3b372 commit 48b0edf
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 55 deletions.
25 changes: 11 additions & 14 deletions include/openssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3342,27 +3342,24 @@ OPENSSL_EXPORT const char *SSL_get_psk_identity(const SSL *ssl);
//
// *** EXPERIMENTAL — PRONE TO CHANGE ***
//
// draft-ietf-tls-subcerts is a proposed extension for TLS 1.3 and above that
// allows an end point to use its certificate to delegate credentials for
// authentication. If the peer indicates support for this extension, then this
// host may use a delegated credential to sign the handshake. Once issued,
// credentials can't be revoked. In order to mitigate the damage in case the
// credential secret key is compromised, the credential is only valid for a
// short time (days, hours, or even minutes). This library implements draft-03
// of the protocol spec.
//
// The extension ID has not been assigned; we're using 0xff02 for the time
// being. Currently only the server side is implemented.
// Delegated credentials (RFC 9345) allow a TLS 1.3 end point to use its
// certificate to issue new credentials for authentication. If the peer
// indicates support for this extension, then this host may use a delegated
// credential to sign the handshake. Once issued, credentials can't be revoked.
// In order to mitigate the damage in case the credential secret key is
// compromised, the credential is only valid for a short time (days, hours, or
// even minutes).
//
// Currently only the server side is implemented.
//
// Servers configure a DC for use in the handshake via
// |SSL_set1_delegated_credential|. It must be signed by the host's end-entity
// certificate as defined in draft-ietf-tls-subcerts-03.
// certificate as defined in RFC 9345.

// SSL_set1_delegated_credential configures the delegated credential (DC) that
// will be sent to the peer for the current connection. |dc| is the DC in wire
// format, and |pkey| or |key_method| is the corresponding private key.
// Currently (as of draft-03), only servers may configure a DC to use in the
// handshake.
// Currently, only servers may configure a DC to use in the handshake.
//
// The DC will only be used if the protocol version is correct and the signature
// scheme is supported by the peer. If not, the DC will not be negotiated and
Expand Down
4 changes: 2 additions & 2 deletions include/openssl/tls1.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ extern "C" {
// ExtensionType value from RFC 5746
#define TLSEXT_TYPE_renegotiate 0xff01

// ExtensionType value from draft-ietf-tls-subcerts.
#define TLSEXT_TYPE_delegated_credential 0x22
// ExtensionType value from RFC 9345
#define TLSEXT_TYPE_delegated_credential 34

// ExtensionType value from draft-vvv-tls-alps. This is not an IANA defined
// extension number.
Expand Down
4 changes: 2 additions & 2 deletions ssl/extensions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2752,7 +2752,7 @@ static bool ext_quic_transport_params_add_serverhello_legacy(SSL_HANDSHAKE *hs,

// Delegated credentials.
//
// https://tools.ietf.org/html/draft-ietf-tls-subcerts
// https://www.rfc-editor.org/rfc/rfc9345.html

static bool ext_delegated_credential_add_clienthello(
const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible,
Expand Down Expand Up @@ -4122,7 +4122,7 @@ bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) {

Span<const uint16_t> sigalgs = kSignSignatureAlgorithms;
if (ssl_signing_with_dc(hs)) {
sigalgs = MakeConstSpan(&dc->expected_cert_verify_algorithm, 1);
sigalgs = MakeConstSpan(&dc->dc_cert_verify_algorithm, 1);
} else if (!cert->sigalgs.empty()) {
sigalgs = cert->sigalgs;
}
Expand Down
11 changes: 4 additions & 7 deletions ssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1574,8 +1574,7 @@ bool ssl_encrypt_client_hello(SSL_HANDSHAKE *hs, Span<const uint8_t> enc);

// Delegated credentials.

// This structure stores a delegated credential (DC) as defined by
// draft-ietf-tls-subcerts-03.
// This structure stores a delegated credential (DC) as defined by RFC 9345.
struct DC {
static constexpr bool kAllowUniquePtr = true;
~DC();
Expand All @@ -1588,13 +1587,11 @@ struct DC {
// |*out_alert|.
static UniquePtr<DC> Parse(CRYPTO_BUFFER *in, uint8_t *out_alert);

// raw is the delegated credential encoded as specified in draft-ietf-tls-
// subcerts-03.
// raw is the delegated credential encoded as specified in RFC 9345.
UniquePtr<CRYPTO_BUFFER> raw;

// expected_cert_verify_algorithm is the signature scheme of the DC public
// key.
uint16_t expected_cert_verify_algorithm = 0;
// dc_cert_verify_algorithm is the signature scheme of the DC public key.
uint16_t dc_cert_verify_algorithm = 0;

// pkey is the public key parsed from |public_key|.
UniquePtr<EVP_PKEY> pkey;
Expand Down
9 changes: 4 additions & 5 deletions ssl/ssl_cert.cc
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ UniquePtr<DC> DC::Dup() {
}

ret->raw = UpRef(raw);
ret->expected_cert_verify_algorithm = expected_cert_verify_algorithm;
ret->dc_cert_verify_algorithm = dc_cert_verify_algorithm;
ret->pkey = UpRef(pkey);
return ret;
}
Expand All @@ -775,7 +775,7 @@ UniquePtr<DC> DC::Parse(CRYPTO_BUFFER *in, uint8_t *out_alert) {
uint16_t algorithm;
CRYPTO_BUFFER_init_CBS(dc->raw.get(), &deleg);
if (!CBS_get_u32(&deleg, &valid_time) ||
!CBS_get_u16(&deleg, &dc->expected_cert_verify_algorithm) ||
!CBS_get_u16(&deleg, &dc->dc_cert_verify_algorithm) ||
!CBS_get_u24_length_prefixed(&deleg, &pubkey) ||
!CBS_get_u16(&deleg, &algorithm) ||
!CBS_get_u16_length_prefixed(&deleg, &sig) ||
Expand Down Expand Up @@ -817,16 +817,15 @@ static bool ssl_can_serve_dc(const SSL_HANDSHAKE *hs) {
// Check that the DC signature algorithm is supported by the peer.
Span<const uint16_t> peer_sigalgs = hs->peer_delegated_credential_sigalgs;
for (uint16_t peer_sigalg : peer_sigalgs) {
if (dc->expected_cert_verify_algorithm == peer_sigalg) {
if (dc->dc_cert_verify_algorithm == peer_sigalg) {
return true;
}
}
return false;
}

bool ssl_signing_with_dc(const SSL_HANDSHAKE *hs) {
// As of draft-ietf-tls-subcert-03, only the server may use delegated
// credentials to authenticate itself.
// We only support delegated credentials as a server.
return hs->ssl->server &&
hs->delegated_credential_requested &&
ssl_can_serve_dc(hs);
Expand Down
2 changes: 1 addition & 1 deletion ssl/test/runner/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ const (
extensionPadding uint16 = 21
extensionExtendedMasterSecret uint16 = 23
extensionCompressedCertAlgs uint16 = 27
extensionDelegatedCredentials uint16 = 34
extensionSessionTicket uint16 = 35
extensionPreSharedKey uint16 = 41
extensionEarlyData uint16 = 42
Expand All @@ -127,7 +128,6 @@ const (
extensionRenegotiationInfo uint16 = 0xff01
extensionQUICTransportParamsLegacy uint16 = 0xffa5 // draft-ietf-quic-tls-32 and earlier
extensionChannelID uint16 = 30032 // not IANA assigned
extensionDelegatedCredentials uint16 = 0x22 // draft-ietf-tls-subcerts-06
extensionDuplicate uint16 = 0xffff // not IANA assigned
extensionEncryptedClientHello uint16 = 0xfe0d // not IANA assigned
extensionECHOuterExtensions uint16 = 0xfd00 // not IANA assigned
Expand Down
2 changes: 1 addition & 1 deletion ssl/test/runner/handshake_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1803,7 +1803,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
// delegatedCredentialSignedMessage returns the bytes that are signed in order
// to authenticate a delegated credential.
func delegatedCredentialSignedMessage(credBytes []byte, algorithm signatureAlgorithm, leafDER []byte) []byte {
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3
// https://www.rfc-editor.org/rfc/rfc9345.html#section-4
ret := make([]byte, 64, 128)
for i := range ret {
ret[i] = 0x20
Expand Down
22 changes: 11 additions & 11 deletions ssl/test/runner/handshake_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -1923,13 +1923,13 @@ type certificateEntry struct {
}

type delegatedCredential struct {
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3
signedBytes []byte
lifetimeSecs uint32
expectedCertVerifyAlgo signatureAlgorithm
pkixPublicKey []byte
algorithm signatureAlgorithm
signature []byte
// https://www.rfc-editor.org/rfc/rfc9345.html#section-4
signedBytes []byte
lifetimeSecs uint32
dcCertVerifyAlgo signatureAlgorithm
pkixPublicKey []byte
algorithm signatureAlgorithm
signature []byte
}

type certificateMsg struct {
Expand Down Expand Up @@ -2030,25 +2030,25 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
case extensionSignedCertificateTimestamp:
cert.sctList = []byte(body)
case extensionDelegatedCredentials:
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3
// https://www.rfc-editor.org/rfc/rfc9345.html#section-4
if cert.delegatedCredential != nil {
return false
}

dc := new(delegatedCredential)
origBody := body
var expectedCertVerifyAlgo, algorithm uint16
var dcCertVerifyAlgo, algorithm uint16

if !body.ReadUint32(&dc.lifetimeSecs) ||
!body.ReadUint16(&expectedCertVerifyAlgo) ||
!body.ReadUint16(&dcCertVerifyAlgo) ||
!readUint24LengthPrefixedBytes(&body, &dc.pkixPublicKey) ||
!body.ReadUint16(&algorithm) ||
!readUint16LengthPrefixedBytes(&body, &dc.signature) ||
len(body) != 0 {
return false
}

dc.expectedCertVerifyAlgo = signatureAlgorithm(expectedCertVerifyAlgo)
dc.dcCertVerifyAlgo = signatureAlgorithm(dcCertVerifyAlgo)
dc.algorithm = signatureAlgorithm(algorithm)
dc.signedBytes = []byte(origBody)[:4+2+3+len(dc.pkixPublicKey)]
cert.delegatedCredential = dc
Expand Down
24 changes: 12 additions & 12 deletions ssl/test/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,9 @@ type delegatedCredentialConfig struct {
// certificate, that the delegated credential is valid for. If zero, then 24
// hours is assumed.
lifetime time.Duration
// expectedAlgo is the signature scheme that should be used with this
// delegated credential. If zero, ECDSA with P-256 is assumed.
expectedAlgo signatureAlgorithm
// dcAlgo is the signature scheme that should be used with this delegated
// credential. If zero, ECDSA with P-256 is assumed.
dcAlgo signatureAlgorithm
// tlsVersion is the version of TLS that should be used with this delegated
// credential. If zero, TLS 1.3 is assumed.
tlsVersion uint16
Expand Down Expand Up @@ -320,14 +320,14 @@ func loadRSAPrivateKey(filename string) (priv *rsa.PrivateKey, privPKCS8 []byte,
}

func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byte, parentPriv crypto.PrivateKey) (dc, privPKCS8 []uint8, err error) {
expectedAlgo := config.expectedAlgo
if expectedAlgo == signatureAlgorithm(0) {
expectedAlgo = signatureECDSAWithP256AndSHA256
dcAlgo := config.dcAlgo
if dcAlgo == signatureAlgorithm(0) {
dcAlgo = signatureECDSAWithP256AndSHA256
}

var pub crypto.PublicKey

switch expectedAlgo {
switch dcAlgo {
case signatureRSAPKCS1WithMD5, signatureRSAPKCS1WithSHA1, signatureRSAPKCS1WithSHA256, signatureRSAPKCS1WithSHA384, signatureRSAPKCS1WithSHA512, signatureRSAPSSWithSHA256, signatureRSAPSSWithSHA384, signatureRSAPSSWithSHA512:
// RSA keys are expensive to generate so load from disk instead.
var priv *rsa.PrivateKey
Expand All @@ -339,7 +339,7 @@ func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byt

case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256, signatureECDSAWithP384AndSHA384, signatureECDSAWithP521AndSHA512:
var curve elliptic.Curve
switch expectedAlgo {
switch dcAlgo {
case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256:
curve = elliptic.P256()
case signatureECDSAWithP384AndSHA384:
Expand All @@ -362,7 +362,7 @@ func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byt
pub = &priv.PublicKey

default:
return nil, nil, fmt.Errorf("unsupported expected signature algorithm: %x", expectedAlgo)
return nil, nil, fmt.Errorf("unsupported DC signature algorithm: %x", dcAlgo)
}

lifetime := config.lifetime
Expand All @@ -382,9 +382,9 @@ func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byt
return nil, nil, fmt.Errorf("delegated credentials require TLS 1.3")
}

// https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3
// https://www.rfc-editor.org/rfc/rfc9345.html#section-4
dc = append(dc, byte(lifetimeSecs>>24), byte(lifetimeSecs>>16), byte(lifetimeSecs>>8), byte(lifetimeSecs))
dc = append(dc, byte(expectedAlgo>>8), byte(expectedAlgo))
dc = append(dc, byte(dcAlgo>>8), byte(dcAlgo))

pubBytes, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
Expand Down Expand Up @@ -7490,7 +7490,7 @@ func addExtensionTests() {
ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
ALPSUseNewCodepoint: clientSends,
},
resumeSession: true,
resumeSession: true,
flags: append([]string{
"-select-alpn", "proto",
"-on-initial-application-settings", "proto,shim1",
Expand Down

0 comments on commit 48b0edf

Please sign in to comment.