mirror of
https://github.com/serai-dex/serai.git
synced 2025-02-02 03:06:31 +00:00
Use a transcript when generating the per-chain binding for a given set of keys
While it was fine as-is, as it only had one variable length property, this is a bit more robust. Also binds the Curve ID, which should declare differently even for just different basepoints, and therefore adds two variable length properties (justifying the transcript).
This commit is contained in:
parent
44452d9bfe
commit
e4fc469e58
8 changed files with 20 additions and 23 deletions
|
@ -135,7 +135,7 @@ impl DLEqProof {
|
||||||
// the proper order if they want to reach consensus
|
// the proper order if they want to reach consensus
|
||||||
// It'd be a poor API to have CLSAG define a new transcript solely to pass here, just to try to
|
// It'd be a poor API to have CLSAG define a new transcript solely to pass here, just to try to
|
||||||
// merge later in some form, when it should instead just merge xH (as it does)
|
// merge later in some form, when it should instead just merge xH (as it does)
|
||||||
let mut transcript = Transcript::new(b"DLEq Proof".to_vec());
|
let mut transcript = Transcript::new(b"DLEq Proof");
|
||||||
// Bit redundant, keeps things consistent
|
// Bit redundant, keeps things consistent
|
||||||
transcript.domain_separate(b"DLEq");
|
transcript.domain_separate(b"DLEq");
|
||||||
// Doesn't include G which is constant, does include H which isn't, even though H manipulation
|
// Doesn't include G which is constant, does include H which isn't, even though H manipulation
|
||||||
|
|
|
@ -96,7 +96,7 @@ fn clsag_multisig() -> Result<(), MultisigError> {
|
||||||
algorithm_machines(
|
algorithm_machines(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
ClsagMultisig::new(
|
ClsagMultisig::new(
|
||||||
Transcript::new(b"Monero Serai CLSAG Test".to_vec()),
|
Transcript::new(b"Monero Serai CLSAG Test"),
|
||||||
Rc::new(RefCell::new(Some(
|
Rc::new(RefCell::new(Some(
|
||||||
ClsagDetails::new(
|
ClsagDetails::new(
|
||||||
ClsagInput::new(
|
ClsagInput::new(
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub struct TransactionMachine {
|
||||||
impl SignableTransaction {
|
impl SignableTransaction {
|
||||||
pub async fn multisig<R: RngCore + CryptoRng>(
|
pub async fn multisig<R: RngCore + CryptoRng>(
|
||||||
mut self,
|
mut self,
|
||||||
label: Vec<u8>,
|
mut transcript: Transcript,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
rpc: &Rpc,
|
rpc: &Rpc,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
@ -56,8 +56,9 @@ impl SignableTransaction {
|
||||||
// Create a RNG out of the input shared keys, which either requires the view key or being every
|
// Create a RNG out of the input shared keys, which either requires the view key or being every
|
||||||
// sender, and the payments (address and amount), which a passive adversary may be able to know
|
// sender, and the payments (address and amount), which a passive adversary may be able to know
|
||||||
// depending on how these transactions are coordinated
|
// depending on how these transactions are coordinated
|
||||||
|
// Being every sender would already let you note rings which happen to use your transactions
|
||||||
|
// multiple times, already breaking privacy there
|
||||||
|
|
||||||
let mut transcript = Transcript::new(label);
|
|
||||||
transcript.domain_separate(b"monero_transaction");
|
transcript.domain_separate(b"monero_transaction");
|
||||||
// Include the height we're using for our data
|
// Include the height we're using for our data
|
||||||
// The data itself will be included, making this unnecessary, yet a lot of this is technically
|
// The data itself will be included, making this unnecessary, yet a lot of this is technically
|
||||||
|
|
|
@ -25,7 +25,7 @@ mod rpc;
|
||||||
use crate::rpc::{rpc, mine_block};
|
use crate::rpc::{rpc, mine_block};
|
||||||
|
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
use monero_serai::frost::Ed25519;
|
use monero_serai::frost::{Transcript, Ed25519};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SEQUENTIAL: Mutex<()> = Mutex::new(());
|
static ref SEQUENTIAL: Mutex<()> = Mutex::new(());
|
||||||
|
@ -145,7 +145,7 @@ async fn send_core(test: usize, multisig: bool) {
|
||||||
machines.insert(
|
machines.insert(
|
||||||
i,
|
i,
|
||||||
signable.clone().multisig(
|
signable.clone().multisig(
|
||||||
b"Monero Serai Test Transaction".to_vec(),
|
Transcript::new(b"Monero Serai Test Transaction"),
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&rpc,
|
&rpc,
|
||||||
rpc.get_height().await.unwrap() - 10,
|
rpc.get_height().await.unwrap() - 10,
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub use merlin::MerlinTranscript;
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
|
|
||||||
pub trait Transcript {
|
pub trait Transcript {
|
||||||
fn domain_separate(&mut self, label: &[u8]);
|
fn domain_separate(&mut self, label: &'static [u8]);
|
||||||
fn append_message(&mut self, label: &'static [u8], message: &[u8]);
|
fn append_message(&mut self, label: &'static [u8], message: &[u8]);
|
||||||
fn challenge(&mut self, label: &'static [u8]) -> Vec<u8>;
|
fn challenge(&mut self, label: &'static [u8]) -> Vec<u8>;
|
||||||
fn rng_seed(&mut self, label: &'static [u8]) -> [u8; 32];
|
fn rng_seed(&mut self, label: &'static [u8]) -> [u8; 32];
|
||||||
|
@ -24,15 +24,12 @@ impl<D: Digest> PartialEq for DigestTranscript<D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Digest> DigestTranscript<D> {
|
impl<D: Digest> DigestTranscript<D> {
|
||||||
pub fn new(label: Vec<u8>) -> Self {
|
pub fn new(label: &'static [u8]) -> Self {
|
||||||
DigestTranscript(label, PhantomData)
|
DigestTranscript(label.to_vec(), PhantomData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Digest> Transcript for DigestTranscript<D> {
|
impl<D: Digest> Transcript for DigestTranscript<D> {
|
||||||
// It may be beneficial for each domain to be a nested transcript which is itself length prefixed
|
|
||||||
// This would go further than Merlin though and require an accurate end_domain function which has
|
|
||||||
// frustrations not worth bothering with when this shouldn't actually be meaningful
|
|
||||||
fn domain_separate(&mut self, label: &[u8]) {
|
fn domain_separate(&mut self, label: &[u8]) {
|
||||||
self.append_message(b"domain", label);
|
self.append_message(b"domain", label);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ impl Debug for MerlinTranscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transcript for MerlinTranscript {
|
impl Transcript for MerlinTranscript {
|
||||||
fn domain_separate(&mut self, label: &[u8]) {
|
fn domain_separate(&mut self, label: &'static [u8]) {
|
||||||
self.append_message(b"dom-sep", label);
|
self.append_message(b"dom-sep", label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ thiserror = "1"
|
||||||
curve25519-dalek = { version = "3", features = ["std"] }
|
curve25519-dalek = { version = "3", features = ["std"] }
|
||||||
blake2 = "0.10"
|
blake2 = "0.10"
|
||||||
|
|
||||||
|
transcript = { path = "../crypto/transcript" }
|
||||||
dalek-ff-group = { path = "../crypto/dalek-ff-group" }
|
dalek-ff-group = { path = "../crypto/dalek-ff-group" }
|
||||||
frost = { path = "../crypto/frost" }
|
frost = { path = "../crypto/frost" }
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use transcript::{Transcript, DigestTranscript};
|
||||||
use frost::{Curve, MultisigKeys};
|
use frost::{Curve, MultisigKeys};
|
||||||
|
|
||||||
use crate::{CoinError, Coin};
|
use crate::{CoinError, Coin};
|
||||||
|
@ -21,17 +22,14 @@ impl<C: Curve> WalletKeys<C> {
|
||||||
// potential ETH group key. While this shouldn't be an issue, as this isn't a private
|
// potential ETH group key. While this shouldn't be an issue, as this isn't a private
|
||||||
// system, there are potentially other benefits to binding this to a specific group key
|
// system, there are potentially other benefits to binding this to a specific group key
|
||||||
// It's no longer possible to influence group key gen to key cancel without breaking the hash
|
// It's no longer possible to influence group key gen to key cancel without breaking the hash
|
||||||
// function, although that degree of influence means key gen is broken already
|
// function as well, although that degree of influence means key gen is broken already
|
||||||
fn bind(&self, chain: &[u8]) -> MultisigKeys<C> {
|
fn bind(&self, chain: &[u8]) -> MultisigKeys<C> {
|
||||||
self.keys.offset(
|
const DST: &[u8] = b"Serai Processor Wallet Chain Bind";
|
||||||
C::hash_to_F(
|
let mut transcript = DigestTranscript::<blake2::Blake2b512>::new(DST);
|
||||||
&[
|
transcript.append_message(b"chain", chain);
|
||||||
b"Serai Processor Wallet",
|
transcript.append_message(b"curve", C::id());
|
||||||
chain,
|
transcript.append_message(b"group_key", &C::G_to_bytes(&self.keys.group_key()));
|
||||||
&C::G_to_bytes(&self.keys.group_key())
|
self.keys.offset(C::hash_to_F(DST, &transcript.challenge(b"offset")))
|
||||||
].concat()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue