diff --git a/.gitmodules b/.gitmodules index b07d555e7..ad2702ed2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,9 +2,6 @@ path = vendor/github.com/ethereum/go-ethereum url = https://github.com/harmony-one/go-ethereum branch = master -[submodule "vendor/github.com/golang/protobuf"] - path = vendor/github.com/golang/protobuf - url = https://github.com/golang/protobuf [submodule "vendor/github.com/libp2p/go-libp2p-kad-dht"] path = vendor/github.com/libp2p/go-libp2p-kad-dht url = https://github.com/libp2p/go-libp2p-kad-dht diff --git a/crypto/vrf/p256/p256.go b/crypto/vrf/p256/p256.go new file mode 100644 index 000000000..553dbc8ce --- /dev/null +++ b/crypto/vrf/p256/p256.go @@ -0,0 +1,303 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package p256 implements a verifiable random function using curve p256. +package p256 + +// Discrete Log based VRF from Appendix A of CONIKS: +// http://www.jbonneau.com/doc/MBBFF15-coniks.pdf +// based on "Unique Ring Signatures, a Practical Construction" +// http://fc13.ifca.ai/proc/5-1.pdf + +import ( + "bytes" + "context" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "crypto/x509" + "encoding/binary" + "encoding/pem" + "errors" + "fmt" + "math/big" + + "github.com/google/keytransparency/core/crypto/vrf" + "github.com/google/trillian/crypto/keys" + + "github.com/golang/protobuf/proto" +) + +var ( + curve = elliptic.P256() + params = curve.Params() + + // ErrPointNotOnCurve occurs when a public key is not on the curve. + ErrPointNotOnCurve = errors.New("point is not on the P256 curve") + // ErrWrongKeyType occurs when a key is not an ECDSA key. + ErrWrongKeyType = errors.New("not an ECDSA key") + // ErrNoPEMFound occurs when attempting to parse a non PEM data structure. + ErrNoPEMFound = errors.New("no PEM block found") + // ErrInvalidVRF occurs when the VRF does not validate. + ErrInvalidVRF = errors.New("invalid VRF proof") +) + +// PublicKey holds a public VRF key. +type PublicKey struct { + *ecdsa.PublicKey +} + +// PrivateKey holds a private VRF key. +type PrivateKey struct { + *ecdsa.PrivateKey +} + +// GenerateKey generates a fresh keypair for this VRF +func GenerateKey() (vrf.PrivateKey, vrf.PublicKey) { + key, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil + } + + return &PrivateKey{PrivateKey: key}, &PublicKey{PublicKey: &key.PublicKey} +} + +// H1 hashes m to a curve point +func H1(m []byte) (x, y *big.Int) { + h := sha512.New() + var i uint32 + byteLen := (params.BitSize + 7) >> 3 + for x == nil && i < 100 { + // TODO: Use a NIST specified DRBG. + h.Reset() + binary.Write(h, binary.BigEndian, i) + h.Write(m) + r := []byte{2} // Set point encoding to "compressed", y=0. + r = h.Sum(r) + x, y = Unmarshal(curve, r[:byteLen+1]) + i++ + } + return +} + +var one = big.NewInt(1) + +// H2 hashes to an integer [1,N-1] +func H2(m []byte) *big.Int { + // NIST SP 800-90A § A.5.1: Simple discard method. + byteLen := (params.BitSize + 7) >> 3 + h := sha512.New() + for i := uint32(0); ; i++ { + // TODO: Use a NIST specified DRBG. + h.Reset() + binary.Write(h, binary.BigEndian, i) + h.Write(m) + b := h.Sum(nil) + k := new(big.Int).SetBytes(b[:byteLen]) + if k.Cmp(new(big.Int).Sub(params.N, one)) == -1 { + return k.Add(k, one) + } + } +} + +// Evaluate returns the verifiable unpredictable function evaluated at m +func (k PrivateKey) Evaluate(m []byte) (index [32]byte, proof []byte) { + nilIndex := [32]byte{} + // Prover chooses r <-- [1,N-1] + r, _, _, err := elliptic.GenerateKey(curve, rand.Reader) + if err != nil { + return nilIndex, nil + } + ri := new(big.Int).SetBytes(r) + + // H = H1(m) + Hx, Hy := H1(m) + + // VRF_k(m) = [k]H + sHx, sHy := params.ScalarMult(Hx, Hy, k.D.Bytes()) + vrf := elliptic.Marshal(curve, sHx, sHy) // 65 bytes. + + // G is the base point + // s = H2(G, H, [k]G, VRF, [r]G, [r]H) + rGx, rGy := params.ScalarBaseMult(r) + rHx, rHy := params.ScalarMult(Hx, Hy, r) + var b bytes.Buffer + b.Write(elliptic.Marshal(curve, params.Gx, params.Gy)) + b.Write(elliptic.Marshal(curve, Hx, Hy)) + b.Write(elliptic.Marshal(curve, k.PublicKey.X, k.PublicKey.Y)) + b.Write(vrf) + b.Write(elliptic.Marshal(curve, rGx, rGy)) + b.Write(elliptic.Marshal(curve, rHx, rHy)) + s := H2(b.Bytes()) + + // t = r−s*k mod N + t := new(big.Int).Sub(ri, new(big.Int).Mul(s, k.D)) + t.Mod(t, params.N) + + // Index = H(vrf) + index = sha256.Sum256(vrf) + + // Write s, t, and vrf to a proof blob. Also write leading zeros before s and t + // if needed. + var buf bytes.Buffer + buf.Write(make([]byte, 32-len(s.Bytes()))) + buf.Write(s.Bytes()) + buf.Write(make([]byte, 32-len(t.Bytes()))) + buf.Write(t.Bytes()) + buf.Write(vrf) + + return index, buf.Bytes() +} + +// ProofToHash asserts that proof is correct for m and outputs index. +func (pk *PublicKey) ProofToHash(m, proof []byte) (index [32]byte, err error) { + nilIndex := [32]byte{} + // verifier checks that s == H2(m, [t]G + [s]([k]G), [t]H1(m) + [s]VRF_k(m)) + if got, want := len(proof), 64+65; got != want { + return nilIndex, ErrInvalidVRF + } + + // Parse proof into s, t, and vrf. + s := proof[0:32] + t := proof[32:64] + vrf := proof[64 : 64+65] + + uHx, uHy := elliptic.Unmarshal(curve, vrf) + if uHx == nil { + return nilIndex, ErrInvalidVRF + } + + // [t]G + [s]([k]G) = [t+ks]G + tGx, tGy := params.ScalarBaseMult(t) + ksGx, ksGy := params.ScalarMult(pk.X, pk.Y, s) + tksGx, tksGy := params.Add(tGx, tGy, ksGx, ksGy) + + // H = H1(m) + // [t]H + [s]VRF = [t+ks]H + Hx, Hy := H1(m) + tHx, tHy := params.ScalarMult(Hx, Hy, t) + sHx, sHy := params.ScalarMult(uHx, uHy, s) + tksHx, tksHy := params.Add(tHx, tHy, sHx, sHy) + + // H2(G, H, [k]G, VRF, [t]G + [s]([k]G), [t]H + [s]VRF) + // = H2(G, H, [k]G, VRF, [t+ks]G, [t+ks]H) + // = H2(G, H, [k]G, VRF, [r]G, [r]H) + var b bytes.Buffer + b.Write(elliptic.Marshal(curve, params.Gx, params.Gy)) + b.Write(elliptic.Marshal(curve, Hx, Hy)) + b.Write(elliptic.Marshal(curve, pk.X, pk.Y)) + b.Write(vrf) + b.Write(elliptic.Marshal(curve, tksGx, tksGy)) + b.Write(elliptic.Marshal(curve, tksHx, tksHy)) + h2 := H2(b.Bytes()) + + // Left pad h2 with zeros if needed. This will ensure that h2 is padded + // the same way s is. + var buf bytes.Buffer + buf.Write(make([]byte, 32-len(h2.Bytes()))) + buf.Write(h2.Bytes()) + + if !hmac.Equal(s, buf.Bytes()) { + return nilIndex, ErrInvalidVRF + } + return sha256.Sum256(vrf), nil +} + +// NewFromWrappedKey creates a VRF signer object from an encrypted private key. +// The opaque private key must resolve to an `ecdsa.PrivateKey` in order to work. +func NewFromWrappedKey(ctx context.Context, wrapped proto.Message) (vrf.PrivateKey, error) { + // Unwrap. + signer, err := keys.NewSigner(ctx, wrapped) + if err != nil { + return nil, err + } + + switch key := signer.(type) { + case *ecdsa.PrivateKey: + return NewVRFSigner(key) + default: + return nil, fmt.Errorf("wrapped key has wrong type: %T, want ecdsa.PrivateKey", key) + } +} + +// NewVRFSigner creates a signer object from a private key. +func NewVRFSigner(key *ecdsa.PrivateKey) (vrf.PrivateKey, error) { + if *(key.Params()) != *curve.Params() { + return nil, ErrPointNotOnCurve + } + if !curve.IsOnCurve(key.X, key.Y) { + return nil, ErrPointNotOnCurve + } + return &PrivateKey{PrivateKey: key}, nil +} + +// Public returns the corresponding public key as bytes. +func (k PrivateKey) Public() crypto.PublicKey { + return &k.PublicKey +} + +// NewVRFVerifier creates a verifier object from a public key. +func NewVRFVerifier(pubkey *ecdsa.PublicKey) (vrf.PublicKey, error) { + if *(pubkey.Params()) != *curve.Params() { + return nil, ErrPointNotOnCurve + } + if !curve.IsOnCurve(pubkey.X, pubkey.Y) { + return nil, ErrPointNotOnCurve + } + return &PublicKey{PublicKey: pubkey}, nil +} + +// NewVRFSignerFromPEM creates a vrf private key from a PEM data structure. +func NewVRFSignerFromPEM(b []byte) (vrf.PrivateKey, error) { + p, _ := pem.Decode(b) + if p == nil { + return nil, ErrNoPEMFound + } + return NewVRFSignerFromRawKey(p.Bytes) +} + +// NewVRFSignerFromRawKey returns the private key from a raw private key bytes. +func NewVRFSignerFromRawKey(b []byte) (vrf.PrivateKey, error) { + k, err := x509.ParseECPrivateKey(b) + if err != nil { + return nil, err + } + return NewVRFSigner(k) +} + +// NewVRFVerifierFromPEM creates a vrf public key from a PEM data structure. +func NewVRFVerifierFromPEM(b []byte) (vrf.PublicKey, error) { + p, _ := pem.Decode(b) + if p == nil { + return nil, ErrNoPEMFound + } + return NewVRFVerifierFromRawKey(p.Bytes) +} + +// NewVRFVerifierFromRawKey returns the public key from a raw public key bytes. +func NewVRFVerifierFromRawKey(b []byte) (vrf.PublicKey, error) { + k, err := x509.ParsePKIXPublicKey(b) + if err != nil { + return nil, err + } + pk, ok := k.(*ecdsa.PublicKey) + if !ok { + return nil, ErrWrongKeyType + } + return NewVRFVerifier(pk) +} diff --git a/crypto/vrf/p256/p256_test.go b/crypto/vrf/p256/p256_test.go new file mode 100644 index 000000000..bf16a80b1 --- /dev/null +++ b/crypto/vrf/p256/p256_test.go @@ -0,0 +1,349 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package p256 + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "io/ioutil" + "math" + "os" + "testing" + + "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/proto" + "github.com/google/keytransparency/core/testdata" + "github.com/google/trillian/crypto/keys" + "github.com/google/trillian/crypto/keys/der" + "github.com/google/trillian/crypto/keyspb" + + pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto" + _ "github.com/google/trillian/crypto/keys/der/proto" +) + +const ( + // openssl ecparam -name prime256v1 -genkey -out p256-key.pem + privKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGbhE2+z8d5lHzb0gmkS78d86gm5gHUtXCpXveFbK3pcoAoGCCqGSM49 +AwEHoUQDQgAEUxX42oxJ5voiNfbjoz8UgsGqh1bD1NXK9m8VivPmQSoYUdVFgNav +csFaQhohkiCEthY51Ga6Xa+ggn+eTZtf9Q== +-----END EC PRIVATE KEY-----` + // openssl ec -in p256-key.pem -pubout -out p256-pubkey.pem + pubKey = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUxX42oxJ5voiNfbjoz8UgsGqh1bD +1NXK9m8VivPmQSoYUdVFgNavcsFaQhohkiCEthY51Ga6Xa+ggn+eTZtf9Q== +-----END PUBLIC KEY-----` +) + +func TestH1(t *testing.T) { + for i := 0; i < 10000; i++ { + m := make([]byte, 100) + if _, err := rand.Read(m); err != nil { + t.Fatalf("Failed generating random message: %v", err) + } + x, y := H1(m) + if x == nil { + t.Errorf("H1(%v)=%v, want curve point", m, x) + } + if got := curve.Params().IsOnCurve(x, y); !got { + t.Errorf("H1(%v)=[%v, %v], is not on curve", m, x, y) + } + } +} + +func TestH2(t *testing.T) { + l := 32 + for i := 0; i < 10000; i++ { + m := make([]byte, 100) + if _, err := rand.Read(m); err != nil { + t.Fatalf("Failed generating random message: %v", err) + } + x := H2(m) + if got := len(x.Bytes()); got < 1 || got > l { + t.Errorf("len(h2(%v)) = %v, want: 1 <= %v <= %v", m, got, got, l) + } + } +} + +func TestNewFromWrappedKey(t *testing.T) { + ctx := context.Background() + for _, tc := range []struct { + desc string + wantFromWrappedErr bool + spec *keyspb.Specification + keygen keys.ProtoGenerator + }{ + { + desc: "DER with ECDSA spec", + spec: &keyspb.Specification{ + Params: &keyspb.Specification_EcdsaParams{ + EcdsaParams: &keyspb.Specification_ECDSA{ + Curve: keyspb.Specification_ECDSA_P256, + }, + }, + }, + keygen: func(ctx context.Context, spec *keyspb.Specification) (proto.Message, error) { + return der.NewProtoFromSpec(spec) + }, + }, + { + desc: "DER with Non-ECDSA spec", + wantFromWrappedErr: true, + spec: &keyspb.Specification{ + Params: &keyspb.Specification_RsaParams{ + RsaParams: &keyspb.Specification_RSA{Bits: 2048}, + }, + }, + keygen: func(ctx context.Context, spec *keyspb.Specification) (proto.Message, error) { + return der.NewProtoFromSpec(spec) + }, + }, + } { + t.Run(tc.desc, func(t *testing.T) { + // Generate VRF key. + wrapped, err := tc.keygen(ctx, tc.spec) + if err != nil { + t.Fatalf("keygen failed: %v", err) + } + vrfPriv, err := NewFromWrappedKey(ctx, wrapped) + if got, want := err != nil, tc.wantFromWrappedErr; got != want { + t.Errorf("NewFromWrappedKey (): %v, want err: %v", err, want) + } + if err != nil { + return + } + + vrfPubDER, err := der.MarshalPublicKey(vrfPriv.Public()) + if err != nil { + t.Fatalf("MarshalPublicKey failed: %v", err) + } + vrfPub, err := NewVRFVerifierFromRawKey(vrfPubDER) + if err != nil { + t.Fatalf("NewVRFVerifierFromRawKey(): %v", err) + } + // Test that the public and private components match. + m := []byte("foobar") + indexA, proof := vrfPriv.Evaluate(m) + indexB, err := vrfPub.ProofToHash(m, proof) + if err != nil { + t.Fatalf("ProofToHash(): %v", err) + } + if got, want := indexB, indexA; got != want { + t.Errorf("ProofToHash(%s, %x): %x, want %x", m, proof, got, want) + } + }) + } +} + +func TestVRF(t *testing.T) { + k, pk := GenerateKey() + + m1 := []byte("data1") + m2 := []byte("data2") + m3 := []byte("data2") + index1, proof1 := k.Evaluate(m1) + index2, proof2 := k.Evaluate(m2) + index3, proof3 := k.Evaluate(m3) + for _, tc := range []struct { + m []byte + index [32]byte + proof []byte + err error + }{ + {m1, index1, proof1, nil}, + {m2, index2, proof2, nil}, + {m3, index3, proof3, nil}, + {m3, index3, proof2, nil}, + {m3, index3, proof1, ErrInvalidVRF}, + } { + index, err := pk.ProofToHash(tc.m, tc.proof) + if got, want := err, tc.err; got != want { + t.Errorf("ProofToHash(%s, %x): %v, want %v", tc.m, tc.proof, got, want) + } + if err != nil { + continue + } + if got, want := index, tc.index; got != want { + t.Errorf("ProofToInex(%s, %x): %x, want %x", tc.m, tc.proof, got, want) + } + } +} + +// Test vectors in core/testdata are generated by running +// go generate ./core/testdata +func TestProofToHash(t *testing.T) { + directoryFile := "../../../test/testdata/directory.json" + f, err := os.Open(directoryFile) + if err != nil { + t.Fatalf("ReadFile(%v): %v", directoryFile, err) + } + defer f.Close() + var directory pb.Directory + if err := jsonpb.Unmarshal(f, &directory); err != nil { + t.Fatalf("jsonpb.Unmarshal(): %v", err) + } + pk, err := NewVRFVerifierFromRawKey(directory.GetVrf().GetDer()) + if err != nil { + t.Fatalf("NewVRFVerifier failure: %v", err) + } + + respFile := "../../../test/testdata/getentryresponse.json" + b, err := ioutil.ReadFile(respFile) + if err != nil { + t.Fatalf("ReadFile(%v): %v", respFile, err) + } + var getUserResponses []testdata.GetUserResponseVector + if err := json.Unmarshal(b, &getUserResponses); err != nil { + t.Fatalf("Unmarshal(): %v", err) + } + for _, tc := range getUserResponses { + t.Run(tc.Desc, func(t *testing.T) { + _, err := pk.ProofToHash([]byte(tc.UserID), tc.Resp.GetLeaf().GetVrfProof()) + if err != nil { + t.Errorf("ProofToHash(%v): %v)", tc.Desc, err) + } + }) + } +} + +func TestReadFromOpenSSL(t *testing.T) { + for _, tc := range []struct { + priv string + pub string + }{ + {privKey, pubKey}, + } { + // Private VRF Key + signer, err := NewVRFSignerFromPEM([]byte(tc.priv)) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + + // Public VRF key + verifier, err := NewVRFVerifierFromPEM([]byte(tc.pub)) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + + // Evaluate and verify. + m := []byte("M") + _, proof := signer.Evaluate(m) + if _, err := verifier.ProofToHash(m, proof); err != nil { + t.Errorf("Failed verifying VRF proof") + } + } +} + +func TestRightTruncateProof(t *testing.T) { + k, pk := GenerateKey() + + data := []byte("data") + _, proof := k.Evaluate(data) + proofLen := len(proof) + for i := 0; i < proofLen; i++ { + proof = proof[:len(proof)-1] + if _, err := pk.ProofToHash(data, proof); err == nil { + t.Errorf("Verify unexpectedly succeeded after truncating %v bytes from the end of proof", i) + } + } +} + +func TestLeftTruncateProof(t *testing.T) { + k, pk := GenerateKey() + + data := []byte("data") + _, proof := k.Evaluate(data) + proofLen := len(proof) + for i := 0; i < proofLen; i++ { + proof = proof[1:] + if _, err := pk.ProofToHash(data, proof); err == nil { + t.Errorf("Verify unexpectedly succeeded after truncating %v bytes from the beginning of proof", i) + } + } +} + +func TestBitFlip(t *testing.T) { + k, pk := GenerateKey() + + data := []byte("data") + _, proof := k.Evaluate(data) + for i := 0; i < len(proof)*8; i++ { + // Flip bit in position i. + if _, err := pk.ProofToHash(data, flipBit(proof, i)); err == nil { + t.Errorf("Verify unexpectedly succeeded after flipping bit %v of vrf", i) + } + } +} + +func flipBit(a []byte, pos int) []byte { + index := int(math.Floor(float64(pos) / 8)) + b := a[index] + b ^= (1 << uint(math.Mod(float64(pos), 8.0))) + + var buf bytes.Buffer + buf.Write(a[:index]) + buf.Write([]byte{b}) + buf.Write(a[index+1:]) + return buf.Bytes() +} + +func TestVectors(t *testing.T) { + k, err := NewVRFSignerFromPEM([]byte(privKey)) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + pk, err := NewVRFVerifierFromPEM([]byte(pubKey)) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + for _, tc := range []struct { + m []byte + index [32]byte + }{ + { + m: []byte("test"), + index: h2i("1af0a7e3d9a96a71be6257cf4ad1a0ffdec57e9959b2eafc4673a6c31241fc9f"), + }, + { + m: nil, + index: h2i("2ebac3669807f474f4d49891a1d0b2fba8e966f945ac01cbfffb3bb48627e67d"), + }, + } { + index, proof := k.Evaluate(tc.m) + if got, want := index, tc.index; got != want { + t.Errorf("Evaluate(%s).Index: %x, want %x", tc.m, got, want) + } + index2, err := pk.ProofToHash(tc.m, proof) + if err != nil { + t.Errorf("ProofToHash(%s): %v", tc.m, err) + } + if got, want := index2, index; got != want { + t.Errorf("ProofToHash(%s): %x, want %x", tc.m, got, want) + } + } +} + +func h2i(h string) [32]byte { + b, err := hex.DecodeString(h) + if err != nil { + panic("Invalid hex") + } + var i [32]byte + copy(i[:], b) + return i +} diff --git a/crypto/vrf/p256/unmarshal.go b/crypto/vrf/p256/unmarshal.go new file mode 100644 index 000000000..947026f88 --- /dev/null +++ b/crypto/vrf/p256/unmarshal.go @@ -0,0 +1,81 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package p256 + +// This file implements compressed point unmarshaling. Preferably this +// functionality would be in a standard library. Code borrowed from: +// https://go-review.googlesource.com/#/c/1883/2/src/crypto/elliptic/elliptic.go + +import ( + "crypto/elliptic" + "math/big" +) + +// Unmarshal a compressed point in the form specified in section 4.3.6 of ANSI X9.62. +func Unmarshal(curve elliptic.Curve, data []byte) (x, y *big.Int) { + byteLen := (curve.Params().BitSize + 7) >> 3 + if (data[0] &^ 1) != 2 { + return // unrecognized point encoding + } + if len(data) != 1+byteLen { + return + } + + // Based on Routine 2.2.4 in NIST Mathematical routines paper + params := curve.Params() + tx := new(big.Int).SetBytes(data[1 : 1+byteLen]) + y2 := y2(params, tx) + sqrt := defaultSqrt + ty := sqrt(y2, params.P) + if ty == nil { + return // "y^2" is not a square: invalid point + } + var y2c big.Int + y2c.Mul(ty, ty).Mod(&y2c, params.P) + if y2c.Cmp(y2) != 0 { + return // sqrt(y2)^2 != y2: invalid point + } + if ty.Bit(0) != uint(data[0]&1) { + ty.Sub(params.P, ty) + } + + x, y = tx, ty // valid point: return it + return +} + +// Use the curve equation to calculate y² given x. +// only applies to curves of the form y² = x³ - 3x + b. +func y2(curve *elliptic.CurveParams, x *big.Int) *big.Int { + + // y² = x³ - 3x + b + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + + threeX := new(big.Int).Lsh(x, 1) + threeX.Add(threeX, x) + + x3.Sub(x3, threeX) + x3.Add(x3, curve.B) + x3.Mod(x3, curve.P) + return x3 +} + +func defaultSqrt(x, p *big.Int) *big.Int { + var r big.Int + if nil == r.ModSqrt(x, p) { + return nil // x is not a square + } + return &r +} diff --git a/crypto/vrf/vrf.go b/crypto/vrf/vrf.go new file mode 100644 index 000000000..c0f9f0cbe --- /dev/null +++ b/crypto/vrf/vrf.go @@ -0,0 +1,40 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package vrf defines the interface to a verifiable random function. +package vrf + +import ( + "crypto" +) + +// A VRF is a pseudorandom function f_k from a secret key k, such that that +// knowledge of k not only enables one to evaluate f_k at for any message m, +// but also to provide an NP-proof that the value f_k(m) is indeed correct +// without compromising the unpredictability of f_k for any m' != m. +// http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=814584 + +// PrivateKey supports evaluating the VRF function. +type PrivateKey interface { + // Evaluate returns the output of H(f_k(m)) and its proof. + Evaluate(m []byte) (index [32]byte, proof []byte) + // Public returns the corresponding public key. + Public() crypto.PublicKey +} + +// PublicKey supports verifying output from the VRF function. +type PublicKey interface { + // ProofToHash verifies the NP-proof supplied by Proof and outputs Index. + ProofToHash(m, proof []byte) (index [32]byte, err error) +} diff --git a/test/testdata/directory.json b/test/testdata/directory.json new file mode 100644 index 000000000..945fad4ff --- /dev/null +++ b/test/testdata/directory.json @@ -0,0 +1,28 @@ +{ + "directoryId": "integration", + "log": { + "treeId": "8541686838476068721", + "treeType": "PREORDERED_LOG", + "hashStrategy": "RFC6962_SHA256", + "hashAlgorithm": "SHA256", + "signatureAlgorithm": "ECDSA", + "publicKey": { + "der": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqGXPnhMIclRmYHSmAnCMmfDUJ9iNBMmFxR/wHJdL12AuVUkgcuhbEp2hy5ETs7bfFc2P95IYFlmbiuHMq3UY/A==" + } + }, + "map": { + "treeId": "6598072539431303895", + "treeType": "MAP", + "hashStrategy": "CONIKS_SHA256", + "hashAlgorithm": "SHA256", + "signatureAlgorithm": "ECDSA", + "publicKey": { + "der": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWLHm0TLYaTzENpPkBl2E79ySqJI+EW51VpoWh7wqY3OjSJcft4zgEeNeHYEb/T2jBFH4eYg4iSN7D/VYaJxJRA==" + } + }, + "vrf": { + "der": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEF2Pm2kKya+JBun1QRmKQMcoMOIBNWp8fjECkJX+/hNWdV1UKb12W+yXcX2MqN7ZMX77hS9mLus/WaE0NS370mA==" + }, + "minInterval": "0.100s", + "maxInterval": "216000s" +} \ No newline at end of file diff --git a/test/testdata/getentryresponse.json b/test/testdata/getentryresponse.json new file mode 100644 index 000000000..7cf5de274 --- /dev/null +++ b/test/testdata/getentryresponse.json @@ -0,0 +1,1503 @@ +[ + { + "Desc": "empty_alice", + "UserID": "alice", + "Resp": { + "revision": { + "map_root": { + "map_root": { + "map_root": "AAEgT5c5rf7RjwprNaZTxAls9fuKTJ0h1PFrBF5VyUgfglMVfD9mQONvCAAAAAAAAAAAAAA=", + "signature": "MEUCIQCNSY1U94y28kwMU6JEzxpVT0NfxgSlBqW+Q+9gss6JqAIgDVQvDTPEC80qML4i0g0d/ZBRU/mRyW6Zf4IcWmFu4Fc=" + } + }, + "latest_log_root": { + "log_root": { + "timestamp_nanos": 1548182080757002000, + "root_hash": "oFazZRJgxpnfHPgM+Ki1hu+w5plbmP2mk8OC7pNtYUM=", + "tree_size": 1, + "tree_revision": 1, + "key_hint": "dooq7H3+13E=", + "log_root": "AAEAAAAAAAAAASCgVrNlEmDGmd8c+Az4qLWG77DmmVuY/aaTw4Luk21hQxV8P2ZbJW8QAAAAAAAAAAEAAA==", + "log_root_signature": "MEUCIQC9f1bDtioj1ToCJE7pFpDXoR4WDW81MZq5LeNkgiNt3wIgfiMGSLgRp1yIAC2L7bFz7qzkBpSCDJ8ECGZ/628aZms=" + } + } + }, + "leaf": { + "vrf_proof": "rxm+eUnt0zCGVeHtrn0SF9dUZ6ZPgRcWYqroKpjNonQ8y+scOc+/3QuS9K9c6otPQK2yuHbSySnCDjsVGFbwawRTJUYW21qW/fCBqGRPufN2+S2CHVyVSP1EWLTIn1M0zJgw+OQcAgHUdSP1DkqoE5X9Le+VWO+nuJtc4eJODJCo", + "map_inclusion": { + "leaf": { + "index": "A9/B/HF0DP6pap5CSp8/Jo05FhoGWECfsTAnRlVdzvM=" + }, + "inclusion": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + } + } + }, + "TrustNewLog": true + }, + { + "Desc": "bob0_set", + "UserID": "bob", + "Resp": { + "revision": { + "map_root": { + "map_root": { + "map_root": "AAEgvZoGMrg0Mz2GPvObuOxCbt0iwm3F4j6oFPssBYof/hgVfD9mqlPR0AAAAAAAAAABABISAhgBEgwQsf2F/Onsj74VGAI=", + "signature": "MEQCIEpt5DMWdTCRsQA9fnzA2lO5XqD5p/fuJBNkO5sJhaX3AiBwPXdC16D69a7/YZY6pICK3GK7n2oYQxSWOVbBHJhqLg==" + }, + "log_inclusion": [ + "oFazZRJgxpnfHPgM+Ki1hu+w5plbmP2mk8OC7pNtYUM=" + ] + }, + "latest_log_root": { + "log_root": { + "timestamp_nanos": 1548182082188587000, + "root_hash": "iLzyO7OaPvSPpetDbGd9eNpo/NfUcEYeBta8z/hYG3M=", + "tree_size": 2, + "tree_revision": 2, + "key_hint": "dooq7H3+13E=", + "log_root": "AAEAAAAAAAAAAiCIvPI7s5o+9I+l60NsZ3142mj819RwRh4G1rzP+FgbcxV8P2awea/4AAAAAAAAAAIAAA==", + "log_root_signature": "MEUCIC8BasnxCIVXyPFjgdRYy64Pnu4ln/fTGHERN+5CqRpKAiEAoYwgFeRYZZoRaKdBgUcu2noiCr4LDoyohzSltYIOX58=" + }, + "log_consistency": [ + "74BJt1hw9+CNRkcAlNhl0+nTymtLDwUBRsf4xRZP+Wk=" + ] + } + }, + "leaf": { + "vrf_proof": "5QGNxndM4zopl1q2G3ry8eGSW5tB9nqP44e6LYpb8duYiNlm42VkdehwD6IDQus3CJOssIz62JxgwJpk/yESggSHy1HLhWFLT+nQFEzzYq4x2psj6PyUNlaPWfnwVizyOB31qTBMPiNmlf7Qgp/yArNoYDSuvrjR9Jmlku+iA5MU", + "map_inclusion": { + "leaf": { + "index": "Umn2fclcSdomcb9UlHcDY1SLm1A/wILzh8NdoYffbcQ=" + }, + "inclusion": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "KQiyHtT5vfRpQnj3OgxqeTUJjwgqKRh8YrhyDumC9EE=", + "" + ] + } + } + }, + "TrustNewLog": false + }, + { + "Desc": "set_carol", + "UserID": "carol", + "Resp": { + "revision": { + "map_root": { + "map_root": { + "map_root": "AAEgq+MeLyWm6X86AnvprF02hvXcohWdvDQ80iI3oSmegPwVfD9mx3OVmAAAAAAAAAACABwSAhgBEhYIsf2F/Onsj74VEIGz9Nvr7I++FRgC", + "signature": "MEYCIQD07j/mrSgmwpxSrJ1Y+Wphn8OQUefOAnVf1uhk8ayylAIhAMcZ3Whap5q86/DoB3PPc559er22dhswOzc2f8Fju+N0" + }, + "log_inclusion": [ + "iLzyO7OaPvSPpetDbGd9eNpo/NfUcEYeBta8z/hYG3M=" + ] + }, + "latest_log_root": { + "log_root": { + "timestamp_nanos": 1548182082690022000, + "root_hash": "e+oc/pnJysibFt+ukJjutna0OsAuYxSjpTnSQ/mfYtg=", + "tree_size": 3, + "tree_revision": 3, + "key_hint": "dooq7H3+13E=", + "log_root": "AAEAAAAAAAAAAyB76hz+mcnKyJsW366QmO62drQ6wC5jFKOlOdJD+Z9i2BV8P2bOXPpwAAAAAAAAAAMAAA==", + "log_root_signature": "MEUCIQDEMBnuDL0+CdMf7tLf53eD0+oA5uaBaZ1nRwWZYf5shAIgZJvUuSH2x3H5KrL+tmPYALaTXCfG7RggbxHnai2DYqA=" + }, + "log_consistency": [ + "74BJt1hw9+CNRkcAlNhl0+nTymtLDwUBRsf4xRZP+Wk=", + "JwjPnsR+/v/pG79eO4J9a+7huD2EB4ulIYwrrWhH7V0=" + ] + } + }, + "leaf": { + "vrf_proof": "2oz80eq1m7ogFje1YDDFi82+kApvcQ3/wSMyegOlE5gNZEzguYGUGWdcG09XWokfGMJwd3NEWR2TLf/uxZkENwSmXjmxq3oAt/q89fwRZiF4eoAGepK4YcyzKtBD4mfS6gB7/AQ7PNRUocMVfUQnZCienTNXyrdtaOTCtzwaDIjc", + "map_inclusion": { + "leaf": { + "index": "JYx5mwUZM4rLnxSas5/NC9GiuqMYVWThqNqJdDz6bvw=" + }, + "inclusion": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "PhzerX1zlg+7TcTDd4dqhaEoCHIBEx/gkOHP4HwMrEE=", + "+Yz47fGxCilRS8cEhVPZNxVJDbwWMtb8kc7ckdny0Yw=", + "" + ] + } + } + }, + "TrustNewLog": false + }, + { + "Desc": "bob1_get", + "UserID": "bob", + "Resp": { + "revision": { + "map_root": { + "map_root": { + "map_root": "AAEgPJMMcS3zYA+lydtkvfNk8bgbQzLfemGSX7FhLr1kM4QVfD9m4Ul9wAAAAAAAAAADABwSAhgBEhYIgbP02+vsj74VEPm2kLrt7I++FRgC", + "signature": "MEUCIBtnr8CGfXlQvwD5zlGHu8YkTLktLv5QWbajrB1/kKQQAiEAzTdT19rnuMNtOROFryClaVo2f2KSRwuxTYFxOA/qNk4=" + }, + "log_inclusion": [ + "JwjPnsR+/v/pG79eO4J9a+7huD2EB4ulIYwrrWhH7V0=", + "iLzyO7OaPvSPpetDbGd9eNpo/NfUcEYeBta8z/hYG3M=" + ] + }, + "latest_log_root": { + "log_root": { + "timestamp_nanos": 1548182083190127000, + "root_hash": "lE/bH+OqOzXs2rFNvTfgu7gAyqYdx8s9kdUPLjHhS1M=", + "tree_size": 4, + "tree_revision": 4, + "key_hint": "dooq7H3+13E=", + "log_root": "AAEAAAAAAAAABCCUT9sf46o7NezasU29N+C7uADKph3Hyz2R1Q8uMeFLUxV8P2bsK/mYAAAAAAAAAAQAAA==", + "log_root_signature": "MEUCIBMfUpMx8mu2yHupR8QZJrTGZL2HSHQW89iDo60LGETzAiEAjmthkwb3+Tzkz5QKSOU93XEmVKA7qzZgwqRuVClWXZc=" + }, + "log_consistency": [ + "74BJt1hw9+CNRkcAlNhl0+nTymtLDwUBRsf4xRZP+Wk=", + "KpnnGZxuBOimg2bVX+gxdBjrJEAWe/6Fr71HavKgH3M=" + ] + } + }, + "leaf": { + "vrf_proof": "BGmKJMizUD1YVQuhuDdCGQkYuwo7fyJVx5naAXFgT8gySm+KCJHXHGQT4B6HVUoFSrDG5Hw3JRdKRn3GgLKyzgSHy1HLhWFLT+nQFEzzYq4x2psj6PyUNlaPWfnwVizyOB31qTBMPiNmlf7Qgp/yArNoYDSuvrjR9Jmlku+iA5MU", + "map_inclusion": { + "leaf": { + "index": "Umn2fclcSdomcb9UlHcDY1SLm1A/wILzh8NdoYffbcQ=", + "leaf_hash": "dHeM44usHkJb0qjAYu8JX02MCq8VHolTyVXLDGJbWdI=", + "leaf_value": "Cv4BGiBSafZ9yVxJ2iZxv1SUdwNjVIubUD/AgvOHw12hh99txDIgHo4zXOSWjFcn5ACKbEwbEI9J4dXDq/gCobR85mMaO3E6lQEIARKQAQqHAQo1dHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuRWNkc2FQdWJsaWNLZXkSTBIGCAMQAhgCGiD7FU52mGR+kS2Xs4XygLK9bDfV1XiGcZtcM9t0WUvWeSIgmsoZ6shH0XVzZaQU9lPYV3EsZYiiNax6wCRQ8d53LbQYAxABGAEgAUIg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFUSTAEAAAABMEUCIFaVVPyv3/XXtr4t9EaFRN7L0aqd6MvqNf5TuriTLAbKAiEA5aSSC4erRALn7WKjs+eXaXG2wa8WYxEjAaHC4a2QLlQ=" + }, + "inclusion": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "H8rbxUJbcMZtCaW49J4nTPywn99uazGOTxYgJwV0XJI=", + "" + ] + }, + "committed": { + "key": "9yOpkO/50Op5NJJKdx1etw==", + "data": "Ym9iLWtleTE=" + } + } + }, + "TrustNewLog": false + }, + { + "Desc": "bob1_set", + "UserID": "bob", + "Resp": { + "revision": { + "map_root": { + "map_root": { + "map_root": "AAEgPJMMcS3zYA+lydtkvfNk8bgbQzLfemGSX7FhLr1kM4QVfD9m4Ul9wAAAAAAAAAADABwSAhgBEhYIgbP02+vsj74VEPm2kLrt7I++FRgC", + "signature": "MEUCIBtnr8CGfXlQvwD5zlGHu8YkTLktLv5QWbajrB1/kKQQAiEAzTdT19rnuMNtOROFryClaVo2f2KSRwuxTYFxOA/qNk4=" + }, + "log_inclusion": [ + "JwjPnsR+/v/pG79eO4J9a+7huD2EB4ulIYwrrWhH7V0=", + "iLzyO7OaPvSPpetDbGd9eNpo/NfUcEYeBta8z/hYG3M=" + ] + }, + "latest_log_root": { + "log_root": { + "timestamp_nanos": 1548182083190127000, + "root_hash": "lE/bH+OqOzXs2rFNvTfgu7gAyqYdx8s9kdUPLjHhS1M=", + "tree_size": 4, + "tree_revision": 4, + "key_hint": "dooq7H3+13E=", + "log_root": "AAEAAAAAAAAABCCUT9sf46o7NezasU29N+C7uADKph3Hyz2R1Q8uMeFLUxV8P2bsK/mYAAAAAAAAAAQAAA==", + "log_root_signature": "MEUCIBMfUpMx8mu2yHupR8QZJrTGZL2HSHQW89iDo60LGETzAiEAjmthkwb3+Tzkz5QKSOU93XEmVKA7qzZgwqRuVClWXZc=" + }, + "log_consistency": [ + "74BJt1hw9+CNRkcAlNhl0+nTymtLDwUBRsf4xRZP+Wk=", + "KpnnGZxuBOimg2bVX+gxdBjrJEAWe/6Fr71HavKgH3M=" + ] + } + }, + "leaf": { + "vrf_proof": "GUOB6Ea7R/98Hdbx8iAcupj7RwWYDucLOTR5h2I4VhfTRhIYL5gdSxCeLHpeM4ryKt+OMjbXMbFbPYLpLf56JQSHy1HLhWFLT+nQFEzzYq4x2psj6PyUNlaPWfnwVizyOB31qTBMPiNmlf7Qgp/yArNoYDSuvrjR9Jmlku+iA5MU", + "map_inclusion": { + "leaf": { + "index": "Umn2fclcSdomcb9UlHcDY1SLm1A/wILzh8NdoYffbcQ=", + "leaf_hash": "dHeM44usHkJb0qjAYu8JX02MCq8VHolTyVXLDGJbWdI=", + "leaf_value": "Cv4BGiBSafZ9yVxJ2iZxv1SUdwNjVIubUD/AgvOHw12hh99txDIgHo4zXOSWjFcn5ACKbEwbEI9J4dXDq/gCobR85mMaO3E6lQEIARKQAQqHAQo1dHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuRWNkc2FQdWJsaWNLZXkSTBIGCAMQAhgCGiD7FU52mGR+kS2Xs4XygLK9bDfV1XiGcZtcM9t0WUvWeSIgmsoZ6shH0XVzZaQU9lPYV3EsZYiiNax6wCRQ8d53LbQYAxABGAEgAUIg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFUSTAEAAAABMEUCIFaVVPyv3/XXtr4t9EaFRN7L0aqd6MvqNf5TuriTLAbKAiEA5aSSC4erRALn7WKjs+eXaXG2wa8WYxEjAaHC4a2QLlQ=" + }, + "inclusion": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "H8rbxUJbcMZtCaW49J4nTPywn99uazGOTxYgJwV0XJI=", + "" + ] + }, + "committed": { + "key": "9yOpkO/50Op5NJJKdx1etw==", + "data": "Ym9iLWtleTE=" + } + } + }, + "TrustNewLog": false + } +] \ No newline at end of file diff --git a/vendor/github.com/golang/protobuf b/vendor/github.com/golang/protobuf deleted file mode 160000 index 8d0c54c12..000000000 --- a/vendor/github.com/golang/protobuf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d0c54c1246661d9a51ca0ba455d22116d485eaa