diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 83f2e297a..15dbd4e48 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -108,23 +108,23 @@ }, { "ImportPath": "github.com/google/certificate-transparency/go", - "Rev": "10e14470ce93d5dfe6eff6b85d3e7a06ef2f4809" + "Rev": "0f6e3d1d1ba4d03fdaab7cd716f36255c2e48341" }, { "ImportPath": "github.com/google/certificate-transparency/go/asn1", - "Rev": "10e14470ce93d5dfe6eff6b85d3e7a06ef2f4809" + "Rev": "0f6e3d1d1ba4d03fdaab7cd716f36255c2e48341" }, { "ImportPath": "github.com/google/certificate-transparency/go/client", - "Rev": "10e14470ce93d5dfe6eff6b85d3e7a06ef2f4809" + "Rev": "0f6e3d1d1ba4d03fdaab7cd716f36255c2e48341" }, { "ImportPath": "github.com/google/certificate-transparency/go/x509", - "Rev": "10e14470ce93d5dfe6eff6b85d3e7a06ef2f4809" + "Rev": "0f6e3d1d1ba4d03fdaab7cd716f36255c2e48341" }, { "ImportPath": "github.com/google/certificate-transparency/go/x509/pkix", - "Rev": "10e14470ce93d5dfe6eff6b85d3e7a06ef2f4809" + "Rev": "0f6e3d1d1ba4d03fdaab7cd716f36255c2e48341" }, { "ImportPath": "github.com/jmhodges/clock", diff --git a/vendor/github.com/google/certificate-transparency/go/client/logclient.go b/vendor/github.com/google/certificate-transparency/go/client/logclient.go index b2c4a1d9e..3c568a50b 100644 --- a/vendor/github.com/google/certificate-transparency/go/client/logclient.go +++ b/vendor/github.com/google/certificate-transparency/go/client/logclient.go @@ -8,27 +8,25 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" + "errors" "fmt" "io/ioutil" "log" "net/http" - "net/url" "strconv" "time" - ct "github.com/google/certificate-transparency/go" + "github.com/google/certificate-transparency/go" "golang.org/x/net/context" ) // URI paths for CT Log endpoints const ( - AddChainPath = "/ct/v1/add-chain" - AddPreChainPath = "/ct/v1/add-pre-chain" - AddJSONPath = "/ct/v1/add-json" - GetSTHPath = "/ct/v1/get-sth" - GetEntriesPath = "/ct/v1/get-entries" - GetProofByHashPath = "/ct/v1/get-proof-by-hash" - GetSTHConsistencyPath = "/ct/v1/get-sth-consistency" + AddChainPath = "/ct/v1/add-chain" + AddPreChainPath = "/ct/v1/add-pre-chain" + AddJSONPath = "/ct/v1/add-json" + GetSTHPath = "/ct/v1/get-sth" + GetEntriesPath = "/ct/v1/get-entries" ) // LogClient represents a client for a given CT Log instance @@ -45,7 +43,7 @@ type LogClient struct { // addChainRequest represents the JSON request body sent to the add-chain CT // method. type addChainRequest struct { - Chain [][]byte `json:"chain"` + Chain []string `json:"chain"` } // addChainResponse represents the JSON response to the add-chain CT method. @@ -53,10 +51,10 @@ type addChainRequest struct { // log within a defined period of time. type addChainResponse struct { SCTVersion ct.Version `json:"sct_version"` // SCT structure version - ID []byte `json:"id"` // Log ID + ID string `json:"id"` // Log ID Timestamp uint64 `json:"timestamp"` // Timestamp of issuance Extensions string `json:"extensions"` // Holder for any CT extensions - Signature []byte `json:"signature"` // Log signature for this SCT + Signature string `json:"signature"` // Log signature for this SCT } // addJSONRequest represents the JSON request body sent ot the add-json CT @@ -69,13 +67,24 @@ type addJSONRequest struct { type getSTHResponse struct { TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree Timestamp uint64 `json:"timestamp"` // Time that the tree was created - SHA256RootHash []byte `json:"sha256_root_hash"` // Root hash of the tree - TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH + SHA256RootHash string `json:"sha256_root_hash"` // Root hash of the tree + TreeHeadSignature string `json:"tree_head_signature"` // Log signature for this STH } -// getConsistencyProofResponse represents the JSON response to the get-consistency-proof CT method +// base64LeafEntry respresents a Base64 encoded leaf entry +type base64LeafEntry struct { + LeafInput string `json:"leaf_input"` + ExtraData string `json:"extra_data"` +} + +// getEntriesReponse respresents the JSON response to the CT get-entries method +type getEntriesResponse struct { + Entries []base64LeafEntry `json:"entries"` // the list of returned entries +} + +// getConsistencyProofResponse represents the JSON response to the CT get-consistency-proof method type getConsistencyProofResponse struct { - Consistency [][]byte `json:"consistency"` + Consistency []string `json:"consistency"` } // getAuditProofResponse represents the JSON response to the CT get-audit-proof method @@ -96,12 +105,6 @@ type getEntryAndProofResponse struct { AuditPath []string `json:"audit_path"` // the corresponding proof } -// GetProofByHashResponse represents the JSON response to the CT get-proof-by-hash method. -type GetProofByHashResponse struct { - LeafIndex int64 `json:"leaf_index"` // The 0-based index of the end entity corresponding to the "hash" parameter. - AuditPath [][]byte `json:"audit_path"` // An array of base64-encoded Merkle Tree nodes proving the inclusion of the chosen certificate. -} - // New constructs a new LogClient instance. // |uri| is the base URI of the CT log instance to interact with, e.g. // http://ct.googleapis.com/pilot @@ -113,33 +116,29 @@ func New(uri string, hc *http.Client) *LogClient { return &LogClient{uri: uri, httpClient: hc} } -// Makes a HTTP call to |uri|, and attempts to parse the response as a -// JSON representation of the structure in |res|. Uses |ctx| to -// control the HTTP call (so it can have a timeout or be cancelled by -// the caller), and |httpClient| to make the actual HTTP call. +// Makes a HTTP call to |uri|, and attempts to parse the response as a JSON +// representation of the structure in |res|. // Returns a non-nil |error| if there was a problem. -func fetchAndParse(ctx context.Context, httpClient *http.Client, uri string, res interface{}) error { - req, err := http.NewRequest(http.MethodGet, uri, nil) +func (c *LogClient) fetchAndParse(uri string, res interface{}) error { + req, err := http.NewRequest("GET", uri, nil) if err != nil { return err } - req.Cancel = ctx.Done() - resp, err := httpClient.Do(req) + resp, err := c.httpClient.Do(req) + var body []byte + if resp != nil { + body, err = ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return err + } + } if err != nil { return err } - defer resp.Body.Close() - // Make sure everything is read, so http.Client can reuse the connection. - defer ioutil.ReadAll(resp.Body) - - if resp.StatusCode != 200 { - return fmt.Errorf("got HTTP Status %s", resp.Status) - } - - if err := json.NewDecoder(resp.Body).Decode(res); err != nil { + if err = json.Unmarshal(body, &res); err != nil { return err } - return nil } @@ -151,7 +150,7 @@ func (c *LogClient) postAndParse(uri string, req interface{}, res interface{}) ( if err != nil { return nil, "", err } - httpReq, err := http.NewRequest(http.MethodPost, uri, bytes.NewReader(postBody)) + httpReq, err := http.NewRequest("POST", uri, bytes.NewReader(postBody)) if err != nil { return nil, "", err } @@ -199,7 +198,7 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain [] var resp addChainResponse var req addChainRequest for _, link := range chain { - req.Chain = append(req.Chain, link) + req.Chain = append(req.Chain, base64.StdEncoding.EncodeToString(link)) } httpStatus := "Unknown" backoffSeconds := 0 @@ -215,7 +214,7 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain [] if backoffSeconds > 0 { backoffSeconds = 0 } - httpResp, _, err := c.postAndParse(c.uri+path, &req, &resp) + httpResp, errorBody, err := c.postAndParse(c.uri+path, &req, &resp) if err != nil { backoffSeconds = 10 continue @@ -234,17 +233,25 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain [] } } default: - return nil, fmt.Errorf("got HTTP Status %s", httpResp.Status) + return nil, fmt.Errorf("got HTTP Status %s: %s", httpResp.Status, errorBody) } httpStatus = httpResp.Status } - ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) + rawLogID, err := base64.StdEncoding.DecodeString(resp.ID) + if err != nil { + return nil, err + } + rawSignature, err := base64.StdEncoding.DecodeString(resp.Signature) + if err != nil { + return nil, err + } + ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature)) if err != nil { return nil, err } var logID ct.SHA256Hash - copy(logID[:], resp.ID) + copy(logID[:], rawLogID) return &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, @@ -269,7 +276,6 @@ func (c *LogClient) AddChainWithContext(ctx context.Context, chain []ct.ASN1Cert return c.addChainWithRetry(ctx, AddChainPath, chain) } -// AddJSON submits arbitrary data to to XJSON server. func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, error) { req := addJSONRequest{ Data: data, @@ -279,12 +285,20 @@ func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, e if err != nil { return nil, err } - ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) + rawLogID, err := base64.StdEncoding.DecodeString(resp.ID) + if err != nil { + return nil, err + } + rawSignature, err := base64.StdEncoding.DecodeString(resp.Signature) + if err != nil { + return nil, err + } + ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature)) if err != nil { return nil, err } var logID ct.SHA256Hash - copy(logID[:], resp.ID) + copy(logID[:], rawLogID) return &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, @@ -297,7 +311,7 @@ func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, e // Returns a populated SignedTreeHead, or a non-nil error. func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) { var resp getSTHResponse - if err = fetchAndParse(context.TODO(), c.httpClient, c.uri+GetSTHPath, &resp); err != nil { + if err = c.fetchAndParse(c.uri+GetSTHPath, &resp); err != nil { return } sth = &ct.SignedTreeHead{ @@ -305,12 +319,20 @@ func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) { Timestamp: resp.Timestamp, } - if len(resp.SHA256RootHash) != sha256.Size { - return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(resp.SHA256RootHash)) + rawRootHash, err := base64.StdEncoding.DecodeString(resp.SHA256RootHash) + if err != nil { + return nil, fmt.Errorf("invalid base64 encoding in sha256_root_hash: %v", err) } - copy(sth.SHA256RootHash[:], resp.SHA256RootHash) + if len(rawRootHash) != sha256.Size { + return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(rawRootHash)) + } + copy(sth.SHA256RootHash[:], rawRootHash) - ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.TreeHeadSignature)) + rawSignature, err := base64.StdEncoding.DecodeString(resp.TreeHeadSignature) + if err != nil { + return nil, errors.New("invalid base64 encoding in tree_head_signature") + } + ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature)) if err != nil { return nil, err } @@ -319,23 +341,47 @@ func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) { return } -// GetSTHConsistency retrieves the consistency proof between two snapshots. -func (c *LogClient) GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) { - u := fmt.Sprintf("%s%s?first=%d&second=%d", c.uri, GetSTHConsistencyPath, first, second) - var resp getConsistencyProofResponse - if err := fetchAndParse(ctx, c.httpClient, u, &resp); err != nil { +// GetEntries attempts to retrieve the entries in the sequence [|start|, |end|] from the CT +// log server. (see section 4.6.) +// Returns a slice of LeafInputs or a non-nil error. +func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) { + if end < 0 { + return nil, errors.New("end should be >= 0") + } + if end < start { + return nil, errors.New("start should be <= end") + } + var resp getEntriesResponse + err := c.fetchAndParse(fmt.Sprintf("%s%s?start=%d&end=%d", c.uri, GetEntriesPath, start, end), &resp) + if err != nil { return nil, err } - return resp.Consistency, nil -} + entries := make([]ct.LogEntry, len(resp.Entries)) + for index, entry := range resp.Entries { + leafBytes, err := base64.StdEncoding.DecodeString(entry.LeafInput) + leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(leafBytes)) + if err != nil { + return nil, err + } + entries[index].Leaf = *leaf + chainBytes, err := base64.StdEncoding.DecodeString(entry.ExtraData) -// GetProofByHash returns an audit path for the hash of an SCT. -func (c *LogClient) GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*GetProofByHashResponse, error) { - b64Hash := url.QueryEscape(base64.StdEncoding.EncodeToString(hash)) - u := fmt.Sprintf("%s%s?tree_size=%d&hash=%v", c.uri, GetProofByHashPath, treeSize, b64Hash) - var resp GetProofByHashResponse - if err := fetchAndParse(ctx, c.httpClient, u, &resp); err != nil { - return nil, err + var chain []ct.ASN1Cert + switch leaf.TimestampedEntry.EntryType { + case ct.X509LogEntryType: + chain, err = ct.UnmarshalX509ChainArray(chainBytes) + + case ct.PrecertLogEntryType: + chain, err = ct.UnmarshalPrecertChainArray(chainBytes) + + default: + return nil, fmt.Errorf("saw unknown entry type: %v", leaf.TimestampedEntry.EntryType) + } + if err != nil { + return nil, err + } + entries[index].Chain = chain + entries[index].Index = start + int64(index) } - return &resp, nil + return entries, nil } diff --git a/vendor/github.com/google/certificate-transparency/go/serialization.go b/vendor/github.com/google/certificate-transparency/go/serialization.go index b83c90117..fd59040c6 100644 --- a/vendor/github.com/google/certificate-transparency/go/serialization.go +++ b/vendor/github.com/google/certificate-transparency/go/serialization.go @@ -6,11 +6,9 @@ import ( "crypto" "encoding/asn1" "encoding/binary" - "encoding/json" "errors" "fmt" "io" - "strings" ) // Variable size structure prefix-header byte lengths @@ -20,7 +18,6 @@ const ( ExtensionsLengthBytes = 2 CertificateChainLengthBytes = 3 SignatureLengthBytes = 2 - JSONLengthBytes = 3 ) // Max lengths @@ -147,10 +144,6 @@ func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error { if t.PrecertEntry.TBSCertificate, err = readVarBytes(r, PreCertificateLengthBytes); err != nil { return err } - case XJSONLogEntryType: - if t.JSONData, err = readVarBytes(r, JSONLengthBytes); err != nil { - return err - } default: return fmt.Errorf("unknown EntryType: %d", t.EntryType) } @@ -158,41 +151,6 @@ func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error { return nil } -// SerializeTimestampedEntry writes timestamped entry to Writer. -// In case of error, w may contain garbage. -func SerializeTimestampedEntry(w io.Writer, t *TimestampedEntry) error { - if err := binary.Write(w, binary.BigEndian, t.Timestamp); err != nil { - return err - } - if err := binary.Write(w, binary.BigEndian, t.EntryType); err != nil { - return err - } - switch t.EntryType { - case X509LogEntryType: - if err := writeVarBytes(w, t.X509Entry, CertificateLengthBytes); err != nil { - return err - } - case PrecertLogEntryType: - if err := binary.Write(w, binary.BigEndian, t.PrecertEntry.IssuerKeyHash); err != nil { - return err - } - if err := writeVarBytes(w, t.PrecertEntry.TBSCertificate, PreCertificateLengthBytes); err != nil { - return err - } - case XJSONLogEntryType: - // TODO: Pending google/certificate-transparency#1243, replace - // with ObjectHash once supported by CT server. - //jsonhash := objecthash.CommonJSONHash(string(t.JSONData)) - if err := writeVarBytes(w, []byte(t.JSONData), JSONLengthBytes); err != nil { - return err - } - default: - return fmt.Errorf("unknown EntryType: %d", t.EntryType) - } - writeVarBytes(w, t.Extensions, ExtensionsLengthBytes) - return nil -} - // ReadMerkleTreeLeaf parses the byte-stream representation of a MerkleTreeLeaf // and returns a pointer to a new MerkleTreeLeaf structure containing the // parsed data. @@ -341,29 +299,6 @@ func serializeV1CertSCTSignatureInput(timestamp uint64, cert ASN1Cert, ext CTExt return buf.Bytes(), nil } -func serializeV1JSONSCTSignatureInput(timestamp uint64, j []byte) ([]byte, error) { - var buf bytes.Buffer - if err := binary.Write(&buf, binary.BigEndian, V1); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, XJSONLogEntryType); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, j, JSONLengthBytes); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, nil, ExtensionsLengthBytes); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - func serializeV1PrecertSCTSignatureInput(timestamp uint64, issuerKeyHash [issuerKeyHashLength]byte, tbs []byte, ext CTExtensions) ([]byte, error) { if err := checkCertificateFormat(tbs); err != nil { return nil, err @@ -410,8 +345,6 @@ func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry return serializeV1PrecertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash, entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate, entry.Leaf.TimestampedEntry.Extensions) - case XJSONLogEntryType: - return serializeV1JSONSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.JSONData) default: return nil, fmt.Errorf("unknown TimestampedEntryLeafType %s", entry.Leaf.TimestampedEntry.EntryType) } @@ -526,7 +459,6 @@ func deserializeSCTV1(r io.Reader, sct *SignedCertificateTimestamp) error { return nil } -// DeserializeSCT reads an SCT from Reader. func DeserializeSCT(r io.Reader) (*SignedCertificateTimestamp, error) { var sct SignedCertificateTimestamp if err := binary.Read(r, binary.BigEndian, &sct.SCTVersion); err != nil { @@ -629,63 +561,3 @@ func SerializeSCTList(scts []SignedCertificateTimestamp) ([]byte, error) { } return asn1.Marshal(buf.Bytes()) // transform to Octet String } - -// SerializeMerkleTreeLeaf writes MerkleTreeLeaf to Writer. -// In case of error, w may contain garbage. -func SerializeMerkleTreeLeaf(w io.Writer, m *MerkleTreeLeaf) error { - if m.Version != V1 { - return fmt.Errorf("unknown Version %d", m.Version) - } - if err := binary.Write(w, binary.BigEndian, m.Version); err != nil { - return err - } - if m.LeafType != TimestampedEntryLeafType { - return fmt.Errorf("unknown LeafType %d", m.LeafType) - } - if err := binary.Write(w, binary.BigEndian, m.LeafType); err != nil { - return err - } - if err := SerializeTimestampedEntry(w, &m.TimestampedEntry); err != nil { - return err - } - return nil -} - -// CreateX509MerkleTreeLeaf generates a MerkleTreeLeaf for an X509 cert -func CreateX509MerkleTreeLeaf(cert ASN1Cert, timestamp uint64) *MerkleTreeLeaf { - return &MerkleTreeLeaf{ - Version: V1, - LeafType: TimestampedEntryLeafType, - TimestampedEntry: TimestampedEntry{ - Timestamp: timestamp, - EntryType: X509LogEntryType, - X509Entry: cert, - }, - } -} - -// CreateJSONMerkleTreeLeaf creates the merkle tree leaf for json data. -func CreateJSONMerkleTreeLeaf(data interface{}, timestamp uint64) *MerkleTreeLeaf { - jsonData, err := json.Marshal(AddJSONRequest{Data: data}) - if err != nil { - return nil - } - // Match the JSON serialization implemented by json-c - jsonStr := strings.Replace(string(jsonData), ":", ": ", -1) - jsonStr = strings.Replace(jsonStr, ",", ", ", -1) - jsonStr = strings.Replace(jsonStr, "{", "{ ", -1) - jsonStr = strings.Replace(jsonStr, "}", " }", -1) - jsonStr = strings.Replace(jsonStr, "/", `\/`, -1) - // TODO: Pending google/certificate-transparency#1243, replace with - // ObjectHash once supported by CT server. - - return &MerkleTreeLeaf{ - Version: V1, - LeafType: TimestampedEntryLeafType, - TimestampedEntry: TimestampedEntry{ - Timestamp: timestamp, - EntryType: XJSONLogEntryType, - JSONData: []byte(jsonStr), - }, - } -} diff --git a/vendor/github.com/google/certificate-transparency/go/types.go b/vendor/github.com/google/certificate-transparency/go/types.go index 507e624a1..8a63e98e6 100644 --- a/vendor/github.com/google/certificate-transparency/go/types.go +++ b/vendor/github.com/google/certificate-transparency/go/types.go @@ -28,8 +28,6 @@ func (e LogEntryType) String() string { return "X509LogEntryType" case PrecertLogEntryType: return "PrecertLogEntryType" - case XJSONLogEntryType: - return "XJSONLogEntryType" } panic(fmt.Sprintf("No string defined for LogEntryType constant value %d", e)) } @@ -38,7 +36,6 @@ func (e LogEntryType) String() string { const ( X509LogEntryType LogEntryType = 0 PrecertLogEntryType LogEntryType = 1 - XJSONLogEntryType LogEntryType = 0x8000 // Experimental. Don't rely on this! ) // MerkleLeafType represents the MerkleLeafType enum from section 3.4 of the @@ -241,7 +238,6 @@ type LogEntry struct { Leaf MerkleTreeLeaf X509Cert *x509.Certificate Precert *Precertificate - JSONData []byte Chain []ASN1Cert } @@ -317,7 +313,6 @@ type TimestampedEntry struct { Timestamp uint64 EntryType LogEntryType X509Entry ASN1Cert - JSONData []byte PrecertEntry PreCert Extensions CTExtensions } @@ -366,9 +361,3 @@ func (e sctError) Error() string { return "unknown error" } } - -// AddJSONRequest represents the JSON request body sent ot the add-json CT -// method. -type AddJSONRequest struct { - Data interface{} `json:"data"` -}