Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
GetProofByHash
Browse files Browse the repository at this point in the history
Contains:
- V1LeafHasher
- Serializer for XJSON
- Creates a MerkleTreeLeaf from an SCT and the original JSON data.
- Tests
  • Loading branch information
gdbelvin committed Jun 15, 2016
1 parent ceb1244 commit 27a8c06
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 20 deletions.
57 changes: 42 additions & 15 deletions go/client/logclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"io/ioutil"
"log"
"net/http"
"net/url"
"strconv"
"time"

Expand All @@ -23,11 +24,12 @@ import (

// 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"
)

// LogClient represents a client for a given CT Log instance
Expand Down Expand Up @@ -58,12 +60,6 @@ type addChainResponse struct {
Signature string `json:"signature"` // Log signature for this SCT
}

// addJSONRequest represents the JSON request body sent ot the add-json CT
// method.
type addJSONRequest struct {
Data interface{} `json:"data"`
}

// getSTHResponse respresents the JSON response to the get-sth CT method
type getSTHResponse struct {
TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree
Expand Down Expand Up @@ -106,6 +102,13 @@ type getEntryAndProofResponse struct {
AuditPath []string `json:"audit_path"` // the corresponding proof
}

type getProofByHashResponse struct {
LeafIndex int `json:"leaf_index"` // The 0-based index of the end entity corresponding to the "hash" parameter.
AuditPath []string `json:"audit_path"` // An array of base64-encoded Merkle Tree nodes proving the inclusion of the chosen certificate.
Success bool `json:"success"`
ErrorMessage string `json:"error_message"`
}

// 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
Expand Down Expand Up @@ -283,10 +286,7 @@ func (c *LogClient) AddChainWithContext(ctx context.Context, chain []ct.ASN1Cert
return c.addChainWithRetry(ctx, AddChainPath, chain)
}

func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, error) {
req := addJSONRequest{
Data: data,
}
func (c *LogClient) AddJSON(req interface{}) (*ct.SignedCertificateTimestamp, error) {
var resp addChainResponse
_, _, err := c.postAndParse(c.uri+AddJSONPath, &req, &resp)
if err != nil {
Expand Down Expand Up @@ -366,6 +366,7 @@ func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) {
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
Expand All @@ -381,6 +382,8 @@ func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) {
case ct.PrecertLogEntryType:
chain, err = ct.UnmarshalPrecertChainArray(chainBytes)

case ct.XJSONLogEntryType:
// Do nothing. XJSON isn't a cert.
default:
return nil, fmt.Errorf("saw unknown entry type: %v", leaf.TimestampedEntry.EntryType)
}
Expand All @@ -392,3 +395,27 @@ func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) {
}
return entries, nil
}

func (c *LogClient) GetProofByHash(hash []byte, treeSize uint64) (ct.AuditPath, error) {
var resp getProofByHashResponse
b64Hash := url.QueryEscape(base64.StdEncoding.EncodeToString(hash))
u := fmt.Sprintf("%s%s?tree_size=%d&hash=%v", c.uri, GetProofByHashPath, treeSize, b64Hash)
log.Printf("%v", u)
err := c.fetchAndParse(u, &resp)
if err != nil {
return nil, err
}
if resp.ErrorMessage != "" {
return nil, fmt.Errorf("%v", resp.ErrorMessage)
}

path := make(ct.AuditPath, len(resp.AuditPath))
for i, p := range resp.AuditPath {
node, err := base64.StdEncoding.DecodeString(p)
if err != nil {
return nil, err
}
path[i] = node
}
return path, nil
}
67 changes: 67 additions & 0 deletions go/client/logclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,45 @@ const (
CertEntryExtraDataB64 = "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"

SubmissionCertB64 = "MIIEijCCA3KgAwIBAgICEk0wDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgY2Fja2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QwHhcNMTUxMDIxMjAxMTUyWhcNMjAxMDE5MjAxMTUyWjAfMR0wGwYDVQQDExRoYXBweSBoYWNrZXIgZmFrZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIKR3maBcUSsncXYzQT13D5Nr+Z3mLxMMh3TUdt6sACmqbJ0btRlgXfMtNLM2OU1I6a3Ju+tIZSdn2v21JBwvxUzpZQ4zy2cimIiMQDZCQHJwzC9GZn8HaW091iz9H0Go3A7WDXwYNmsdLNRi00o14UjoaVqaPsYrZWvRKaIRqaU0hHmS0AWwQSvN/93iMIXuyiwywmkwKbWnnxCQ/gsctKFUtcNrwEx9Wgj6KlhwDTyI1QWSBbxVYNyUgPFzKxrSmwMO0yNff7ho+QT9x5+Y/7XE59S4Mc4ZXxcXKew/gSlN9U5mvT+D2BhDtkCupdfsZNCQWp27A+b/DmrFI9NqsCAwEAAaOCAcIwggG+MBIGA1UdEwEB/wQIMAYBAf8CAQAwQwYDVR0eBDwwOqE4MAaCBC5taWwwCocIAAAAAAAAAAAwIocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAyBggrBgEFBQcwAYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5jb20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMvZHN0cm9vdGNheDMucDdjMB8GA1UdIwQYMBaAFOmkP+6epeby1dd5YDyTpi4kjpeqMFQGA1UdIARNMEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUHAgEWImh0dHA6Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JMLmNybDAdBgNVHQ4EFgQU+3hPEvlgFYMsnxd/NBmzLjbqQYkwDQYJKoZIhvcNAQELBQADggEBAA0YAeLXOklx4hhCikUUl+BdnFfn1g0W5AiQLVNIOL6PnqXu0wjnhNyhqdwnfhYMnoy4idRh4lB6pz8Gf9pnlLd/DnWSV3gS+/I/mAl1dCkKby6H2V790e6IHmIK2KYm3jm+U++FIdGpBdsQTSdmiX/rAyuxMDM0adMkNBwTfQmZQCz6nGHw1QcSPZMvZpsC8SkvekzxsjF1otOrMUPNPQvtTWrVx8GlR2qfx/4xbQa1v2frNvFBCmO59goz+jnWvfTtj2NjwDZ7vlMBsPm16dbKYC840uvRoZjxqsdc3ChCZjqimFqlNG/xoPA8+dTicZzCXE9ijPIcvW6y1aa3bGw="
AddJSONResp = `{
"sct_version":0,
"id":"KHYaGJAn++880NYaAY12sFBXKcenQRvMvfYE9F1CYVM=",
"timestamp":1337,
"extensions":"",
"signature":"BAMARjBEAiAIc21J5ZbdKZHw5wLxCP+MhBEsV5+nfvGyakOIv6FOvAIgWYMZb6Pw///uiNM7QTg2Of1OqmK1GbeGuEl9VJN8v8c="
}`
ProofByHashResp = `
{
"leaf_index": 3,
"audit_path": [
"pMumx96PIUB3TX543ljlpQ/RgZRqitRfykupIZrXq0Q=",
"5s2NQWkjmesu+Kqgp70TCwVLwq8obpHw/JyMGwN56pQ=",
"7VelXijfmGFSl62BWIsG8LRmxJGBq9XP8FxmszuT2Cg="
]
}`
)

// Create a test CT server.
func CtServer(t *testing.T) *httptest.Server {
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/ct/v1/get-sth":
fmt.Fprintf(w, `{"tree_size": %d, "timestamp": %d, "sha256_root_hash": "%s", "tree_head_signature": "%s"}`,
ValidSTHResponseTreeSize,
int64(ValidSTHResponseTimestamp),
ValidSTHResponseSHA256RootHash,
ValidSTHResponseTreeHeadSignature)

case r.URL.Path == "/ct/v1/add-json":
w.Write([]byte(AddJSONResp))
case r.URL.Path == "/ct/v1/get-proof-by-hash":
w.Write([]byte(ProofByHashResp))
default:
t.Fatalf("Incorrect URL path: %s", r.URL.Path)
}
}))
return hs
}
func TestGetEntriesWorks(t *testing.T) {
positiveDecimalNumber := regexp.MustCompile("[0-9]+")
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -214,3 +251,33 @@ func TestAddJSON(t *testing.T) {
}
}
}

func TestGetProofByHash(t *testing.T) {
hs := CtServer(t)
defer hs.Close()

c := New(hs.URL)

tests := []struct {
hash []byte
treesize uint64
}{
{
[]byte{0x4A, 0x9E, 0x8E, 0xDB, 0xE5, 0xCE, 0x2D, 0x2D,
0xA6, 0x9D, 0x48, 0x3E, 0xDB, 0x45, 0x18, 0x66,
0x75, 0xD4, 0xBE, 0x37, 0xB6, 0x49, 0xD4, 0x09,
0x23, 0xB1, 0x56, 0xA7, 0xD1, 0x27, 0x74, 0x63}, // random SHA256 output
5,
},
}

for _, tc := range tests {
auditPath, err := c.GetProofByHash(tc.hash, tc.treesize)
if err != nil {
t.Errorf("GetProofByHash(%h, %v): %v", tc.hash, tc.treesize, err)
}
if got := len(auditPath); got < 1 {
t.Errorf("len(GetProofByHash(%h, %v)): %v, want > 1", tc.hash, tc.treesize, got)
}
}
}
89 changes: 84 additions & 5 deletions go/serialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"container/list"
"crypto"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"

"github.com/benlaurie/objecthash/go/objecthash"
)

// Variable size structure prefix-header byte lengths
Expand All @@ -17,6 +20,7 @@ const (
ExtensionsLengthBytes = 2
CertificateChainLengthBytes = 3
SignatureLengthBytes = 2
JSONLengthBytes = 3
)

// Max lengths
Expand Down Expand Up @@ -80,7 +84,7 @@ func readVarBytes(r io.Reader, numLenBytes int) ([]byte, error) {
return nil, err
}
data := make([]byte, l)
if n, err := io.ReadFull(r, data); err != nil {
if n, err := io.ReadFull(r, data); err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
return nil, fmt.Errorf("short read: expected %d but got %d", l, n)
}
Expand Down Expand Up @@ -123,10 +127,10 @@ func readASN1CertList(r io.Reader, totalLenBytes int, elementLenBytes int) ([]AS
// Returns a non-nil error if there was a problem.
func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error {
var err error
if err = binary.Read(r, binary.BigEndian, &t.Timestamp); err != nil {
if err := binary.Read(r, binary.BigEndian, &t.Timestamp); err != nil {
return err
}
if err = binary.Read(r, binary.BigEndian, &t.EntryType); err != nil {
if err := binary.Read(r, binary.BigEndian, &t.EntryType); err != nil {
return err
}
switch t.EntryType {
Expand All @@ -141,13 +145,48 @@ 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)
}
t.Extensions, err = readVarBytes(r, ExtensionsLengthBytes)
return nil
}

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:
jsonhash := objecthash.CommonJSONHash(string(t.JSONData))
if err := writeVarBytes(w, jsonhash[:], 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.
Expand All @@ -174,6 +213,25 @@ func ReadMerkleTreeLeaf(r io.Reader) (*MerkleTreeLeaf, error) {
return &m, nil
}

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
}

// UnmarshalX509ChainArray unmarshalls the contents of the "chain:" entry in a
// GetEntries response in the case where the entry refers to an X509 leaf.
func UnmarshalX509ChainArray(b []byte) ([]ASN1Cert, error) {
Expand Down Expand Up @@ -337,9 +395,12 @@ func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry
}
switch entry.Leaf.TimestampedEntry.EntryType {
case X509LogEntryType:
return serializeV1CertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.X509Entry, entry.Leaf.TimestampedEntry.Extensions)
return serializeV1CertSCTSignatureInput(sct.Timestamp,
entry.Leaf.TimestampedEntry.X509Entry,
entry.Leaf.TimestampedEntry.Extensions)
case PrecertLogEntryType:
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.Extensions)
default:
Expand Down Expand Up @@ -509,3 +570,21 @@ func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) {
return nil, fmt.Errorf("unsupported STH version %d", sth.Version)
}
}

func SerializeV1SCTMerkleTreeLeafJSON(w io.Writer, sct *SignedCertificateTimestamp, data interface{}) error {
jsonData, err := json.Marshal(data)
if err != nil {
return nil
}
m := &MerkleTreeLeaf{
Version: sct.SCTVersion,
LeafType: TimestampedEntryLeafType,
TimestampedEntry: TimestampedEntry{
Timestamp: sct.Timestamp,
EntryType: XJSONLogEntryType,
JSONData: jsonData,
Extensions: sct.Extensions,
},
}
return SerializeMerkleTreeLeaf(w, m)
}
14 changes: 14 additions & 0 deletions go/signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"errors"
"flag"
"fmt"
"hash"
"log"
"math/big"
)
Expand Down Expand Up @@ -129,3 +130,16 @@ func (s SignatureVerifier) VerifySTHSignature(sth SignedTreeHead) error {
}
return s.verifySignature(sthData, sth.TreeHeadSignature)
}

func V1LeafHash() hash.Hash {
// V1 leaf hash is 0x00 || serialized proto.
h := sha256.New()
h.Write([]byte{0x00})
return h
}

func JSONV1LeafHash(sct *SignedCertificateTimestamp, data interface{}) []byte {
h := V1LeafHash()
SerializeV1SCTMerkleTreeLeafJSON(h, sct, data)
return h.Sum(nil)
}
Loading

0 comments on commit 27a8c06

Please sign in to comment.