github.com/MetalBlockchain/metalgo@v1.11.9/staking/parse.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package staking
     5  
     6  import (
     7  	"crypto"
     8  	"crypto/ecdsa"
     9  	"crypto/elliptic"
    10  	"crypto/rsa"
    11  	"encoding/asn1"
    12  	"errors"
    13  	"fmt"
    14  	"math/big"
    15  
    16  	"golang.org/x/crypto/cryptobyte"
    17  
    18  	"github.com/MetalBlockchain/metalgo/utils/units"
    19  
    20  	cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
    21  )
    22  
    23  const (
    24  	MaxCertificateLen = 2 * units.KiB
    25  
    26  	allowedRSASmallModulusLen     = 2048
    27  	allowedRSALargeModulusLen     = 4096
    28  	allowedRSAPublicExponentValue = 65537
    29  )
    30  
    31  var (
    32  	ErrCertificateTooLarge                   = fmt.Errorf("staking: certificate length is greater than %d", MaxCertificateLen)
    33  	ErrMalformedCertificate                  = errors.New("staking: malformed certificate")
    34  	ErrMalformedTBSCertificate               = errors.New("staking: malformed tbs certificate")
    35  	ErrMalformedVersion                      = errors.New("staking: malformed version")
    36  	ErrMalformedSerialNumber                 = errors.New("staking: malformed serial number")
    37  	ErrMalformedSignatureAlgorithmIdentifier = errors.New("staking: malformed signature algorithm identifier")
    38  	ErrMalformedIssuer                       = errors.New("staking: malformed issuer")
    39  	ErrMalformedValidity                     = errors.New("staking: malformed validity")
    40  	ErrMalformedSPKI                         = errors.New("staking: malformed spki")
    41  	ErrMalformedPublicKeyAlgorithmIdentifier = errors.New("staking: malformed public key algorithm identifier")
    42  	ErrMalformedSubjectPublicKey             = errors.New("staking: malformed subject public key")
    43  	ErrMalformedOID                          = errors.New("staking: malformed oid")
    44  	ErrInvalidRSAPublicKey                   = errors.New("staking: invalid RSA public key")
    45  	ErrInvalidRSAModulus                     = errors.New("staking: invalid RSA modulus")
    46  	ErrInvalidRSAPublicExponent              = errors.New("staking: invalid RSA public exponent")
    47  	ErrRSAModulusNotPositive                 = errors.New("staking: RSA modulus is not a positive number")
    48  	ErrUnsupportedRSAModulusBitLen           = errors.New("staking: unsupported RSA modulus bitlen")
    49  	ErrRSAModulusIsEven                      = errors.New("staking: RSA modulus is an even number")
    50  	ErrUnsupportedRSAPublicExponent          = errors.New("staking: unsupported RSA public exponent")
    51  	ErrFailedUnmarshallingEllipticCurvePoint = errors.New("staking: failed to unmarshal elliptic curve point")
    52  	ErrUnknownPublicKeyAlgorithm             = errors.New("staking: unknown public key algorithm")
    53  )
    54  
    55  // ParseCertificate parses a single certificate from the given ASN.1.
    56  //
    57  // This function does not validate that the certificate is valid to be used
    58  // against normal TLS implementations.
    59  //
    60  // Ref: https://github.com/golang/go/blob/go1.19.12/src/crypto/x509/parser.go#L789-L968
    61  func ParseCertificate(bytes []byte) (*Certificate, error) {
    62  	if len(bytes) > MaxCertificateLen {
    63  		return nil, ErrCertificateTooLarge
    64  	}
    65  
    66  	input := cryptobyte.String(bytes)
    67  	// Consume the length and tag bytes.
    68  	if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
    69  		return nil, ErrMalformedCertificate
    70  	}
    71  
    72  	// Read the "to be signed" certificate into input.
    73  	if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
    74  		return nil, ErrMalformedTBSCertificate
    75  	}
    76  	if !input.SkipOptionalASN1(cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) {
    77  		return nil, ErrMalformedVersion
    78  	}
    79  	if !input.SkipASN1(cryptobyte_asn1.INTEGER) {
    80  		return nil, ErrMalformedSerialNumber
    81  	}
    82  	if !input.SkipASN1(cryptobyte_asn1.SEQUENCE) {
    83  		return nil, ErrMalformedSignatureAlgorithmIdentifier
    84  	}
    85  	if !input.SkipASN1(cryptobyte_asn1.SEQUENCE) {
    86  		return nil, ErrMalformedIssuer
    87  	}
    88  	if !input.SkipASN1(cryptobyte_asn1.SEQUENCE) {
    89  		return nil, ErrMalformedValidity
    90  	}
    91  	if !input.SkipASN1(cryptobyte_asn1.SEQUENCE) {
    92  		return nil, ErrMalformedIssuer
    93  	}
    94  
    95  	// Read the "subject public key info" into input.
    96  	if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
    97  		return nil, ErrMalformedSPKI
    98  	}
    99  
   100  	// Read the public key algorithm identifier.
   101  	var pkAISeq cryptobyte.String
   102  	if !input.ReadASN1(&pkAISeq, cryptobyte_asn1.SEQUENCE) {
   103  		return nil, ErrMalformedPublicKeyAlgorithmIdentifier
   104  	}
   105  	var pkAI asn1.ObjectIdentifier
   106  	if !pkAISeq.ReadASN1ObjectIdentifier(&pkAI) {
   107  		return nil, ErrMalformedOID
   108  	}
   109  
   110  	// Note: Unlike the x509 package, we require parsing the public key.
   111  
   112  	var spk asn1.BitString
   113  	if !input.ReadASN1BitString(&spk) {
   114  		return nil, ErrMalformedSubjectPublicKey
   115  	}
   116  	publicKey, err := parsePublicKey(pkAI, spk)
   117  	return &Certificate{
   118  		Raw:       bytes,
   119  		PublicKey: publicKey,
   120  	}, err
   121  }
   122  
   123  // Ref: https://github.com/golang/go/blob/go1.19.12/src/crypto/x509/parser.go#L215-L306
   124  func parsePublicKey(oid asn1.ObjectIdentifier, publicKey asn1.BitString) (crypto.PublicKey, error) {
   125  	der := cryptobyte.String(publicKey.RightAlign())
   126  	switch {
   127  	case oid.Equal(oidPublicKeyRSA):
   128  		pub := &rsa.PublicKey{N: new(big.Int)}
   129  		if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
   130  			return nil, ErrInvalidRSAPublicKey
   131  		}
   132  		if !der.ReadASN1Integer(pub.N) {
   133  			return nil, ErrInvalidRSAModulus
   134  		}
   135  		if !der.ReadASN1Integer(&pub.E) {
   136  			return nil, ErrInvalidRSAPublicExponent
   137  		}
   138  
   139  		if pub.N.Sign() <= 0 {
   140  			return nil, ErrRSAModulusNotPositive
   141  		}
   142  
   143  		if bitLen := pub.N.BitLen(); bitLen != allowedRSALargeModulusLen && bitLen != allowedRSASmallModulusLen {
   144  			return nil, fmt.Errorf("%w: %d", ErrUnsupportedRSAModulusBitLen, bitLen)
   145  		}
   146  		if pub.N.Bit(0) == 0 {
   147  			return nil, ErrRSAModulusIsEven
   148  		}
   149  		if pub.E != allowedRSAPublicExponentValue {
   150  			return nil, fmt.Errorf("%w: %d", ErrUnsupportedRSAPublicExponent, pub.E)
   151  		}
   152  		return pub, nil
   153  	case oid.Equal(oidPublicKeyECDSA):
   154  		namedCurve := elliptic.P256()
   155  		x, y := elliptic.Unmarshal(namedCurve, der)
   156  		if x == nil {
   157  			return nil, ErrFailedUnmarshallingEllipticCurvePoint
   158  		}
   159  		return &ecdsa.PublicKey{
   160  			Curve: namedCurve,
   161  			X:     x,
   162  			Y:     y,
   163  		}, nil
   164  	default:
   165  		return nil, ErrUnknownPublicKeyAlgorithm
   166  	}
   167  }