Merge pull request #965 from coolcottontail/bls_vrf
VRF generation using BLS (base code and test case)pull/972/head
commit
cebc3451bd
@ -0,0 +1,98 @@ |
||||
package blsvrf |
||||
|
||||
import ( |
||||
"crypto" |
||||
"crypto/sha256" |
||||
"errors" |
||||
|
||||
"github.com/harmony-one/bls/ffi/go/bls" |
||||
"github.com/harmony-one/harmony/crypto/vrf" |
||||
) |
||||
|
||||
var ( |
||||
// ErrInvalidVRF occurs when the VRF does not validate.
|
||||
ErrInvalidVRF = errors.New("invalid VRF proof") |
||||
) |
||||
|
||||
// PublicKey holds a public VRF key.
|
||||
type PublicKey struct { |
||||
*bls.PublicKey |
||||
} |
||||
|
||||
// PrivateKey holds a private VRF key.
|
||||
type PrivateKey struct { |
||||
*bls.SecretKey |
||||
} |
||||
|
||||
func init() { |
||||
bls.Init(bls.BLS12_381) |
||||
} |
||||
|
||||
// Public returns the corresponding public key as bytes.
|
||||
func (k *PrivateKey) Public() crypto.PublicKey { |
||||
return *k.SecretKey.GetPublicKey() |
||||
} |
||||
|
||||
// Serialize serialize the public key into bytes
|
||||
func (pk *PublicKey) Serialize() []byte { |
||||
return pk.Serialize() |
||||
} |
||||
|
||||
// Deserialize de-serialize bytes into public key
|
||||
func (pk *PublicKey) Deserialize(data []byte) { |
||||
pk.Deserialize(data) |
||||
} |
||||
|
||||
// NewVRFVerifier creates a verifier object from a public key.
|
||||
func NewVRFVerifier(pubkey *bls.PublicKey) vrf.PublicKey { |
||||
return &PublicKey{pubkey} |
||||
} |
||||
|
||||
// NewVRFSigner creates a signer object from a private key.
|
||||
func NewVRFSigner(seck *bls.SecretKey) vrf.PrivateKey { |
||||
return &PrivateKey{seck} |
||||
} |
||||
|
||||
// Evaluate returns the verifiable unpredictable function evaluated using alpha
|
||||
// verifiable unpredictable function using BLS
|
||||
// reference: https://tools.ietf.org/html/draft-goldbe-vrf-01
|
||||
// properties of VRF-BLS:
|
||||
// 1) Full Uniqueness : satisfied, it is deterministic
|
||||
// 2) Full Pseudorandomness : satisfied through sha256
|
||||
// 3) Full Collison Resistance : satisfied through sha256
|
||||
func (k *PrivateKey) Evaluate(alpha []byte) ([32]byte, []byte) { |
||||
//get the BLS signature of the message
|
||||
//pi = VRF_prove(SK, alpha)
|
||||
msgHash := sha256.Sum256(alpha) |
||||
pi := k.SignHash(msgHash[:]) |
||||
|
||||
//hash the signature and output as VRF beta
|
||||
//beta = VRF_proof2hash(pi)
|
||||
beta := sha256.Sum256(pi.Serialize()) |
||||
|
||||
return beta, pi.Serialize() |
||||
} |
||||
|
||||
// ProofToHash asserts that proof is correct for input alpha and output VRF hash
|
||||
func (pk *PublicKey) ProofToHash(alpha, pi []byte) ([32]byte, error) { |
||||
nilIndex := [32]byte{} |
||||
|
||||
if len(pi) == 0 { |
||||
return nilIndex, ErrInvalidVRF |
||||
} |
||||
|
||||
msgSig := bls.Sign{} |
||||
|
||||
err := msgSig.Deserialize(pi) |
||||
|
||||
if err != nil { |
||||
return nilIndex, err |
||||
} |
||||
|
||||
msgHash := sha256.Sum256(alpha) |
||||
if !msgSig.VerifyHash(pk.PublicKey, msgHash[:]) { |
||||
return nilIndex, ErrInvalidVRF |
||||
} |
||||
|
||||
return sha256.Sum256(pi), nil |
||||
} |
@ -0,0 +1,131 @@ |
||||
package blsvrf |
||||
|
||||
import ( |
||||
"bytes" |
||||
"math" |
||||
"testing" |
||||
|
||||
"github.com/harmony-one/harmony/crypto/bls" |
||||
) |
||||
|
||||
func TestVRF1(t *testing.T) { |
||||
blsSk := bls.RandPrivateKey() |
||||
|
||||
vrfSk := NewVRFSigner(blsSk) |
||||
vrfPk := NewVRFVerifier(blsSk.GetPublicKey()) |
||||
|
||||
m1 := []byte("data1") |
||||
|
||||
vrf, proof := vrfSk.Evaluate(m1) |
||||
hash, err := vrfPk.ProofToHash(m1, proof) |
||||
|
||||
if err != nil { |
||||
t.Errorf("error generating proof to hash") |
||||
} |
||||
|
||||
if hash != vrf { |
||||
t.Errorf("error hash doesn't match") |
||||
} |
||||
} |
||||
|
||||
func TestVRF2(t *testing.T) { |
||||
blsSk := bls.RandPrivateKey() |
||||
|
||||
k := NewVRFSigner(blsSk) |
||||
pk := NewVRFVerifier(blsSk.GetPublicKey()) |
||||
|
||||
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) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TestRightTruncateProof(t *testing.T) { |
||||
blsSk := bls.RandPrivateKey() |
||||
|
||||
k := NewVRFSigner(blsSk) |
||||
pk := NewVRFVerifier(blsSk.GetPublicKey()) |
||||
|
||||
data := []byte("data") |
||||
_, proof := k.Evaluate(data) |
||||
proofLen := len(proof) |
||||
for i := 0; i < proofLen; i++ { |
||||
proof = proof[:len(proof)-1] |
||||
if i < 47 { |
||||
continue |
||||
} |
||||
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) { |
||||
blsSk := bls.RandPrivateKey() |
||||
|
||||
k := NewVRFSigner(blsSk) |
||||
pk := NewVRFVerifier(blsSk.GetPublicKey()) |
||||
|
||||
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) { |
||||
blsSk := bls.RandPrivateKey() |
||||
|
||||
k := NewVRFSigner(blsSk) |
||||
pk := NewVRFVerifier(blsSk.GetPublicKey()) |
||||
|
||||
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() |
||||
} |
Loading…
Reference in new issue