mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
@@ -0,0 +1,231 @@
|
||||
// Package vrf implements a verifiable random function using the Edwards form
|
||||
// of Curve25519, SHA3 and the Elligator map.
|
||||
//
|
||||
// E is Curve25519 (in Edwards coordinates), h is SHA3.
|
||||
// f is the elligator map (bytes->E) that covers half of E.
|
||||
// 8 is the cofactor of E, the group order is 8*l for prime l.
|
||||
// Setup : the prover publicly commits to a public key (P : E)
|
||||
// H : names -> E
|
||||
// H(n) = f(h(n))^8
|
||||
// VRF : keys -> names -> vrfs
|
||||
// VRF_x(n) = h(n, H(n)^x))
|
||||
// Prove : keys -> names -> proofs
|
||||
// Prove_x(n) = tuple(c=h(n, g^r, H(n)^r), t=r-c*x, ii=H(n)^x)
|
||||
// where r = h(x, n) is used as a source of randomness
|
||||
// Check : E -> names -> vrfs -> proofs -> bool
|
||||
// Check(P, n, vrf, (c,t,ii)) = vrf == h(n, ii)
|
||||
// && c == h(n, g^t*P^c, H(n)^t*ii^c)
|
||||
package vrf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/sonr-io/sonr/crypto/internal/ed25519/edwards25519"
|
||||
"github.com/sonr-io/sonr/crypto/internal/ed25519/extra25519"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
const (
|
||||
PublicKeySize = 32
|
||||
PrivateKeySize = 64
|
||||
Size = 32
|
||||
intermediateSize = 32
|
||||
ProofSize = 32 + 32 + intermediateSize
|
||||
)
|
||||
|
||||
var ErrGetPubKey = errors.New("[vrf] Couldn't get corresponding public-key from private-key")
|
||||
|
||||
type (
|
||||
PrivateKey []byte
|
||||
PublicKey []byte
|
||||
)
|
||||
|
||||
// GenerateKey creates a public/private key pair using rnd for randomness.
|
||||
// If rnd is nil, crypto/rand is used.
|
||||
func GenerateKey(rnd io.Reader) (sk PrivateKey, err error) {
|
||||
if rnd == nil {
|
||||
rnd = rand.Reader
|
||||
}
|
||||
sk = make([]byte, 64)
|
||||
_, err = io.ReadFull(rnd, sk[:32])
|
||||
if err != nil {
|
||||
return sk, err
|
||||
}
|
||||
x, _ := sk.expandSecret()
|
||||
|
||||
var pkP edwards25519.ExtendedGroupElement
|
||||
edwards25519.GeScalarMultBase(&pkP, x)
|
||||
var pkBytes [PublicKeySize]byte
|
||||
pkP.ToBytes(&pkBytes)
|
||||
|
||||
copy(sk[32:], pkBytes[:])
|
||||
return sk, err
|
||||
}
|
||||
|
||||
// Public extracts the public VRF key from the underlying private-key
|
||||
// and returns a boolean indicating if the operation was successful.
|
||||
func (sk PrivateKey) Public() (PublicKey, bool) {
|
||||
pk, ok := ed25519.PrivateKey(sk).Public().(ed25519.PublicKey)
|
||||
return PublicKey(pk), ok
|
||||
}
|
||||
|
||||
func (sk PrivateKey) expandSecret() (x, skhr *[32]byte) {
|
||||
x, skhr = new([32]byte), new([32]byte)
|
||||
hash := sha3.NewShake256()
|
||||
hash.Write(sk[:32])
|
||||
hash.Read(x[:])
|
||||
hash.Read(skhr[:])
|
||||
x[0] &= 248
|
||||
x[31] &= 127
|
||||
x[31] |= 64
|
||||
return x, skhr
|
||||
}
|
||||
|
||||
// Compute generates the vrf value for the byte slice m using the
|
||||
// underlying private key sk.
|
||||
func (sk PrivateKey) Compute(m []byte) []byte {
|
||||
x, _ := sk.expandSecret()
|
||||
var ii edwards25519.ExtendedGroupElement
|
||||
var iiB [32]byte
|
||||
edwards25519.GeScalarMult(&ii, x, hashToCurve(m))
|
||||
ii.ToBytes(&iiB)
|
||||
|
||||
hash := sha3.NewShake256()
|
||||
hash.Write(iiB[:]) // const length: Size
|
||||
hash.Write(m)
|
||||
var vrf [Size]byte
|
||||
hash.Read(vrf[:])
|
||||
return vrf[:]
|
||||
}
|
||||
|
||||
func hashToCurve(m []byte) *edwards25519.ExtendedGroupElement {
|
||||
// H(n) = (f(h(n))^8)
|
||||
var hmb [32]byte
|
||||
sha3.ShakeSum256(hmb[:], m)
|
||||
var hm edwards25519.ExtendedGroupElement
|
||||
extra25519.HashToEdwards(&hm, &hmb)
|
||||
edwards25519.GeDouble(&hm, &hm)
|
||||
edwards25519.GeDouble(&hm, &hm)
|
||||
edwards25519.GeDouble(&hm, &hm)
|
||||
return &hm
|
||||
}
|
||||
|
||||
// Prove returns the vrf value and a proof such that
|
||||
// Verify(m, vrf, proof) == true. The vrf value is the
|
||||
// same as returned by Compute(m).
|
||||
func (sk PrivateKey) Prove(m []byte) (vrf, proof []byte) {
|
||||
x, skhr := sk.expandSecret()
|
||||
var sH, rH [64]byte
|
||||
var r, s, minusS, t, gB, grB, hrB, hxB, hB [32]byte
|
||||
var ii, gr, hr edwards25519.ExtendedGroupElement
|
||||
|
||||
h := hashToCurve(m)
|
||||
h.ToBytes(&hB)
|
||||
edwards25519.GeScalarMult(&ii, x, h)
|
||||
ii.ToBytes(&hxB)
|
||||
|
||||
// use hash of private-, public-key and msg as randomness source:
|
||||
hash := sha3.NewShake256()
|
||||
hash.Write(skhr[:])
|
||||
hash.Write(sk[32:]) // public key, as in ed25519
|
||||
hash.Write(m)
|
||||
hash.Read(rH[:])
|
||||
hash.Reset()
|
||||
edwards25519.ScReduce(&r, &rH)
|
||||
|
||||
edwards25519.GeScalarMultBase(&gr, &r)
|
||||
edwards25519.GeScalarMult(&hr, &r, h)
|
||||
gr.ToBytes(&grB)
|
||||
hr.ToBytes(&hrB)
|
||||
gB = edwards25519.BaseBytes
|
||||
|
||||
// H2(g, h, g^x, h^x, g^r, h^r, m)
|
||||
hash.Write(gB[:])
|
||||
hash.Write(hB[:])
|
||||
hash.Write(sk[32:]) // ed25519 public-key
|
||||
hash.Write(hxB[:])
|
||||
hash.Write(grB[:])
|
||||
hash.Write(hrB[:])
|
||||
hash.Write(m)
|
||||
hash.Read(sH[:])
|
||||
hash.Reset()
|
||||
edwards25519.ScReduce(&s, &sH)
|
||||
|
||||
edwards25519.ScNeg(&minusS, &s)
|
||||
edwards25519.ScMulAdd(&t, x, &minusS, &r)
|
||||
|
||||
proof = make([]byte, ProofSize)
|
||||
copy(proof[:32], s[:])
|
||||
copy(proof[32:64], t[:])
|
||||
copy(proof[64:96], hxB[:])
|
||||
|
||||
hash.Write(hxB[:])
|
||||
hash.Write(m)
|
||||
vrf = make([]byte, Size)
|
||||
hash.Read(vrf[:])
|
||||
return vrf, proof
|
||||
}
|
||||
|
||||
// Verify returns true iff vrf=Compute(m) for the sk that
|
||||
// corresponds to pk.
|
||||
func (pkBytes PublicKey) Verify(m, vrfBytes, proof []byte) bool {
|
||||
if len(proof) != ProofSize || len(vrfBytes) != Size || len(pkBytes) != PublicKeySize {
|
||||
return false
|
||||
}
|
||||
var pk, s, sRef, t, vrf, hxB, hB, gB, ABytes, BBytes [32]byte
|
||||
copy(vrf[:], vrfBytes)
|
||||
copy(pk[:], pkBytes[:])
|
||||
copy(s[:32], proof[:32])
|
||||
copy(t[:32], proof[32:64])
|
||||
copy(hxB[:], proof[64:96])
|
||||
|
||||
hash := sha3.NewShake256()
|
||||
hash.Write(hxB[:]) // const length
|
||||
hash.Write(m)
|
||||
var hCheck [Size]byte
|
||||
hash.Read(hCheck[:])
|
||||
if !bytes.Equal(hCheck[:], vrf[:]) {
|
||||
return false
|
||||
}
|
||||
hash.Reset()
|
||||
|
||||
var P, B, ii, iic edwards25519.ExtendedGroupElement
|
||||
var A, hmtP, iicP edwards25519.ProjectiveGroupElement
|
||||
if !P.FromBytesBaseGroup(&pk) {
|
||||
return false
|
||||
}
|
||||
if !ii.FromBytesBaseGroup(&hxB) {
|
||||
return false
|
||||
}
|
||||
edwards25519.GeDoubleScalarMultVartime(&A, &s, &P, &t)
|
||||
A.ToBytes(&ABytes)
|
||||
gB = edwards25519.BaseBytes
|
||||
|
||||
h := hashToCurve(m) // h = H1(m)
|
||||
h.ToBytes(&hB)
|
||||
edwards25519.GeDoubleScalarMultVartime(&hmtP, &t, h, &[32]byte{})
|
||||
edwards25519.GeDoubleScalarMultVartime(&iicP, &s, &ii, &[32]byte{})
|
||||
iicP.ToExtended(&iic)
|
||||
hmtP.ToExtended(&B)
|
||||
edwards25519.GeAdd(&B, &B, &iic)
|
||||
B.ToBytes(&BBytes)
|
||||
|
||||
var sH [64]byte
|
||||
// sRef = H2(g, h, g^x, v, g^t·G^s,H1(m)^t·v^s, m), with v=H1(m)^x=h^x
|
||||
hash.Write(gB[:])
|
||||
hash.Write(hB[:])
|
||||
hash.Write(pkBytes)
|
||||
hash.Write(hxB[:])
|
||||
hash.Write(ABytes[:]) // const length (g^t*G^s)
|
||||
hash.Write(BBytes[:]) // const length (H1(m)^t*v^s)
|
||||
hash.Write(m)
|
||||
hash.Read(sH[:])
|
||||
|
||||
edwards25519.ScReduce(&sRef, &sH)
|
||||
return sRef == s
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package vrf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
// "fmt"
|
||||
)
|
||||
|
||||
func TestHonestComplete(t *testing.T) {
|
||||
sk, err := GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pk, _ := sk.Public()
|
||||
alice := []byte("alice")
|
||||
aliceVRF := sk.Compute(alice)
|
||||
aliceVRFFromProof, aliceProof := sk.Prove(alice)
|
||||
|
||||
// fmt.Printf("pk: %X\n", pk)
|
||||
// fmt.Printf("sk: %X\n", *sk)
|
||||
// fmt.Printf("alice(bytes): %X\n", alice)
|
||||
// fmt.Printf("aliceVRF: %X\n", aliceVRF)
|
||||
// fmt.Printf("aliceProof: %X\n", aliceProof)
|
||||
|
||||
if !pk.Verify(alice, aliceVRF, aliceProof) {
|
||||
t.Error("Gen -> Compute -> Prove -> Verify -> FALSE")
|
||||
}
|
||||
if !bytes.Equal(aliceVRF, aliceVRFFromProof) {
|
||||
t.Error("Compute != Prove")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertPrivateKeyToPublicKey(t *testing.T) {
|
||||
sk, err := GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pk, ok := sk.Public()
|
||||
if !ok {
|
||||
t.Fatal("Couldn't obtain public key.")
|
||||
}
|
||||
if !bytes.Equal(sk[32:], pk) {
|
||||
t.Fatal("Raw byte respresentation doesn't match public key.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlipBitForgery(t *testing.T) {
|
||||
sk, err := GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pk, _ := sk.Public()
|
||||
alice := []byte("alice")
|
||||
for i := 0; i < 32; i++ {
|
||||
for j := uint(0); j < 8; j++ {
|
||||
aliceVRF := sk.Compute(alice)
|
||||
aliceVRF[i] ^= 1 << j
|
||||
_, aliceProof := sk.Prove(alice)
|
||||
if pk.Verify(alice, aliceVRF, aliceProof) {
|
||||
t.Fatalf("forged by using aliceVRF[%d]^=%d:\n (sk=%x)", i, j, sk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHashToGE(b *testing.B) {
|
||||
alice := []byte("alice")
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
hashToCurve(alice)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompute(b *testing.B) {
|
||||
sk, err := GenerateKey(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
alice := []byte("alice")
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
sk.Compute(alice)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProve(b *testing.B) {
|
||||
sk, err := GenerateKey(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
alice := []byte("alice")
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
sk.Prove(alice)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkVerify(b *testing.B) {
|
||||
sk, err := GenerateKey(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
alice := []byte("alice")
|
||||
aliceVRF := sk.Compute(alice)
|
||||
_, aliceProof := sk.Prove(alice)
|
||||
pk, _ := sk.Public()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
pk.Verify(alice, aliceVRF, aliceProof)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user