parent
1d2150ce79
commit
187fa1abc8
@ -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) |
||||
} |
@ -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 := "../../../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 := "../../../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 |
||||
} |
@ -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 |
||||
} |
@ -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) |
||||
} |
Loading…
Reference in new issue