oneCRLDiffCCADB/oneCRL/oneCRL.go (89 lines of code) (raw):

/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package oneCRL import ( "bytes" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" "encoding/json" "fmt" "math/big" "net/http" ) const OneCRLEndpoint = "https://firefox.settings.services.mozilla.com/v1/buckets/blocklists/collections/certificates/records" type OneCRLIntermediates struct { Data []*OneCRLIntermediate `json:"data"` } type OneCRLIntermediate struct { Schema int `json:"schema"` Details struct { Bug string `json:"bug"` Who string `json:"who"` Why string `json:"why"` Name string `json:"name"` Created string `json:"created"` } `json:"details"` Enabled bool `json:"enabled"` IssuerName Name `json:"issuerName"` SerialNumber string `json:"serialNumber"` Id string `json:"id"` LastModified int `json:"last_modified"` } // Key constructs a string that is the concatenation of the certificate serial (decoded from base64 to an decimal value) // the issuer common name, and the issuer organization name. This key is used to join the results of OneCRL with the // CCADB. func (o *OneCRLIntermediate) Key() string { cn, org := o.IssuerName.Key() return fmt.Sprintf("%s%s%s", o.decodeSerial(), cn, org) } // Retrieve downloads the OneCRL report located at // https://firefox.settings.services.mozilla.com/v1/buckets/blocklists/collections/certificates/records // and returns a mapping "key"s to entries. // // The "key" in this case is the string concatenation of the decimal value of the certificate serial number, // the issuer common name, and the issuer organization name. func Retrieve() (map[string]*OneCRLIntermediate, error) { result := make(map[string]*OneCRLIntermediate) var intermediates OneCRLIntermediates resp, err := http.DefaultClient.Get(OneCRLEndpoint) if err != nil { return result, err } defer resp.Body.Close() err = json.NewDecoder(resp.Body).Decode(&intermediates) if err != nil { return result, err } for _, cert := range intermediates.Data { result[cert.Key()] = cert } return result, nil } func (o *OneCRLIntermediate) decodeSerial() string { s, err := base64.StdEncoding.DecodeString(o.SerialNumber) if err != nil { panic(err) } return big.NewInt(0).SetBytes(s).String() } // Name wraps a a vanilla RDN so that we can attach further methods for deserialization from JSON and extraction // of the issuer Common Name and Organization Name. type Name struct { // https://tools.ietf.org/html/rfc5280#section-4.1.2.4 pkix.RDNSequence } func (n *Name) Key() (string, string) { cn := "" on := "" for _, i := range n.RDNSequence { for _, j := range i { switch j.Type.String() { // CN http://oidref.com/2.5.4.3 case "2.5.4.3": cn = fmt.Sprint(j.Value) // ON http://oidref.com/2.5.4.10 case "2.5.4.10": on = fmt.Sprint(j.Value) } } } return cn, on } func (n *Name) UnmarshalJSON(raw []byte) error { // As it comes in, this buffer is just a JSON string, which // includes double quotes that we do not want or need. raw = bytes.Trim(raw, `"`) dst := make([]byte, len(raw)) _, err := base64.StdEncoding.Decode(dst, raw) if err != nil { return err } _, err = asn1.Unmarshal(dst, &n.RDNSequence) if err != nil { return err } return nil }