mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-28 19:41:29 +00:00
551 lines
16 KiB
Go
551 lines
16 KiB
Go
// Copyright 2020-2021 Cloudflare, Inc. All rights reserved. Use of this source code
|
|
// is governed by a BSD-style license that can be found in the LICENSE file.
|
|
|
|
package tls
|
|
|
|
// Delegated Credentials for TLS
|
|
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts) is an IETF Internet
|
|
// draft and proposed TLS extension. If the client or server supports this
|
|
// extension, then the server or client may use a "delegated credential" as the
|
|
// signing key in the handshake. A delegated credential is a short lived
|
|
// public/secret key pair delegated to the peer by an entity trusted by the
|
|
// corresponding peer. This allows a reverse proxy to terminate a TLS connection
|
|
// on behalf of the entity. Credentials can't be revoked; in order to
|
|
// mitigate risk in case the reverse proxy is compromised, the credential is only
|
|
// valid for a short time (days, hours, or even minutes).
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/cryptobyte"
|
|
)
|
|
|
|
const (
|
|
// In the absence of an application profile standard specifying otherwise,
|
|
// the maximum validity period is set to 7 days.
|
|
dcMaxTTLSeconds = 60 * 60 * 24 * 7
|
|
dcMaxTTL = time.Duration(dcMaxTTLSeconds * time.Second)
|
|
dcMaxPubLen = (1 << 24) - 1 // Bytes
|
|
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
|
|
// Credentials.
|
|
func isValidForDelegation(cert *x509.Certificate) bool {
|
|
// Check that the digitalSignature key usage is set.
|
|
// The certificate must contains the digitalSignature KeyUsage.
|
|
if (cert.KeyUsage & x509.KeyUsageDigitalSignature) == 0 {
|
|
return false
|
|
}
|
|
|
|
// Check that the certificate has the DelegationUsage extension and that
|
|
// it's marked as non-critical (See Section 4.2 of RFC5280).
|
|
for _, extension := range cert.Extensions {
|
|
if extension.Id.Equal(extensionDelegatedCredential) {
|
|
if extension.Critical {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isExpired returns true if the credential has expired. The end of the validity
|
|
// interval is defined as the delegator certificate's notBefore field ('start')
|
|
// plus dc.cred.validTime seconds. This function simply checks that the current time
|
|
// ('now') is before the end of the validity interval.
|
|
func (dc *DelegatedCredential) isExpired(start, now time.Time) bool {
|
|
end := start.Add(dc.cred.validTime)
|
|
return !now.Before(end)
|
|
}
|
|
|
|
// invalidTTL returns true if the credential's validity period is longer than the
|
|
// maximum permitted. This is defined by the certificate's notBefore field
|
|
// ('start') plus the dc.validTime, minus the current time ('now').
|
|
func (dc *DelegatedCredential) invalidTTL(start, now time.Time) bool {
|
|
return dc.cred.validTime > (now.Sub(start) + dcMaxTTL).Round(time.Second)
|
|
}
|
|
|
|
// credential stores the public components of a Delegated Credential.
|
|
type credential struct {
|
|
// The amount of time for which the credential is valid. Specifically, the
|
|
// the credential expires 'validTime' seconds after the 'notBefore' of the
|
|
// delegation certificate. The delegator shall not issue Delegated
|
|
// Credentials that are valid for more than 7 days from the current time.
|
|
//
|
|
// When this data structure is serialized, this value is converted to a
|
|
// uint32 representing the duration in seconds.
|
|
validTime time.Duration
|
|
// The signature scheme associated with the credential public key.
|
|
// This is expected to be the same as the CertificateVerify.algorithm
|
|
// sent by the client or server.
|
|
expCertVerfAlgo SignatureScheme
|
|
// The credential's public key.
|
|
publicKey crypto.PublicKey
|
|
}
|
|
|
|
// DelegatedCredential stores a Delegated Credential with the credential and its
|
|
// signature.
|
|
type DelegatedCredential struct {
|
|
// The serialized form of the Delegated Credential.
|
|
raw []byte
|
|
|
|
// Cred stores the public components of a Delegated Credential.
|
|
cred *credential
|
|
|
|
// The signature scheme used to sign the Delegated Credential.
|
|
algorithm SignatureScheme
|
|
|
|
// The Credential's delegation: a signature that binds the credential to
|
|
// the end-entity certificate's public key.
|
|
signature []byte
|
|
}
|
|
|
|
// marshalPublicKeyInfo returns a DER encoded PublicKeyInfo
|
|
// from a Delegated Credential (as defined in the X.509 standard).
|
|
// The following key types are currently supported: *ecdsa.PublicKey
|
|
// and ed25519.PublicKey. Unsupported key types result in an error.
|
|
// rsa.PublicKey is not supported as defined by the draft.
|
|
func (cred *credential) marshalPublicKeyInfo() ([]byte, error) {
|
|
switch cred.expCertVerfAlgo {
|
|
case ECDSAWithP256AndSHA256,
|
|
ECDSAWithP384AndSHA384,
|
|
ECDSAWithP521AndSHA512,
|
|
Ed25519:
|
|
rawPub, err := x509.MarshalPKIXPublicKey(cred.publicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rawPub, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("tls: unsupported signature scheme: 0x%04x", cred.expCertVerfAlgo)
|
|
}
|
|
}
|
|
|
|
// marshal encodes the credential struct of the Delegated Credential.
|
|
func (cred *credential) marshal() ([]byte, error) {
|
|
var b cryptobyte.Builder
|
|
|
|
b.AddUint32(uint32(cred.validTime / time.Second))
|
|
b.AddUint16(uint16(cred.expCertVerfAlgo))
|
|
|
|
// Encode the public key
|
|
rawPub, err := cred.marshalPublicKeyInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Assert that the public key encoding is no longer than 2^24-1 bytes.
|
|
if len(rawPub) > dcMaxPubLen {
|
|
return nil, errors.New("tls: public key length exceeds 2^24-1 limit")
|
|
}
|
|
|
|
b.AddUint24(uint32(len(rawPub)))
|
|
b.AddBytes(rawPub)
|
|
|
|
raw := b.BytesOrPanic()
|
|
return raw, nil
|
|
}
|
|
|
|
// unmarshalCredential decodes serialized bytes and returns a credential, if possible.
|
|
func unmarshalCredential(raw []byte) (*credential, error) {
|
|
if len(raw) < 10 {
|
|
return nil, errors.New("tls: Delegated Credential is not valid: invalid length")
|
|
}
|
|
|
|
s := cryptobyte.String(raw)
|
|
var t uint32
|
|
if !s.ReadUint32(&t) {
|
|
return nil, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
validTime := time.Duration(t) * time.Second
|
|
|
|
var pubAlgo uint16
|
|
if !s.ReadUint16(&pubAlgo) {
|
|
return nil, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
algo := SignatureScheme(pubAlgo)
|
|
|
|
var pubLen uint32
|
|
s.ReadUint24(&pubLen)
|
|
|
|
pubKey, err := x509.ParsePKIXPublicKey(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &credential{validTime, algo, pubKey}, nil
|
|
}
|
|
|
|
// getCredentialLen returns the number of bytes comprising the serialized
|
|
// credential struct inside the Delegated Credential.
|
|
func getCredentialLen(raw []byte) (int, error) {
|
|
if len(raw) < 10 {
|
|
return 0, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
var read []byte
|
|
s := cryptobyte.String(raw)
|
|
s.ReadBytes(&read, 6)
|
|
|
|
var pubLen uint32
|
|
s.ReadUint24(&pubLen)
|
|
if !(pubLen > 0) {
|
|
return 0, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
raw = raw[6:]
|
|
if len(raw) < int(pubLen) {
|
|
return 0, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
return 9 + int(pubLen), nil
|
|
}
|
|
|
|
// getHash maps the SignatureScheme to its corresponding hash function.
|
|
func getHash(scheme SignatureScheme) crypto.Hash {
|
|
switch scheme {
|
|
case ECDSAWithP256AndSHA256:
|
|
return crypto.SHA256
|
|
case ECDSAWithP384AndSHA384:
|
|
return crypto.SHA384
|
|
case ECDSAWithP521AndSHA512:
|
|
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
|
|
}
|
|
}
|
|
|
|
// getECDSACurve maps the SignatureScheme to its corresponding ecdsa elliptic.Curve.
|
|
func getECDSACurve(scheme SignatureScheme) elliptic.Curve {
|
|
switch scheme {
|
|
case ECDSAWithP256AndSHA256:
|
|
return elliptic.P256()
|
|
case ECDSAWithP384AndSHA384:
|
|
return elliptic.P384()
|
|
case ECDSAWithP521AndSHA512:
|
|
return elliptic.P521()
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// prepareDelegationSignatureInput returns the message that the delegator is going to sign.
|
|
func prepareDelegationSignatureInput(hash crypto.Hash, cred *credential, dCert []byte, algo SignatureScheme, isClient bool) ([]byte, error) {
|
|
header := make([]byte, 64)
|
|
for i := range header {
|
|
header[i] = 0x20
|
|
}
|
|
|
|
var context string
|
|
if !isClient {
|
|
context = "TLS, server delegated credentials\x00"
|
|
} else {
|
|
context = "TLS, client delegated credentials\x00"
|
|
}
|
|
|
|
rawCred, err := cred.marshal()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rawAlgo [2]byte
|
|
binary.BigEndian.PutUint16(rawAlgo[:], uint16(algo))
|
|
|
|
if hash == directSigning {
|
|
b := &bytes.Buffer{}
|
|
b.Write(header)
|
|
io.WriteString(b, context)
|
|
b.Write(dCert)
|
|
b.Write(rawCred)
|
|
b.Write(rawAlgo[:])
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
h := hash.New()
|
|
h.Write(header)
|
|
io.WriteString(h, context)
|
|
h.Write(dCert)
|
|
h.Write(rawCred)
|
|
h.Write(rawAlgo[:])
|
|
return h.Sum(nil), nil
|
|
}
|
|
|
|
// Extract the algorithm used to sign the Delegated Credential from the
|
|
// end-entity (leaf) certificate.
|
|
func getSignatureAlgorithm(cert *Certificate) (SignatureScheme, error) {
|
|
switch sk := cert.PrivateKey.(type) {
|
|
case *ecdsa.PrivateKey:
|
|
pk := sk.Public().(*ecdsa.PublicKey)
|
|
curveName := pk.Curve.Params().Name
|
|
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 undefinedSignatureScheme, fmt.Errorf("using curve %s for %s is not supported", curveName, cert.Leaf.SignatureAlgorithm)
|
|
}
|
|
case ed25519.PrivateKey:
|
|
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
|
|
default:
|
|
return undefinedSignatureScheme, fmt.Errorf("tls: unsupported algorithm for signing Delegated Credential")
|
|
}
|
|
}
|
|
|
|
// NewDelegatedCredential creates a new Delegated Credential using 'cert' for
|
|
// delegation, depending if the caller is the client or the server (defined by
|
|
// 'isClient'). It generates a public/private key pair for the provided signature
|
|
// algorithm ('pubAlgo') and it defines a validity interval (defined
|
|
// by 'cert.Leaf.notBefore' and 'validTime'). It signs the Delegated Credential
|
|
// using 'cert.PrivateKey'.
|
|
func NewDelegatedCredential(cert *Certificate, pubAlgo SignatureScheme, validTime time.Duration, isClient bool) (*DelegatedCredential, crypto.PrivateKey, error) {
|
|
// The granularity of DC validity is seconds.
|
|
validTime = validTime.Round(time.Second)
|
|
|
|
// Parse the leaf certificate if needed.
|
|
var err error
|
|
if cert.Leaf == nil {
|
|
if len(cert.Certificate[0]) == 0 {
|
|
return nil, nil, errors.New("tls: missing leaf certificate for Delegated Credential")
|
|
}
|
|
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
// Check that the leaf certificate can be used for delegation.
|
|
if !isValidForDelegation(cert.Leaf) {
|
|
return nil, nil, errors.New("tls: certificate not authorized for delegation")
|
|
}
|
|
|
|
sigAlgo, err := getSignatureAlgorithm(cert)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Generate the Delegated Credential key pair based on the provided scheme
|
|
var privK crypto.PrivateKey
|
|
var pubK crypto.PublicKey
|
|
switch pubAlgo {
|
|
case ECDSAWithP256AndSHA256,
|
|
ECDSAWithP384AndSHA384,
|
|
ECDSAWithP521AndSHA512:
|
|
privK, err = ecdsa.GenerateKey(getECDSACurve(pubAlgo), rand.Reader)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
pubK = privK.(*ecdsa.PrivateKey).Public()
|
|
case Ed25519:
|
|
pubK, privK, err = ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
default:
|
|
return nil, nil, fmt.Errorf("tls: unsupported algorithm for Delegated Credential: %s", pubAlgo)
|
|
}
|
|
|
|
// Prepare the credential for signing
|
|
hash := getHash(sigAlgo)
|
|
credential := &credential{validTime, pubAlgo, pubK}
|
|
values, err := prepareDelegationSignatureInput(hash, credential, cert.Leaf.Raw, sigAlgo, isClient)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var sig []byte
|
|
switch sk := cert.PrivateKey.(type) {
|
|
case *ecdsa.PrivateKey:
|
|
opts := crypto.SignerOpts(hash)
|
|
sig, err = sk.Sign(rand.Reader, values, opts)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
case ed25519.PrivateKey:
|
|
opts := crypto.SignerOpts(hash)
|
|
sig, err = sk.Sign(rand.Reader, values, opts)
|
|
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")
|
|
}
|
|
|
|
if len(sig) > dcMaxSignatureLen {
|
|
return nil, nil, errors.New("tls: unable to create a Delegated Credential")
|
|
}
|
|
|
|
return &DelegatedCredential{
|
|
cred: credential,
|
|
algorithm: sigAlgo,
|
|
signature: sig,
|
|
}, privK, nil
|
|
}
|
|
|
|
// Validate validates the Delegated Credential by checking that the signature is
|
|
// valid, that it hasn't expired, and that the TTL is valid. It also checks that
|
|
// certificate can be used for delegation.
|
|
func (dc *DelegatedCredential) Validate(cert *x509.Certificate, isClient bool, now time.Time, certVerifyMsg *certificateVerifyMsg) bool {
|
|
if dc.isExpired(cert.NotBefore, now) {
|
|
return false
|
|
}
|
|
|
|
if dc.invalidTTL(cert.NotBefore, now) {
|
|
return false
|
|
}
|
|
|
|
if dc.cred.expCertVerfAlgo != certVerifyMsg.signatureAlgorithm {
|
|
return false
|
|
}
|
|
|
|
if !isValidForDelegation(cert) {
|
|
return false
|
|
}
|
|
|
|
hash := getHash(dc.algorithm)
|
|
in, err := prepareDelegationSignatureInput(hash, dc.cred, cert.Raw, dc.algorithm, isClient)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
switch dc.algorithm {
|
|
case ECDSAWithP256AndSHA256,
|
|
ECDSAWithP384AndSHA384,
|
|
ECDSAWithP521AndSHA512:
|
|
pk, ok := cert.PublicKey.(*ecdsa.PublicKey)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return ecdsa.VerifyASN1(pk, in, dc.signature)
|
|
case Ed25519:
|
|
pk, ok := cert.PublicKey.(ed25519.PublicKey)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
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
|
|
// encoding.
|
|
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 {
|
|
return nil, err
|
|
}
|
|
|
|
var b cryptobyte.Builder
|
|
b.AddBytes(raw)
|
|
b.AddUint16(uint16(dc.algorithm))
|
|
b.AddUint16(uint16(len(dc.signature)))
|
|
b.AddBytes(dc.signature)
|
|
|
|
dc.raw = b.BytesOrPanic()
|
|
return dc.raw, nil
|
|
}
|
|
|
|
// UnmarshalDelegatedCredential decodes a DelegatedCredential structure.
|
|
func UnmarshalDelegatedCredential(raw []byte) (*DelegatedCredential, error) {
|
|
rawCredentialLen, err := getCredentialLen(raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
credential, err := unmarshalCredential(raw[:rawCredentialLen])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
raw = raw[rawCredentialLen:]
|
|
if len(raw) < 4 {
|
|
return nil, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
s := cryptobyte.String(raw)
|
|
|
|
var algo uint16
|
|
if !s.ReadUint16(&algo) {
|
|
return nil, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
var rawSignatureLen uint16
|
|
if !s.ReadUint16(&rawSignatureLen) {
|
|
return nil, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
var sig []byte
|
|
if !s.ReadBytes(&sig, int(rawSignatureLen)) {
|
|
return nil, errors.New("tls: Delegated Credential is not valid")
|
|
}
|
|
|
|
return &DelegatedCredential{
|
|
cred: credential,
|
|
algorithm: SignatureScheme(algo),
|
|
signature: sig,
|
|
}, nil
|
|
}
|