...

Source file src/github.com/letsencrypt/boulder/precert/corr.go

Documentation: github.com/letsencrypt/boulder/precert

     1  package precert
     2  
     3  import (
     4  	"bytes"
     5  	encoding_asn1 "encoding/asn1"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"golang.org/x/crypto/cryptobyte"
    10  	"golang.org/x/crypto/cryptobyte/asn1"
    11  )
    12  
    13  // Correspond returns nil if the two certificates are a valid precertificate/final certificate pair.
    14  // Order of the arguments matters: the precertificate is first and the final certificate is second.
    15  // Note that RFC 6962 allows the precertificate and final certificate to have different Issuers, but
    16  // this function rejects such pairs.
    17  func Correspond(precertDER, finalDER []byte) error {
    18  	preTBS, err := tbsDERFromCertDER(precertDER)
    19  	if err != nil {
    20  		return fmt.Errorf("parsing precert: %w", err)
    21  	}
    22  
    23  	finalTBS, err := tbsDERFromCertDER(finalDER)
    24  	if err != nil {
    25  		return fmt.Errorf("parsing final cert: %w", err)
    26  	}
    27  
    28  	// The first 7 fields of TBSCertificate must be byte-for-byte identical.
    29  	// The next 2 fields (issuerUniqueID and subjectUniqueID) are forbidden
    30  	// by the Baseline Requirements so we assume they are not present (if they
    31  	// are, they will fail the next check, for extensions).
    32  	// https://datatracker.ietf.org/doc/html/rfc5280#page-117
    33  	// TBSCertificate  ::=  SEQUENCE  {
    34  	//      version         [0]  Version DEFAULT v1,
    35  	//      serialNumber         CertificateSerialNumber,
    36  	//      signature            AlgorithmIdentifier,
    37  	//      issuer               Name,
    38  	//      validity             Validity,
    39  	//      subject              Name,
    40  	//      subjectPublicKeyInfo SubjectPublicKeyInfo,
    41  	//      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
    42  	//      					 -- If present, version MUST be v2 or v3
    43  	//      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
    44  	//      					 -- If present, version MUST be v2 or v3
    45  	//      extensions      [3]  Extensions OPTIONAL
    46  	//      					 -- If present, version MUST be v3 --  }
    47  	for i := 0; i < 7; i++ {
    48  		if err := readIdenticalElement(&preTBS, &finalTBS); err != nil {
    49  			return fmt.Errorf("checking for identical field %d: %w", i, err)
    50  		}
    51  	}
    52  
    53  	// The extensions should be mostly the same, with these exceptions:
    54  	//  - The precertificate should have exactly one precertificate poison extension
    55  	//    not present in the final certificate.
    56  	//  - The final certificate should have exactly one SCTList extension not present
    57  	//    in the precertificate.
    58  	//  - As a consequence, the byte lengths of the extensions fields will not be the
    59  	//    same, so we ignore the lengths (so long as they parse)
    60  	precertExtensionBytes, err := unwrapExtensions(preTBS)
    61  	if err != nil {
    62  		return fmt.Errorf("parsing precert extensions: %w", err)
    63  	}
    64  
    65  	finalCertExtensionBytes, err := unwrapExtensions(finalTBS)
    66  	if err != nil {
    67  		return fmt.Errorf("parsing final cert extensions: %w", err)
    68  	}
    69  
    70  	precertParser := extensionParser{bytes: precertExtensionBytes, skippableOID: poisonOID}
    71  	finalCertParser := extensionParser{bytes: finalCertExtensionBytes, skippableOID: sctListOID}
    72  
    73  	for i := 0; ; i++ {
    74  		precertExtn, err := precertParser.Next()
    75  		if err != nil {
    76  			return err
    77  		}
    78  
    79  		finalCertExtn, err := finalCertParser.Next()
    80  		if err != nil {
    81  			return err
    82  		}
    83  
    84  		if !bytes.Equal(precertExtn, finalCertExtn) {
    85  			return fmt.Errorf("precert extension %d (%x) not equal to final cert extension %d (%x)",
    86  				i+precertParser.skipped, precertExtn, i+finalCertParser.skipped, finalCertExtn)
    87  		}
    88  
    89  		if precertExtn == nil && finalCertExtn == nil {
    90  			break
    91  		}
    92  	}
    93  
    94  	if precertParser.skipped == 0 {
    95  		return fmt.Errorf("no poison extension found in precert")
    96  	}
    97  	if precertParser.skipped > 1 {
    98  		return fmt.Errorf("multiple poison extensions found in precert")
    99  	}
   100  	if finalCertParser.skipped == 0 {
   101  		return fmt.Errorf("no SCTList extension found in final cert")
   102  	}
   103  	if finalCertParser.skipped > 1 {
   104  		return fmt.Errorf("multiple SCTList extensions found in final cert")
   105  	}
   106  	return nil
   107  }
   108  
   109  var poisonOID = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
   110  var sctListOID = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
   111  
   112  // extensionParser takes a sequence of bytes representing the inner bytes of the
   113  // `extensions` field. Repeated calls to Next() will return all the extensions
   114  // except those that match the skippableOID. The skipped extensions will be
   115  // counted in `skipped`.
   116  type extensionParser struct {
   117  	skippableOID encoding_asn1.ObjectIdentifier
   118  	bytes        cryptobyte.String
   119  	skipped      int
   120  }
   121  
   122  // Next returns the next extension in the sequence, skipping (and counting)
   123  // any extension that matches the skippableOID.
   124  // Returns nil, nil when there are no more extensions.
   125  func (e *extensionParser) Next() (cryptobyte.String, error) {
   126  	if e.bytes.Empty() {
   127  		return nil, nil
   128  	}
   129  
   130  	var next cryptobyte.String
   131  	if !e.bytes.ReadASN1(&next, asn1.SEQUENCE) {
   132  		return nil, fmt.Errorf("failed to parse extension")
   133  	}
   134  
   135  	var oid encoding_asn1.ObjectIdentifier
   136  	nextCopy := next
   137  	if !nextCopy.ReadASN1ObjectIdentifier(&oid) {
   138  		return nil, fmt.Errorf("failed to parse extension OID")
   139  	}
   140  
   141  	if oid.Equal(e.skippableOID) {
   142  		e.skipped++
   143  		return e.Next()
   144  	}
   145  
   146  	return next, nil
   147  }
   148  
   149  // unwrapExtensions takes a given a sequence of bytes representing the `extensions` field
   150  // of a TBSCertificate and parses away the outermost two layers, returning the inner bytes
   151  // of the Extensions SEQUENCE.
   152  //
   153  // https://datatracker.ietf.org/doc/html/rfc5280#page-117
   154  //
   155  //	TBSCertificate  ::=  SEQUENCE  {
   156  //	   ...
   157  //	   extensions      [3]  Extensions OPTIONAL
   158  //	}
   159  //
   160  // Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
   161  func unwrapExtensions(field cryptobyte.String) (cryptobyte.String, error) {
   162  	var extensions cryptobyte.String
   163  	if !field.ReadASN1(&extensions, asn1.Tag(3).Constructed().ContextSpecific()) {
   164  		return nil, errors.New("error reading extensions")
   165  	}
   166  
   167  	var extensionsInner cryptobyte.String
   168  	if !extensions.ReadASN1(&extensionsInner, asn1.SEQUENCE) {
   169  		return nil, errors.New("error reading extensions inner")
   170  	}
   171  
   172  	return extensionsInner, nil
   173  }
   174  
   175  // readIdenticalElement parses a single ASN1 element and returns an error if
   176  // their tags are different or their contents are different.
   177  func readIdenticalElement(a, b *cryptobyte.String) error {
   178  	var aInner, bInner cryptobyte.String
   179  	var aTag, bTag asn1.Tag
   180  	if !a.ReadAnyASN1Element(&aInner, &aTag) {
   181  		return fmt.Errorf("failed to read element from first input")
   182  	}
   183  	if !b.ReadAnyASN1Element(&bInner, &bTag) {
   184  		return fmt.Errorf("failed to read element from first input")
   185  	}
   186  	if aTag != bTag {
   187  		return fmt.Errorf("tags differ: %d != %d", aTag, bTag)
   188  	}
   189  	if !bytes.Equal([]byte(aInner), []byte(bInner)) {
   190  		return fmt.Errorf("elements differ: %x != %x", aInner, bInner)
   191  	}
   192  	return nil
   193  }
   194  
   195  // tbsDERFromCertDER takes a Certificate object encoded as DER, and parses
   196  // away the outermost two SEQUENCEs to get the inner bytes of the TBSCertificate.
   197  //
   198  // https://datatracker.ietf.org/doc/html/rfc5280#page-116
   199  //
   200  //		Certificate  ::=  SEQUENCE  {
   201  //		    tbsCertificate       TBSCertificate,
   202  //		    ...
   203  //
   204  //		TBSCertificate  ::=  SEQUENCE  {
   205  //		    version         [0]  Version DEFAULT v1,
   206  //		    serialNumber         CertificateSerialNumber,
   207  //	     ...
   208  func tbsDERFromCertDER(certDER []byte) (cryptobyte.String, error) {
   209  	var inner cryptobyte.String
   210  	input := cryptobyte.String(certDER)
   211  
   212  	if !input.ReadASN1(&inner, asn1.SEQUENCE) {
   213  		return nil, fmt.Errorf("failed to read outer sequence")
   214  	}
   215  
   216  	var tbsCertificate cryptobyte.String
   217  	if !inner.ReadASN1(&tbsCertificate, asn1.SEQUENCE) {
   218  		return nil, fmt.Errorf("failed to read tbsCertificate")
   219  	}
   220  
   221  	return tbsCertificate, nil
   222  }
   223  

View as plain text