-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat[pallas-crypto]: Implement libsodium vrf signature verification
- Loading branch information
1 parent
48f26db
commit 8a78978
Showing
8 changed files
with
248 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "pallas-crypto/contrib/libsodium"] | ||
path = pallas-crypto/contrib/libsodium | ||
url = https://github.com/input-output-hk/libsodium |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,11 @@ homepage = "https://github.com/txpipe/pallas" | |
documentation = "https://docs.rs/pallas-crypto" | ||
license = "Apache-2.0" | ||
readme = "README.md" | ||
authors = ["Nicolas Di Prima <[email protected]>"] | ||
authors = [ | ||
"Nicolas Di Prima <[email protected]>", | ||
"Andrew Westberg <[email protected]>", | ||
] | ||
build = "build.rs" | ||
|
||
[dependencies] | ||
hex = "0.4" | ||
|
@@ -24,3 +28,9 @@ quickcheck = "1.0" | |
quickcheck_macros = "1.0" | ||
rand = "0.8" | ||
serde_test = "1.0.143" | ||
|
||
[build-dependencies] | ||
autotools = "0.2" | ||
pkg-config = "0.3" | ||
cc = "1.1" | ||
regex = "1.10" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use std::process::Command; | ||
|
||
macro_rules! ok (($expression:expr) => ($expression.unwrap())); | ||
macro_rules! log { | ||
($fmt:expr) => (println!(concat!("pallas-crypto/build.rs:{}: ", $fmt), line!())); | ||
($fmt:expr, $($arg:tt)*) => (println!(concat!("pallas-crypto/build.rs:{}: ", $fmt), | ||
line!(), $($arg)*)); | ||
} | ||
|
||
fn main() { | ||
// Build and link libsodium | ||
run("git", |command| { | ||
command | ||
.arg("submodule") | ||
.arg("update") | ||
.arg("--init") | ||
.arg("--recursive") | ||
.arg("--force") | ||
}); | ||
|
||
// if windows | ||
#[cfg(target_os = "windows")] | ||
{ | ||
// Build libsodium with MSBuild | ||
run("msbuild", |command| { | ||
command | ||
.current_dir("contrib/libsodium/builds/msvc/vs2019") | ||
.arg("libsodium.sln") | ||
.arg("/p:Configuration=StaticRelease") | ||
.arg("/p:Platform=x64") | ||
.arg("/t:Rebuild") | ||
.arg("/m") | ||
.arg("/v:m") | ||
.arg("/nologo") | ||
.arg("/clp:NoSummary;NoItemAndPropertyList;ErrorsOnly") | ||
.arg("/nr:false") | ||
.arg("/fl") | ||
.arg("/flp:NoSummary;NoItemAndPropertyList;ErrorsOnly") | ||
.arg("/p:PlatformToolset=v142") | ||
.arg("/p:WindowsTargetPlatformVersion=10.0") | ||
.arg("/p:PreferredToolArchitecture=x64") | ||
.arg("/p:DefineConstants=SODIUM_STATIC") | ||
}); | ||
|
||
// debugging: find the path to libsodium.lib | ||
run("where", |command| { | ||
command | ||
.current_dir("contrib/libsodium") | ||
.arg("/r") | ||
.arg(".") | ||
.arg("libsodium.lib") | ||
}); | ||
|
||
// debugging: print the current working directory | ||
log!("Current working directory: {:?}", std::env::current_dir()); | ||
|
||
println!("cargo:rustc-link-search=native=contrib/libsodium/bin/x64/Release/v142/static"); | ||
println!("cargo:rustc-link-lib=static=libsodium"); | ||
panic!("intentionally panic to debug"); | ||
} | ||
|
||
// if not windows | ||
#[cfg(not(target_os = "windows"))] | ||
{ | ||
// Build libsodium automatically (as part of rust build) | ||
let libsodium = autotools::Config::new("contrib/libsodium/") | ||
.reconf("-vfi") | ||
.enable_static() | ||
.disable_shared() | ||
.build(); | ||
println!( | ||
"cargo:rustc-link-search=native={}", | ||
libsodium.join("lib").display() | ||
); | ||
println!("cargo:rustc-link-lib=static=sodium"); | ||
} | ||
println!("cargo:rerun-if-changed=build.rs"); | ||
} | ||
|
||
fn run<F>(name: &str, mut configure: F) | ||
where | ||
F: FnMut(&mut Command) -> &mut Command, | ||
{ | ||
let mut command = Command::new(name); | ||
let configured = configure(&mut command); | ||
log!("Executing {:?}", configured); | ||
if !ok!(configured.status()).success() { | ||
panic!("failed to execute {:?}", configured); | ||
} | ||
log!("Command {:?} finished successfully", configured); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ pub mod hash; | |
pub mod key; | ||
pub mod memsec; | ||
pub mod nonce; | ||
pub mod vrf; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
use thiserror::Error; | ||
|
||
#[link(name = "sodium", kind = "static")] | ||
extern "C" { | ||
// int crypto_vrf_ietfdraft03_prove(unsigned char *proof, const unsigned char *sk, const unsigned char *m, unsigned long long mlen); | ||
fn crypto_vrf_ietfdraft03_prove(proof: *mut u8, sk: *const u8, m: *const u8, mlen: u64) -> i32; | ||
|
||
// int crypto_vrf_ietfdraft03_proof_to_hash(unsigned char *hash, const unsigned char *proof); | ||
fn crypto_vrf_ietfdraft03_proof_to_hash(hash: *mut u8, proof: *const u8) -> i32; | ||
|
||
// int crypto_vrf_ietfdraft03_verify(unsigned char *output, const unsigned char *pk, const unsigned char *proof, const unsigned char *m, unsigned long long mlen) | ||
fn crypto_vrf_ietfdraft03_verify( | ||
output: *mut u8, | ||
pk: *const u8, | ||
proof: *const u8, | ||
m: *const u8, | ||
mlen: u64, | ||
) -> i32; | ||
} | ||
|
||
#[derive(Error, Debug)] | ||
pub enum Error { | ||
#[error("{0}")] | ||
Libsodium(String), | ||
} | ||
|
||
/// Sign a seed value with a vrf secret key and produce a proof signature | ||
pub fn sodium_crypto_vrf_prove(secret_key: &[u8], seed: &[u8]) -> Result<Vec<u8>, Error> { | ||
let mut proof: Vec<u8> = Vec::with_capacity(80); | ||
unsafe { | ||
let rc = crypto_vrf_ietfdraft03_prove( | ||
proof.as_mut_ptr(), | ||
secret_key.as_ptr(), | ||
seed.as_ptr(), | ||
seed.len() as u64, | ||
); | ||
if rc != 0 { | ||
Err(Error::Libsodium(format!( | ||
"libsodium crypto_vrf_ietfdraft03_prove() failed, returned {rc}, expected 0" | ||
))) | ||
} else { | ||
proof.set_len(80); | ||
Ok(proof) | ||
} | ||
} | ||
} | ||
|
||
/// Convert a proof signature to a hash | ||
pub fn sodium_crypto_vrf_proof_to_hash(proof: &[u8]) -> Result<Vec<u8>, Error> { | ||
let mut hash: Vec<u8> = Vec::with_capacity(64); | ||
unsafe { | ||
let rc = crypto_vrf_ietfdraft03_proof_to_hash(hash.as_mut_ptr(), proof.as_ptr()); | ||
if rc != 0 { | ||
Err(Error::Libsodium(format!( | ||
"libsodium crypto_vrf_ietfdraft03_proof_to_hash() failed, returned {rc}, expected 0" | ||
))) | ||
} else { | ||
hash.set_len(64); | ||
Ok(hash) | ||
} | ||
} | ||
} | ||
|
||
/// Verify a proof signature with a vrf public key. This will return a hash to compare with the original | ||
/// signature hash. | ||
pub fn sodium_crypto_vrf_verify( | ||
public_key: &[u8], | ||
signature: &[u8], | ||
seed: &[u8], | ||
) -> Result<Vec<u8>, Error> { | ||
let mut verification: Vec<u8> = Vec::with_capacity(64); | ||
unsafe { | ||
let rc = crypto_vrf_ietfdraft03_verify( | ||
verification.as_mut_ptr(), | ||
public_key.as_ptr(), | ||
signature.as_ptr(), | ||
seed.as_ptr(), | ||
seed.len() as u64, | ||
); | ||
if rc != 0 { | ||
Err(Error::Libsodium(format!( | ||
"libsodium crypto_vrf_ietfdraft03_verify() failed, returned {rc}, expected 0" | ||
))) | ||
} else { | ||
verification.set_len(64); | ||
Ok(verification) | ||
} | ||
} | ||
} | ||
|
||
#[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 = sodium_crypto_vrf_prove(&vrf_skey, &seed).unwrap(); | ||
let proof_hash = sodium_crypto_vrf_proof_to_hash(&proof_signature).unwrap(); | ||
|
||
// verify the proof signature with the public vrf public key | ||
let verified_hash = sodium_crypto_vrf_verify(&vrf_vkey, &proof_signature, &seed).unwrap(); | ||
assert_eq!(proof_hash, verified_hash); | ||
} | ||
} |