Merge pull request #27748 from amitkris/uprev_certificate_transparency
Uprev google/certificate-transparency to include Solaris support
This commit is contained in:
commit
77f4c30a21
6 changed files with 378 additions and 126 deletions
|
@ -151,7 +151,7 @@ clone git github.com/docker/swarmkit 3b221eb0391d34ae0b9dac65df02b5b64de6dff2
|
||||||
clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
|
clone git github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
|
||||||
clone git github.com/gogo/protobuf v0.3
|
clone git github.com/gogo/protobuf v0.3
|
||||||
clone git github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
|
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/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2 https://github.com/golang/crypto.git
|
||||||
clone git golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb https://github.com/golang/time.git
|
clone git golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb https://github.com/golang/time.git
|
||||||
clone git github.com/mreiferson/go-httpclient 63fe23f7434723dc904c901043af07931f293c47
|
clone git github.com/mreiferson/go-httpclient 63fe23f7434723dc904c901043af07931f293c47
|
||||||
|
|
88
vendor/src/github.com/google/certificate-transparency/go/client/getentries.go
vendored
Normal file
88
vendor/src/github.com/google/certificate-transparency/go/client/getentries.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -13,10 +13,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/certificate-transparency/go"
|
ct "github.com/google/certificate-transparency/go"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,12 +28,15 @@ const (
|
||||||
AddJSONPath = "/ct/v1/add-json"
|
AddJSONPath = "/ct/v1/add-json"
|
||||||
GetSTHPath = "/ct/v1/get-sth"
|
GetSTHPath = "/ct/v1/get-sth"
|
||||||
GetEntriesPath = "/ct/v1/get-entries"
|
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
|
// LogClient represents a client for a given CT Log instance
|
||||||
type LogClient struct {
|
type LogClient struct {
|
||||||
uri string // the base URI of the log. e.g. http://ct.googleapis/pilot
|
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
|
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
|
// addChainRequest represents the JSON request body sent to the add-chain CT
|
||||||
// method.
|
// method.
|
||||||
type addChainRequest struct {
|
type addChainRequest struct {
|
||||||
Chain []string `json:"chain"`
|
Chain [][]byte `json:"chain"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// addChainResponse represents the JSON response to the add-chain CT method.
|
// 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.
|
// log within a defined period of time.
|
||||||
type addChainResponse struct {
|
type addChainResponse struct {
|
||||||
SCTVersion ct.Version `json:"sct_version"` // SCT structure version
|
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
|
Timestamp uint64 `json:"timestamp"` // Timestamp of issuance
|
||||||
Extensions string `json:"extensions"` // Holder for any CT extensions
|
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.
|
// method.
|
||||||
type addJSONRequest struct {
|
type addJSONRequest struct {
|
||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
|
@ -67,24 +71,13 @@ type addJSONRequest struct {
|
||||||
type getSTHResponse struct {
|
type getSTHResponse struct {
|
||||||
TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree
|
TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree
|
||||||
Timestamp uint64 `json:"timestamp"` // Time that the tree was created
|
Timestamp uint64 `json:"timestamp"` // Time that the tree was created
|
||||||
SHA256RootHash string `json:"sha256_root_hash"` // Root hash of the tree
|
SHA256RootHash []byte `json:"sha256_root_hash"` // Root hash of the tree
|
||||||
TreeHeadSignature string `json:"tree_head_signature"` // Log signature for this STH
|
TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH
|
||||||
}
|
}
|
||||||
|
|
||||||
// base64LeafEntry respresents a Base64 encoded leaf entry
|
// getConsistencyProofResponse represents the JSON response to the get-consistency-proof CT method
|
||||||
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 {
|
type getConsistencyProofResponse struct {
|
||||||
Consistency []string `json:"consistency"`
|
Consistency [][]byte `json:"consistency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAuditProofResponse represents the JSON response to the CT get-audit-proof method
|
// 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
|
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.
|
// New constructs a new LogClient instance.
|
||||||
// |uri| is the base URI of the CT log instance to interact with, e.g.
|
// |uri| is the base URI of the CT log instance to interact with, e.g.
|
||||||
// http://ct.googleapis.com/pilot
|
// http://ct.googleapis.com/pilot
|
||||||
|
@ -116,29 +115,56 @@ func New(uri string, hc *http.Client) *LogClient {
|
||||||
return &LogClient{uri: uri, httpClient: hc}
|
return &LogClient{uri: uri, httpClient: hc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes a HTTP call to |uri|, and attempts to parse the response as a JSON
|
// NewWithPubKey constructs a new LogClient instance that includes public
|
||||||
// representation of the structure in |res|.
|
// 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.
|
// Returns a non-nil |error| if there was a problem.
|
||||||
func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
|
func fetchAndParse(ctx context.Context, httpClient *http.Client, uri string, res interface{}) error {
|
||||||
req, err := http.NewRequest("GET", uri, nil)
|
req, err := http.NewRequest(http.MethodGet, uri, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := c.httpClient.Do(req)
|
req.Cancel = ctx.Done()
|
||||||
var body []byte
|
resp, err := httpClient.Do(req)
|
||||||
if resp != nil {
|
|
||||||
body, err = ioutil.ReadAll(resp.Body)
|
|
||||||
resp.Body.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 != nil {
|
|
||||||
return err
|
if err := json.NewDecoder(resp.Body).Decode(res); err != nil {
|
||||||
}
|
|
||||||
if err = json.Unmarshal(body, &res); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +176,7 @@ func (c *LogClient) postAndParse(uri string, req interface{}, res interface{}) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
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 {
|
if err != nil {
|
||||||
return nil, "", err
|
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
|
// Attempts to add |chain| to the log, using the api end-point specified by
|
||||||
// |path|. If provided context expires before submission is complete an
|
// |path|. If provided context expires before submission is complete an
|
||||||
// error will be returned.
|
// 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 resp addChainResponse
|
||||||
var req addChainRequest
|
var req addChainRequest
|
||||||
for _, link := range chain {
|
for _, link := range chain {
|
||||||
req.Chain = append(req.Chain, base64.StdEncoding.EncodeToString(link))
|
req.Chain = append(req.Chain, link)
|
||||||
}
|
}
|
||||||
httpStatus := "Unknown"
|
httpStatus := "Unknown"
|
||||||
backoffSeconds := 0
|
backoffSeconds := 0
|
||||||
|
@ -214,7 +240,7 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain []
|
||||||
if backoffSeconds > 0 {
|
if backoffSeconds > 0 {
|
||||||
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 {
|
if err != nil {
|
||||||
backoffSeconds = 10
|
backoffSeconds = 10
|
||||||
continue
|
continue
|
||||||
|
@ -233,49 +259,48 @@ func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
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
|
httpStatus = httpResp.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
rawLogID, err := base64.StdEncoding.DecodeString(resp.ID)
|
ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature))
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var logID ct.SHA256Hash
|
var logID ct.SHA256Hash
|
||||||
copy(logID[:], rawLogID)
|
copy(logID[:], resp.ID)
|
||||||
return &ct.SignedCertificateTimestamp{
|
sct := &ct.SignedCertificateTimestamp{
|
||||||
SCTVersion: resp.SCTVersion,
|
SCTVersion: resp.SCTVersion,
|
||||||
LogID: logID,
|
LogID: logID,
|
||||||
Timestamp: resp.Timestamp,
|
Timestamp: resp.Timestamp,
|
||||||
Extensions: ct.CTExtensions(resp.Extensions),
|
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.
|
// AddChain adds the (DER represented) X509 |chain| to the log.
|
||||||
func (c *LogClient) AddChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
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.
|
// AddPreChain adds the (DER represented) Precertificate |chain| to the log.
|
||||||
func (c *LogClient) AddPreChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
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
|
// AddChainWithContext adds the (DER represented) X509 |chain| to the log and
|
||||||
// fails if the provided context expires before the chain is submitted.
|
// fails if the provided context expires before the chain is submitted.
|
||||||
func (c *LogClient) AddChainWithContext(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
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) {
|
func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, error) {
|
||||||
req := addJSONRequest{
|
req := addJSONRequest{
|
||||||
Data: data,
|
Data: data,
|
||||||
|
@ -285,20 +310,12 @@ func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rawLogID, err := base64.StdEncoding.DecodeString(resp.ID)
|
ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature))
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var logID ct.SHA256Hash
|
var logID ct.SHA256Hash
|
||||||
copy(logID[:], rawLogID)
|
copy(logID[:], resp.ID)
|
||||||
return &ct.SignedCertificateTimestamp{
|
return &ct.SignedCertificateTimestamp{
|
||||||
SCTVersion: resp.SCTVersion,
|
SCTVersion: resp.SCTVersion,
|
||||||
LogID: logID,
|
LogID: logID,
|
||||||
|
@ -311,7 +328,7 @@ func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, e
|
||||||
// Returns a populated SignedTreeHead, or a non-nil error.
|
// Returns a populated SignedTreeHead, or a non-nil error.
|
||||||
func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) {
|
func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) {
|
||||||
var resp getSTHResponse
|
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
|
return
|
||||||
}
|
}
|
||||||
sth = &ct.SignedTreeHead{
|
sth = &ct.SignedTreeHead{
|
||||||
|
@ -319,69 +336,77 @@ func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) {
|
||||||
Timestamp: resp.Timestamp,
|
Timestamp: resp.Timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
rawRootHash, err := base64.StdEncoding.DecodeString(resp.SHA256RootHash)
|
if len(resp.SHA256RootHash) != sha256.Size {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(resp.SHA256RootHash))
|
||||||
return nil, fmt.Errorf("invalid base64 encoding in sha256_root_hash: %v", err)
|
|
||||||
}
|
}
|
||||||
if len(rawRootHash) != sha256.Size {
|
copy(sth.SHA256RootHash[:], resp.SHA256RootHash)
|
||||||
return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(rawRootHash))
|
|
||||||
}
|
|
||||||
copy(sth.SHA256RootHash[:], rawRootHash)
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// TODO(alcutter): Verify signature
|
|
||||||
sth.TreeHeadSignature = *ds
|
sth.TreeHeadSignature = *ds
|
||||||
|
err = c.VerifySTHSignature(*sth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEntries attempts to retrieve the entries in the sequence [|start|, |end|] from the CT
|
// VerifySTHSignature checks the signature in sth, returning any error encountered or nil if verification is
|
||||||
// log server. (see section 4.6.)
|
// successful.
|
||||||
// Returns a slice of LeafInputs or a non-nil error.
|
func (c *LogClient) VerifySTHSignature(sth ct.SignedTreeHead) error {
|
||||||
func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) {
|
if c.verifier == nil {
|
||||||
if end < 0 {
|
// Can't verify signatures without a verifier
|
||||||
return nil, errors.New("end should be >= 0")
|
return nil
|
||||||
}
|
}
|
||||||
if end < start {
|
return c.verifier.VerifySTHSignature(sth)
|
||||||
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)
|
// VerifySCTSignature checks the signature in sct for the given LogEntryType, with associated certificate chain.
|
||||||
if err != nil {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 nil, err
|
||||||
}
|
}
|
||||||
entries := make([]ct.LogEntry, len(resp.Entries))
|
return resp.Consistency, nil
|
||||||
for index, entry := range resp.Entries {
|
}
|
||||||
leafBytes, err := base64.StdEncoding.DecodeString(entry.LeafInput)
|
|
||||||
leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(leafBytes))
|
// GetProofByHash returns an audit path for the hash of an SCT.
|
||||||
if err != nil {
|
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 nil, err
|
||||||
}
|
}
|
||||||
entries[index].Leaf = *leaf
|
return &resp, nil
|
||||||
chainBytes, err := base64.StdEncoding.DecodeString(entry.ExtraData)
|
|
||||||
|
|
||||||
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 entries, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Variable size structure prefix-header byte lengths
|
// Variable size structure prefix-header byte lengths
|
||||||
|
@ -18,6 +20,7 @@ const (
|
||||||
ExtensionsLengthBytes = 2
|
ExtensionsLengthBytes = 2
|
||||||
CertificateChainLengthBytes = 3
|
CertificateChainLengthBytes = 3
|
||||||
SignatureLengthBytes = 2
|
SignatureLengthBytes = 2
|
||||||
|
JSONLengthBytes = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// Max lengths
|
// Max lengths
|
||||||
|
@ -144,6 +147,10 @@ func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error {
|
||||||
if t.PrecertEntry.TBSCertificate, err = readVarBytes(r, PreCertificateLengthBytes); err != nil {
|
if t.PrecertEntry.TBSCertificate, err = readVarBytes(r, PreCertificateLengthBytes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case XJSONLogEntryType:
|
||||||
|
if t.JSONData, err = readVarBytes(r, JSONLengthBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown EntryType: %d", t.EntryType)
|
return fmt.Errorf("unknown EntryType: %d", t.EntryType)
|
||||||
}
|
}
|
||||||
|
@ -151,6 +158,41 @@ func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error {
|
||||||
return nil
|
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
|
// ReadMerkleTreeLeaf parses the byte-stream representation of a MerkleTreeLeaf
|
||||||
// and returns a pointer to a new MerkleTreeLeaf structure containing the
|
// and returns a pointer to a new MerkleTreeLeaf structure containing the
|
||||||
// parsed data.
|
// parsed data.
|
||||||
|
@ -299,6 +341,29 @@ func serializeV1CertSCTSignatureInput(timestamp uint64, cert ASN1Cert, ext CTExt
|
||||||
return buf.Bytes(), nil
|
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) {
|
func serializeV1PrecertSCTSignatureInput(timestamp uint64, issuerKeyHash [issuerKeyHashLength]byte, tbs []byte, ext CTExtensions) ([]byte, error) {
|
||||||
if err := checkCertificateFormat(tbs); err != nil {
|
if err := checkCertificateFormat(tbs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -345,6 +410,8 @@ func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry
|
||||||
return serializeV1PrecertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash,
|
return serializeV1PrecertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash,
|
||||||
entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate,
|
entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate,
|
||||||
entry.Leaf.TimestampedEntry.Extensions)
|
entry.Leaf.TimestampedEntry.Extensions)
|
||||||
|
case XJSONLogEntryType:
|
||||||
|
return serializeV1JSONSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.JSONData)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown TimestampedEntryLeafType %s", entry.Leaf.TimestampedEntry.EntryType)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeserializeSCT reads an SCT from Reader.
|
||||||
func DeserializeSCT(r io.Reader) (*SignedCertificateTimestamp, error) {
|
func DeserializeSCT(r io.Reader) (*SignedCertificateTimestamp, error) {
|
||||||
var sct SignedCertificateTimestamp
|
var sct SignedCertificateTimestamp
|
||||||
if err := binary.Read(r, binary.BigEndian, &sct.SCTVersion); err != nil {
|
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
|
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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ func (e LogEntryType) String() string {
|
||||||
return "X509LogEntryType"
|
return "X509LogEntryType"
|
||||||
case PrecertLogEntryType:
|
case PrecertLogEntryType:
|
||||||
return "PrecertLogEntryType"
|
return "PrecertLogEntryType"
|
||||||
|
case XJSONLogEntryType:
|
||||||
|
return "XJSONLogEntryType"
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("No string defined for LogEntryType constant value %d", e))
|
panic(fmt.Sprintf("No string defined for LogEntryType constant value %d", e))
|
||||||
}
|
}
|
||||||
|
@ -36,6 +38,7 @@ func (e LogEntryType) String() string {
|
||||||
const (
|
const (
|
||||||
X509LogEntryType LogEntryType = 0
|
X509LogEntryType LogEntryType = 0
|
||||||
PrecertLogEntryType LogEntryType = 1
|
PrecertLogEntryType LogEntryType = 1
|
||||||
|
XJSONLogEntryType LogEntryType = 0x8000 // Experimental. Don't rely on this!
|
||||||
)
|
)
|
||||||
|
|
||||||
// MerkleLeafType represents the MerkleLeafType enum from section 3.4 of the
|
// MerkleLeafType represents the MerkleLeafType enum from section 3.4 of the
|
||||||
|
@ -238,6 +241,7 @@ type LogEntry struct {
|
||||||
Leaf MerkleTreeLeaf
|
Leaf MerkleTreeLeaf
|
||||||
X509Cert *x509.Certificate
|
X509Cert *x509.Certificate
|
||||||
Precert *Precertificate
|
Precert *Precertificate
|
||||||
|
JSONData []byte
|
||||||
Chain []ASN1Cert
|
Chain []ASN1Cert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +317,7 @@ type TimestampedEntry struct {
|
||||||
Timestamp uint64
|
Timestamp uint64
|
||||||
EntryType LogEntryType
|
EntryType LogEntryType
|
||||||
X509Entry ASN1Cert
|
X509Entry ASN1Cert
|
||||||
|
JSONData []byte
|
||||||
PrecertEntry PreCert
|
PrecertEntry PreCert
|
||||||
Extensions CTExtensions
|
Extensions CTExtensions
|
||||||
}
|
}
|
||||||
|
@ -361,3 +366,9 @@ func (e sctError) Error() string {
|
||||||
return "unknown error"
|
return "unknown error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddJSONRequest represents the JSON request body sent ot the add-json CT
|
||||||
|
// method.
|
||||||
|
type AddJSONRequest struct {
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build dragonfly freebsd linux openbsd netbsd
|
// +build dragonfly freebsd linux openbsd netbsd solaris
|
||||||
|
|
||||||
package x509
|
package x509
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue