Adjust vrf_{sign, verify} with one variant (#171)

This commit is contained in:
Jaco Greeff
2021-01-20 12:08:27 +01:00
committed by GitHub
parent a7f8342458
commit e6bc31ac5d
9 changed files with 427 additions and 463 deletions
+7
View File
@@ -1,5 +1,12 @@
# CHANGELOG
## 3.2.x
Contributed:
- Expose sr25519 `ext_vrf_{sign, verify}` methods (Thanks to https://github.com/stiiifff)
## 3.1.1 Dec 19, 2020
Changes:
+2 -2
View File
@@ -24,8 +24,8 @@
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@polkadot/dev": "^0.61.21",
"fflate": "^0.4.8",
"@polkadot/dev": "^0.61.24",
"fflate": "^0.5.3",
"override-require": "^1.1.1"
},
"version": "3.1.2-3"
+2 -2
View File
@@ -14,8 +14,8 @@
"@polkadot/wasm-crypto-wasm": "^3.1.2-3"
},
"devDependencies": {
"@polkadot/util": "^5.2.3",
"@polkadot/x-randomvalues": "^5.2.3"
"@polkadot/util": "^5.3.1",
"@polkadot/x-randomvalues": "^5.3.1"
},
"peerDependencies": {
"@polkadot/util": "*",
+3 -27
View File
@@ -160,50 +160,26 @@ export const sr25519Verify = withWasm((wasm) =>
);
export const vrfSign = withWasm((wasm) =>
(secret: Uint8Array, context: Uint8Array, message: Uint8Array): Uint8Array => {
const [ptr0, len0] = allocU8a(secret);
const [ptr1, len1] = allocU8a(context);
const [ptr2, len2] = allocU8a(message);
wasm.ext_vrf_sign(8, ptr0, len0, ptr1, len1, ptr2, len2);
return resultU8a();
}
);
export const vrfVerify = withWasm((wasm) =>
(pubkey: Uint8Array, context: Uint8Array, message: Uint8Array, outAndProof: Uint8Array): boolean => {
const [ptr0, len0] = allocU8a(pubkey);
const [ptr1, len1] = allocU8a(context);
const [ptr2, len2] = allocU8a(message);
const [ptr3, len3] = allocU8a(outAndProof);
const ret = wasm.ext_vrf_verify(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
return ret !== 0;
}
);
export const vrfSignExtra = withWasm((wasm) =>
(secret: Uint8Array, context: Uint8Array, message: Uint8Array, extra: Uint8Array): Uint8Array => {
const [ptr0, len0] = allocU8a(secret);
const [ptr1, len1] = allocU8a(context);
const [ptr2, len2] = allocU8a(message);
const [ptr3, len3] = allocU8a(extra);
wasm.ext_vrf_sign_extra(8, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
wasm.ext_vrf_sign(8, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
return resultU8a();
}
);
export const vrfVerifyExtra = withWasm((wasm) =>
export const vrfVerify = withWasm((wasm) =>
(pubkey: Uint8Array, context: Uint8Array, message: Uint8Array, extra: Uint8Array, outAndProof: Uint8Array): boolean => {
const [ptr0, len0] = allocU8a(pubkey);
const [ptr1, len1] = allocU8a(context);
const [ptr2, len2] = allocU8a(message);
const [ptr3, len3] = allocU8a(extra);
const [ptr4, len4] = allocU8a(outAndProof);
const ret = wasm.ext_vrf_verify_extra(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4);
const ret = wasm.ext_vrf_verify(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4);
return ret !== 0;
}
+10
View File
@@ -0,0 +1,10 @@
root = true
[*]
indent_style=tab
indent_size=tab
tab_width=4
end_of_line=lf
charset=utf-8
trim_trailing_whitespace=true
max_line_length=100
insert_final_newline=true
+131 -142
View File
@@ -8,11 +8,7 @@
// forked at commit eff430ddc3090f56317c80654208b8298ef7ab3f
use merlin::Transcript;
use schnorrkel::{
signing_context,
vrf::{VRFOutput, VRFProof},
PublicKey, SecretKey,
};
use schnorrkel::{signing_context, vrf::{VRFOutput, VRFProof}, PublicKey, SecretKey};
use wasm_bindgen::prelude::*;
/// Size of VRF output, bytes
@@ -27,46 +23,26 @@ pub const VRF_RAW_OUTPUT_SIZE: usize = 16;
/// Size of VRF limit, bytes
pub const VRF_THRESHOLD_SIZE: usize = 16;
/// Run a Random Verifiable Function (VRF) on one single input (message) transcript,
/// producing the output signature and corresponding short proof.
///
/// * secret: UIntArray with 64 element
/// * context: Arbitrary length UIntArray
/// * message: Arbitrary length UIntArray
///
/// * returned vector is the 32-byte output (signature) and 64-byte proof.
#[wasm_bindgen]
pub fn ext_vrf_sign(secret: &[u8], context: &[u8], message: &[u8]) -> Vec<u8> {
let keypair = SecretKey::from_ed25519_bytes(secret).unwrap().to_keypair();
/// Size of the final full output
pub const VRF_OUTPUT_FULL: usize = VRF_OUTPUT_SIZE + VRF_PROOF_SIZE;
let (io, proof, _) = keypair.vrf_sign(signing_context(context).bytes(message));
pub fn create_transcript(extra: &[u8]) -> Transcript {
let mut transcript = Transcript::new(b"VRF");
let mut result: [u8; VRF_OUTPUT_SIZE + VRF_PROOF_SIZE] =
[0u8; VRF_OUTPUT_SIZE + VRF_PROOF_SIZE];
result[..VRF_OUTPUT_SIZE].copy_from_slice(io.as_output_bytes());
result[VRF_OUTPUT_SIZE..].copy_from_slice(&proof.to_bytes());
result.to_vec()
// if is important that we don't append when empty, since the
// append in merging encodes length information
if extra.len() > 0 {
transcript.append_message(b"", extra);
}
transcript
}
/// Verify VRF proof for one single input transcript and corresponding output.
///
/// * pubkey: UIntArray with 32 element
/// * context: Arbitrary length UIntArray
/// * message: Arbitrary length UIntArray
/// * out_and_proof: 96-byte output & proof array from the ext_vrf_sign function.
#[wasm_bindgen]
pub fn ext_vrf_verify(pubkey: &[u8], context: &[u8], message: &[u8], out_and_proof: &[u8]) -> bool {
match (
PublicKey::from_bytes(pubkey),
VRFOutput::from_bytes(&out_and_proof[..VRF_OUTPUT_SIZE]),
VRFProof::from_bytes(&out_and_proof[VRF_OUTPUT_SIZE..(VRF_OUTPUT_SIZE+VRF_PROOF_SIZE)]),
) {
(Ok(public), Ok(out), Ok(proof)) => public
.vrf_verify(signing_context(context).bytes(message), &out, &proof)
.is_ok(),
_ => false,
}
}
// We don't use the non-extra vrf_sign and vrf_verify internally since in sr25519 they only wrap -
//
// https://github.com/w3f/schnorrkel/blob/8fa2ad3e9fbf0b652c724df6a87a4b3c5500f759/src/vrf.rs#L660
//
// The compat test (also commented) proofs this compatibility - vrf_extra_compat()
/// Run a Random Verifiable Function (VRF) on one single input
/// (message) transcript, and an extra message transcript,
@@ -79,20 +55,15 @@ pub fn ext_vrf_verify(pubkey: &[u8], context: &[u8], message: &[u8], out_and_pro
///
/// * returned vector is the 32-byte output (signature) and 64-byte proof.
#[wasm_bindgen]
pub fn ext_vrf_sign_extra(secret: &[u8], context: &[u8], message: &[u8], extra: &[u8]) -> Vec<u8> {
let keypair = SecretKey::from_ed25519_bytes(secret).unwrap().to_keypair();
pub fn ext_vrf_sign(secret: &[u8], context: &[u8], message: &[u8], extra: &[u8]) -> Vec<u8> {
let keypair = SecretKey::from_ed25519_bytes(secret).unwrap().to_keypair();
let transcript = create_transcript(extra);
let (io, proof, _) = keypair.vrf_sign_extra(signing_context(context).bytes(message), transcript);
let mut result: [u8; VRF_OUTPUT_FULL] = [0u8; VRF_OUTPUT_FULL];
let mut transcript = Transcript::new(b"VRF");
transcript.append_message(b"", extra);
let (io, proof, _) =
keypair.vrf_sign_extra(signing_context(context).bytes(message), transcript);
let mut result: [u8; VRF_OUTPUT_SIZE + VRF_PROOF_SIZE] =
[0u8; VRF_OUTPUT_SIZE + VRF_PROOF_SIZE];
result[..VRF_OUTPUT_SIZE].copy_from_slice(io.as_output_bytes());
result[VRF_OUTPUT_SIZE..].copy_from_slice(&proof.to_bytes());
result.to_vec()
result[..VRF_OUTPUT_SIZE].copy_from_slice(io.as_output_bytes());
result[VRF_OUTPUT_SIZE..].copy_from_slice(&proof.to_bytes());
result.to_vec()
}
/// Verify VRF proof for one single input transcript, and an extra message transcript,
@@ -104,108 +75,126 @@ pub fn ext_vrf_sign_extra(secret: &[u8], context: &[u8], message: &[u8], extra:
/// * extra: Arbitrary length UIntArray
/// * out_and_proof: 96-byte output & proof array from the ext_vrf_sign function.
#[wasm_bindgen]
pub fn ext_vrf_verify_extra(
pubkey: &[u8],
context: &[u8],
message: &[u8],
extra: &[u8],
out_and_proof: &[u8],
) -> bool {
match (
PublicKey::from_bytes(pubkey),
VRFOutput::from_bytes(&out_and_proof[..VRF_OUTPUT_SIZE]),
VRFProof::from_bytes(&out_and_proof[VRF_OUTPUT_SIZE..(VRF_OUTPUT_SIZE+VRF_PROOF_SIZE)]),
) {
(Ok(public), Ok(out), Ok(proof)) => {
let mut transcript = Transcript::new(b"VRF");
transcript.append_message(b"", extra);
public
.vrf_verify_extra(
signing_context(context).bytes(message),
&out,
&proof,
transcript,
)
.is_ok()
}
_ => false,
}
pub fn ext_vrf_verify(pubkey: &[u8], context: &[u8], message: &[u8], extra: &[u8], out_and_proof: &[u8]) -> bool {
match (
PublicKey::from_bytes(pubkey),
VRFOutput::from_bytes(&out_and_proof[..VRF_OUTPUT_SIZE]),
VRFProof::from_bytes(&out_and_proof[VRF_OUTPUT_SIZE..(VRF_OUTPUT_FULL)])
) {
(Ok(public), Ok(out), Ok(proof)) => {
public
.vrf_verify_extra(
signing_context(context).bytes(message),
&out,
&proof,
create_transcript(extra)
)
.is_ok()
}
_ => false,
}
}
#[cfg(test)]
pub mod tests {
extern crate rand;
extern crate schnorrkel;
extern crate rand;
extern crate schnorrkel;
use super::*;
use crate::sr25519::ext_sr_from_seed;
use schnorrkel::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
use super::*;
use crate::sr25519::ext_sr_from_seed;
use schnorrkel::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
fn generate_random_seed() -> Vec<u8> {
(0..32).map(|_| rand::random::<u8>()).collect()
}
fn generate_random_seed() -> Vec<u8> {
(0..32).map(|_| rand::random::<u8>()).collect()
}
#[test]
fn vrf_sign_and_verify() {
let seed = generate_random_seed();
let keypair = ext_sr_from_seed(seed.as_slice());
let private = &keypair[0..SECRET_KEY_LENGTH];
let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
let context = b"my VRF context";
let message = b"this is a message";
#[test]
fn vrf_sign_extra_and_verify() {
let seed = generate_random_seed();
let keypair = ext_sr_from_seed(seed.as_slice());
let private = &keypair[0..SECRET_KEY_LENGTH];
let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
let context = b"my VRF context";
let message = b"this is a message";
let extra = b"this is an extra";
// Call vrf_sign w/ given context & message
let out_and_proof = ext_vrf_sign(private, context, message);
// Perform multiple vrf_sign_extra calls w/ same context, message, extra args
let out_and_proof1 = ext_vrf_sign(private, context, message, extra);
let out_and_proof2 = ext_vrf_sign(private, context, message, extra);
// Basic size checks
assert!(out_and_proof.len() == VRF_OUTPUT_SIZE + VRF_PROOF_SIZE);
// Basic size checks
assert!(out_and_proof1.len() == VRF_OUTPUT_FULL);
assert!(out_and_proof2.len() == VRF_OUTPUT_FULL);
// VRF output can verified w/ original context & message
assert!(ext_vrf_verify(public, context, message, &out_and_proof));
}
// Given the same context, message & extra, output should be deterministic
let out1 = &out_and_proof1[..VRF_OUTPUT_SIZE];
let out2 = &out_and_proof2[..VRF_OUTPUT_SIZE];
assert_eq!(out1, out2);
#[test]
fn vrf_sign_extra_and_verify() {
let seed = generate_random_seed();
let keypair = ext_sr_from_seed(seed.as_slice());
let private = &keypair[0..SECRET_KEY_LENGTH];
let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
let context = b"my VRF context";
let message = b"this is a message";
let extra = b"this is an extra";
// But proof is non-deterministic
let proof1 = &out_and_proof1[VRF_OUTPUT_SIZE..];
let proof2 = &out_and_proof2[VRF_OUTPUT_SIZE..];
assert_ne!(proof1, proof2);
// Perform multiple vrf_sign_extra calls w/ same context, message, extra args
let out_and_proof1 = ext_vrf_sign_extra(private, context, message, extra);
let out_and_proof2 = ext_vrf_sign_extra(private, context, message, extra);
// VRF outputs can verified w/ original context, message & extra args
assert!(ext_vrf_verify(
public,
context,
message,
extra,
&out_and_proof1
));
assert!(ext_vrf_verify(
public,
context,
message,
extra,
&out_and_proof2
));
}
// Basic size checks
assert!(out_and_proof1.len() == VRF_OUTPUT_SIZE + VRF_PROOF_SIZE);
assert!(out_and_proof2.len() == VRF_OUTPUT_SIZE + VRF_PROOF_SIZE);
// #[test]
// fn vrf_extra_compat() {
// let seed = generate_random_seed();
// let keypair = ext_sr_from_seed(seed.as_slice());
// let private = &keypair[0..SECRET_KEY_LENGTH];
// let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
// let context = b"my VRF context";
// let message = b"this is a message";
// let extra = b"";
// Given the same context, message & extra, output should be deterministic
let out1 = &out_and_proof1[..VRF_OUTPUT_SIZE];
let out2 = &out_and_proof2[..VRF_OUTPUT_SIZE];
assert_eq!(out1, out2);
// // Perform multiple vrf_sign_extra calls w/ same context, message, extra args
// // (Here we refer to the current functions with an _extra postfix)
// let out_std = ext_vrf_sign(private, context, message);
// let out_ext = ext_vrf_sign_extra(private, context, message, extra);
// But proof is non-deterministic
let proof1 = &out_and_proof1[VRF_OUTPUT_SIZE..];
let proof2 = &out_and_proof2[VRF_OUTPUT_SIZE..];
assert_ne!(proof1, proof2);
// // Basic size checks
// assert!(out_std.len() == VRF_OUTPUT_FULL);
// assert!(out_ext.len() == VRF_OUTPUT_FULL);
// VRF outputs can verified w/ original context, message & extra args
assert!(ext_vrf_verify_extra(
public,
context,
message,
extra,
&out_and_proof1
));
assert!(ext_vrf_verify_extra(
public,
context,
message,
extra,
&out_and_proof2
));
}
// // Given the same context, message & extra, output should be deterministic
// let out1 = &out_std[..VRF_OUTPUT_SIZE];
// let out2 = &out_ext[..VRF_OUTPUT_SIZE];
// assert_eq!(out1, out2);
// // But proof is non-deterministic
// let proof1 = &out_std[VRF_OUTPUT_SIZE..];
// let proof2 = &out_ext[VRF_OUTPUT_SIZE..];
// assert_ne!(proof1, proof2);
// // Cross-verify
// assert!(ext_vrf_verify_extra(
// public,
// context,
// message,
// extra,
// &out_std
// ));
// assert!(ext_vrf_verify(
// public,
// context,
// message,
// &out_ext
// ));
// }
}
+2 -4
View File
@@ -32,10 +32,8 @@ export interface WasmCryptoInstance {
ext_sr_from_seed(a: number, b: number, c: number): void;
ext_sr_sign(a: number, b: number, c: number, d: number, e: number, f: number, g: number): void;
ext_sr_verify(a: number, b: number, c: number, d: number, e: number, f: number): number;
ext_vrf_sign(a: number, b: number, c: number, d: number, e: number, f: number, g: number): void;
ext_vrf_verify(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number): number;
ext_vrf_sign_extra(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): void;
ext_vrf_verify_extra(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): number;
ext_vrf_sign(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): void;
ext_vrf_verify(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): number;
}
export type AsmCreator = (wbg: Record<string, WebAssembly.ImportValue>) => WasmCryptoInstance;
+13 -13
View File
@@ -4,7 +4,7 @@
/* eslint-disable camelcase */
const crypto = require('crypto');
const { assert, hexToU8a, stringToU8a, u8aToHex } = require('@polkadot/util');
const { assert, stringToU8a, u8aToHex } = require('@polkadot/util');
function extractKeys (pair) {
return [pair, pair.slice(64), pair.slice(0, 64)];
@@ -14,10 +14,10 @@ function randomPair (wasm) {
return extractKeys(wasm.sr25519KeypairFromSeed(crypto.randomBytes(32)));
}
function vrf_signAndVerify (wasm) {
function vrf_signAndVerify_compat (wasm) {
const [, pk, sk] = randomPair(wasm);
const outAndProof = wasm.vrfSign(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'));
const isValid = wasm.vrfVerify(pk, stringToU8a('my VRF context'), stringToU8a('this is a message'), outAndProof);
const outAndProof = wasm.vrfSign(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), new Uint8Array());
const isValid = wasm.vrfVerify(pk, stringToU8a('my VRF context'), stringToU8a('this is a message'), new Uint8Array(), outAndProof);
console.log('\tVRF', u8aToHex(outAndProof));
console.log('\tRES', isValid);
@@ -25,10 +25,10 @@ function vrf_signAndVerify (wasm) {
assert(isValid, 'ERROR: Unable to verify VRF output & proof');
}
function vrf_signAndVerifyExtra (wasm) {
function vrf_signAndVerify (wasm) {
const [, pk, sk] = randomPair(wasm);
const outAndProof = wasm.vrfSignExtra(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'));
const isValid = wasm.vrfVerifyExtra(pk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'), outAndProof);
const outAndProof = wasm.vrfSign(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'));
const isValid = wasm.vrfVerify(pk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'), outAndProof);
console.log('\tVRF', u8aToHex(outAndProof));
console.log('\tRES', isValid);
@@ -36,10 +36,10 @@ function vrf_signAndVerifyExtra (wasm) {
assert(isValid, 'ERROR: Unable to verify VRF extra output & proof');
}
function vrf_signAndVerifyExtra_is_deterministic (wasm) {
const [, _pk, sk] = randomPair(wasm);
const outAndProof1 = wasm.vrfSignExtra(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'));
const outAndProof2 = wasm.vrfSignExtra(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'));
function vrf_signAndVerify_deterministic (wasm) {
const [,, sk] = randomPair(wasm);
const outAndProof1 = wasm.vrfSign(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'));
const outAndProof2 = wasm.vrfSign(sk, stringToU8a('my VRF context'), stringToU8a('this is a message'), stringToU8a('extra param'));
const sig1 = u8aToHex(outAndProof1.slice(0, 32));
const sig2 = u8aToHex(outAndProof2.slice(0, 32));
@@ -51,6 +51,6 @@ function vrf_signAndVerifyExtra_is_deterministic (wasm) {
module.exports = {
vrf_signAndVerify,
vrf_signAndVerifyExtra,
vrf_signAndVerifyExtra_is_deterministic
vrf_signAndVerify_compat,
vrf_signAndVerify_deterministic
};
+257 -273
View File
File diff suppressed because it is too large Load Diff