Ver Fonte

Uprev google/certificate-transparency to include Solaris support

Signed-off-by: Amit Krishnan <krish.amit@gmail.com>
Amit Krishnan há 8 anos atrás
pai
commit
16db092af2

+ 1 - 1
hack/vendor.sh

@@ -151,7 +151,7 @@ clone git github.com/docker/swarmkit 3b221eb0391d34ae0b9dac65df02b5b64de6dff2
 clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
 clone git github.com/gogo/protobuf v0.3
 clone git github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
-clone git github.com/google/certificate-transparency 0f6e3d1d1ba4d03fdaab7cd716f36255c2e48341
+clone git github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e
 clone git golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2 https://github.com/golang/crypto.git
 clone git golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb https://github.com/golang/time.git
 clone git github.com/mreiferson/go-httpclient 63fe23f7434723dc904c901043af07931f293c47

+ 88 - 0
vendor/src/github.com/google/certificate-transparency/go/client/getentries.go

@@ -0,0 +1,88 @@
+package client
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+
+	ct "github.com/google/certificate-transparency/go"
+	"golang.org/x/net/context"
+)
+
+// LeafEntry respresents a JSON leaf entry.
+type LeafEntry struct {
+	LeafInput []byte `json:"leaf_input"`
+	ExtraData []byte `json:"extra_data"`
+}
+
+// GetEntriesResponse respresents the JSON response to the CT get-entries method.
+type GetEntriesResponse struct {
+	Entries []LeafEntry `json:"entries"` // the list of returned entries
+}
+
+// GetRawEntries exposes the /ct/v1/get-entries result with only the JSON parsing done.
+func GetRawEntries(ctx context.Context, httpClient *http.Client, logURL string, start, end int64) (*GetEntriesResponse, error) {
+	if end < 0 {
+		return nil, errors.New("end should be >= 0")
+	}
+	if end < start {
+		return nil, errors.New("start should be <= end")
+	}
+
+	baseURL, err := url.Parse(strings.TrimRight(logURL, "/") + GetEntriesPath)
+	if err != nil {
+		return nil, err
+	}
+
+	baseURL.RawQuery = url.Values{
+		"start": []string{strconv.FormatInt(start, 10)},
+		"end":   []string{strconv.FormatInt(end, 10)},
+	}.Encode()
+
+	var resp GetEntriesResponse
+	err = fetchAndParse(context.TODO(), httpClient, baseURL.String(), &resp)
+	if err != nil {
+		return nil, err
+	}
+
+	return &resp, 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) {
+	resp, err := GetRawEntries(context.TODO(), c.httpClient, c.uri, start, end)
+	if err != nil {
+		return nil, err
+	}
+	entries := make([]ct.LogEntry, len(resp.Entries))
+	for index, entry := range resp.Entries {
+		leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(entry.LeafInput))
+		if err != nil {
+			return nil, err
+		}
+		entries[index].Leaf = *leaf
+
+		var chain []ct.ASN1Cert
+		switch leaf.TimestampedEntry.EntryType {
+		case ct.X509LogEntryType:
+			chain, err = ct.UnmarshalX509ChainArray(entry.ExtraData)
+
+		case ct.PrecertLogEntryType:
+			chain, err = ct.UnmarshalPrecertChainArray(entry.ExtraData)
+
+		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 entries, nil
+}

+ 146 - 121
vendor/src/github.com/google/certificate-transparency/go/client/logclient.go

@@ -13,26 +13,30 @@ import (
 	"io/ioutil"
 	"log"
 	"net/http"
+	"net/url"
 	"strconv"
 	"time"
 
-	"github.com/google/certificate-transparency/go"
+	ct "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"
+	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"
 )
 
 // LogClient represents a client for a given CT Log instance
 type LogClient struct {
-	uri        string       // the base URI of the log. e.g. http://ct.googleapis/pilot
-	httpClient *http.Client // used to interact with the log via HTTP
+	uri        string                // the base URI of the log. e.g. http://ct.googleapis/pilot
+	httpClient *http.Client          // used to interact with the log via HTTP
+	verifier   *ct.SignatureVerifier // nil if no public key for log available
 }
 
 //////////////////////////////////////////////////////////////////////////////////
@@ -43,7 +47,7 @@ type LogClient struct {
 // addChainRequest represents the JSON request body sent to the add-chain CT
 // method.
 type addChainRequest struct {
-	Chain []string `json:"chain"`
+	Chain [][]byte `json:"chain"`
 }
 
 // addChainResponse represents the JSON response to the add-chain CT method.
@@ -51,13 +55,13 @@ type addChainRequest struct {
 // log within a defined period of time.
 type addChainResponse struct {
 	SCTVersion ct.Version `json:"sct_version"` // SCT structure version
-	ID         string     `json:"id"`          // Log ID
+	ID         []byte     `json:"id"`          // Log ID
 	Timestamp  uint64     `json:"timestamp"`   // Timestamp of issuance
 	Extensions string     `json:"extensions"`  // Holder for any CT extensions
-	Signature  string     `json:"signature"`   // Log signature for this SCT
+	Signature  []byte     `json:"signature"`   // Log signature for this SCT
 }
 
-// addJSONRequest represents the JSON request body sent ot the add-json CT
+// addJSONRequest represents the JSON request body sent to the add-json CT
 // method.
 type addJSONRequest struct {
 	Data interface{} `json:"data"`
@@ -67,24 +71,13 @@ 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    string `json:"sha256_root_hash"`    // Root hash of the tree
-	TreeHeadSignature string `json:"tree_head_signature"` // Log signature for this STH
+	SHA256RootHash    []byte `json:"sha256_root_hash"`    // Root hash of the tree
+	TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH
 }
 
-// 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
+// getConsistencyProofResponse represents the JSON response to the get-consistency-proof CT method
 type getConsistencyProofResponse struct {
-	Consistency []string `json:"consistency"`
+	Consistency [][]byte `json:"consistency"`
 }
 
 // getAuditProofResponse represents the JSON response to the CT get-audit-proof method
@@ -105,6 +98,12 @@ 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
@@ -116,29 +115,56 @@ 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|.
+// NewWithPubKey constructs a new LogClient instance that includes public
+// key information for the log; this instance will check signatures on
+// responses from the log.
+func NewWithPubKey(uri string, hc *http.Client, pemEncodedKey string) (*LogClient, error) {
+	pubkey, _, rest, err := ct.PublicKeyFromPEM([]byte(pemEncodedKey))
+	if err != nil {
+		return nil, err
+	}
+	if len(rest) > 0 {
+		return nil, errors.New("extra data found after PEM key decoded")
+	}
+
+	verifier, err := ct.NewSignatureVerifier(pubkey)
+	if err != nil {
+		return nil, err
+	}
+
+	if hc == nil {
+		hc = new(http.Client)
+	}
+	return &LogClient{uri: uri, httpClient: hc, verifier: verifier}, nil
+}
+
+// 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.
 // Returns a non-nil |error| if there was a problem.
-func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
-	req, err := http.NewRequest("GET", uri, nil)
+func fetchAndParse(ctx context.Context, httpClient *http.Client, uri string, res interface{}) error {
+	req, err := http.NewRequest(http.MethodGet, uri, nil)
 	if err != nil {
 		return err
 	}
-	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
-		}
-	}
+	req.Cancel = ctx.Done()
+	resp, err := httpClient.Do(req)
 	if err != nil {
 		return err
 	}
-	if err = json.Unmarshal(body, &res); err != nil {
+	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 {
 		return err
 	}
+
 	return nil
 }
 
@@ -150,7 +176,7 @@ func (c *LogClient) postAndParse(uri string, req interface{}, res interface{}) (
 	if err != nil {
 		return nil, "", err
 	}
-	httpReq, err := http.NewRequest("POST", uri, bytes.NewReader(postBody))
+	httpReq, err := http.NewRequest(http.MethodPost, uri, bytes.NewReader(postBody))
 	if err != nil {
 		return nil, "", err
 	}
@@ -194,11 +220,11 @@ func backoffForRetry(ctx context.Context, d time.Duration) error {
 // Attempts to add |chain| to the log, using the api end-point specified by
 // |path|. If provided context expires before submission is complete an
 // error will be returned.
-func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
+func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
 	var resp addChainResponse
 	var req addChainRequest
 	for _, link := range chain {
-		req.Chain = append(req.Chain, base64.StdEncoding.EncodeToString(link))
+		req.Chain = append(req.Chain, link)
 	}
 	httpStatus := "Unknown"
 	backoffSeconds := 0
@@ -214,7 +240,7 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain []
 		if backoffSeconds > 0 {
 			backoffSeconds = 0
 		}
-		httpResp, errorBody, err := c.postAndParse(c.uri+path, &req, &resp)
+		httpResp, _, err := c.postAndParse(c.uri+path, &req, &resp)
 		if err != nil {
 			backoffSeconds = 10
 			continue
@@ -233,49 +259,48 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain []
 				}
 			}
 		default:
-			return nil, fmt.Errorf("got HTTP Status %s: %s", httpResp.Status, errorBody)
+			return nil, fmt.Errorf("got HTTP Status %s", httpResp.Status)
 		}
 		httpStatus = httpResp.Status
 	}
 
-	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))
+	ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature))
 	if err != nil {
 		return nil, err
 	}
+
 	var logID ct.SHA256Hash
-	copy(logID[:], rawLogID)
-	return &ct.SignedCertificateTimestamp{
+	copy(logID[:], resp.ID)
+	sct := &ct.SignedCertificateTimestamp{
 		SCTVersion: resp.SCTVersion,
 		LogID:      logID,
 		Timestamp:  resp.Timestamp,
 		Extensions: ct.CTExtensions(resp.Extensions),
-		Signature:  *ds}, nil
+		Signature:  *ds}
+	err = c.VerifySCTSignature(*sct, ctype, chain)
+	if err != nil {
+		return nil, err
+	}
+	return sct, nil
 }
 
 // AddChain adds the (DER represented) X509 |chain| to the log.
 func (c *LogClient) AddChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
-	return c.addChainWithRetry(nil, AddChainPath, chain)
+	return c.addChainWithRetry(nil, ct.X509LogEntryType, AddChainPath, chain)
 }
 
 // AddPreChain adds the (DER represented) Precertificate |chain| to the log.
 func (c *LogClient) AddPreChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
-	return c.addChainWithRetry(nil, AddPreChainPath, chain)
+	return c.addChainWithRetry(nil, ct.PrecertLogEntryType, AddPreChainPath, chain)
 }
 
 // AddChainWithContext adds the (DER represented) X509 |chain| to the log and
 // fails if the provided context expires before the chain is submitted.
 func (c *LogClient) AddChainWithContext(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
-	return c.addChainWithRetry(ctx, AddChainPath, chain)
+	return c.addChainWithRetry(ctx, ct.X509LogEntryType, AddChainPath, chain)
 }
 
+// AddJSON submits arbitrary data to to XJSON server.
 func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, error) {
 	req := addJSONRequest{
 		Data: data,
@@ -285,20 +310,12 @@ func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, e
 	if err != nil {
 		return nil, err
 	}
-	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))
+	ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature))
 	if err != nil {
 		return nil, err
 	}
 	var logID ct.SHA256Hash
-	copy(logID[:], rawLogID)
+	copy(logID[:], resp.ID)
 	return &ct.SignedCertificateTimestamp{
 		SCTVersion: resp.SCTVersion,
 		LogID:      logID,
@@ -311,7 +328,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 = c.fetchAndParse(c.uri+GetSTHPath, &resp); err != nil {
+	if err = fetchAndParse(context.TODO(), c.httpClient, c.uri+GetSTHPath, &resp); err != nil {
 		return
 	}
 	sth = &ct.SignedTreeHead{
@@ -319,69 +336,77 @@ func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) {
 		Timestamp: resp.Timestamp,
 	}
 
-	rawRootHash, err := base64.StdEncoding.DecodeString(resp.SHA256RootHash)
-	if err != nil {
-		return nil, fmt.Errorf("invalid base64 encoding in sha256_root_hash: %v", err)
-	}
-	if len(rawRootHash) != sha256.Size {
-		return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(rawRootHash))
+	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))
 	}
-	copy(sth.SHA256RootHash[:], rawRootHash)
+	copy(sth.SHA256RootHash[:], resp.SHA256RootHash)
 
-	rawSignature, err := base64.StdEncoding.DecodeString(resp.TreeHeadSignature)
+	ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.TreeHeadSignature))
 	if err != nil {
-		return nil, errors.New("invalid base64 encoding in tree_head_signature")
+		return nil, err
 	}
-	ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature))
+	sth.TreeHeadSignature = *ds
+	err = c.VerifySTHSignature(*sth)
 	if err != nil {
 		return nil, err
 	}
-	// TODO(alcutter): Verify signature
-	sth.TreeHeadSignature = *ds
 	return
 }
 
-// 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")
+// VerifySTHSignature checks the signature in sth, returning any error encountered or nil if verification is
+// successful.
+func (c *LogClient) VerifySTHSignature(sth ct.SignedTreeHead) error {
+	if c.verifier == nil {
+		// Can't verify signatures without a verifier
+		return nil
 	}
-	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 c.verifier.VerifySTHSignature(sth)
+}
+
+// VerifySCTSignature checks the signature in sct for the given LogEntryType, with associated certificate chain.
+func (c *LogClient) VerifySCTSignature(sct ct.SignedCertificateTimestamp, ctype ct.LogEntryType, certData []ct.ASN1Cert) error {
+	if c.verifier == nil {
+		// Can't verify signatures without a verifier
+		return 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)
 
-		var chain []ct.ASN1Cert
-		switch leaf.TimestampedEntry.EntryType {
-		case ct.X509LogEntryType:
-			chain, err = ct.UnmarshalX509ChainArray(chainBytes)
+	if ctype == ct.PrecertLogEntryType {
+		// TODO(drysdale): cope with pre-certs, which need to have the
+		// following fields set:
+		//    leaf.PrecertEntry.TBSCertificate
+		//    leaf.PrecertEntry.IssuerKeyHash  (SHA-256 of issuer's public key)
+		return errors.New("SCT verification for pre-certificates unimplemented")
+	}
+	// Build enough of a Merkle tree leaf for the verifier to work on.
+	leaf := ct.MerkleTreeLeaf{
+		Version:  sct.SCTVersion,
+		LeafType: ct.TimestampedEntryLeafType,
+		TimestampedEntry: ct.TimestampedEntry{
+			Timestamp:  sct.Timestamp,
+			EntryType:  ctype,
+			X509Entry:  certData[0],
+			Extensions: sct.Extensions}}
+	entry := ct.LogEntry{Leaf: leaf}
+	return c.verifier.VerifySCTSignature(sct, entry)
+}
 
-		case ct.PrecertLogEntryType:
-			chain, err = ct.UnmarshalPrecertChainArray(chainBytes)
+// 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 {
+		return nil, err
+	}
+	return resp.Consistency, nil
+}
 
-		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)
+// 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
 	}
-	return entries, nil
+	return &resp, nil
 }

+ 128 - 0
vendor/src/github.com/google/certificate-transparency/go/serialization.go

@@ -6,9 +6,11 @@ import (
 	"crypto"
 	"encoding/asn1"
 	"encoding/binary"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
+	"strings"
 )
 
 // Variable size structure prefix-header byte lengths
@@ -18,6 +20,7 @@ const (
 	ExtensionsLengthBytes       = 2
 	CertificateChainLengthBytes = 3
 	SignatureLengthBytes        = 2
+	JSONLengthBytes             = 3
 )
 
 // Max lengths
@@ -144,6 +147,10 @@ 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)
 	}
@@ -151,6 +158,41 @@ 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.
@@ -299,6 +341,29 @@ 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
@@ -345,6 +410,8 @@ 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)
 	}
@@ -459,6 +526,7 @@ 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 {
@@ -561,3 +629,63 @@ 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),
+		},
+	}
+}

+ 11 - 0
vendor/src/github.com/google/certificate-transparency/go/types.go

@@ -28,6 +28,8 @@ 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))
 }
@@ -36,6 +38,7 @@ 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
@@ -238,6 +241,7 @@ type LogEntry struct {
 	Leaf     MerkleTreeLeaf
 	X509Cert *x509.Certificate
 	Precert  *Precertificate
+	JSONData []byte
 	Chain    []ASN1Cert
 }
 
@@ -313,6 +317,7 @@ type TimestampedEntry struct {
 	Timestamp    uint64
 	EntryType    LogEntryType
 	X509Entry    ASN1Cert
+	JSONData     []byte
 	PrecertEntry PreCert
 	Extensions   CTExtensions
 }
@@ -361,3 +366,9 @@ 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"`
+}

+ 1 - 1
vendor/src/github.com/google/certificate-transparency/go/x509/root_unix.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build dragonfly freebsd linux openbsd netbsd
+// +build dragonfly freebsd linux openbsd netbsd solaris
 
 package x509