github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/x509/ct/types.go (about)

     1  package ct
     2  
     3  // This file contains selectively chosen snippets of
     4  // github.com/google/certificate-transparency-go@ 5cfe585726ad9d990d4db524d6ce2567b13e2f80
     5  //
     6  // These snippets only perform deserialization for SCTs and are recreated here to prevent pulling in the whole of the ct
     7  // which contains yet another version of x509,asn1 and tls
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/sha256"
    12  	"encoding/base64"
    13  	"encoding/json"
    14  	"fmt"
    15  )
    16  
    17  // CTExtensions is a representation of the raw bytes of any CtExtension
    18  // structure (see section 3.2)
    19  type CTExtensions []byte
    20  
    21  // SHA256Hash represents the output from the SHA256 hash function.
    22  type SHA256Hash [sha256.Size]byte
    23  
    24  // FromBase64String populates the SHA256 struct with the contents of the base64 data passed in.
    25  func (s *SHA256Hash) FromBase64String(b64 string) error {
    26  	bs, err := base64.StdEncoding.DecodeString(b64)
    27  	if err != nil {
    28  		return fmt.Errorf("failed to unbase64 LogID: %v", err)
    29  	}
    30  	if len(bs) != sha256.Size {
    31  		return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs))
    32  	}
    33  	copy(s[:], bs)
    34  	return nil
    35  }
    36  
    37  // Base64String returns the base64 representation of this SHA256Hash.
    38  func (s SHA256Hash) Base64String() string {
    39  	return base64.StdEncoding.EncodeToString(s[:])
    40  }
    41  
    42  // MarshalJSON implements the json.Marshaller interface for SHA256Hash.
    43  func (s SHA256Hash) MarshalJSON() ([]byte, error) {
    44  	return []byte(`"` + s.Base64String() + `"`), nil
    45  }
    46  
    47  // UnmarshalJSON implements the json.Unmarshaller interface.
    48  func (s *SHA256Hash) UnmarshalJSON(b []byte) error {
    49  	var content string
    50  	if err := json.Unmarshal(b, &content); err != nil {
    51  		return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err)
    52  	}
    53  	return s.FromBase64String(content)
    54  }
    55  
    56  // HashAlgorithm from the DigitallySigned struct
    57  type HashAlgorithm byte
    58  
    59  // HashAlgorithm constants
    60  const (
    61  	None   HashAlgorithm = 0
    62  	MD5    HashAlgorithm = 1
    63  	SHA1   HashAlgorithm = 2
    64  	SHA224 HashAlgorithm = 3
    65  	SHA256 HashAlgorithm = 4
    66  	SHA384 HashAlgorithm = 5
    67  	SHA512 HashAlgorithm = 6
    68  )
    69  
    70  func (h HashAlgorithm) String() string {
    71  	switch h {
    72  	case None:
    73  		return "None"
    74  	case MD5:
    75  		return "MD5"
    76  	case SHA1:
    77  		return "SHA1"
    78  	case SHA224:
    79  		return "SHA224"
    80  	case SHA256:
    81  		return "SHA256"
    82  	case SHA384:
    83  		return "SHA384"
    84  	case SHA512:
    85  		return "SHA512"
    86  	default:
    87  		return fmt.Sprintf("UNKNOWN(%d)", h)
    88  	}
    89  }
    90  
    91  // SignatureAlgorithm from the the DigitallySigned struct
    92  type SignatureAlgorithm byte
    93  
    94  // SignatureAlgorithm constants
    95  const (
    96  	Anonymous SignatureAlgorithm = 0
    97  	RSA       SignatureAlgorithm = 1
    98  	DSA       SignatureAlgorithm = 2
    99  	ECDSA     SignatureAlgorithm = 3
   100  )
   101  
   102  func (s SignatureAlgorithm) String() string {
   103  	switch s {
   104  	case Anonymous:
   105  		return "Anonymous"
   106  	case RSA:
   107  		return "RSA"
   108  	case DSA:
   109  		return "DSA"
   110  	case ECDSA:
   111  		return "ECDSA"
   112  	default:
   113  		return fmt.Sprintf("UNKNOWN(%d)", s)
   114  	}
   115  }
   116  
   117  // DigitallySigned represents an RFC5246 DigitallySigned structure
   118  type DigitallySigned struct {
   119  	HashAlgorithm      HashAlgorithm
   120  	SignatureAlgorithm SignatureAlgorithm
   121  	Signature          []byte
   122  }
   123  
   124  // FromBase64String populates the DigitallySigned structure from the base64 data passed in.
   125  // Returns an error if the base64 data is invalid.
   126  func (d *DigitallySigned) FromBase64String(b64 string) error {
   127  	raw, err := base64.StdEncoding.DecodeString(b64)
   128  	if err != nil {
   129  		return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err)
   130  	}
   131  	ds, err := UnmarshalDigitallySigned(bytes.NewReader(raw))
   132  	if err != nil {
   133  		return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
   134  	}
   135  	*d = *ds
   136  	return nil
   137  }
   138  
   139  // Base64String returns the base64 representation of the DigitallySigned struct.
   140  func (d DigitallySigned) Base64String() (string, error) {
   141  	b, err := MarshalDigitallySigned(d)
   142  	if err != nil {
   143  		return "", err
   144  	}
   145  	return base64.StdEncoding.EncodeToString(b), nil
   146  }
   147  
   148  // MarshalJSON implements the json.Marshaller interface.
   149  func (d DigitallySigned) MarshalJSON() ([]byte, error) {
   150  	b64, err := d.Base64String()
   151  	if err != nil {
   152  		return []byte{}, err
   153  	}
   154  	return []byte(`"` + b64 + `"`), nil
   155  }
   156  
   157  // UnmarshalJSON implements the json.Unmarshaler interface.
   158  func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
   159  	var content string
   160  	if err := json.Unmarshal(b, &content); err != nil {
   161  		return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
   162  	}
   163  	return d.FromBase64String(content)
   164  }
   165  
   166  // Version represents the Version enum from section 3.2 of the RFC:
   167  // enum { v1(0), (255) } Version;
   168  type Version uint8
   169  
   170  func (v Version) String() string {
   171  	switch v {
   172  	case V1:
   173  		return "V1"
   174  	default:
   175  		return fmt.Sprintf("UnknownVersion(%d)", v)
   176  	}
   177  }
   178  
   179  // CT Version constants, see section 3.2 of the RFC.
   180  const (
   181  	V1 Version = 0
   182  )
   183  
   184  // SignedCertificateTimestamp represents the structure returned by the
   185  // add-chain and add-pre-chain methods after base64 decoding. (see RFC sections
   186  // 3.2 ,4.1 and 4.2)
   187  type SignedCertificateTimestamp struct {
   188  	SCTVersion Version    `json:"version"` // The version of the protocol to which the SCT conforms
   189  	LogID      SHA256Hash `json:"log_id"`  // the SHA-256 hash of the log's public key, calculated over
   190  	// the DER encoding of the key represented as SubjectPublicKeyInfo.
   191  	Timestamp  uint64          `json:"timestamp,omitempty"`  // Timestamp (in ms since unix epoc) at which the SCT was issued. NOTE: When this is serialized, the output is in seconds, not milliseconds.
   192  	Extensions CTExtensions    `json:"extensions,omitempty"` // For future extensions to the protocol
   193  	Signature  DigitallySigned `json:"signature"`            // The Log's signature for this SCT
   194  }
   195  
   196  // Copied from ct/types.go 2018/06/15 to deal with BQ timestamp overflow; output
   197  // is expected to be seconds, not milliseconds.
   198  type auxSignedCertificateTimestamp SignedCertificateTimestamp
   199  
   200  const kMaxTimestamp = 253402300799
   201  
   202  // MarshalJSON implements the JSON.Marshaller interface.
   203  func (sct *SignedCertificateTimestamp) MarshalJSON() ([]byte, error) {
   204  	aux := auxSignedCertificateTimestamp(*sct)
   205  	aux.Timestamp = sct.Timestamp / 1000 // convert ms to sec
   206  	if aux.Timestamp > kMaxTimestamp {
   207  		aux.Timestamp = 0
   208  	}
   209  	return json.Marshal(&aux)
   210  }
   211  
   212  type sctError int
   213  
   214  // Preallocate errors for performance
   215  var (
   216  	ErrInvalidVersion  error = sctError(1)
   217  	ErrNotEnoughBuffer error = sctError(2)
   218  )
   219  
   220  func (e sctError) Error() string {
   221  	switch e {
   222  	case ErrInvalidVersion:
   223  		return "invalid SCT version detected"
   224  	case ErrNotEnoughBuffer:
   225  		return "provided buffer was too small"
   226  	default:
   227  		return "unknown error"
   228  	}
   229  }