github.com/Psiphon-Labs/tls-tris@v0.0.0-20230824155421-58bf6d336a9a/subcerts.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tls
     6  
     7  // Delegated credentials for TLS
     8  // (https://tools.ietf.org/html/draft-ietf-tls-subcerts-02) is an IETF Internet
     9  // draft and proposed TLS extension. This allows a backend server to delegate
    10  // TLS termination to a trusted frontend. If the client supports this extension,
    11  // then the frontend may use a "delegated credential" as the signing key in the
    12  // handshake. A delegated credential is a short lived key pair delegated to the
    13  // server by an entity trusted by the client. Once issued, credentials can't be
    14  // revoked; in order to mitigate risk in case the frontend is compromised, the
    15  // credential is only valid for a short time (days, hours, or even minutes).
    16  //
    17  // This implements draft 02. This draft doesn't specify an object identifier for
    18  // the X.509 extension; we use one assigned by Cloudflare. In addition, IANA has
    19  // not assigned an extension ID for this extension; we picked up one that's not
    20  // yet taken.
    21  //
    22  // TODO(cjpatton) Only ECDSA is supported with delegated credentials for now;
    23  // we'd like to suppoort for EcDSA signatures once these have better support
    24  // upstream.
    25  
    26  import (
    27  	"bytes"
    28  	"crypto"
    29  	"crypto/ecdsa"
    30  	"crypto/elliptic"
    31  	"crypto/x509"
    32  	"encoding/asn1"
    33  	"encoding/binary"
    34  	"errors"
    35  	"fmt"
    36  	"time"
    37  )
    38  
    39  const (
    40  	// length of the public key field
    41  	dcPubKeyFieldLen  = 3
    42  	dcMaxTTLSeconds   = 60 * 60 * 24 * 7 // 7 days
    43  	dcMaxTTL          = time.Duration(dcMaxTTLSeconds * time.Second)
    44  	dcMaxPublicKeyLen = 1 << 24 // Bytes
    45  	dcMaxSignatureLen = 1 << 16 // Bytes
    46  )
    47  
    48  var errNoDelegationUsage = errors.New("certificate not authorized for delegation")
    49  
    50  // delegationUsageId is the DelegationUsage X.509 extension OID
    51  //
    52  // NOTE(cjpatton) This OID is a child of Cloudflare's IANA-assigned OID.
    53  var delegationUsageId = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 44}
    54  
    55  // canDelegate returns true if a certificate can be used for delegated
    56  // credentials.
    57  func canDelegate(cert *x509.Certificate) bool {
    58  	// Check that the digitalSignature key usage is set.
    59  	if (cert.KeyUsage & x509.KeyUsageDigitalSignature) == 0 {
    60  		return false
    61  	}
    62  
    63  	// Check that the certificate has the DelegationUsage extension and that
    64  	// it's non-critical (per the spec).
    65  	for _, extension := range cert.Extensions {
    66  		if extension.Id.Equal(delegationUsageId) {
    67  			return true
    68  		}
    69  	}
    70  	return false
    71  }
    72  
    73  // credential stores the public components of a credential.
    74  type credential struct {
    75  	// The serialized form of the credential.
    76  	raw []byte
    77  
    78  	// The amount of time for which the credential is valid. Specifically, the
    79  	// the credential expires `ValidTime` seconds after the `notBefore` of the
    80  	// delegation certificate. The delegator shall not issue delegated
    81  	// credentials that are valid for more than 7 days from the current time.
    82  	//
    83  	// When this data structure is serialized, this value is converted to a
    84  	// uint32 representing the duration in seconds.
    85  	validTime time.Duration
    86  
    87  	// The signature scheme associated with the delegated credential public key.
    88  	expectedCertVerifyAlgorithm SignatureScheme
    89  
    90  	// The version of TLS in which the credential will be used.
    91  	expectedVersion uint16
    92  
    93  	// The credential public key.
    94  	publicKey crypto.PublicKey
    95  }
    96  
    97  // isExpired returns true if the credential has expired. The end of the validity
    98  // interval is defined as the delegator certificate's notBefore field (`start`)
    99  // plus ValidTime seconds. This function simply checks that the current time
   100  // (`now`) is before the end of the valdity interval.
   101  func (cred *credential) isExpired(start, now time.Time) bool {
   102  	end := start.Add(cred.validTime)
   103  	return !now.Before(end)
   104  }
   105  
   106  // invalidTTL returns true if the credential's validity period is longer than the
   107  // maximum permitted. This is defined by the certificate's notBefore field
   108  // (`start`) plus the ValidTime, minus the current time (`now`).
   109  func (cred *credential) invalidTTL(start, now time.Time) bool {
   110  	return cred.validTime > (now.Sub(start) + dcMaxTTL).Round(time.Second)
   111  }
   112  
   113  // marshalSubjectPublicKeyInfo returns a DER encoded SubjectPublicKeyInfo structure
   114  // (as defined in the X.509 standard) for the credential.
   115  func (cred *credential) marshalSubjectPublicKeyInfo() ([]byte, error) {
   116  	switch cred.expectedCertVerifyAlgorithm {
   117  	case ECDSAWithP256AndSHA256,
   118  		ECDSAWithP384AndSHA384,
   119  		ECDSAWithP521AndSHA512:
   120  		serializedPublicKey, err := x509.MarshalPKIXPublicKey(cred.publicKey)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  		return serializedPublicKey, nil
   125  
   126  	default:
   127  		return nil, fmt.Errorf("unsupported signature scheme: 0x%04x", cred.expectedCertVerifyAlgorithm)
   128  	}
   129  }
   130  
   131  // marshal encodes a credential in the wire format specified in
   132  // https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
   133  func (cred *credential) marshal() ([]byte, error) {
   134  	// The number of bytes comprising the DC parameters, which includes the
   135  	// validity time (4 bytes), the signature scheme of the public key (2 bytes), and
   136  	// the protocol version (2 bytes).
   137  	paramsLen := 8
   138  
   139  	// The first 4 bytes are the valid_time, scheme, and version fields.
   140  	serialized := make([]byte, paramsLen+dcPubKeyFieldLen)
   141  	binary.BigEndian.PutUint32(serialized, uint32(cred.validTime/time.Second))
   142  	binary.BigEndian.PutUint16(serialized[4:], uint16(cred.expectedCertVerifyAlgorithm))
   143  	binary.BigEndian.PutUint16(serialized[6:], cred.expectedVersion)
   144  
   145  	// Encode the public key and assert that the encoding is no longer than 2^16
   146  	// bytes (per the spec).
   147  	serializedPublicKey, err := cred.marshalSubjectPublicKeyInfo()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	if len(serializedPublicKey) > dcMaxPublicKeyLen {
   152  		return nil, errors.New("public key is too long")
   153  	}
   154  
   155  	// The next 3 bytes are the length of the public key field, which may be up
   156  	// to 2^24 bytes long.
   157  	putUint24(serialized[paramsLen:], len(serializedPublicKey))
   158  
   159  	// The remaining bytes are the public key itself.
   160  	serialized = append(serialized, serializedPublicKey...)
   161  	cred.raw = serialized
   162  	return serialized, nil
   163  }
   164  
   165  // unmarshalCredential decodes a credential and returns it.
   166  func unmarshalCredential(serialized []byte) (*credential, error) {
   167  	// The number of bytes comprising the DC parameters.
   168  	paramsLen := 8
   169  
   170  	if len(serialized) < paramsLen+dcPubKeyFieldLen {
   171  		return nil, errors.New("credential is too short")
   172  	}
   173  
   174  	// Parse the valid_time, scheme, and version fields.
   175  	validTime := time.Duration(binary.BigEndian.Uint32(serialized)) * time.Second
   176  	scheme := SignatureScheme(binary.BigEndian.Uint16(serialized[4:]))
   177  	version := binary.BigEndian.Uint16(serialized[6:])
   178  
   179  	// Parse the SubjectPublicKeyInfo.
   180  	pk, err := x509.ParsePKIXPublicKey(serialized[paramsLen+dcPubKeyFieldLen:])
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	if _, ok := pk.(*ecdsa.PublicKey); !ok {
   186  		return nil, fmt.Errorf("unsupported delegation key type: %T", pk)
   187  	}
   188  
   189  	return &credential{
   190  		raw:                         serialized,
   191  		validTime:                   validTime,
   192  		expectedCertVerifyAlgorithm: scheme,
   193  		expectedVersion:             version,
   194  		publicKey:                   pk,
   195  	}, nil
   196  }
   197  
   198  // getCredentialLen returns the number of bytes comprising the serialized
   199  // credential that starts at the beginning of the input slice. It returns an
   200  // error if the input is too short to contain a credential.
   201  func getCredentialLen(serialized []byte) (int, error) {
   202  	paramsLen := 8
   203  	if len(serialized) < paramsLen+dcPubKeyFieldLen {
   204  		return 0, errors.New("credential is too short")
   205  	}
   206  	// First several bytes are the valid_time, scheme, and version fields.
   207  	serialized = serialized[paramsLen:]
   208  
   209  	// The next 3 bytes are the length of the serialized public key, which may
   210  	// be up to 2^24 bytes in length.
   211  	serializedPublicKeyLen := getUint24(serialized)
   212  	serialized = serialized[dcPubKeyFieldLen:]
   213  
   214  	if len(serialized) < serializedPublicKeyLen {
   215  		return 0, errors.New("public key of credential is too short")
   216  	}
   217  
   218  	return paramsLen + dcPubKeyFieldLen + serializedPublicKeyLen, nil
   219  }
   220  
   221  // delegatedCredential stores a credential and its delegation.
   222  type delegatedCredential struct {
   223  	raw []byte
   224  
   225  	// The credential, which contains a public and its validity time.
   226  	cred *credential
   227  
   228  	// The signature scheme used to sign the credential.
   229  	algorithm SignatureScheme
   230  
   231  	// The credential's delegation.
   232  	signature []byte
   233  }
   234  
   235  // ensureCertificateHasLeaf parses the leaf certificate if needed.
   236  func ensureCertificateHasLeaf(cert *Certificate) error {
   237  	var err error
   238  	if cert.Leaf == nil {
   239  		if len(cert.Certificate[0]) == 0 {
   240  			return errors.New("missing leaf certificate")
   241  		}
   242  		cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
   243  		if err != nil {
   244  			return err
   245  		}
   246  	}
   247  	return nil
   248  }
   249  
   250  // validate checks that that the signature is valid, that the credential hasn't
   251  // expired, and that the TTL is valid. It also checks that certificate can be
   252  // used for delegation.
   253  func (dc *delegatedCredential) validate(cert *x509.Certificate, now time.Time) (bool, error) {
   254  	// Check that the cert can delegate.
   255  	if !canDelegate(cert) {
   256  		return false, errNoDelegationUsage
   257  	}
   258  
   259  	if dc.cred.isExpired(cert.NotBefore, now) {
   260  		return false, errors.New("credential has expired")
   261  	}
   262  
   263  	if dc.cred.invalidTTL(cert.NotBefore, now) {
   264  		return false, errors.New("credential TTL is invalid")
   265  	}
   266  
   267  	// Prepare the credential for verification.
   268  	rawCred, err := dc.cred.marshal()
   269  	if err != nil {
   270  		return false, err
   271  	}
   272  	hash := getHash(dc.algorithm)
   273  	in := prepareDelegation(hash, rawCred, cert.Raw, dc.algorithm)
   274  
   275  	// TODO(any) This code overlaps significantly with verifyHandshakeSignature()
   276  	// in ../auth.go. This should be refactored.
   277  	switch dc.algorithm {
   278  	case ECDSAWithP256AndSHA256,
   279  		ECDSAWithP384AndSHA384,
   280  		ECDSAWithP521AndSHA512:
   281  		pk, ok := cert.PublicKey.(*ecdsa.PublicKey)
   282  		if !ok {
   283  			return false, errors.New("expected ECDSA public key")
   284  		}
   285  		sig := new(ecdsaSignature)
   286  		if _, err = asn1.Unmarshal(dc.signature, sig); err != nil {
   287  			return false, err
   288  		}
   289  		return ecdsa.Verify(pk, in, sig.R, sig.S), nil
   290  
   291  	default:
   292  		return false, fmt.Errorf(
   293  			"unsupported signature scheme: 0x%04x", dc.algorithm)
   294  	}
   295  }
   296  
   297  // unmarshalDelegatedCredential decodes a DelegatedCredential structure.
   298  func unmarshalDelegatedCredential(serialized []byte) (*delegatedCredential, error) {
   299  	// Get the length of the serialized credential that begins at the start of
   300  	// the input slice.
   301  	serializedCredentialLen, err := getCredentialLen(serialized)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	// Parse the credential.
   307  	cred, err := unmarshalCredential(serialized[:serializedCredentialLen])
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	// Parse the signature scheme.
   313  	serialized = serialized[serializedCredentialLen:]
   314  	if len(serialized) < 4 {
   315  		return nil, errors.New("delegated credential is too short")
   316  	}
   317  	scheme := SignatureScheme(binary.BigEndian.Uint16(serialized))
   318  
   319  	// Parse the signature length.
   320  	serialized = serialized[2:]
   321  	serializedSignatureLen := binary.BigEndian.Uint16(serialized)
   322  
   323  	// Prase the signature.
   324  	serialized = serialized[2:]
   325  	if len(serialized) < int(serializedSignatureLen) {
   326  		return nil, errors.New("signature of delegated credential is too short")
   327  	}
   328  	sig := serialized[:serializedSignatureLen]
   329  
   330  	return &delegatedCredential{
   331  		raw:       serialized,
   332  		cred:      cred,
   333  		algorithm: scheme,
   334  		signature: sig,
   335  	}, nil
   336  }
   337  
   338  // getCurve maps the SignatureScheme to its corresponding elliptic.Curve.
   339  func getCurve(scheme SignatureScheme) elliptic.Curve {
   340  	switch scheme {
   341  	case ECDSAWithP256AndSHA256:
   342  		return elliptic.P256()
   343  	case ECDSAWithP384AndSHA384:
   344  		return elliptic.P384()
   345  	case ECDSAWithP521AndSHA512:
   346  		return elliptic.P521()
   347  	default:
   348  		return nil
   349  	}
   350  }
   351  
   352  // getHash maps the SignatureScheme to its corresponding hash function.
   353  //
   354  // TODO(any) This function overlaps with hashForSignatureScheme in 13.go.
   355  func getHash(scheme SignatureScheme) crypto.Hash {
   356  	switch scheme {
   357  	case ECDSAWithP256AndSHA256:
   358  		return crypto.SHA256
   359  	case ECDSAWithP384AndSHA384:
   360  		return crypto.SHA384
   361  	case ECDSAWithP521AndSHA512:
   362  		return crypto.SHA512
   363  	default:
   364  		return 0 // Unknown hash function
   365  	}
   366  }
   367  
   368  // prepareDelegation returns a hash of the message that the delegator is to
   369  // sign. The inputs are the credential (`cred`), the DER-encoded delegator
   370  // certificate (`delegatorCert`) and the signature scheme of the delegator
   371  // (`delegatorAlgorithm`).
   372  func prepareDelegation(hash crypto.Hash, cred, delegatorCert []byte, delegatorAlgorithm SignatureScheme) []byte {
   373  	h := hash.New()
   374  
   375  	// The header.
   376  	h.Write(bytes.Repeat([]byte{0x20}, 64))
   377  	h.Write([]byte("TLS, server delegated credentials"))
   378  	h.Write([]byte{0x00})
   379  
   380  	// The delegation certificate.
   381  	h.Write(delegatorCert)
   382  
   383  	// The credential.
   384  	h.Write(cred)
   385  
   386  	// The delegator signature scheme.
   387  	var serializedScheme [2]byte
   388  	binary.BigEndian.PutUint16(serializedScheme[:], uint16(delegatorAlgorithm))
   389  	h.Write(serializedScheme[:])
   390  
   391  	return h.Sum(nil)
   392  }