mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Finally make modular-frost work with alloc alone
Carries the update to `frost-schnorrkel` and `bitcoin-serai`.
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -3546,11 +3546,11 @@ version = "0.2.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
"group",
|
|
||||||
"modular-frost",
|
"modular-frost",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"schnorr-signatures",
|
"schnorr-signatures",
|
||||||
"schnorrkel",
|
"schnorrkel",
|
||||||
|
"std-shims",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6098,6 +6098,7 @@ dependencies = [
|
|||||||
"schnorr-signatures",
|
"schnorr-signatures",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
|
"std-shims",
|
||||||
"subtle",
|
"subtle",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.16",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -10235,10 +10236,15 @@ dependencies = [
|
|||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
"dalek-ff-group 0.5.0",
|
"dalek-ff-group 0.5.0",
|
||||||
"dkg",
|
"dkg",
|
||||||
|
"dkg-dealer",
|
||||||
"dkg-evrf",
|
"dkg-evrf",
|
||||||
|
"dkg-musig",
|
||||||
|
"dkg-recovery",
|
||||||
"embedwards25519",
|
"embedwards25519",
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
|
"frost-schnorrkel",
|
||||||
"minimal-ed448",
|
"minimal-ed448",
|
||||||
|
"modular-frost",
|
||||||
"multiexp",
|
"multiexp",
|
||||||
"prime-field",
|
"prime-field",
|
||||||
"schnorr-signatures",
|
"schnorr-signatures",
|
||||||
|
|||||||
@@ -17,33 +17,35 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = { version = "2", default-features = false, features = ["std"] }
|
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
thiserror = { version = "2", default-features = false }
|
||||||
rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] }
|
rand_core = { version = "0.6", default-features = false, features = ["alloc"] }
|
||||||
subtle = { version = "^2.4", default-features = false, features = ["std"] }
|
rand_chacha = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"], optional = true }
|
zeroize = { version = "^1.5", default-features = false, features = ["alloc", "zeroize_derive"] }
|
||||||
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["std", "recommended"] }
|
hex = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
|
||||||
dalek-ff-group = { path = "../dalek-ff-group", version = "0.5", default-features = false, features = ["std"], optional = true }
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
||||||
minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, features = ["std"], optional = true }
|
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["std"] }
|
dalek-ff-group = { path = "../dalek-ff-group", version = "0.5", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
|
||||||
|
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] }
|
||||||
sha2 = { version = "0.10.0", default-features = false, optional = true }
|
sha2 = { version = "0.10.0", default-features = false, optional = true }
|
||||||
elliptic-curve = { version = "0.13", default-features = false, features = ["hash2curve"], optional = true }
|
elliptic-curve = { version = "0.13", default-features = false, features = ["hash2curve"], optional = true }
|
||||||
ciphersuite-kp256 = { path = "../ciphersuite/kp256", version = "0.4", default-features = false, features = ["std"], optional = true }
|
ciphersuite-kp256 = { path = "../ciphersuite/kp256", version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
|
|
||||||
multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["std", "batch"] }
|
multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["alloc", "batch"] }
|
||||||
|
|
||||||
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1", default-features = false, features = ["std"] }
|
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
dkg = { path = "../dkg", version = "0.6.1", default-features = false, features = ["std"] }
|
dkg = { path = "../dkg", version = "0.6.1", default-features = false }
|
||||||
dkg-recovery = { path = "../dkg/recovery", version = "0.6", default-features = false, features = ["std"], optional = true }
|
dkg-recovery = { path = "../dkg/recovery", version = "0.6", default-features = false, optional = true }
|
||||||
dkg-dealer = { path = "../dkg/dealer", version = "0.6", default-features = false, features = ["std"], optional = true }
|
dkg-dealer = { path = "../dkg/dealer", version = "0.6", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
@@ -54,6 +56,38 @@ dkg-recovery = { path = "../dkg/recovery", default-features = false, features =
|
|||||||
dkg-dealer = { path = "../dkg/dealer", default-features = false, features = ["std"] }
|
dkg-dealer = { path = "../dkg/dealer", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
std = [
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
|
"thiserror/std",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
"rand_chacha/std",
|
||||||
|
|
||||||
|
"zeroize/std",
|
||||||
|
"subtle/std",
|
||||||
|
|
||||||
|
"hex?/std",
|
||||||
|
|
||||||
|
"transcript/std",
|
||||||
|
|
||||||
|
"dalek-ff-group?/std",
|
||||||
|
"minimal-ed448?/std",
|
||||||
|
|
||||||
|
"ciphersuite/std",
|
||||||
|
"sha2?/std",
|
||||||
|
"elliptic-curve?/std",
|
||||||
|
"ciphersuite-kp256?/std",
|
||||||
|
|
||||||
|
"multiexp/std",
|
||||||
|
|
||||||
|
"schnorr/std",
|
||||||
|
|
||||||
|
"dkg/std",
|
||||||
|
"dkg-recovery?/std",
|
||||||
|
"dkg-dealer?/std",
|
||||||
|
]
|
||||||
|
|
||||||
ed25519 = ["dalek-ff-group"]
|
ed25519 = ["dalek-ff-group"]
|
||||||
ristretto = ["dalek-ff-group"]
|
ristretto = ["dalek-ff-group"]
|
||||||
|
|
||||||
@@ -63,3 +97,5 @@ p256 = ["sha2", "elliptic-curve", "ciphersuite-kp256"]
|
|||||||
ed448 = ["minimal-ed448"]
|
ed448 = ["minimal-ed448"]
|
||||||
|
|
||||||
tests = ["hex", "rand_core/getrandom", "dkg-dealer", "dkg-recovery"]
|
tests = ["hex", "rand_core/getrandom", "dkg-dealer", "dkg-recovery"]
|
||||||
|
|
||||||
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use core::{marker::PhantomData, fmt::Debug};
|
use core::{marker::PhantomData, fmt::Debug};
|
||||||
use std::io::{self, Read, Write};
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read, Write};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use core::{ops::Deref, convert::AsRef};
|
use core::{ops::Deref, convert::AsRef};
|
||||||
use std::io::{self, Read};
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use std::collections::HashMap;
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::collections::HashMap;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
// Each nonce remains of the form (d, e) and made into a proper nonce with d + (e * b)
|
// Each nonce remains of the form (d, e) and made into a proper nonce with d + (e * b)
|
||||||
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::{
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::{
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use core::{ops::Deref, fmt::Debug};
|
use core::{ops::Deref, fmt::Debug};
|
||||||
use std::{
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::{
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std_shims::collections::HashMap;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::io::{self, Read};
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std_shims::collections::HashMap;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
|||||||
@@ -17,18 +17,35 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand_core = "0.6"
|
std-shims = { version = "0.1", default-features = false, features = ["alloc"] }
|
||||||
zeroize = "^1.5"
|
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", features = ["merlin"] }
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
zeroize = { version = "1.5", default-features = false, features = ["zeroize_derive", "alloc"] }
|
||||||
|
|
||||||
group = "0.13"
|
transcript = { package = "flexible-transcript", path = "../transcript", version = "0.3.2", default-features = false, features = ["merlin"] }
|
||||||
|
|
||||||
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", features = ["std"] }
|
ciphersuite = { path = "../ciphersuite", version = "0.4.1", default-features = false, features = ["alloc"] }
|
||||||
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1" }
|
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "0.5.1", default-features = false, features = ["alloc"] }
|
||||||
frost = { path = "../frost", package = "modular-frost", version = "0.11.0", features = ["ristretto"] }
|
frost = { path = "../frost", package = "modular-frost", version = "0.11.0", default-features = false, features = ["ristretto"] }
|
||||||
|
|
||||||
schnorrkel = { version = "0.11" }
|
schnorrkel = { version = "0.11", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
frost = { path = "../frost", package = "modular-frost", features = ["tests"] }
|
frost = { path = "../frost", package = "modular-frost", features = ["tests"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
"zeroize/std",
|
||||||
|
|
||||||
|
"transcript/std",
|
||||||
|
|
||||||
|
"ciphersuite/std",
|
||||||
|
"schnorr/std",
|
||||||
|
"frost/std",
|
||||||
|
|
||||||
|
"schnorrkel/std",
|
||||||
|
]
|
||||||
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use std::io::{self, Read};
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use group::GroupEncoding;
|
use ciphersuite::group::GroupEncoding;
|
||||||
use frost::{
|
use frost::{
|
||||||
Participant,
|
Participant,
|
||||||
tests::{key_gen, algorithm_machines, sign},
|
tests::{key_gen, algorithm_machines, sign},
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ rand_core = { version = "0.6", default-features = false }
|
|||||||
bitcoin = { version = "0.32", default-features = false }
|
bitcoin = { version = "0.32", default-features = false }
|
||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
||||||
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.11", default-features = false, features = ["secp256k1"], optional = true }
|
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.11", default-features = false, features = ["secp256k1"] }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, optional = true }
|
hex = { version = "0.4", default-features = false, optional = true }
|
||||||
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
|
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
|
||||||
@@ -55,7 +55,7 @@ std = [
|
|||||||
"bitcoin/serde",
|
"bitcoin/serde",
|
||||||
|
|
||||||
"k256/std",
|
"k256/std",
|
||||||
"frost",
|
"frost/std",
|
||||||
|
|
||||||
"hex/std",
|
"hex/std",
|
||||||
"serde/std",
|
"serde/std",
|
||||||
|
|||||||
@@ -1,9 +1,27 @@
|
|||||||
#[cfg(feature = "std")]
|
use core::fmt::Debug;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::io;
|
||||||
|
|
||||||
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable};
|
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable};
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use k256::{elliptic_curve::sec1::ToEncodedPoint, ProjectivePoint};
|
use k256::{
|
||||||
|
elliptic_curve::{ops::Reduce, sec1::ToEncodedPoint},
|
||||||
|
U256, Scalar, ProjectivePoint,
|
||||||
|
};
|
||||||
|
|
||||||
use bitcoin::key::XOnlyPublicKey;
|
use bitcoin::{
|
||||||
|
hashes::{HashEngine, Hash, sha256::Hash as Sha256},
|
||||||
|
key::XOnlyPublicKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
use frost::{
|
||||||
|
curve::{WrappedGroup, Secp256k1},
|
||||||
|
Participant, ThresholdKeys, ThresholdView, FrostError,
|
||||||
|
algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr},
|
||||||
|
};
|
||||||
|
|
||||||
/// Get the x coordinate of a non-infinity point.
|
/// Get the x coordinate of a non-infinity point.
|
||||||
///
|
///
|
||||||
@@ -21,142 +39,118 @@ pub(crate) fn x_only(key: &ProjectivePoint) -> XOnlyPublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return if a point must be negated to have an even Y coordinate and be eligible for use.
|
/// Return if a point must be negated to have an even Y coordinate and be eligible for use.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub(crate) fn needs_negation(key: &ProjectivePoint) -> Choice {
|
pub(crate) fn needs_negation(key: &ProjectivePoint) -> Choice {
|
||||||
use k256::elliptic_curve::sec1::Tag;
|
use k256::elliptic_curve::sec1::Tag;
|
||||||
u8::from(key.to_encoded_point(true).tag()).ct_eq(&u8::from(Tag::CompressedOddY))
|
u8::from(key.to_encoded_point(true).tag()).ct_eq(&u8::from(Tag::CompressedOddY))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
|
||||||
mod frost_crypto {
|
///
|
||||||
use core::fmt::Debug;
|
/// If passed an odd nonce, the challenge will be negated.
|
||||||
use std_shims::{vec::Vec, io};
|
///
|
||||||
|
/// If either `R` or `A` is the point at infinity, this will panic.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Hram;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl HramTrait<Secp256k1> for Hram {
|
||||||
|
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
||||||
|
const TAG_HASH: Sha256 = Sha256::const_hash(b"BIP0340/challenge");
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
let mut data = Sha256::engine();
|
||||||
use rand_core::{RngCore, CryptoRng};
|
data.input(TAG_HASH.as_ref());
|
||||||
|
data.input(TAG_HASH.as_ref());
|
||||||
|
data.input(&x(R));
|
||||||
|
data.input(&x(A));
|
||||||
|
data.input(m);
|
||||||
|
|
||||||
use bitcoin::hashes::{HashEngine, Hash, sha256::Hash as Sha256};
|
let c = Scalar::reduce(U256::from_be_slice(Sha256::from_engine(data).as_ref()));
|
||||||
|
// If the nonce was odd, sign `r - cx` instead of `r + cx`, allowing us to negate `s` at the
|
||||||
use k256::{elliptic_curve::ops::Reduce, U256, Scalar};
|
// end to sign as `-r + cx`
|
||||||
|
<_>::conditional_select(&c, &-c, needs_negation(R))
|
||||||
use frost::{
|
}
|
||||||
curve::{WrappedGroup, Secp256k1},
|
}
|
||||||
Participant, ThresholdKeys, ThresholdView, FrostError,
|
|
||||||
algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr},
|
/// BIP-340 Schnorr signature algorithm.
|
||||||
};
|
///
|
||||||
|
/// This may panic if called with nonces/a group key which are the point at infinity (which have
|
||||||
use super::*;
|
/// a negligible probability for a well-reasoned caller, even with malicious participants
|
||||||
|
/// present).
|
||||||
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
|
///
|
||||||
///
|
/// `verify`, `verify_share` MUST be called after `sign_share` is called. Otherwise, this library
|
||||||
/// If passed an odd nonce, the challenge will be negated.
|
/// MAY panic.
|
||||||
///
|
#[derive(Clone)]
|
||||||
/// If either `R` or `A` is the point at infinity, this will panic.
|
pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>);
|
||||||
#[derive(Clone, Copy, Debug)]
|
impl Schnorr {
|
||||||
pub struct Hram;
|
/// Construct a Schnorr algorithm continuing the specified transcript.
|
||||||
#[allow(non_snake_case)]
|
#[allow(clippy::new_without_default)]
|
||||||
impl HramTrait<Secp256k1> for Hram {
|
pub fn new() -> Schnorr {
|
||||||
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
Schnorr(FrostSchnorr::ietf())
|
||||||
const TAG_HASH: Sha256 = Sha256::const_hash(b"BIP0340/challenge");
|
}
|
||||||
|
}
|
||||||
let mut data = Sha256::engine();
|
|
||||||
data.input(TAG_HASH.as_ref());
|
impl Algorithm<Secp256k1> for Schnorr {
|
||||||
data.input(TAG_HASH.as_ref());
|
type Transcript = <FrostSchnorr<Secp256k1, Hram> as Algorithm<Secp256k1>>::Transcript;
|
||||||
data.input(&x(R));
|
type Addendum = ();
|
||||||
data.input(&x(A));
|
type Signature = [u8; 64];
|
||||||
data.input(m);
|
|
||||||
|
fn transcript(&mut self) -> &mut Self::Transcript {
|
||||||
let c = Scalar::reduce(U256::from_be_slice(Sha256::from_engine(data).as_ref()));
|
self.0.transcript()
|
||||||
// If the nonce was odd, sign `r - cx` instead of `r + cx`, allowing us to negate `s` at the
|
}
|
||||||
// end to sign as `-r + cx`
|
|
||||||
<_>::conditional_select(&c, &-c, needs_negation(R))
|
fn nonces(&self) -> Vec<Vec<ProjectivePoint>> {
|
||||||
}
|
self.0.nonces()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// BIP-340 Schnorr signature algorithm.
|
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
||||||
///
|
&mut self,
|
||||||
/// This may panic if called with nonces/a group key which are the point at infinity (which have
|
rng: &mut R,
|
||||||
/// a negligible probability for a well-reasoned caller, even with malicious participants
|
keys: &ThresholdKeys<Secp256k1>,
|
||||||
/// present).
|
) {
|
||||||
///
|
self.0.preprocess_addendum(rng, keys)
|
||||||
/// `verify`, `verify_share` MUST be called after `sign_share` is called. Otherwise, this library
|
}
|
||||||
/// MAY panic.
|
|
||||||
#[derive(Clone)]
|
fn read_addendum<R: io::Read>(&self, reader: &mut R) -> io::Result<Self::Addendum> {
|
||||||
pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>);
|
self.0.read_addendum(reader)
|
||||||
impl Schnorr {
|
}
|
||||||
/// Construct a Schnorr algorithm continuing the specified transcript.
|
|
||||||
#[allow(clippy::new_without_default)]
|
fn process_addendum(
|
||||||
pub fn new() -> Schnorr {
|
&mut self,
|
||||||
Schnorr(FrostSchnorr::ietf())
|
view: &ThresholdView<Secp256k1>,
|
||||||
}
|
i: Participant,
|
||||||
}
|
addendum: (),
|
||||||
|
) -> Result<(), FrostError> {
|
||||||
impl Algorithm<Secp256k1> for Schnorr {
|
self.0.process_addendum(view, i, addendum)
|
||||||
type Transcript = <FrostSchnorr<Secp256k1, Hram> as Algorithm<Secp256k1>>::Transcript;
|
}
|
||||||
type Addendum = ();
|
|
||||||
type Signature = [u8; 64];
|
fn sign_share(
|
||||||
|
&mut self,
|
||||||
fn transcript(&mut self) -> &mut Self::Transcript {
|
params: &ThresholdView<Secp256k1>,
|
||||||
self.0.transcript()
|
nonce_sums: &[Vec<<Secp256k1 as WrappedGroup>::G>],
|
||||||
}
|
nonces: Vec<Zeroizing<<Secp256k1 as WrappedGroup>::F>>,
|
||||||
|
msg: &[u8],
|
||||||
fn nonces(&self) -> Vec<Vec<ProjectivePoint>> {
|
) -> <Secp256k1 as WrappedGroup>::F {
|
||||||
self.0.nonces()
|
self.0.sign_share(params, nonce_sums, nonces, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
fn verify(
|
||||||
&mut self,
|
&self,
|
||||||
rng: &mut R,
|
group_key: ProjectivePoint,
|
||||||
keys: &ThresholdKeys<Secp256k1>,
|
nonces: &[Vec<ProjectivePoint>],
|
||||||
) {
|
sum: Scalar,
|
||||||
self.0.preprocess_addendum(rng, keys)
|
) -> Option<Self::Signature> {
|
||||||
}
|
self.0.verify(group_key, nonces, sum).map(|mut sig| {
|
||||||
|
sig.s = <_>::conditional_select(&sum, &-sum, needs_negation(&sig.R));
|
||||||
fn read_addendum<R: io::Read>(&self, reader: &mut R) -> io::Result<Self::Addendum> {
|
// Convert to a Bitcoin signature by dropping the byte for the point's sign bit
|
||||||
self.0.read_addendum(reader)
|
sig.serialize()[1 ..].try_into().unwrap()
|
||||||
}
|
})
|
||||||
|
}
|
||||||
fn process_addendum(
|
|
||||||
&mut self,
|
fn verify_share(
|
||||||
view: &ThresholdView<Secp256k1>,
|
&self,
|
||||||
i: Participant,
|
verification_share: ProjectivePoint,
|
||||||
addendum: (),
|
nonces: &[Vec<ProjectivePoint>],
|
||||||
) -> Result<(), FrostError> {
|
share: Scalar,
|
||||||
self.0.process_addendum(view, i, addendum)
|
) -> Result<Vec<(Scalar, ProjectivePoint)>, ()> {
|
||||||
}
|
self.0.verify_share(verification_share, nonces, share)
|
||||||
|
|
||||||
fn sign_share(
|
|
||||||
&mut self,
|
|
||||||
params: &ThresholdView<Secp256k1>,
|
|
||||||
nonce_sums: &[Vec<<Secp256k1 as WrappedGroup>::G>],
|
|
||||||
nonces: Vec<Zeroizing<<Secp256k1 as WrappedGroup>::F>>,
|
|
||||||
msg: &[u8],
|
|
||||||
) -> <Secp256k1 as WrappedGroup>::F {
|
|
||||||
self.0.sign_share(params, nonce_sums, nonces, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify(
|
|
||||||
&self,
|
|
||||||
group_key: ProjectivePoint,
|
|
||||||
nonces: &[Vec<ProjectivePoint>],
|
|
||||||
sum: Scalar,
|
|
||||||
) -> Option<Self::Signature> {
|
|
||||||
self.0.verify(group_key, nonces, sum).map(|mut sig| {
|
|
||||||
sig.s = <_>::conditional_select(&sum, &-sum, needs_negation(&sig.R));
|
|
||||||
// Convert to a Bitcoin signature by dropping the byte for the point's sign bit
|
|
||||||
sig.serialize()[1 ..].try_into().unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_share(
|
|
||||||
&self,
|
|
||||||
verification_share: ProjectivePoint,
|
|
||||||
nonces: &[Vec<ProjectivePoint>],
|
|
||||||
share: Scalar,
|
|
||||||
) -> Result<Vec<(Scalar, ProjectivePoint)>, ()> {
|
|
||||||
self.0.verify_share(verification_share, nonces, share)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use frost_crypto::*;
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
/// The bitcoin Rust library.
|
/// The bitcoin Rust library.
|
||||||
pub use bitcoin;
|
pub use bitcoin;
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,31 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
use std_shims::{
|
use std_shims::{
|
||||||
vec::Vec,
|
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io::{self, Write},
|
io::{self, Read, Write},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::io::{Read, BufReader};
|
|
||||||
|
|
||||||
use k256::{
|
use k256::{
|
||||||
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
||||||
Scalar, ProjectivePoint,
|
Scalar, ProjectivePoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use frost::{
|
use frost::{
|
||||||
curve::{WrappedGroup, GroupIo, Secp256k1},
|
curve::{WrappedGroup, GroupIo, Secp256k1},
|
||||||
ThresholdKeys,
|
ThresholdKeys,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
consensus::encode::serialize, key::TweakedPublicKey, OutPoint, ScriptBuf, TxOut, Transaction,
|
hashes::Hash,
|
||||||
Block,
|
key::TweakedPublicKey,
|
||||||
|
TapTweakHash,
|
||||||
|
consensus::encode::{Decodable, serialize},
|
||||||
|
OutPoint, ScriptBuf, TxOut, Transaction, Block,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use bitcoin::{hashes::Hash, consensus::encode::Decodable, TapTweakHash};
|
|
||||||
|
|
||||||
use crate::crypto::x_only;
|
use crate::crypto::{x_only, needs_negation};
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use crate::crypto::needs_negation;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
mod send;
|
mod send;
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use send::*;
|
pub use send::*;
|
||||||
|
|
||||||
/// Tweak keys to ensure they're usable with Bitcoin's Taproot upgrade.
|
/// Tweak keys to ensure they're usable with Bitcoin's Taproot upgrade.
|
||||||
@@ -42,7 +37,6 @@ pub use send::*;
|
|||||||
/// After adding an unspendable script path, the key is negated if odd.
|
/// After adding an unspendable script path, the key is negated if odd.
|
||||||
///
|
///
|
||||||
/// This has a neligible probability of returning keys whose group key is the point at infinity.
|
/// This has a neligible probability of returning keys whose group key is the point at infinity.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
||||||
// Adds the unspendable script path per
|
// Adds the unspendable script path per
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23
|
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23
|
||||||
@@ -118,18 +112,23 @@ impl ReceivedOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read a ReceivedOutput from a generic satisfying Read.
|
/// Read a ReceivedOutput from a generic satisfying Read.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
||||||
let offset = Secp256k1::read_F(r)?;
|
let offset = Secp256k1::read_F(r)?;
|
||||||
let output;
|
|
||||||
let outpoint;
|
struct BitcoinRead<R: Read>(R);
|
||||||
{
|
impl<R: Read> bitcoin::io::Read for BitcoinRead<R> {
|
||||||
let mut buf_r = BufReader::with_capacity(0, r);
|
fn read(&mut self, buf: &mut [u8]) -> bitcoin::io::Result<usize> {
|
||||||
output =
|
self
|
||||||
TxOut::consensus_decode(&mut buf_r).map_err(|_| io::Error::other("invalid TxOut"))?;
|
.0
|
||||||
outpoint =
|
.read(buf)
|
||||||
OutPoint::consensus_decode(&mut buf_r).map_err(|_| io::Error::other("invalid OutPoint"))?;
|
.map_err(|e| bitcoin::io::Error::new(bitcoin::io::ErrorKind::Other, e.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let mut r = BitcoinRead(r);
|
||||||
|
|
||||||
|
let output = TxOut::consensus_decode(&mut r).map_err(|_| io::Error::other("invalid TxOut"))?;
|
||||||
|
let outpoint =
|
||||||
|
OutPoint::consensus_decode(&mut r).map_err(|_| io::Error::other("invalid OutPoint"))?;
|
||||||
Ok(ReceivedOutput { offset, output, outpoint })
|
Ok(ReceivedOutput { offset, output, outpoint })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
use std_shims::{
|
use std_shims::{
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
|||||||
@@ -36,9 +36,13 @@ secq256k1 = { path = "../../crypto/secq256k1", default-features = false }
|
|||||||
embedwards25519 = { path = "../../crypto/embedwards25519", default-features = false }
|
embedwards25519 = { path = "../../crypto/embedwards25519", default-features = false }
|
||||||
|
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, optional = true }
|
dkg = { path = "../../crypto/dkg", default-features = false, optional = true }
|
||||||
|
dkg-dealer = { path = "../../crypto/dkg/dealer", default-features = false, optional = true }
|
||||||
|
dkg-recovery = { path = "../../crypto/dkg/recovery", default-features = false, optional = true }
|
||||||
|
dkg-musig = { path = "../../crypto/dkg/musig", default-features = false, optional = true }
|
||||||
dkg-evrf = { path = "../../crypto/dkg/evrf", default-features = false, features = ["secp256k1", "ed25519"], optional = true }
|
dkg-evrf = { path = "../../crypto/dkg/evrf", default-features = false, features = ["secp256k1", "ed25519"], optional = true }
|
||||||
# modular-frost = { path = "../../crypto/frost", default-features = false }
|
|
||||||
# frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false }
|
modular-frost = { path = "../../crypto/frost", default-features = false, optional = true }
|
||||||
|
frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false, optional = true }
|
||||||
|
|
||||||
bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, features = ["hazmat"], optional = true }
|
bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, features = ["hazmat"], optional = true }
|
||||||
|
|
||||||
@@ -62,7 +66,13 @@ alloc = [
|
|||||||
"embedwards25519/alloc",
|
"embedwards25519/alloc",
|
||||||
|
|
||||||
"dkg",
|
"dkg",
|
||||||
|
"dkg-dealer",
|
||||||
|
"dkg-recovery",
|
||||||
|
"dkg-musig",
|
||||||
"dkg-evrf",
|
"dkg-evrf",
|
||||||
|
|
||||||
|
"modular-frost",
|
||||||
|
"frost-schnorrkel",
|
||||||
|
|
||||||
"bitcoin-serai",
|
"bitcoin-serai",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ pub mod alloc {
|
|||||||
pub use multiexp;
|
pub use multiexp;
|
||||||
|
|
||||||
pub use dkg;
|
pub use dkg;
|
||||||
|
pub use dkg_dealer;
|
||||||
|
pub use dkg_recovery;
|
||||||
|
pub use dkg_musig;
|
||||||
pub use dkg_evrf;
|
pub use dkg_evrf;
|
||||||
|
|
||||||
pub use bitcoin_serai;
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub use modular_frost;
|
pub use modular_frost;
|
||||||
pub use frost_schnorrkel;
|
pub use frost_schnorrkel;
|
||||||
*/
|
|
||||||
|
pub use bitcoin_serai;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user