mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Add FROST key promotion
Closes https://github.com/serai-dex/serai/issues/72. Adds a trait, with a commented impl for a semi-unsafe niche feature, which will be used in https://github.com/serai-dex/serai/issues/73.
This commit is contained in:
@@ -14,6 +14,7 @@ use crate::{
|
||||
// Test suites for public usage
|
||||
pub mod curve;
|
||||
pub mod schnorr;
|
||||
pub mod promote;
|
||||
pub mod vectors;
|
||||
|
||||
// Literal test definitions to run during `cargo test`
|
||||
|
||||
125
crypto/frost/src/tests/promote.rs
Normal file
125
crypto/frost/src/tests/promote.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use std::{marker::PhantomData, collections::HashMap};
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use group::Group;
|
||||
|
||||
use crate::{
|
||||
Curve, // FrostKeys,
|
||||
promote::{GeneratorPromotion /* CurvePromote */},
|
||||
tests::{clone_without, key_gen, schnorr::sign_core},
|
||||
};
|
||||
|
||||
/*
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
struct AltFunctions<C: Curve> {
|
||||
_curve: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: Curve> Curve for AltFunctions<C> {
|
||||
type F = C::F;
|
||||
type G = C::G;
|
||||
|
||||
const ID: &'static [u8] = b"alt_functions";
|
||||
|
||||
fn generator() -> Self::G {
|
||||
C::generator()
|
||||
}
|
||||
|
||||
fn hash_msg(msg: &[u8]) -> Vec<u8> {
|
||||
C::hash_msg(&[msg, b"alt"].concat())
|
||||
}
|
||||
|
||||
fn hash_binding_factor(binding: &[u8]) -> Self::F {
|
||||
C::hash_to_F(b"rho_alt", binding)
|
||||
}
|
||||
|
||||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
||||
C::hash_to_F(&[dst, b"alt"].concat(), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Test promotion of FROST keys to another set of functions for interoperability
|
||||
fn test_ciphersuite_promotion<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
let keys = key_gen::<_, C>(&mut *rng);
|
||||
for keys in keys.values() {
|
||||
let promoted: FrostKeys<AltFunctions<C>> = keys.clone().promote();
|
||||
// Verify equivalence via their serializations, minus the ID's length and ID itself
|
||||
assert_eq!(
|
||||
keys.serialize()[(4 + C::ID.len()) ..],
|
||||
promoted.serialize()[(4 + AltFunctions::<C>::ID.len()) ..]
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
struct AltGenerator<C: Curve> {
|
||||
_curve: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: Curve> Curve for AltGenerator<C> {
|
||||
type F = C::F;
|
||||
type G = C::G;
|
||||
|
||||
const ID: &'static [u8] = b"alt_generator";
|
||||
|
||||
fn generator() -> Self::G {
|
||||
C::G::generator() * C::hash_to_F(b"FROST_tests", b"generator")
|
||||
}
|
||||
|
||||
fn hash_msg(msg: &[u8]) -> Vec<u8> {
|
||||
C::hash_msg(msg)
|
||||
}
|
||||
|
||||
fn hash_binding_factor(binding: &[u8]) -> Self::F {
|
||||
C::hash_binding_factor(binding)
|
||||
}
|
||||
|
||||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
||||
C::hash_to_F(dst, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Test promotion of FROST keys to another generator
|
||||
fn test_generator_promotion<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
// A seeded RNG can theoretically generate for C1 and C2, verifying promotion that way?
|
||||
// TODO
|
||||
let keys = key_gen::<_, C>(&mut *rng);
|
||||
|
||||
let mut promotions = HashMap::new();
|
||||
let mut proofs = HashMap::new();
|
||||
for (i, keys) in &keys {
|
||||
let promotion = GeneratorPromotion::<_, AltGenerator<C>>::promote(&mut *rng, keys.clone());
|
||||
promotions.insert(*i, promotion.0);
|
||||
proofs.insert(*i, promotion.1);
|
||||
}
|
||||
|
||||
let mut new_keys = HashMap::new();
|
||||
let mut group_key = None;
|
||||
let mut verification_shares = None;
|
||||
for (i, promoting) in promotions.drain() {
|
||||
let promoted = promoting.complete(&clone_without(&proofs, &i)).unwrap();
|
||||
assert_eq!(keys[&i].params(), promoted.params());
|
||||
assert_eq!(keys[&i].secret_share(), promoted.secret_share());
|
||||
|
||||
if group_key.is_none() {
|
||||
group_key = Some(keys[&i].group_key());
|
||||
verification_shares = Some(keys[&i].verification_shares());
|
||||
}
|
||||
assert_eq!(keys[&i].group_key(), group_key.unwrap());
|
||||
assert_eq!(&keys[&i].verification_shares(), verification_shares.as_ref().unwrap());
|
||||
|
||||
new_keys.insert(i, promoted);
|
||||
}
|
||||
|
||||
// Sign with the keys to ensure their integrity
|
||||
sign_core(rng, &new_keys);
|
||||
}
|
||||
|
||||
pub fn test_promotion<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
// test_ciphersuite_promotion::<_, C>(rng);
|
||||
test_generator_promotion::<_, C>(rng);
|
||||
}
|
||||
@@ -75,15 +75,16 @@ pub(crate) fn core_batch_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_core<R: RngCore + CryptoRng, C: Curve>(
|
||||
pub(crate) fn sign_core<R: RngCore + CryptoRng, C: Curve>(
|
||||
rng: &mut R,
|
||||
group_key: C::G,
|
||||
keys: &HashMap<u16, FrostKeys<C>>,
|
||||
) {
|
||||
const MESSAGE: &[u8] = b"Hello, World!";
|
||||
|
||||
let machines = algorithm_machines(rng, Schnorr::<C, TestHram<C>>::new(), keys);
|
||||
let sig = sign_test(&mut *rng, machines, MESSAGE);
|
||||
|
||||
let group_key = keys[&1].group_key();
|
||||
assert!(schnorr::verify(group_key, TestHram::<C>::hram(&sig.R, &group_key, MESSAGE), &sig));
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ impl<C: Curve> Hram<C> for TestHram<C> {
|
||||
|
||||
fn sign<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
let keys = key_gen::<_, C>(&mut *rng);
|
||||
sign_core(rng, keys[&1].group_key(), &keys);
|
||||
sign_core(rng, &keys);
|
||||
}
|
||||
|
||||
fn sign_with_offset<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
@@ -112,8 +113,9 @@ fn sign_with_offset<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
keys.insert(i, keys[&i].offset(offset));
|
||||
}
|
||||
let offset_key = group_key + (C::generator() * offset);
|
||||
assert_eq!(keys[&1].group_key(), offset_key);
|
||||
|
||||
sign_core(rng, offset_key, &keys);
|
||||
sign_core(rng, &keys);
|
||||
}
|
||||
|
||||
pub fn test_schnorr<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||
|
||||
@@ -9,7 +9,9 @@ use crate::{
|
||||
FrostCore, FrostKeys,
|
||||
algorithm::{Schnorr, Hram},
|
||||
sign::{PreprocessPackage, SignMachine, SignatureMachine, AlgorithmMachine},
|
||||
tests::{clone_without, curve::test_curve, schnorr::test_schnorr, recover},
|
||||
tests::{
|
||||
clone_without, curve::test_curve, schnorr::test_schnorr, promote::test_promotion, recover,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Vectors {
|
||||
@@ -66,6 +68,7 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
||||
// Do basic tests before trying the vectors
|
||||
test_curve::<_, C>(&mut *rng);
|
||||
test_schnorr::<_, C>(rng);
|
||||
test_promotion::<_, C>(rng);
|
||||
|
||||
// Test against the vectors
|
||||
let keys = vectors_to_multisig_keys::<C>(&vectors);
|
||||
|
||||
Reference in New Issue
Block a user