Skip to content
This repository has been archived by the owner on Jan 19, 2023. It is now read-only.

Commit

Permalink
Refactor VRF not to expose output of VRF function in two representati…
Browse files Browse the repository at this point in the history
…ons (#36)

* Refactor VRF not to expose output of VRF function in two representations.

* Fix merge with master
  • Loading branch information
Harrm authored and Warchant committed Jan 28, 2020
1 parent 8fe68fe commit 79a4056
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 78 deletions.
130 changes: 69 additions & 61 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ extern crate schnorrkel;
// which was adopted from the initial https://github.com/paritytech/schnorrkel-js/
// forked at commit eff430ddc3090f56317c80654208b8298ef7ab3f

use schnorrkel::{
context::signing_context,
derive::{CHAIN_CODE_LENGTH, ChainCode, Derivation}, Keypair, MiniSecretKey, PublicKey, SecretKey,
Signature, SignatureError, vrf::{VRFOutput, VRFProof}, ExpansionMode};

use std::os::raw::c_ulong;
use std::ptr;
use std::slice;
use std::os::raw::c_ulong;

use schnorrkel::{
context::signing_context,
derive::{CHAIN_CODE_LENGTH, ChainCode, Derivation}, ExpansionMode, Keypair, MiniSecretKey, PublicKey,
SecretKey, Signature, SignatureError, vrf::{VRFOutput, VRFProof}};
use std::fmt::{Formatter, Error};

// cbindgen has an issue with macros, so define it outside,
// otherwise it would've been possible to avoid duplication of macro variant list
Expand Down Expand Up @@ -117,24 +118,17 @@ pub const SR25519_SIGNATURE_SIZE: c_ulong = 64;
/// Size of SR25519 KEYPAIR. [32 bytes key | 32 bytes nonce | 32 bytes public]
pub const SR25519_KEYPAIR_SIZE: c_ulong = 96;

/// Size of VRF limit, bytes
pub const SR25519_VRF_LIMIT_SIZE: c_ulong = 16;

/// Size of VRF input, bytes
pub const SR25519_VRF_INPUT_SIZE: c_ulong = 32;

/// Size of VRF output, bytes
pub const SR25519_VRF_OUTPUT_SIZE: c_ulong = 32;

/// Size of VRF raw output, bytes
pub const SR25519_VRF_RAW_OUTPUT_SIZE: c_ulong = 16;

/// Size of VRF input + output, bytes
pub const SR25519_VRF_IN_OUT_SIZE: c_ulong = 64;

/// Size of VRF proof, bytes
pub const SR25519_VRF_PROOF_SIZE: c_ulong = 64;

/// Size of VRF raw output, bytes
pub const SR25519_VRF_RAW_OUTPUT_SIZE: c_ulong = 16;

/// Size of VRF limit, bytes
pub const SR25519_VRF_THRESHOLD_SIZE: c_ulong = 16;

/// Perform a derivation on a secret
///
Expand Down Expand Up @@ -277,15 +271,35 @@ pub unsafe extern "C" fn sr25519_verify(
}

#[repr(C)]
pub struct VrfSignResult {
pub struct VrfResult {
pub result: Sr25519SignatureResult,
pub is_less: bool,
}

impl VrfResult {
fn create_err(err: &SignatureError) -> VrfResult {
VrfResult { is_less: false, result: convert_error(&err) }
}

fn create_val(is_less: bool) -> VrfResult {
VrfResult { is_less, result: Sr25519SignatureResult::Ok }
}
}

impl std::fmt::Debug for VrfResult {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str("VrfResult { ")?;
f.write_str(self.is_less.to_string().as_str())?;
f.write_str(", ")?;
write!(f, "{:?}", self)?;
f.write_str(" }")?;
Result::Ok(())
}
}

/// Sign the provided message using a Verifiable Random Function and
/// if the result is less than \param limit provide the proof
/// @param out_and_proof_ptr pointer to output array, where the VRF out and proof will be written
/// @param raw_out_ptr pointer to output array, where the VRF raw out will be written
/// @param keypair_ptr byte representation of the keypair that will be used during signing
/// @param message_ptr byte array to be signed
/// @param limit_ptr byte array, must be 16 bytes long
Expand All @@ -294,21 +308,23 @@ pub struct VrfSignResult {
#[no_mangle]
pub unsafe extern "C" fn sr25519_vrf_sign_if_less(
out_and_proof_ptr: *mut u8,
raw_out_ptr: *mut u8,
keypair_ptr: *const u8,
message_ptr: *const u8,
message_length: c_ulong,
limit_ptr: *const u8,
) -> VrfSignResult {
) -> VrfResult {
let keypair_bytes = slice::from_raw_parts(keypair_ptr, SR25519_KEYPAIR_SIZE as usize);
let keypair = create_from_pair(keypair_bytes);
let message = slice::from_raw_parts(message_ptr, message_length as usize);
let limit = slice::from_raw_parts(limit_ptr, SR25519_VRF_LIMIT_SIZE as usize);
let mut limit_arr: [u8; SR25519_VRF_LIMIT_SIZE as usize] = Default::default();
limit_arr.copy_from_slice(&limit[0..SR25519_VRF_LIMIT_SIZE as usize]);

let limit = slice::from_raw_parts(limit_ptr, SR25519_VRF_THRESHOLD_SIZE as usize);
let mut limit_arr: [u8; SR25519_VRF_THRESHOLD_SIZE as usize] = Default::default();
limit_arr.copy_from_slice(&limit[0..SR25519_VRF_THRESHOLD_SIZE as usize]);

let res =
keypair.vrf_sign(
signing_context(SIGNING_CTX).bytes(message));

let limit_int = u128::from_le_bytes(limit_arr);

let inout = keypair.vrf_create_hash(signing_context(SIGNING_CTX).bytes(message));
Expand All @@ -320,41 +336,20 @@ pub unsafe extern "C" fn sr25519_vrf_sign_if_less(
let (io, proof, _) = res;
ptr::copy(io.output.to_bytes().as_ptr(), out_and_proof_ptr, SR25519_VRF_OUTPUT_SIZE as usize);
ptr::copy(proof.to_bytes().as_ptr(), out_and_proof_ptr.add(SR25519_VRF_OUTPUT_SIZE as usize), SR25519_VRF_PROOF_SIZE as usize);
ptr::copy(raw_out_bytes.as_ptr(), raw_out_ptr, SR25519_VRF_RAW_OUTPUT_SIZE as usize);
return VrfSignResult { is_less: true, result: Sr25519SignatureResult::Ok };
VrfResult::create_val(true)
} else {
return VrfSignResult { is_less: false, result: Sr25519SignatureResult::Ok };
VrfResult::create_val(false)
}
}

/// Check if \param output is less than \param limit using a Verifiable Random Function
/// @param raw_out_ptr pointer to output array, where the VRF raw out is stored
/// @param limit_ptr byte array, must be 16 bytes long
///
#[allow(unused_attributes)]
#[no_mangle]
pub unsafe extern "C" fn sr25519_vrf_check_if_less(
raw_out_ptr: *const u8,
limit_ptr: *const u8) -> bool {
let raw_out_bytes = slice::from_raw_parts(raw_out_ptr, SR25519_VRF_RAW_OUTPUT_SIZE as usize);
let mut raw_out_arr: [u8; SR25519_VRF_RAW_OUTPUT_SIZE as usize] = Default::default();
raw_out_arr.copy_from_slice(&raw_out_bytes[0..SR25519_VRF_RAW_OUTPUT_SIZE as usize]);

let limit = slice::from_raw_parts(limit_ptr, SR25519_VRF_LIMIT_SIZE as usize);
let mut limit_arr: [u8; SR25519_VRF_LIMIT_SIZE as usize] = Default::default();
limit_arr.copy_from_slice(&limit[0..SR25519_VRF_LIMIT_SIZE as usize]);
let limit_int = u128::from_le_bytes(limit_arr);

let check = u128::from_le_bytes(raw_out_arr) < limit_int;
return check;
}


/// Verify a signature produced by a VRF with its original input and the corresponding proof
/// Verify a signature produced by a VRF with its original input and the corresponding proof and
/// check if the result of the function is less than the threshold.
/// @note If errors, is_less of the returned structure is not meant to contain a valid value
/// @param public_key_ptr byte representation of the public key that was used to sign the message
/// @param message_ptr the orignal signed message
/// @param output_ptr the signature
/// @param proof_ptr the proof of the signature
/// @param threshold_ptr the threshold to be compared against
#[allow(unused_attributes)]
#[no_mangle]
pub unsafe extern "C" fn sr25519_vrf_verify(
Expand All @@ -363,43 +358,55 @@ pub unsafe extern "C" fn sr25519_vrf_verify(
message_length: c_ulong,
output_ptr: *const u8,
proof_ptr: *const u8,
) -> Sr25519SignatureResult {
threshold_ptr: *const u8,
) -> VrfResult {
let public_key = create_public(slice::from_raw_parts(public_key_ptr, SR25519_PUBLIC_SIZE as usize));
let message = slice::from_raw_parts(message_ptr, message_length as usize);
let ctx = signing_context(SIGNING_CTX).bytes(message);
let vrf_out = match VRFOutput::from_bytes(
slice::from_raw_parts(output_ptr, SR25519_VRF_OUTPUT_SIZE as usize)) {
Ok(val) => val,
Err(err) => return convert_error(&err)
Err(err) => return VrfResult::create_err(&err)
};
let vrf_proof = match VRFProof::from_bytes(
slice::from_raw_parts(proof_ptr, SR25519_VRF_PROOF_SIZE as usize)) {
Ok(val) => val,
Err(err) => return convert_error(&err)
Err(err) => return VrfResult::create_err(&err)
};
let (in_out, proof) =
match public_key.vrf_verify(ctx.clone(), &vrf_out, &vrf_proof) {
Ok(val) => val,
Err(err) => return convert_error(&err)
Err(err) => return VrfResult::create_err(&err)
};
let raw_output = in_out.make_bytes::<[u8; SR25519_VRF_RAW_OUTPUT_SIZE as usize]>(BABE_VRF_PREFIX);

let threshold = slice::from_raw_parts(threshold_ptr, SR25519_VRF_THRESHOLD_SIZE as usize);
let mut threshold_arr: [u8; SR25519_VRF_THRESHOLD_SIZE as usize] = Default::default();
threshold_arr.copy_from_slice(&threshold[0..SR25519_VRF_THRESHOLD_SIZE as usize]);
let threshold_int = u128::from_le_bytes(threshold_arr);

let check = u128::from_le_bytes(raw_output) < threshold_int;

let decomp_proof = match
proof.shorten_vrf(&public_key, ctx.clone(), &in_out.to_output()) {
Ok(val) => val,
Err(e) => return convert_error(&e)
Err(e) => return VrfResult::create_err(&e)
};
if in_out.to_output() == vrf_out &&
decomp_proof == vrf_proof {
Sr25519SignatureResult::Ok
VrfResult::create_val(check)
} else {
convert_error(&SignatureError::EquationFalse)
VrfResult::create_err(&SignatureError::EquationFalse)
}
}

#[cfg(test)]
pub mod tests {
extern crate rand;
extern crate schnorrkel;

use super::*;

use hex_literal::hex;
use schnorrkel::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};

Expand Down Expand Up @@ -538,11 +545,12 @@ pub mod tests {
&keypair.public, ctx.clone(), &io.to_output()).expect("Shorten VRF");
assert_eq!(proof, decomp_proof);
unsafe {
let threshold_bytes = [0u8; SR25519_VRF_THRESHOLD_SIZE as usize];
let res = sr25519_vrf_verify(public.as_ptr(),
message.as_ptr(), message.len() as c_ulong,
io.as_output_bytes().as_ptr(),
proof.to_bytes().as_ptr());
assert_eq!(res, Sr25519SignatureResult::Ok);
proof.to_bytes().as_ptr(), threshold_bytes.as_ptr());
assert_eq!(res.result, Sr25519SignatureResult::Ok);
}
}
}
26 changes: 9 additions & 17 deletions test/vrf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,27 @@ TEST(VrfTest, Verify) {
auto keypair = randomKeypair();
std::array<uint8_t, SR25519_VRF_OUTPUT_SIZE + SR25519_VRF_PROOF_SIZE>
out_and_proof;
std::array<uint8_t, SR25519_VRF_RAW_OUTPUT_SIZE>
raw_out;

auto message = "Hello, world!"_v;
auto limit = std::vector<uint8_t>(32, 0xFF);
auto limit = std::vector<uint8_t>(16, 0xFF);

auto res1 =
sr25519_vrf_sign_if_less(out_and_proof.data(), raw_out.data(), keypair.data(),
sr25519_vrf_sign_if_less(out_and_proof.data(), keypair.data(),
message.data(), message.size(), limit.data());
ASSERT_EQ(res1.result, Sr25519SignatureResult::Ok);
ASSERT_TRUE(res1.is_less);

auto res2 = sr25519_vrf_verify(
keypair.data() + 64, message.data(), message.size(), out_and_proof.data(),
out_and_proof.data() + SR25519_VRF_OUTPUT_SIZE);
ASSERT_EQ(res2, Sr25519SignatureResult::Ok);
out_and_proof.data() + SR25519_VRF_OUTPUT_SIZE, limit.data());
ASSERT_EQ(res2.result, Sr25519SignatureResult::Ok);
ASSERT_TRUE(res2.is_less);

out_and_proof[5] += 3;
auto res3 = sr25519_vrf_verify(
keypair.data() + 64, message.data(), message.size(), out_and_proof.data(),
out_and_proof.data() + SR25519_VRF_OUTPUT_SIZE);
ASSERT_NE(res3, Sr25519SignatureResult::Ok);
out_and_proof.data() + SR25519_VRF_OUTPUT_SIZE, limit.data());
ASSERT_NE(res3.result, Sr25519SignatureResult::Ok);


}
Expand All @@ -46,14 +45,12 @@ TEST(VrfTest, ResultNotLess) {
auto keypair = "915bb406968655c3412df5773c3de3dee9f6da84668b5de8d2f34d0304d20b0bac5ea3a293dfd93859ee64a5b825937753864c19be857f045758dcae10259ba1049b21bb9cb88471b9dadb50b925135cfb291a463043635b58599a2d01b1fd18"_unhex;
std::array<uint8_t, SR25519_VRF_OUTPUT_SIZE + SR25519_VRF_PROOF_SIZE>
out_and_proof;
std::array<uint8_t, SR25519_VRF_RAW_OUTPUT_SIZE>
raw_output;

auto message = "Hello, world!"_v;
auto limit = std::vector<uint8_t>(16, 0x55);

auto res1 =
sr25519_vrf_sign_if_less(out_and_proof.data(), raw_output.data(), keypair.data(),
sr25519_vrf_sign_if_less(out_and_proof.data(), keypair.data(),
message.data(), message.size(), limit.data());
ASSERT_EQ(res1.result, Sr25519SignatureResult::Ok);
EXPECT_FALSE(res1.is_less);
Expand All @@ -63,18 +60,13 @@ TEST(VrfTest, SignAndCheck) {
auto keypair = "915bb406968655c3412df5773c3de3dee9f6da84668b5de8d2f34d0304d20b0bac5ea3a293dfd93859ee64a5b825937753864c19be857f045758dcae10259ba1049b21bb9cb88471b9dadb50b925135cfb291a463043635b58599a2d01b1fd18"_unhex;
std::array<uint8_t, SR25519_VRF_OUTPUT_SIZE + SR25519_VRF_PROOF_SIZE>
out_and_proof;
std::array<uint8_t, SR25519_VRF_RAW_OUTPUT_SIZE>
raw_output;

auto message = "Hello, world!"_v;
auto limit = std::vector<uint8_t>(16, 0xAA);

auto res1 =
sr25519_vrf_sign_if_less(out_and_proof.data(), raw_output.data(), keypair.data(),
sr25519_vrf_sign_if_less(out_and_proof.data(), keypair.data(),
message.data(), message.size(), limit.data());
ASSERT_EQ(res1.result, Sr25519SignatureResult::Ok);
EXPECT_TRUE(res1.is_less);
auto res2 =
sr25519_vrf_check_if_less(raw_output.data(), limit.data());
ASSERT_TRUE(res2);
}

0 comments on commit 79a4056

Please sign in to comment.