mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Smash the singular Ciphersuite trait into multiple
This helps identify where the various functionalities are used, or rather, not used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating the entire FCMP++ tree, only requires the markers _and_ canonical point decoding. I've opened a PR to upstream such a trait into `group` (https://github.com/zkcrypto/group/pull/68). `WrappedGroup` is still justified for as long as `Group::generator` exists. Moving `::generator()` to its own trait, on an independent structure (upstream) would be massively appreciated. @tarcieri also wanted to update from `fn generator()` to `const GENERATOR`, which would encourage further discussion on https://github.com/zkcrypto/group/issues/32 and https://github.com/zkcrypto/group/issues/45, which have been stagnant. The `Id` trait is occasionally used yet really should be first off the chopping block. Finally, `WithPreferredHash` is only actually used around a third of the time, which more than justifies it being a separate trait. --- Updates `dalek_ff_group::Scalar` to directly re-export `curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint` also could be replaced with an export of `curve25519_dalek::RistrettoPoint`, yet the coordinator relies on how we implemented `Hash` on it for the hell of it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't implement `zeroize`, `subtle` traits within a released, non-yanked version. Relevance to https://github.com/serai-dex/serai/issues/201 and https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746. Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over `SHA2-512`. In order to maintain compliance with FROST's IETF standard, `modular-frost` defines its own ciphersuite for Ristretto which still uses `SHA2-512`.
This commit is contained in:
@@ -24,7 +24,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals
|
||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||
|
||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
|
||||
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ristretto"] }
|
||||
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std"] }
|
||||
|
||||
serai-client = { path = "../../substrate/client", default-features = false }
|
||||
serai-cosign = { path = "../../coordinator/cosign" }
|
||||
|
||||
@@ -4,9 +4,9 @@ use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
use ciphersuite::{
|
||||
group::{ff::PrimeField, GroupEncoding},
|
||||
Ciphersuite,
|
||||
*,
|
||||
};
|
||||
use dkg::{Curves, Ristretto};
|
||||
use dkg::Curves;
|
||||
|
||||
use serai_client::validator_sets::primitives::Session;
|
||||
|
||||
@@ -14,7 +14,7 @@ use serai_env as env;
|
||||
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};
|
||||
|
||||
use primitives::EncodableG;
|
||||
use ::key_gen::{KeyGenParams, KeyGen};
|
||||
use ::key_gen::{Ristretto, KeyGenParams, KeyGen};
|
||||
use scheduler::{SignableTransaction, TransactionFor};
|
||||
use scanner::{ScannerFeed, Scanner, KeyFor, Scheduler};
|
||||
use signers::{TransactionPublisher, Signers};
|
||||
@@ -80,7 +80,7 @@ pub fn url() -> String {
|
||||
}
|
||||
|
||||
fn key_gen<K: KeyGenParams>() -> KeyGen<K> {
|
||||
fn read_key_from_env<C: Ciphersuite>(label: &'static str) -> Zeroizing<C::F> {
|
||||
fn read_key_from_env<C: WrappedGroup>(label: &'static str) -> Zeroizing<C::F> {
|
||||
let key_hex =
|
||||
Zeroizing::new(env::var(label).unwrap_or_else(|| panic!("{label} wasn't provided")));
|
||||
let bytes = Zeroizing::new(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
use ciphersuite::{group::GroupEncoding, *};
|
||||
use dkg::{ThresholdKeys, Curves, Secp256k1};
|
||||
|
||||
use crate::{primitives::x_coord_to_even_point, scan::scanner};
|
||||
@@ -18,7 +18,7 @@ impl key_gen::KeyGenParams for KeyGenParams {
|
||||
}
|
||||
|
||||
fn encode_key(
|
||||
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G,
|
||||
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G,
|
||||
) -> Vec<u8> {
|
||||
let key = key.to_bytes();
|
||||
let key: &[u8] = key.as_ref();
|
||||
@@ -28,7 +28,7 @@ impl key_gen::KeyGenParams for KeyGenParams {
|
||||
|
||||
fn decode_key(
|
||||
key: &[u8],
|
||||
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G> {
|
||||
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G> {
|
||||
x_coord_to_even_point(key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::fmt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use bitcoin_serai::bitcoin::block::{Header, Block as BBlock};
|
||||
@@ -35,7 +35,7 @@ impl<D: Db> fmt::Debug for Block<D> {
|
||||
impl<D: Db> primitives::Block for Block<D> {
|
||||
type Header = BlockHeader;
|
||||
|
||||
type Key = <Secp256k1 as Ciphersuite>::G;
|
||||
type Key = <Secp256k1 as WrappedGroup>::G;
|
||||
type Address = Address;
|
||||
type Output = Output;
|
||||
type Eventuality = Eventuality;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use bitcoin_serai::bitcoin::key::{Parity, XOnlyPublicKey};
|
||||
@@ -7,7 +7,7 @@ pub(crate) mod output;
|
||||
pub(crate) mod transaction;
|
||||
pub(crate) mod block;
|
||||
|
||||
pub(crate) fn x_coord_to_even_point(key: &[u8]) -> Option<<Secp256k1 as Ciphersuite>::G> {
|
||||
pub(crate) fn x_coord_to_even_point(key: &[u8]) -> Option<<Secp256k1 as WrappedGroup>::G> {
|
||||
if key.len() != 32 {
|
||||
None?
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::io;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use bitcoin_serai::{
|
||||
@@ -55,7 +55,7 @@ pub(crate) struct Output {
|
||||
impl Output {
|
||||
pub(crate) fn new(
|
||||
getter: &impl Get,
|
||||
key: <Secp256k1 as Ciphersuite>::G,
|
||||
key: <Secp256k1 as WrappedGroup>::G,
|
||||
tx: &Transaction,
|
||||
output: WalletOutput,
|
||||
) -> Self {
|
||||
@@ -71,7 +71,7 @@ impl Output {
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_presumed_origin(
|
||||
key: <Secp256k1 as Ciphersuite>::G,
|
||||
key: <Secp256k1 as WrappedGroup>::G,
|
||||
tx: &Transaction,
|
||||
presumed_origin: Option<Address>,
|
||||
output: WalletOutput,
|
||||
@@ -88,7 +88,7 @@ impl Output {
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||
impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
|
||||
type Id = OutputId;
|
||||
type TransactionId = [u8; 32];
|
||||
|
||||
@@ -108,7 +108,7 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||
res
|
||||
}
|
||||
|
||||
fn key(&self) -> <Secp256k1 as Ciphersuite>::G {
|
||||
fn key(&self) -> <Secp256k1 as WrappedGroup>::G {
|
||||
// We read the key from the script pubkey so we don't have to independently store it
|
||||
let script = &self.output.output().script_pubkey;
|
||||
|
||||
@@ -121,7 +121,7 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||
.expect("last item in scanned v1 Taproot script wasn't a valid x-only public key");
|
||||
|
||||
// The output's key minus the output's offset is the root key
|
||||
key - (<Secp256k1 as Ciphersuite>::G::GENERATOR * self.output.offset())
|
||||
key - (<Secp256k1 as WrappedGroup>::G::GENERATOR * self.output.offset())
|
||||
}
|
||||
|
||||
fn presumed_origin(&self) -> Option<Address> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{sync::LazyLock, collections::HashMap};
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use bitcoin_serai::{
|
||||
@@ -20,20 +20,20 @@ use primitives::OutputType;
|
||||
use crate::hash_bytes;
|
||||
|
||||
// TODO: Bitcoin HD derivation, instead of these bespoke labels?
|
||||
static BRANCH_BASE_OFFSET: LazyLock<<Secp256k1 as Ciphersuite>::F> =
|
||||
static BRANCH_BASE_OFFSET: LazyLock<<Secp256k1 as WrappedGroup>::F> =
|
||||
LazyLock::new(|| Secp256k1::hash_to_F(b"branch"));
|
||||
static CHANGE_BASE_OFFSET: LazyLock<<Secp256k1 as Ciphersuite>::F> =
|
||||
static CHANGE_BASE_OFFSET: LazyLock<<Secp256k1 as WrappedGroup>::F> =
|
||||
LazyLock::new(|| Secp256k1::hash_to_F(b"change"));
|
||||
static FORWARD_BASE_OFFSET: LazyLock<<Secp256k1 as Ciphersuite>::F> =
|
||||
static FORWARD_BASE_OFFSET: LazyLock<<Secp256k1 as WrappedGroup>::F> =
|
||||
LazyLock::new(|| Secp256k1::hash_to_F(b"forward"));
|
||||
|
||||
// Unfortunately, we have per-key offsets as it's the root key plus the base offset may not be
|
||||
// even. While we could tweak the key until all derivations are even, that'd require significantly
|
||||
// more tweaking. This algorithmic complexity is preferred.
|
||||
pub(crate) fn offsets_for_key(
|
||||
key: <Secp256k1 as Ciphersuite>::G,
|
||||
) -> HashMap<OutputType, <Secp256k1 as Ciphersuite>::F> {
|
||||
let mut offsets = HashMap::from([(OutputType::External, <Secp256k1 as Ciphersuite>::F::ZERO)]);
|
||||
key: <Secp256k1 as WrappedGroup>::G,
|
||||
) -> HashMap<OutputType, <Secp256k1 as WrappedGroup>::F> {
|
||||
let mut offsets = HashMap::from([(OutputType::External, <Secp256k1 as WrappedGroup>::F::ZERO)]);
|
||||
|
||||
// We create an actual Bitcoin scanner as upon adding an offset, it yields the tweaked offset
|
||||
// actually used
|
||||
@@ -50,7 +50,7 @@ pub(crate) fn offsets_for_key(
|
||||
offsets
|
||||
}
|
||||
|
||||
pub(crate) fn scanner(key: <Secp256k1 as Ciphersuite>::G) -> Scanner {
|
||||
pub(crate) fn scanner(key: <Secp256k1 as WrappedGroup>::G) -> Scanner {
|
||||
let mut scanner = Scanner::new(key).unwrap();
|
||||
for (_, offset) in offsets_for_key(key) {
|
||||
let tweaked_offset = scanner.register_offset(offset).unwrap();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::future::Future;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use bitcoin_serai::{
|
||||
@@ -26,8 +26,8 @@ use crate::{
|
||||
rpc::Rpc,
|
||||
};
|
||||
|
||||
fn address_from_serai_key(key: <Secp256k1 as Ciphersuite>::G, kind: OutputType) -> Address {
|
||||
let offset = <Secp256k1 as Ciphersuite>::G::GENERATOR * offsets_for_key(key)[&kind];
|
||||
fn address_from_serai_key(key: <Secp256k1 as WrappedGroup>::G, kind: OutputType) -> Address {
|
||||
let offset = <Secp256k1 as WrappedGroup>::G::GENERATOR * offsets_for_key(key)[&kind];
|
||||
Address::new(
|
||||
p2tr_script_buf(key + offset)
|
||||
.expect("creating address from Serai key which wasn't properly tweaked"),
|
||||
@@ -72,7 +72,7 @@ fn signable_transaction<D: Db>(
|
||||
*/
|
||||
payments.push((
|
||||
// The generator is even so this is valid
|
||||
p2tr_script_buf(<Secp256k1 as Ciphersuite>::G::GENERATOR).unwrap(),
|
||||
p2tr_script_buf(<Secp256k1 as WrappedGroup>::G::GENERATOR).unwrap(),
|
||||
// This uses the minimum output value allowed, as defined as a constant in bitcoin-serai
|
||||
// TODO: Add a test for this comparing to bitcoin's `minimal_non_dust`
|
||||
bitcoin_serai::wallet::DUST,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use dkg::{ThresholdKeys, Curves, Secp256k1};
|
||||
|
||||
use ethereum_schnorr::PublicKey;
|
||||
@@ -13,19 +13,19 @@ impl key_gen::KeyGenParams for KeyGenParams {
|
||||
keys: &mut ThresholdKeys<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve>,
|
||||
) {
|
||||
while PublicKey::new(keys.group_key()).is_none() {
|
||||
*keys = keys.clone().offset(<<Secp256k1 as Curves>::ToweringCurve as Ciphersuite>::F::ONE);
|
||||
*keys = keys.clone().offset(<<Secp256k1 as Curves>::ToweringCurve as WrappedGroup>::F::ONE);
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_key(
|
||||
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G,
|
||||
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G,
|
||||
) -> Vec<u8> {
|
||||
PublicKey::new(key).unwrap().eth_repr().to_vec()
|
||||
}
|
||||
|
||||
fn decode_key(
|
||||
key: &[u8],
|
||||
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G> {
|
||||
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G> {
|
||||
PublicKey::from_eth_repr(key.try_into().ok()?).map(|key| key.point())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use serai_client::networks::ethereum::Address;
|
||||
@@ -41,7 +41,7 @@ pub(crate) struct FullEpoch {
|
||||
impl primitives::Block for FullEpoch {
|
||||
type Header = Epoch;
|
||||
|
||||
type Key = <Secp256k1 as Ciphersuite>::G;
|
||||
type Key = <Secp256k1 as WrappedGroup>::G;
|
||||
type Address = Address;
|
||||
type Output = Output;
|
||||
type Eventuality = Eventuality;
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{io, collections::HashMap};
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
use frost::{
|
||||
dkg::{Participant, ThresholdKeys},
|
||||
@@ -24,10 +24,10 @@ pub struct EthereumHram;
|
||||
impl Hram<Secp256k1> for EthereumHram {
|
||||
#[allow(non_snake_case)]
|
||||
fn hram(
|
||||
R: &<Secp256k1 as Ciphersuite>::G,
|
||||
A: &<Secp256k1 as Ciphersuite>::G,
|
||||
R: &<Secp256k1 as WrappedGroup>::G,
|
||||
A: &<Secp256k1 as WrappedGroup>::G,
|
||||
m: &[u8],
|
||||
) -> <Secp256k1 as Ciphersuite>::F {
|
||||
) -> <Secp256k1 as WrappedGroup>::F {
|
||||
Signature::challenge(*R, &PublicKey::new(*A).unwrap(), m)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::io;
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
use ciphersuite::{group::GroupEncoding, *};
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use alloy_core::primitives::U256;
|
||||
@@ -61,10 +61,10 @@ impl AsMut<[u8]> for OutputId {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) enum Output {
|
||||
Output { key: <Secp256k1 as Ciphersuite>::G, instruction: EthereumInInstruction },
|
||||
Eventuality { key: <Secp256k1 as Ciphersuite>::G, nonce: u64 },
|
||||
Output { key: <Secp256k1 as WrappedGroup>::G, instruction: EthereumInInstruction },
|
||||
Eventuality { key: <Secp256k1 as WrappedGroup>::G, nonce: u64 },
|
||||
}
|
||||
impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||
impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
|
||||
type Id = OutputId;
|
||||
type TransactionId = [u8; 32];
|
||||
|
||||
@@ -107,7 +107,7 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
|
||||
}
|
||||
}
|
||||
|
||||
fn key(&self) -> <Secp256k1 as Ciphersuite>::G {
|
||||
fn key(&self) -> <Secp256k1 as WrappedGroup>::G {
|
||||
match self {
|
||||
Output::Output { key, .. } | Output::Eventuality { key, .. } => *key,
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
||||
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std"] }
|
||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
|
||||
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ristretto"] }
|
||||
embedwards25519 = { path = "../../crypto/embedwards25519", default-features = false, features = ["std"] }
|
||||
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std"] }
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["ristretto"] }
|
||||
|
||||
# Substrate
|
||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
use ciphersuite::{group::GroupEncoding, *};
|
||||
use dkg::*;
|
||||
|
||||
use serai_validator_sets_primitives::Session;
|
||||
@@ -11,15 +11,15 @@ use serai_validator_sets_primitives::Session;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use serai_db::{Get, DbTxn};
|
||||
|
||||
use crate::KeyGenParams;
|
||||
use crate::{Ristretto, KeyGenParams};
|
||||
|
||||
pub(crate) struct Params<P: KeyGenParams> {
|
||||
pub(crate) t: u16,
|
||||
pub(crate) n: u16,
|
||||
pub(crate) substrate_evrf_public_keys:
|
||||
Vec<<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::G>,
|
||||
Vec<<<Ristretto as Curves>::EmbeddedCurve as WrappedGroup>::G>,
|
||||
pub(crate) network_evrf_public_keys:
|
||||
Vec<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::G>,
|
||||
Vec<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as WrappedGroup>::G>,
|
||||
}
|
||||
|
||||
#[derive(BorshSerialize, BorshDeserialize)]
|
||||
@@ -85,17 +85,16 @@ impl<P: KeyGenParams> KeyGenDb<P> {
|
||||
.substrate_evrf_public_keys
|
||||
.into_iter()
|
||||
.map(|key| {
|
||||
<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::read_G(&mut key.as_slice())
|
||||
.unwrap()
|
||||
<<Ristretto as Curves>::EmbeddedCurve as GroupIo>::read_G(&mut key.as_slice()).unwrap()
|
||||
})
|
||||
.collect(),
|
||||
network_evrf_public_keys: params
|
||||
.network_evrf_public_keys
|
||||
.into_iter()
|
||||
.map(|key| {
|
||||
<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::read_G::<
|
||||
&[u8],
|
||||
>(&mut key.as_ref())
|
||||
<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as GroupIo>::read_G::<&[u8]>(
|
||||
&mut key.as_ref(),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -13,7 +13,7 @@ use blake2::{Digest, Blake2s256};
|
||||
use transcript::{Transcript, RecommendedTranscript};
|
||||
use ciphersuite::{
|
||||
group::{Group, GroupEncoding},
|
||||
Ciphersuite,
|
||||
*,
|
||||
};
|
||||
use dkg::*;
|
||||
|
||||
@@ -28,6 +28,14 @@ use generators::generators;
|
||||
mod db;
|
||||
use db::{Params, Participations, KeyGenDb};
|
||||
|
||||
/// Ristretto, and an elliptic curve defined over its scalar field (embedwards25519).
|
||||
pub struct Ristretto;
|
||||
impl Curves for Ristretto {
|
||||
type ToweringCurve = frost::curve::Ristretto;
|
||||
type EmbeddedCurve = embedwards25519::Embedwards25519;
|
||||
type EmbeddedCurveParameters = embedwards25519::Embedwards25519;
|
||||
}
|
||||
|
||||
/// Parameters for a key generation.
|
||||
pub trait KeyGenParams {
|
||||
/// The ID for this instantiation.
|
||||
@@ -49,7 +57,7 @@ pub trait KeyGenParams {
|
||||
///
|
||||
/// A default implementation is provided which calls the traditional `to_bytes`.
|
||||
fn encode_key(
|
||||
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G,
|
||||
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G,
|
||||
) -> Vec<u8> {
|
||||
key.to_bytes().as_ref().to_vec()
|
||||
}
|
||||
@@ -59,11 +67,10 @@ pub trait KeyGenParams {
|
||||
/// A default implementation is provided which calls the traditional `from_bytes`.
|
||||
fn decode_key(
|
||||
mut key: &[u8],
|
||||
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G> {
|
||||
let res = <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::read_G(
|
||||
&mut key,
|
||||
)
|
||||
.ok()?;
|
||||
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G> {
|
||||
let res =
|
||||
<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as GroupIo>::read_G(&mut key)
|
||||
.ok()?;
|
||||
if !key.is_empty() {
|
||||
None?;
|
||||
}
|
||||
@@ -101,14 +108,14 @@ pub trait KeyGenParams {
|
||||
*/
|
||||
fn coerce_keys<C: 'static + Curves>(
|
||||
key_bytes: &[impl AsRef<[u8]>],
|
||||
) -> (Vec<<C::EmbeddedCurve as Ciphersuite>::G>, Vec<Participant>) {
|
||||
fn evrf_key<C: 'static + Curves>(key: &[u8]) -> Option<<C::EmbeddedCurve as Ciphersuite>::G> {
|
||||
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
|
||||
) -> (Vec<<C::EmbeddedCurve as WrappedGroup>::G>, Vec<Participant>) {
|
||||
fn evrf_key<C: 'static + Curves>(key: &[u8]) -> Option<<C::EmbeddedCurve as WrappedGroup>::G> {
|
||||
let mut repr = <<C::EmbeddedCurve as WrappedGroup>::G as GroupEncoding>::Repr::default();
|
||||
if repr.as_ref().len() != key.len() {
|
||||
None?;
|
||||
}
|
||||
repr.as_mut().copy_from_slice(key);
|
||||
let point = Option::<<C::EmbeddedCurve as Ciphersuite>::G>::from(<_>::from_bytes(&repr))?;
|
||||
let point = Option::<<C::EmbeddedCurve as WrappedGroup>::G>::from(<_>::from_bytes(&repr))?;
|
||||
if bool::from(point.is_identity()) {
|
||||
None?;
|
||||
}
|
||||
@@ -131,10 +138,10 @@ fn coerce_keys<C: 'static + Curves>(
|
||||
// Generate a random key
|
||||
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(key).into());
|
||||
loop {
|
||||
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default();
|
||||
let mut repr = <<C::EmbeddedCurve as WrappedGroup>::G as GroupEncoding>::Repr::default();
|
||||
rng.fill_bytes(repr.as_mut());
|
||||
if let Some(key) =
|
||||
Option::<<C::EmbeddedCurve as Ciphersuite>::G>::from(<_>::from_bytes(&repr))
|
||||
Option::<<C::EmbeddedCurve as WrappedGroup>::G>::from(<_>::from_bytes(&repr))
|
||||
{
|
||||
break key;
|
||||
}
|
||||
@@ -149,18 +156,20 @@ fn coerce_keys<C: 'static + Curves>(
|
||||
/// An instance of the Serai key generation protocol.
|
||||
#[derive(Debug)]
|
||||
pub struct KeyGen<P: KeyGenParams> {
|
||||
substrate_evrf_private_key: Zeroizing<<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::F>,
|
||||
substrate_evrf_private_key: Zeroizing<<<Ristretto as Curves>::EmbeddedCurve as WrappedGroup>::F>,
|
||||
network_evrf_private_key:
|
||||
Zeroizing<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::F>,
|
||||
Zeroizing<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as WrappedGroup>::F>,
|
||||
}
|
||||
|
||||
impl<P: KeyGenParams> KeyGen<P> {
|
||||
/// Create a new key generation instance.
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(
|
||||
substrate_evrf_private_key: Zeroizing<<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::F>,
|
||||
substrate_evrf_private_key: Zeroizing<
|
||||
<<Ristretto as Curves>::EmbeddedCurve as WrappedGroup>::F,
|
||||
>,
|
||||
network_evrf_private_key: Zeroizing<
|
||||
<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::F,
|
||||
<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as WrappedGroup>::F,
|
||||
>,
|
||||
) -> KeyGen<P> {
|
||||
KeyGen { substrate_evrf_private_key, network_evrf_private_key }
|
||||
@@ -214,8 +223,8 @@ impl<P: KeyGenParams> KeyGen<P> {
|
||||
fn participate<C: 'static + Curves>(
|
||||
context: [u8; 32],
|
||||
threshold: u16,
|
||||
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
||||
evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
|
||||
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as WrappedGroup>::F>,
|
||||
output: &mut impl io::Write,
|
||||
) {
|
||||
let participation = Dkg::<C>::participate(
|
||||
@@ -411,7 +420,7 @@ impl<P: KeyGenParams> KeyGen<P> {
|
||||
session: Session,
|
||||
true_if_substrate_false_if_network: bool,
|
||||
threshold: u16,
|
||||
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||
evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
|
||||
substrate_participations: &mut HashMap<Participant, Vec<u8>>,
|
||||
network_participations: &mut HashMap<Participant, Vec<u8>>,
|
||||
) -> Result<Dkg<C>, Vec<ProcessorMessage>> {
|
||||
|
||||
@@ -29,8 +29,8 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = fals
|
||||
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ed25519"] }
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
|
||||
|
||||
monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "6966575e05fe09b77674c46984b21686ed9304ff", default-features = false, features = ["std", "multisig"] }
|
||||
monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "6966575e05fe09b77674c46984b21686ed9304ff", default-features = false }
|
||||
monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7f37cc8f770858aa1739e0f56dbe447db86f4ba6", default-features = false, features = ["std", "multisig"] }
|
||||
monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7f37cc8f770858aa1739e0f56dbe447db86f4ba6", default-features = false }
|
||||
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["monero"] }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use dalek_ff_group::Ed25519;
|
||||
|
||||
use monero_wallet::{
|
||||
@@ -32,7 +32,7 @@ pub(crate) struct Block(pub(crate) MScannableBlock);
|
||||
impl primitives::Block for Block {
|
||||
type Header = BlockHeader;
|
||||
|
||||
type Key = <Ed25519 as Ciphersuite>::G;
|
||||
type Key = <Ed25519 as WrappedGroup>::G;
|
||||
type Address = Address;
|
||||
type Output = Output;
|
||||
type Eventuality = Eventuality;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use dalek_ff_group::Ed25519;
|
||||
|
||||
use monero_wallet::{address::SubaddressIndex, ViewPairError, GuaranteedViewPair};
|
||||
@@ -28,8 +28,8 @@ pub(crate) const FORWARDED_SUBADDRESS: SubaddressIndex = match SubaddressIndex::
|
||||
None => panic!("SubaddressIndex for FORWARDED_SUBADDRESS was None"),
|
||||
};
|
||||
|
||||
pub(crate) fn view_pair(key: <Ed25519 as Ciphersuite>::G) -> GuaranteedViewPair {
|
||||
match GuaranteedViewPair::new(key.0, Zeroizing::new(*view_key::<Ed25519>(0))) {
|
||||
pub(crate) fn view_pair(key: <Ed25519 as WrappedGroup>::G) -> GuaranteedViewPair {
|
||||
match GuaranteedViewPair::new(key.0, Zeroizing::new(view_key::<Ed25519>(0))) {
|
||||
Ok(view_pair) => view_pair,
|
||||
Err(ViewPairError::TorsionedSpendKey) => {
|
||||
unreachable!("dalek_ff_group::EdwardsPoint had torsion")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::io;
|
||||
|
||||
use ciphersuite::{group::Group, Ciphersuite};
|
||||
use ciphersuite::WrappedGroup;
|
||||
use dalek_ff_group::Ed25519;
|
||||
|
||||
use monero_wallet::WalletOutput;
|
||||
@@ -35,7 +35,7 @@ impl AsMut<[u8]> for OutputId {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) struct Output(pub(crate) WalletOutput);
|
||||
impl ReceivedOutput<<Ed25519 as Ciphersuite>::G, Address> for Output {
|
||||
impl ReceivedOutput<<Ed25519 as WrappedGroup>::G, Address> for Output {
|
||||
type Id = OutputId;
|
||||
type TransactionId = [u8; 32];
|
||||
|
||||
@@ -64,12 +64,12 @@ impl ReceivedOutput<<Ed25519 as Ciphersuite>::G, Address> for Output {
|
||||
self.0.transaction()
|
||||
}
|
||||
|
||||
fn key(&self) -> <Ed25519 as Ciphersuite>::G {
|
||||
fn key(&self) -> <Ed25519 as WrappedGroup>::G {
|
||||
// The spend key will be a key we generated, so it'll be in the prime-order subgroup
|
||||
// The output's key is the spend key + (key_offset * G), so it's in the prime-order subgroup if
|
||||
// the spend key is
|
||||
dalek_ff_group::EdwardsPoint(
|
||||
self.0.key() - (*<Ed25519 as Ciphersuite>::G::generator() * self.0.key_offset()),
|
||||
self.0.key() - (*<Ed25519 as WrappedGroup>::generator() * self.0.key_offset()),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use zeroize::Zeroizing;
|
||||
use rand_core::SeedableRng;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
use dalek_ff_group::Ed25519;
|
||||
|
||||
use monero_wallet::rpc::{FeeRate, RpcError};
|
||||
@@ -33,7 +33,7 @@ use crate::{
|
||||
rpc::Rpc,
|
||||
};
|
||||
|
||||
fn address_from_serai_key(key: <Ed25519 as Ciphersuite>::G, kind: OutputType) -> Address {
|
||||
fn address_from_serai_key(key: <Ed25519 as WrappedGroup>::G, kind: OutputType) -> Address {
|
||||
view_pair(key)
|
||||
.address(
|
||||
Network::Mainnet,
|
||||
@@ -118,8 +118,8 @@ async fn signable_transaction(
|
||||
MoneroAddress::new(
|
||||
Network::Mainnet,
|
||||
AddressType::Legacy,
|
||||
<Ed25519 as Ciphersuite>::generator().0,
|
||||
<Ed25519 as Ciphersuite>::generator().0,
|
||||
<Ed25519 as WrappedGroup>::generator().0,
|
||||
<Ed25519 as WrappedGroup>::generator().0,
|
||||
),
|
||||
0,
|
||||
));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use core::marker::PhantomData;
|
||||
use std::io;
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
use ciphersuite::{group::GroupEncoding, *};
|
||||
use frost::{dkg::ThresholdKeys, sign::PreprocessMachine};
|
||||
|
||||
use serai_db::DbTxn;
|
||||
|
||||
@@ -26,8 +26,7 @@ zeroize = { version = "1", default-features = false, features = ["std"] }
|
||||
|
||||
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
|
||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
|
||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, features = ["std"] }
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["ristretto"] }
|
||||
frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false }
|
||||
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -3,8 +3,7 @@ use std::collections::HashSet;
|
||||
|
||||
use blake2::{digest::typenum::U32, Digest, Blake2b};
|
||||
use ciphersuite::group::GroupEncoding;
|
||||
use dalek_ff_group::Ristretto;
|
||||
use frost::dkg::ThresholdKeys;
|
||||
use frost::{dkg::ThresholdKeys, curve::Ristretto};
|
||||
|
||||
use scale::Encode;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use core::future::Future;
|
||||
|
||||
use dalek_ff_group::Ristretto;
|
||||
use frost::dkg::ThresholdKeys;
|
||||
use frost::{dkg::ThresholdKeys, curve::Ristretto};
|
||||
|
||||
use scale::Encode;
|
||||
use serai_primitives::Signature;
|
||||
|
||||
@@ -7,9 +7,8 @@ use std::collections::HashMap;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
use dalek_ff_group::Ristretto;
|
||||
use frost::dkg::ThresholdKeys;
|
||||
use ciphersuite::{group::GroupEncoding, *};
|
||||
use frost::{dkg::ThresholdKeys, curve::Ristretto};
|
||||
|
||||
use serai_primitives::Signature;
|
||||
use serai_validator_sets_primitives::{Session, SlashReport};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use core::{marker::PhantomData, future::Future};
|
||||
|
||||
use dalek_ff_group::Ristretto;
|
||||
use frost::dkg::ThresholdKeys;
|
||||
use frost::{dkg::ThresholdKeys, curve::Ristretto};
|
||||
|
||||
use serai_primitives::Signature;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
|
||||
@@ -5,9 +5,9 @@ use std::{
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use dalek_ff_group::Ristretto;
|
||||
use frost::{
|
||||
dkg::{Participant, ThresholdKeys},
|
||||
curve::Ristretto,
|
||||
FrostError,
|
||||
algorithm::Algorithm,
|
||||
sign::*,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use ciphersuite::Ciphersuite;
|
||||
use ciphersuite::*;
|
||||
|
||||
/// Generate a view key for usage within Serai.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user