Merge pull request #965 from coolcottontail/bls_vrf

VRF generation using BLS (base code and test case)
pull/972/head
coolcottontail 6 years ago committed by GitHub
commit cebc3451bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 98
      crypto/vrf/bls/bls_vrf.go
  2. 131
      crypto/vrf/bls/bls_vrf_test.go

@ -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…
Cancel
Save