go/types.go (314 lines of code) (raw):

package types import ( "bytes" "crypto/sha256" "encoding/asn1" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "github.com/google/certificate-transparency-go/x509" "net/url" "sort" "strings" "time" ) const ( kExpirationFormat = "2006-01-02" kExpirationFormatWithHour = "2006-01-02-15" ) var ( kOidExtensionReasonCode = []int{2, 5, 29, 21} ) /* The CTLogMetadata struct contains the information that we receive * `ct-logs` Remote Settings collection. */ type CTLogMetadata struct { CRLiteEnrolled bool `json:"crlite_enrolled"` Description string `json:"description"` Key string `json:"key"` LogID string `json:"logID"` MMD int `json:"mmd"` URL string `json:"url"` } func (o *CTLogMetadata) MetricKey() string { metricKey := o.URL metricKey = strings.TrimPrefix(metricKey, "https://") metricKey = strings.TrimSuffix(metricKey, "/") metricKey = strings.ReplaceAll(metricKey, "/", ".") return metricKey } /* The CTLogState struct contains information necessary to describe a filter's * coverage of a CT log. */ type CTLogState struct { LogID string `db:"logID"` // The log's RFC 6962 LogID MMD uint64 `db:"mmd"` // The log's maximum merge delay in seconds ShortURL string `db:"url"` // URL to the log MinEntry uint64 `db:"minEntry"` // The smallest index we've downloaded MaxEntry uint64 `db:"maxEntry"` // The largest index we've downloaded MinTimestamp uint64 `db:"minTimestamp"` // Unix timestamp of the earliest entry we've downloaded MaxTimestamp uint64 `db:"maxTimestamp"` // Unix timestamp of the most recent entry we've downloaded LastUpdateTime time.Time `db:"lastUpdateTime"` // Date when we completed the last update } func (o *CTLogState) String() string { return fmt.Sprintf("[%s] MinEntry=%d, MaxEntry=%d, MaxTimestamp=%d, LastUpdateTime=%s", o.ShortURL, o.MinEntry, o.MaxEntry, o.MaxTimestamp, o.LastUpdateTime) } type Issuer struct { id *string spki SPKI } func NewIssuer(aCert *x509.Certificate) Issuer { obj := Issuer{ id: nil, spki: SPKI{aCert.RawSubjectPublicKeyInfo}, } return obj } func NewIssuerFromString(aStr string) Issuer { obj := Issuer{ id: &aStr, } return obj } func (o *Issuer) ID() string { if o.id == nil { encodedDigest := o.spki.Sha256DigestURLEncodedBase64() o.id = &encodedDigest } return *o.id } func (o *Issuer) MarshalJSON() ([]byte, error) { return json.Marshal(o.ID()) } func (o *Issuer) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &o.id) } type SPKI struct { spki []byte } func (o SPKI) ID() string { return base64.URLEncoding.EncodeToString(o.spki) } func (o SPKI) String() string { return hex.EncodeToString(o.spki) } func (o SPKI) Sha256DigestURLEncodedBase64() string { binaryDigest := sha256.Sum256(o.spki) encodedDigest := base64.URLEncoding.EncodeToString(binaryDigest[:]) return encodedDigest } type Serial struct { serial []byte } func (s Serial) String() string { return s.HexString() } func (s Serial) BinaryString() string { return string(s.serial) } func (s Serial) HexString() string { return hex.EncodeToString(s.serial) } func (s Serial) MarshalJSON() ([]byte, error) { return json.Marshal(s.HexString()) } func (s *Serial) UnmarshalJSON(data []byte) error { if data[0] != '"' || data[len(data)-1] != '"' { return fmt.Errorf("Expected surrounding quotes") } b, err := hex.DecodeString(string(data[1 : len(data)-1])) s.serial = b return err } type SerialList []Serial func (s SerialList) Len() int { return len(s) } func (s SerialList) Less(i, j int) bool { return bytes.Compare(s[i].serial, s[j].serial) < 0 } func (s SerialList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s SerialList) Dedup() SerialList { if len(s) < 2 { return s } sort.Sort(s) end := 1 for i := 1; i < len(s); i++ { if bytes.Equal(s[i].serial, s[i-1].serial) { continue } s[end] = s[i] end++ } return s[:end] } // A serial number with a revocation reason code type SerialAndReason struct { Serial Serial Reason uint8 } type IssuerCrlMap map[string]map[string]bool func (self IssuerCrlMap) Merge(other IssuerCrlMap) { for issuer, crls := range other { selfCrls, pres := self[issuer] if !pres { selfCrls = make(map[string]bool) } for crl, _ := range crls { selfCrls[crl] = true } self[issuer] = selfCrls } } type IssuerCrlUrls struct { Issuer Issuer Urls []url.URL } type UrlPath struct { Url url.URL Path string } type IssuerCrlUrlPaths struct { Issuer Issuer IssuerDN string CrlUrlPaths []UrlPath } type TBSCertificateListWithRawSerials struct { Raw asn1.RawContent Version int `asn1:"optional,default:0"` Signature asn1.RawValue Issuer asn1.RawValue ThisUpdate time.Time NextUpdate time.Time `asn1:"optional"` RevokedCertificates []RevokedCertificateWithRawSerial `asn1:"optional"` } type Extension struct { Id asn1.ObjectIdentifier Critical bool `asn1:"optional"` Value []byte } type RevokedCertificateWithRawSerial struct { Raw asn1.RawContent SerialNumber asn1.RawValue RevocationTime time.Time Extensions []Extension `asn1:"optional"` } func (c RevokedCertificateWithRawSerial) Reason() (asn1.Enumerated, error) { seen := false reasonCode := asn1.Enumerated(0) for _, ext := range c.Extensions { if ext.Id.Equal(kOidExtensionReasonCode) { if seen { return reasonCode, fmt.Errorf("Repeated CRLReason extension") } _, err := asn1.Unmarshal(ext.Value, &reasonCode) if err != nil { return reasonCode, err } seen = true } } if reasonCode < 0 || reasonCode > 255 { return reasonCode, fmt.Errorf("Invalid reason code") } return reasonCode, nil } func (c RevokedCertificateWithRawSerial) SerialAndReason() (SerialAndReason, error) { reason, err := c.Reason() if err != nil { return SerialAndReason{}, err } return SerialAndReason{ NewSerialFromBytes(c.SerialNumber.Bytes), uint8(reason), }, nil } func DecodeRawTBSCertList(data []byte) (*TBSCertificateListWithRawSerials, error) { var tbsCertList TBSCertificateListWithRawSerials _, err := asn1.Unmarshal(data, &tbsCertList) return &tbsCertList, err } func NewSerialFromBytes(b []byte) Serial { obj := Serial{ serial: b, } return obj } type tbsCertWithRawSerial struct { Raw asn1.RawContent Version asn1.RawValue `asn1:"optional,explicit,default:0,tag:0"` SerialNumber asn1.RawValue } func NewSerial(aCert *x509.Certificate) Serial { var tbsCert tbsCertWithRawSerial _, err := asn1.Unmarshal(aCert.RawTBSCertificate, &tbsCert) if err != nil { panic(err) } return NewSerialFromBytes(tbsCert.SerialNumber.Bytes) } func NewSerialFromHex(s string) Serial { b, err := hex.DecodeString(s) if err != nil { panic(err) } return Serial{ serial: b, } } func NewSerialFromBinaryString(s string) (Serial, error) { bytes := []byte(s) return NewSerialFromBytes(bytes), nil } type ExpDate struct { date time.Time lastGood time.Time hourResolution bool } func NewExpDateFromTime(t time.Time) ExpDate { truncTime := t.Truncate(time.Hour) return ExpDate{ date: truncTime, lastGood: truncTime.Add(-1 * time.Millisecond), hourResolution: true, } } func NewExpDate(s string) (ExpDate, error) { if len(s) > 10 { t, err := time.Parse(kExpirationFormatWithHour, s) if err == nil { lastGood := t.Add(1 * time.Hour) lastGood = lastGood.Add(-1 * time.Millisecond) return ExpDate{t, lastGood, true}, nil } } t, err := time.Parse(kExpirationFormat, s) if err == nil { lastGood := t.Add(24 * time.Hour) lastGood = lastGood.Add(-1 * time.Millisecond) return ExpDate{t, lastGood, false}, nil } return ExpDate{}, err } func (e ExpDate) IsExpiredAt(t time.Time) bool { return e.lastGood.Before(t) } func (e ExpDate) ExpireTime() time.Time { return e.date } func (e ExpDate) String() string { return e.ID() } func (e ExpDate) Unix() int64 { return e.date.Unix() } func (e ExpDate) ID() string { if e.hourResolution { return e.date.Format(kExpirationFormatWithHour) } return e.date.Format(kExpirationFormat) } type IssuerDate struct { Issuer Issuer ExpDates []ExpDate } func IsPreIssuer(issuer *x509.Certificate) bool { for _, eku := range issuer.ExtKeyUsage { if eku == x509.ExtKeyUsageCertificateTransparency { return true } } return false }