@@ -7,9 +7,12 @@ package core
77
88import (
99 "crypto/x509"
10+ "encoding/asn1"
1011 "encoding/base64"
1112 "encoding/json"
13+ "errors"
1214 "fmt"
15+ "math/big"
1316 "net"
1417 "strings"
1518 "time"
@@ -553,6 +556,95 @@ type OCSPSigningRequest struct {
553556 RevokedAt time.Time
554557}
555558
559+ type SignedCertificateTimestamp struct {
560+ ID int `db:"id"`
561+ // The version of the protocol to which the SCT conforms
562+ SCTVersion uint8 `db:"sctVersion"`
563+ // the SHA-256 hash of the log's public key, calculated over
564+ // the DER encoding of the key represented as SubjectPublicKeyInfo.
565+ LogID string `db:"logID"`
566+ // Timestamp (in ms since unix epoc) at which the SCT was issued
567+ Timestamp uint64 `db:"timestamp"`
568+ // For future extensions to the protocol
569+ Extensions []byte `db:"extensions"`
570+ // The Log's signature for this SCT
571+ Signature []byte `db:"signature"`
572+
573+ // The serial of the certificate this SCT is for
574+ CertificateSerial string `db:"certificateSerial"`
575+
576+ LockCol int64
577+ }
578+
579+ type RPCSignedCertificateTimestamp SignedCertificateTimestamp
580+
581+ type rawSignedCertificateTimestamp struct {
582+ Version uint8 `json:"sct_version"`
583+ LogID string `json:"id"`
584+ Timestamp uint64 `json:"timestamp"`
585+ Signature string `json:"signature"`
586+ Extensions string `json:"extensions"`
587+ }
588+
589+ func (sct * SignedCertificateTimestamp ) UnmarshalJSON (data []byte ) error {
590+ var err error
591+ var rawSCT rawSignedCertificateTimestamp
592+ if err = json .Unmarshal (data , & rawSCT ); err != nil {
593+ return fmt .Errorf ("Failed to unmarshal SCT receipt, %s" , err )
594+ }
595+ sct .LogID = rawSCT .LogID
596+ if err != nil {
597+ return fmt .Errorf ("Failed to decode log ID, %s" , err )
598+ }
599+ sct .Signature , err = base64 .StdEncoding .DecodeString (rawSCT .Signature )
600+ if err != nil {
601+ return fmt .Errorf ("Failed to decode SCT signature, %s" , err )
602+ }
603+ sct .Extensions , err = base64 .StdEncoding .DecodeString (rawSCT .Extensions )
604+ if err != nil {
605+ return fmt .Errorf ("Failed to decode SCT extensions, %s" , err )
606+ }
607+ sct .SCTVersion = rawSCT .Version
608+ sct .Timestamp = rawSCT .Timestamp
609+ return nil
610+ }
611+
612+ const (
613+ sctHashSHA256 = 4
614+ sctSigECDSA = 3
615+ )
616+
617+ // CheckSignature validates that the returned SCT signature is a valid SHA256 +
618+ // ECDSA signature but does not verify that a specific public key signed it.
619+ func (sct * SignedCertificateTimestamp ) CheckSignature () error {
620+ if len (sct .Signature ) < 4 {
621+ return errors .New ("SCT signature is truncated" )
622+ }
623+ // Since all of the known logs currently only use SHA256 hashes and ECDSA
624+ // keys, only allow those
625+ if sct .Signature [0 ] != sctHashSHA256 {
626+ return fmt .Errorf ("Unsupported SCT hash function [%d]" , sct .Signature [0 ])
627+ }
628+ if sct .Signature [1 ] != sctSigECDSA {
629+ return fmt .Errorf ("Unsupported SCT signature algorithm [%d]" , sct .Signature [1 ])
630+ }
631+
632+ var ecdsaSig struct {
633+ R , S * big.Int
634+ }
635+ // Ignore the two length bytes and attempt to unmarshal the signature directly
636+ signatureBytes := sct .Signature [4 :]
637+ signatureBytes , err := asn1 .Unmarshal (signatureBytes , & ecdsaSig )
638+ if err != nil {
639+ return fmt .Errorf ("Failed to parse SCT signature, %s" , err )
640+ }
641+ if len (signatureBytes ) > 0 {
642+ return fmt .Errorf ("Trailing garbage after signature" )
643+ }
644+
645+ return nil
646+ }
647+
556648// RevocationCode is used to specify a certificate revocation reason
557649type RevocationCode int
558650
0 commit comments