Skip to content

Commit

Permalink
add unit-test case
Browse files Browse the repository at this point in the history
  • Loading branch information
torao committed Nov 19, 2019
1 parent e31605b commit 885b885
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 163 deletions.
118 changes: 118 additions & 0 deletions crypto/vrf/internal/vrf/vrf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// This vrf package makes the VRF API in Algorand's libsodium C library available to golang.
package vrf

/*
#cgo CFLAGS: -I./libsodium/src/libsodium/include
#cgo LDFLAGS: -lsodium
#include "./libsodium/src/libsodium/include/sodium.h"
*/
import "C"
import (
"encoding/hex"
"errors"
"fmt"
"unsafe"
)

const PUBLICKEYBYTES = uint32(C.crypto_vrf_PUBLICKEYBYTES)

const SECRETKEYBYTES = uint32(C.crypto_vrf_SECRETKEYBYTES)

const SEEDBYTES = uint32(C.crypto_vrf_SEEDBYTES)

const PROOFBYTES = uint32(C.crypto_vrf_PROOFBYTES)

const OUTPUTBYTES = uint32(C.crypto_vrf_OUTPUTBYTES)

const PRIMITIVE = C.crypto_vrf_PRIMITIVE

// Generate an Ed25519 key pair for use with VRF.
func KeyPair() ([PUBLICKEYBYTES]byte, [SECRETKEYBYTES]byte) {
var publicKey [PUBLICKEYBYTES]byte
var privateKey [SECRETKEYBYTES]byte
publicKeyPtr := (*C.uchar)(unsafe.Pointer(&publicKey))
privateKeyPtr := (*C.uchar)(unsafe.Pointer(&privateKey))
C.crypto_vrf_keypair(publicKeyPtr, privateKeyPtr)
return publicKey, privateKey
}

// Generate an Ed25519 key pair for use with VRF. Parameter `seed` means the cofactor in Curve25519 and EdDSA.
func KeyPairFromSeed(seed [SEEDBYTES]byte) ([PUBLICKEYBYTES]byte, [SECRETKEYBYTES]byte) {
var publicKey [PUBLICKEYBYTES]byte
var privateKey [SECRETKEYBYTES]byte
publicKeyPtr := (*C.uchar)(unsafe.Pointer(&publicKey))
privateKeyPtr := (*C.uchar)(unsafe.Pointer(&privateKey))
seedPtr := (*C.uchar)(unsafe.Pointer(&seed))
C.crypto_vrf_keypair_from_seed(publicKeyPtr, privateKeyPtr, seedPtr)
return publicKey, privateKey
}

// Verifies that the specified public key is valid.
func IsValidKey(publicKey [PUBLICKEYBYTES]byte) bool {
publicKeyPtr := (*C.uchar)(unsafe.Pointer(&publicKey))
return C.crypto_vrf_is_valid_key(publicKeyPtr) != 0
}

// Construct a VRF proof from given secret key and message.
func Prove(privateKey [SECRETKEYBYTES]byte, message []byte) ([PROOFBYTES]byte, error) {
var proof [PROOFBYTES]byte
proofPtr := (*C.uchar)(unsafe.Pointer(&proof))
privateKeyPtr := (*C.uchar)(unsafe.Pointer(&privateKey))
messagePtr := (*C.uchar)(unsafe.Pointer(&message))
messageLen := (C.ulonglong)(len(message))
if C.crypto_vrf_prove(proofPtr, privateKeyPtr, messagePtr, messageLen) != 0 {
return proof, errors.New(fmt.Sprintf("unable to decode the given privateKey: %s",
hex.EncodeToString(privateKey[:])))
}
return proof, nil
}

// Verifies that proof was legitimately generated by private key for the given public key, and stores the
// VRF hash in output. Note that VRF "verify()" means the process of generating output from public key,
// proof, and message.
// https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04#section-5.3
func Verify(publicKey [PUBLICKEYBYTES]byte, proof [PROOFBYTES]byte, message []byte) ([OUTPUTBYTES]byte, error) {
var output [OUTPUTBYTES]byte
outputPtr := (*C.uchar)(unsafe.Pointer(&output))
publicKeyPtr := (*C.uchar)(unsafe.Pointer(&publicKey))
proofPtr := (*C.uchar)(unsafe.Pointer(&proof))
messagePtr := (*C.uchar)(unsafe.Pointer(&message))
messageLen := (C.ulonglong)(len(message))
if C.crypto_vrf_verify(outputPtr, publicKeyPtr, proofPtr, messagePtr, messageLen) != 0 {
return output, errors.New(fmt.Sprintf(
"given public key is invalid, or the proof isn't legitimately generated for the message:"+
" public_key=%s, proof=%s, message=%s",
hex.EncodeToString(publicKey[:]), hex.EncodeToString(proof[:]), hex.EncodeToString(message[:])))
}
return output, nil
}

// Calculate the output (hash value) from the specified proof.
// In essence, this function returns a valid value if given proof is any point on the finite field. Otherwise,
// this will return an error.
func ProofToHash(proof [PROOFBYTES]byte) ([OUTPUTBYTES]byte, error) {
var output [OUTPUTBYTES]byte
outputPtr := (*C.uchar)(unsafe.Pointer(&output))
proofPtr := (*C.uchar)(unsafe.Pointer(&proof))
if C.crypto_vrf_proof_to_hash(outputPtr, proofPtr) != 0 {
return output, errors.New(fmt.Sprintf(
"given proof isn't legitimately generated: proof=%s", hex.EncodeToString(proof[:])))
}
return output, nil
}

func SkToPk(privateKey [SECRETKEYBYTES]byte) [PUBLICKEYBYTES]byte {
var publicKey [PUBLICKEYBYTES]byte
publicKeyPtr := (*C.uchar)(unsafe.Pointer(&publicKey))
privateKeyPtr := (*C.uchar)(unsafe.Pointer(&privateKey))
C.crypto_vrf_sk_to_pk(publicKeyPtr, privateKeyPtr) // void
return publicKey
}

func SkToSeed(privateKey [SECRETKEYBYTES]byte) [SEEDBYTES]byte {
var seed [SEEDBYTES]byte
seedPtr := (*C.uchar)(unsafe.Pointer(&seed))
privateKeyPtr := (*C.uchar)(unsafe.Pointer(&privateKey))
C.crypto_vrf_sk_to_seed(seedPtr, privateKeyPtr) // void
return seed
}
117 changes: 13 additions & 104 deletions crypto/vrf/vrf.go
Original file line number Diff line number Diff line change
@@ -1,123 +1,32 @@
// This vrf package makes the VRF API in Algorand's libsodium C library available to golang.
package vrf

/*
#cgo CFLAGS: -I./internal/vrf/libsodium/src/libsodium/include
#cgo LDFLAGS: -lsodium
#include <sodium.h>
*/
import "C"
import (
"encoding/hex"
"errors"
"fmt"
"github.com/tendermint/tendermint/crypto/ed25519"
vrfimpl "github.com/tendermint/tendermint/crypto/vrf/internal/vrf"
"unsafe"
)

const PUBLICKEYBYTES = uint32(C.crypto_vrf_PUBLICKEYBYTES)
const PROOFBYTES = vrfimpl.PROOFBYTES

const SECRETKEYBYTES = uint32(C.crypto_vrf_SECRETKEYBYTES)
const OUTPUTBYTES = vrfimpl.OUTPUTBYTES

const SEEDBYTES = uint32(C.crypto_vrf_SEEDBYTES)

const PROOFBYTES = uint32(C.crypto_vrf_PROOFBYTES)

const OUTPUTBYTES = uint32(C.crypto_vrf_OUTPUTBYTES)

const PRIMITIVE = C.crypto_vrf_PRIMITIVE

// Generate an Ed25519 key pair for use with VRF.
// Note that this key pair is proprietary to libsodium and is NOT compatible with the Ed25519 key
// used by Tendermint.
func KeyPair() ([PUBLICKEYBYTES]byte, [SECRETKEYBYTES]byte) {
var publicKey [PUBLICKEYBYTES]byte
var privateKey [SECRETKEYBYTES]byte
var publicKeyPtr = (*C.uchar)(unsafe.Pointer(&publicKey))
var privateKeyPtr = (*C.uchar)(unsafe.Pointer(&privateKey))
C.crypto_vrf_keypair(publicKeyPtr, privateKeyPtr)
return publicKey, privateKey
}

// Generate an Ed25519 key pair for use with VRF. Parameter `seed` means the cofactor (secret)
// in Curve25519 and EdDSA.
// Note that this key pair is proprietary to libsodium and is NOT compatible with the Ed25519 key
// used by Tendermint.
func KeyPairFromSeed(seed [SEEDBYTES]byte) ([PUBLICKEYBYTES]byte, [SECRETKEYBYTES]byte) {
var publicKey [PUBLICKEYBYTES]byte
var privateKey [SECRETKEYBYTES]byte
var publicKeyPtr = (*C.uchar)(unsafe.Pointer(&publicKey))
var privateKeyPtr = (*C.uchar)(unsafe.Pointer(&privateKey))
var seedPtr = (*C.uchar)(unsafe.Pointer(&seed))
C.crypto_vrf_keypair_from_seed(publicKeyPtr, privateKeyPtr, seedPtr)
return publicKey, privateKey
}

// Verifies that the specified public key is valid.
func IsValidKey(publicKey [PUBLICKEYBYTES]byte) bool {
var publicKeyPtr = (*C.uchar)(unsafe.Pointer(&publicKey))
return C.crypto_vrf_is_valid_key(publicKeyPtr) != 0
func convertSK(privateKey ed25519.PrivKeyEd25519) [64]byte {
return *(*[64]byte)(unsafe.Pointer(&privateKey[0]))
}

// If the proof was legitimately generated for the message using the public key, return its output (hash value).
// Otherwise an error is returned.
func Prove(privateKey [SECRETKEYBYTES]byte, message []byte) ([PROOFBYTES]byte, error) {
var proof [PROOFBYTES]byte
var proofPtr = (*C.uchar)(unsafe.Pointer(&proof))
var privateKeyPtr = (*C.uchar)(unsafe.Pointer(&privateKey))
var messagePtr = (*C.uchar)(unsafe.Pointer(&message))
var messageLen = (C.ulonglong)(len(message))
if C.crypto_vrf_prove(proofPtr, privateKeyPtr, messagePtr, messageLen) != 0 {
// TODO determine error message
return proof, errors.New("failed to ")
}
return proof, nil
func convertPK(publicKey ed25519.PubKeyEd25519) [ed25519.PubKeyEd25519Size]byte {
return *(*[ed25519.PubKeyEd25519Size]byte)(unsafe.Pointer(&publicKey[0]))
}

// VRF "verify()" means the process of generating output from public key, proof, and message. If verification
// succeeds, return the VRF output hash that specified in IRFT Draft section 5.3.
// https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04#section-5.3
//
// For a given public key and message, there are many possible proofs but only one possible output hash.
func Verify(publicKey [PUBLICKEYBYTES]byte, proof [PROOFBYTES]byte, message []byte) ([OUTPUTBYTES]byte, error) {
var output [OUTPUTBYTES]byte
var outputPtr = (*C.uchar)(unsafe.Pointer(&output))
var publicKeyPtr = (*C.uchar)(unsafe.Pointer(&publicKey))
var proofPtr = (*C.uchar)(unsafe.Pointer(&proof))
var messagePtr = (*C.uchar)(unsafe.Pointer(&message))
var messageLen = (C.ulonglong)(len(message))
if C.crypto_vrf_verify(outputPtr, publicKeyPtr, proofPtr, messagePtr, messageLen) != 0 {
return output, errors.New(fmt.Sprintf("verification failed: pk=%s, pi=%s, m=%s",
hex.EncodeToString(publicKey[:]), hex.EncodeToString(proof[:]), hex.EncodeToString(message[:])))
}
return output, nil
func Prove(privateKey ed25519.PrivKeyEd25519, message []byte) ([PROOFBYTES]byte, error) {
return vrfimpl.Prove(convertSK(privateKey), message)
}

// Calculate the output (hash value) from the specified proof.
// In essence, this function returns a valid value if given proof is any point on the finite field. Otherwise,
// this will return an error.
func ProofToHash(proof [PROOFBYTES]byte) ([OUTPUTBYTES]byte, error) {
var output [OUTPUTBYTES]byte
var outputPtr = (*C.uchar)(unsafe.Pointer(&output))
var proofPtr = (*C.uchar)(unsafe.Pointer(&proof))
if C.crypto_vrf_proof_to_hash(outputPtr, proofPtr) != 0 {
// TODO determine error message
return output, errors.New("")
}
return output, nil
}

func SkToPk(privateKey [SECRETKEYBYTES]byte) [PUBLICKEYBYTES]byte {
var publicKey [PUBLICKEYBYTES]byte
var publicKeyPtr = (*C.uchar)(unsafe.Pointer(&publicKey))
var privateKeyPtr = (*C.uchar)(unsafe.Pointer(&privateKey))
C.crypto_vrf_sk_to_pk(publicKeyPtr, privateKeyPtr) // void
return publicKey
return vrfimpl.ProofToHash(proof)
}

func SkToSeed(privateKey [SECRETKEYBYTES]byte) [SEEDBYTES]byte {
var seed [SEEDBYTES]byte
var seedPtr = (*C.uchar)(unsafe.Pointer(&seed))
var privateKeyPtr = (*C.uchar)(unsafe.Pointer(&privateKey))
C.crypto_vrf_sk_to_seed(seedPtr, privateKeyPtr) // void
return seed
func Verify(publicKey ed25519.PubKeyEd25519, proof [PROOFBYTES]byte, message []byte) ([OUTPUTBYTES]byte, error) {
return vrfimpl.Verify(convertPK(publicKey), proof, message)
}
Loading

0 comments on commit 885b885

Please sign in to comment.