Skip to content

Commit

Permalink
feat[pallas-crypto]: Implement libsodium vrf signature verification
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewWestberg committed Sep 7, 2024
1 parent 48f26db commit 835c482
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 2 deletions.
10 changes: 9 additions & 1 deletion pallas-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ homepage = "https://github.com/txpipe/pallas"
documentation = "https://docs.rs/pallas-crypto"
license = "Apache-2.0"
readme = "README.md"
authors = ["Nicolas Di Prima <nicolas@primetype.co.uk>"]
authors = [
"Nicolas Di Prima <nicolas@primetype.co.uk>",
"Andrew Westberg <andrewwestberg@gmail.com>",
]

[dependencies]
hex = "0.4"
Expand All @@ -18,6 +21,11 @@ rand_core = "0.6"
pallas-codec = { version = "=0.30.1", path = "../pallas-codec" }
serde = "1.0.143"

# FIXME: This needs to be a properly deployed crate from the input-output-hk/vrf repository after my PR is merged
# The vrf crate has not been fully tested in production environments and still has several upstream issues that
# are open PRs but not merged yet.
vrf_dalek = { git = "https://github.com/AndrewWestberg/vrf", rev = "6fc1440b197098feb6d75e2b71517019b8e2e9c2" }

[dev-dependencies]
itertools = "0.13"
quickcheck = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion pallas-crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Crate with all the cryptographic material to support Cardano protocol:
- [x] Ed25519 Extended asymmetric key pair
- [ ] Bip32-Ed25519 key derivation
- [ ] BIP39 mnemonics
- [ ] VRF
- [x] VRF
- [ ] KES
- [ ] SECP256k1
- [x] Nonce calculations
1 change: 1 addition & 0 deletions pallas-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod hash;
pub mod key;
pub mod memsec;
pub mod nonce;
pub mod vrf;
78 changes: 78 additions & 0 deletions pallas-crypto/src/vrf/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use thiserror::Error;
use vrf_dalek::vrf03::{PublicKey03, SecretKey03, VrfProof03};

#[derive(Error, Debug)]
pub enum Error {
#[error("TryFromSlice {0}")]
TryFromSlice(#[from] std::array::TryFromSliceError),

#[error("VrfError {0}")]
VrfError(#[from] vrf_dalek::errors::VrfError),
}

/// Sign a seed value with a vrf secret key and produce a proof signature
pub fn rust_crypto_vrf_prove(secret_key: &[u8], seed: &[u8]) -> Result<Vec<u8>, Error> {
let sk = SecretKey03::from_bytes(secret_key[..32].try_into()?);
let pk = PublicKey03::from(&sk);
let proof = VrfProof03::generate(&pk, &sk, seed);
Ok(proof.to_bytes().to_vec())
}

/// Convert a proof signature to a hash
pub fn rust_crypto_vrf_proof_to_hash(proof: &[u8]) -> Result<Vec<u8>, Error> {
let proof = VrfProof03::from_bytes(proof[..80].try_into()?)?;
Ok(proof.proof_to_hash().to_vec())
}

/// Verify a proof signature with a vrf public key. This will return a hash to compare with the original
/// signature hash, but any non-error result is considered a successful verification without needing
/// to do the extra comparison check.
pub fn rust_crypto_vrf_verify(
public_key: &[u8],
signature: &[u8],
seed: &[u8],
) -> Result<Vec<u8>, Error> {
let pk = PublicKey03::from_bytes(public_key.try_into()?);
let proof = VrfProof03::from_bytes(signature.try_into()?)?;
Ok(proof.verify(&pk, seed)?.to_vec())
}

#[cfg(test)]
mod tests {
use super::*;
use rand::{thread_rng, Rng};

#[test]
fn sodium_crypto_vrf_prove_and_verify() {
// Node operational VRF-Verification-Key: pool.vrf.vkey
// {
// "type": "VrfVerificationKey_PraosVRF",
// "description": "VRF Verification Key",
// "cborHex": "5820e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381"
// }
//
// Node operational VRF-Signing-Key: pool.vrf.skey
// {
// "type": "VrfSigningKey_PraosVRF",
// "description": "VRF Signing Key",
// "cborHex": "5840adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381"
// }

let vrf_skey = hex::decode("adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381").unwrap();
let vrf_vkey =
hex::decode("e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381")
.unwrap();

// random seed to sign with vrf_skey
let mut seed = [0u8; 64];
thread_rng().fill(&mut seed);

// create a proof signature and hash of the seed
let proof_signature = rust_crypto_vrf_prove(&vrf_skey, &seed).unwrap();
let proof_hash = rust_crypto_vrf_proof_to_hash(&proof_signature).unwrap();

// verify the proof signature with the public vrf public key
let verified_hash = rust_crypto_vrf_verify(&vrf_vkey, &proof_signature, &seed).unwrap();
assert_eq!(proof_hash, verified_hash);
}
}

0 comments on commit 835c482

Please sign in to comment.