Skip to content

Commit

Permalink
Merge pull request #49 from mit-pdos/fix-vrf-api
Browse files Browse the repository at this point in the history
fix vrf api
  • Loading branch information
sanjit-bhat authored Nov 18, 2024
2 parents 47a046d + 3071050 commit 8f0cc0e
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 169 deletions.
58 changes: 41 additions & 17 deletions cryptoffi/cryptoffi.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package cryptoffi

import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"crypto/sha512"
"crypto/x509"
"github.com/google/keytransparency/core/crypto/vrf"
"github.com/google/keytransparency/core/crypto/vrf/p256"
"log"
)

const (
Expand All @@ -34,7 +33,7 @@ type SigPublicKey ed25519.PublicKey
func SigGenerateKey() (SigPublicKey, *SigPrivateKey) {
pk, sk, err := ed25519.GenerateKey(nil)
if err != nil {
log.Fatal(err)
panic("cryptoffi: ed25519 keygen err")
}
return SigPublicKey(pk), &SigPrivateKey{sk: sk}
}
Expand All @@ -43,15 +42,19 @@ func (sk *SigPrivateKey) Sign(message []byte) []byte {
return ed25519.Sign(ed25519.PrivateKey(sk.sk), message)
}

// Verify rets okay if proof verifies.
// Verify verifies the sig and rets any errs.
func (pk SigPublicKey) Verify(message []byte, sig []byte) bool {
return ed25519.Verify(ed25519.PublicKey(pk), message, sig)
return !ed25519.Verify(ed25519.PublicKey(pk), message, sig)
}

// # VRF

// VrfPrivateKey has an unexported sk, which can't be accessed outside
// the package, without reflection or unsafe.
// it uses Google KT's ecvrf under the hood, which satisfies [full uniqueness],
// i.e., determinism under adversarial pks.
// this is the only property that pav requires.
// [full uniqueness]: https://www.rfc-editor.org/rfc/rfc9381#name-elliptic-curve-vrf-ecvrf
type VrfPrivateKey struct {
sk vrf.PrivateKey
}
Expand All @@ -65,23 +68,45 @@ func VrfGenerateKey() (*VrfPublicKey, *VrfPrivateKey) {
return &VrfPublicKey{pk: pk}, &VrfPrivateKey{sk: sk}
}

// TODO: check that Google CT's VRF satisfies all the properties we need.
// maybe re-write to use sha256 and the more robust [internal ed25519].
// [internal ed25519]: https://pkg.go.dev/filippo.io/edwards25519
// Hash computes the hash of data, along with a proof.
// TODO: rewrite to use ed25519 with the low-level [ed25519].
// [ed25519]: https://pkg.go.dev/filippo.io/edwards25519
func (sk *VrfPrivateKey) Hash(data []byte) ([]byte, []byte) {
h, proof := sk.sk.Evaluate(data)
// TODO: check that proof doesn't have h inside it.
// that'd be a waste of space.
return h[:], proof
}

// Verify rets okay if proof verifies.
func (pk *VrfPublicKey) Verify(data, hash, proof []byte) bool {
h, err := pk.pk.ProofToHash(data, proof)
// Verify verifies data against the proof and returns the hash and an err.
// it should perform the [ECVRF_verify] checks to run even on adversarial proofs.
// it can assume a valid pk.
// [ECVRF_verify]: https://www.rfc-editor.org/rfc/rfc9381#name-ecvrf-verifying
func (pk *VrfPublicKey) Verify(data, proof []byte) ([]byte, bool) {
hash, err := pk.pk.ProofToHash(data, proof)
if err != nil {
return false
return nil, true
}
return bytes.Equal(hash, h[:])
return hash[:], false
}

// VrfPublicKeyEncodes encodes a valid pk as bytes.
func VrfPublicKeyEncode(pk *VrfPublicKey) []byte {
pk2 := pk.pk.(*p256.PublicKey).PublicKey
b, err := x509.MarshalPKIXPublicKey(pk2)
if err != nil {
panic("cryptoffi: vrf encoding err")
}
return b
}

// VrfPublicKeyDecode decodes [b].
// it should perform the [ECVRF_validate_key] checks to run even on adversarial pks.
// [ECVRF_validate_key]: https://www.rfc-editor.org/rfc/rfc9381#name-ecvrf-validate-key
func VrfPublicKeyDecode(b []byte) *VrfPublicKey {
pk, err := p256.NewVRFVerifierFromRawKey(b)
if err != nil {
panic("cryptoffi: vrf decoding err")
}
return &VrfPublicKey{pk: pk}
}

// # Random
Expand All @@ -90,9 +115,8 @@ func (pk *VrfPublicKey) Verify(data, hash, proof []byte) bool {
func RandBytes(n uint64) []byte {
b := make([]byte, n)
_, err := rand.Read(b)
// don't care about recovering from crypto/rand failures.
if err != nil {
panic("crypto/rand call failed")
panic("cryptoffi: crypto/rand err")
}
return b
}
89 changes: 54 additions & 35 deletions cryptoffi/cryptoffi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,103 @@ import (
"testing"
)

func TestHashSame(t *testing.T) {
d := []byte("d")
h1 := Hash(d)
h2 := Hash(d)
func TestHash(t *testing.T) {
// same hashes for same input.
d1 := []byte("d1")
h1 := Hash(d1)
h2 := Hash(d1)
if !bytes.Equal(h1, h2) {
t.Fatal()
}
if uint64(len(h1)) != HashLen {
t.Fatal()
}
}

func TestHashDiff(t *testing.T) {
d1 := []byte("d1")
// diff hashes for diff inputs.
d2 := []byte("d2")
h1 := Hash(d1)
h2 := Hash(d2)
if bytes.Equal(h1, h2) {
h3 := Hash(d2)
if bytes.Equal(h1, h3) {
t.Fatal()
}
}

func TestVerifyTrue(t *testing.T) {
func TestSig(t *testing.T) {
// verify true.
d := []byte("d")
pk, sk := SigGenerateKey()
sig := sk.Sign(d)
if !pk.Verify(d, sig) {
if pk.Verify(d, sig) {
t.Fatal()
}
}

func TestVerifyFalse(t *testing.T) {
d1 := []byte("d1")
d2 := []byte("d2")
pk, sk := SigGenerateKey()
sig := sk.Sign(d1)
if pk.Verify(d2, sig) {
// verify false for bad msg.
if !pk.Verify([]byte("d1"), sig) {
t.Fatal()
}

// verify false for bad pk.
pk2, _ := SigGenerateKey()
if !pk2.Verify(d, sig) {
t.Fatal()
}

// verify false for bad sig.
sig2 := bytes.Clone(sig)
sig2[0] = ^sig2[0]
if !pk.Verify(d, sig2) {
t.Fatal()
}
}

func TestVRF(t *testing.T) {
pk0, sk0 := VrfGenerateKey()

// check same hashes for same input.
// verify true.
d0 := []byte("d0")
h0, p0 := sk0.Hash(d0)
if !pk0.Verify(d0, h0, p0) {
h0, p := sk0.Hash(d0)
h0Again, err := pk0.Verify(d0, p)
if err {
t.Fatal()
}
h1, p1 := sk0.Hash(d0)
if !pk0.Verify(d0, h1, p1) {
if !bytes.Equal(h0, h0Again) {
t.Fatal()
}

// same hashes for same input.
h1, _ := sk0.Hash(d0)
if !bytes.Equal(h0, h1) {
t.Fatal()
}

// check diff hashes for diff inputs.
// diff hashes for diff inputs.
d1 := []byte("d1")
h2, p2 := sk0.Hash(d1)
if !pk0.Verify(d1, h2, p2) {
t.Fatal()
}
h2, _ := sk0.Hash(d1)
if bytes.Equal(h0, h2) {
t.Fatal()
}

// check verify false if use bad pk.
// verify false for bad pk.
pk1, _ := VrfGenerateKey()
if pk1.Verify(d1, h2, p2) {
if _, err = pk1.Verify(d0, p); !err {
t.Fatal()
}

// check verify false on bad proof.
p3 := bytes.Clone(p2)
p3[0] = ^p3[0]
if pk0.Verify(d1, h2, p3) {
// verify false for bad proof.
p1 := bytes.Clone(p)
p1[0] = ^p1[0]
if _, err = pk0.Verify(d0, p1); !err {
t.Fatal()
}
}

func TestVRFSerde(t *testing.T) {
pk0, sk := VrfGenerateKey()
d := []byte("d")
_, p := sk.Hash(d)

pk0B := VrfPublicKeyEncode(pk0)
pk1 := VrfPublicKeyDecode(pk0B)
if _, err := pk1.Verify(d, p); err {
t.Fatal()
}
}
2 changes: 1 addition & 1 deletion kt/auditor.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (a *Auditor) Get(epoch uint64) (*AdtrEpochInfo, bool) {
return info, false
}

func newAuditor() (*Auditor, cryptoffi.SigPublicKey) {
func NewAuditor() (*Auditor, cryptoffi.SigPublicKey) {
mu := new(sync.Mutex)
pk, sk := cryptoffi.SigGenerateKey()
m := &merkle.Tree{}
Expand Down
Loading

0 comments on commit 8f0cc0e

Please sign in to comment.