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 }