2022-05-24 21:41:14 -04:00
|
|
|
use core::fmt;
|
2022-07-15 01:26:07 -04:00
|
|
|
use std::{
|
|
|
|
|
io::{Read, Cursor},
|
|
|
|
|
collections::HashMap,
|
|
|
|
|
};
|
2022-04-21 21:36:18 -04:00
|
|
|
|
|
|
|
|
use rand_core::{RngCore, CryptoRng};
|
|
|
|
|
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
use zeroize::Zeroize;
|
|
|
|
|
|
2022-05-03 07:20:24 -04:00
|
|
|
use transcript::Transcript;
|
|
|
|
|
|
2022-07-15 01:26:07 -04:00
|
|
|
use group::{
|
|
|
|
|
ff::{Field, PrimeField},
|
|
|
|
|
Group, GroupEncoding,
|
|
|
|
|
};
|
2022-07-12 03:21:22 -04:00
|
|
|
use multiexp::multiexp_vartime;
|
|
|
|
|
|
2022-07-13 23:29:48 -04:00
|
|
|
use dleq::DLEqProof;
|
2022-07-12 01:28:01 -04:00
|
|
|
|
2022-05-24 21:41:14 -04:00
|
|
|
use crate::{
|
2022-07-15 01:26:07 -04:00
|
|
|
curve::Curve, FrostError, FrostParams, FrostKeys, FrostView, algorithm::Algorithm, validate_map,
|
2022-05-24 21:41:14 -04:00
|
|
|
};
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-06-28 00:06:12 -04:00
|
|
|
/// Pairing of an Algorithm with a FrostKeys instance and this specific signing set
|
2022-04-21 21:36:18 -04:00
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Params<C: Curve, A: Algorithm<C>> {
|
|
|
|
|
algorithm: A,
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
keys: FrostKeys<C>,
|
2022-06-28 00:06:12 -04:00
|
|
|
view: FrostView<C>,
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-04-29 22:36:43 -04:00
|
|
|
// Currently public to enable more complex operations as desired, yet solely used in testing
|
2022-04-21 21:36:18 -04:00
|
|
|
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
|
|
|
|
|
pub fn new(
|
|
|
|
|
algorithm: A,
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
keys: FrostKeys<C>,
|
2022-05-24 21:41:14 -04:00
|
|
|
included: &[u16],
|
2022-04-29 15:28:04 -04:00
|
|
|
) -> Result<Params<C, A>, FrostError> {
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
let params = keys.params();
|
|
|
|
|
|
2022-04-21 21:36:18 -04:00
|
|
|
let mut included = included.to_vec();
|
2022-07-22 02:34:36 -04:00
|
|
|
included.sort_unstable();
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-04-30 04:32:19 -04:00
|
|
|
// Included < threshold
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
if included.len() < usize::from(params.t) {
|
2022-07-13 02:38:29 -04:00
|
|
|
Err(FrostError::InvalidSigningSet("not enough signers"))?;
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
// Invalid index
|
|
|
|
|
if included[0] == 0 {
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
Err(FrostError::InvalidParticipantIndex(included[0], params.n))?;
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
// OOB index
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
if included[included.len() - 1] > params.n {
|
|
|
|
|
Err(FrostError::InvalidParticipantIndex(included[included.len() - 1], params.n))?;
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
// Same signer included multiple times
|
|
|
|
|
for i in 0 .. included.len() - 1 {
|
|
|
|
|
if included[i] == included[i + 1] {
|
2022-07-13 02:38:29 -04:00
|
|
|
Err(FrostError::DuplicatedIndex(included[i]))?;
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Not included
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
if !included.contains(¶ms.i) {
|
2022-07-13 02:38:29 -04:00
|
|
|
Err(FrostError::InvalidSigningSet("signing despite not being included"))?;
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-04-30 04:32:19 -04:00
|
|
|
// Out of order arguments to prevent additional cloning
|
|
|
|
|
Ok(Params { algorithm, view: keys.view(&included).unwrap(), keys })
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-06-28 00:06:12 -04:00
|
|
|
pub fn multisig_params(&self) -> FrostParams {
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
self.keys.params()
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
2022-04-29 15:28:04 -04:00
|
|
|
|
2022-06-28 00:06:12 -04:00
|
|
|
pub fn view(&self) -> FrostView<C> {
|
2022-04-29 15:28:04 -04:00
|
|
|
self.view.clone()
|
|
|
|
|
}
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-07-12 01:28:01 -04:00
|
|
|
fn nonce_transcript<T: Transcript>() -> T {
|
|
|
|
|
T::new(b"FROST_nonce_dleq")
|
|
|
|
|
}
|
|
|
|
|
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
#[derive(Zeroize)]
|
2022-06-03 01:25:46 -04:00
|
|
|
pub(crate) struct PreprocessPackage<C: Curve> {
|
2022-07-12 01:28:01 -04:00
|
|
|
pub(crate) nonces: Vec<[C::F; 2]>,
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
#[zeroize(skip)]
|
2022-07-13 02:38:29 -04:00
|
|
|
pub(crate) commitments: Vec<Vec<[C::G; 2]>>,
|
|
|
|
|
pub(crate) addendum: Vec<u8>,
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
impl<C: Curve> Drop for PreprocessPackage<C> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.zeroize()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-21 21:36:18 -04:00
|
|
|
// This library unifies the preprocessing step with signing due to security concerns and to provide
|
|
|
|
|
// a simpler UX
|
|
|
|
|
fn preprocess<R: RngCore + CryptoRng, C: Curve, A: Algorithm<C>>(
|
|
|
|
|
rng: &mut R,
|
2022-04-29 01:34:48 -04:00
|
|
|
params: &mut Params<C, A>,
|
2022-07-13 02:38:29 -04:00
|
|
|
) -> (PreprocessPackage<C>, Vec<u8>) {
|
|
|
|
|
let mut serialized = Vec::with_capacity(2 * C::G_len());
|
2022-07-15 01:26:07 -04:00
|
|
|
let (nonces, commitments) = params
|
|
|
|
|
.algorithm
|
|
|
|
|
.nonces()
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|generators| {
|
2022-07-12 01:28:01 -04:00
|
|
|
let nonces = [
|
|
|
|
|
C::random_nonce(params.view().secret_share(), &mut *rng),
|
2022-07-15 01:26:07 -04:00
|
|
|
C::random_nonce(params.view().secret_share(), &mut *rng),
|
2022-07-12 01:28:01 -04:00
|
|
|
];
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
let commit = |generator: C::G, buf: &mut Vec<u8>| {
|
2022-07-12 01:28:01 -04:00
|
|
|
let commitments = [generator * nonces[0], generator * nonces[1]];
|
2022-07-13 02:38:29 -04:00
|
|
|
buf.extend(commitments[0].to_bytes().as_ref());
|
|
|
|
|
buf.extend(commitments[1].to_bytes().as_ref());
|
|
|
|
|
commitments
|
2022-07-12 01:28:01 -04:00
|
|
|
};
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
let mut commitments = Vec::with_capacity(generators.len());
|
2022-07-12 01:28:01 -04:00
|
|
|
for generator in generators.iter() {
|
2022-07-13 02:38:29 -04:00
|
|
|
commitments.push(commit(*generator, &mut serialized));
|
2022-07-13 23:29:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Provide a DLEq proof to verify these commitments are for the same nonce
|
|
|
|
|
if generators.len() >= 2 {
|
|
|
|
|
// Uses an independent transcript as each signer must do this now, yet we validate them
|
|
|
|
|
// sequentially by the global order. Avoids needing to clone and fork the transcript around
|
|
|
|
|
let mut transcript = nonce_transcript::<A::Transcript>();
|
|
|
|
|
|
|
|
|
|
// This could be further optimized with a multi-nonce proof.
|
|
|
|
|
// See https://github.com/serai-dex/serai/issues/38
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
for mut nonce in nonces {
|
2022-07-22 02:34:36 -04:00
|
|
|
DLEqProof::prove(&mut *rng, &mut transcript, generators, nonce)
|
2022-07-15 01:26:07 -04:00
|
|
|
.serialize(&mut serialized)
|
|
|
|
|
.unwrap();
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
nonce.zeroize();
|
2022-07-12 01:28:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
(nonces, commitments)
|
2022-07-15 01:26:07 -04:00
|
|
|
})
|
|
|
|
|
.unzip();
|
2022-07-12 01:28:01 -04:00
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
let addendum = params.algorithm.preprocess_addendum(rng, ¶ms.view);
|
|
|
|
|
serialized.extend(&addendum);
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
(PreprocessPackage { nonces, commitments, addendum }, serialized)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
fn read_D_E<Re: Read, C: Curve>(cursor: &mut Re, l: u16) -> Result<[C::G; 2], FrostError> {
|
|
|
|
|
Ok([
|
|
|
|
|
C::read_G(cursor).map_err(|_| FrostError::InvalidCommitment(l))?,
|
2022-07-15 01:26:07 -04:00
|
|
|
C::read_G(cursor).map_err(|_| FrostError::InvalidCommitment(l))?,
|
2022-07-13 02:38:29 -04:00
|
|
|
])
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
struct Package<C: Curve> {
|
2022-07-12 02:45:18 -04:00
|
|
|
B: HashMap<u16, (Vec<Vec<[C::G; 2]>>, C::F)>,
|
2022-07-12 01:28:01 -04:00
|
|
|
Rs: Vec<Vec<C::G>>,
|
2022-07-13 02:38:29 -04:00
|
|
|
share: C::F,
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Has every signer perform the role of the signature aggregator
|
|
|
|
|
// Step 1 was already deprecated by performing nonce generation as needed
|
|
|
|
|
// Step 2 is simply the broadcast round from step 1
|
2022-07-13 02:38:29 -04:00
|
|
|
fn sign_with_share<Re: Read, C: Curve, A: Algorithm<C>>(
|
2022-04-21 21:36:18 -04:00
|
|
|
params: &mut Params<C, A>,
|
|
|
|
|
our_preprocess: PreprocessPackage<C>,
|
2022-07-13 02:38:29 -04:00
|
|
|
mut commitments: HashMap<u16, Re>,
|
2022-04-21 21:36:18 -04:00
|
|
|
msg: &[u8],
|
|
|
|
|
) -> Result<(Package<C>, Vec<u8>), FrostError> {
|
|
|
|
|
let multisig_params = params.multisig_params();
|
2022-07-13 02:38:29 -04:00
|
|
|
validate_map(&mut commitments, ¶ms.view.included, multisig_params.i)?;
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-05-06 07:33:08 -04:00
|
|
|
{
|
2022-05-23 03:24:33 -04:00
|
|
|
// Domain separate FROST
|
2022-07-13 02:38:29 -04:00
|
|
|
params.algorithm.transcript().domain_separate(b"FROST");
|
2022-05-06 07:33:08 -04:00
|
|
|
}
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
let nonces = params.algorithm.nonces();
|
2022-04-21 21:36:18 -04:00
|
|
|
#[allow(non_snake_case)]
|
2022-05-24 21:41:14 -04:00
|
|
|
let mut B = HashMap::<u16, _>::with_capacity(params.view.included.len());
|
2022-07-12 02:45:18 -04:00
|
|
|
{
|
2022-05-24 21:41:14 -04:00
|
|
|
// Parse the commitments
|
|
|
|
|
for l in ¶ms.view.included {
|
2022-07-13 02:38:29 -04:00
|
|
|
{
|
|
|
|
|
params.algorithm.transcript().append_message(b"participant", &l.to_be_bytes());
|
|
|
|
|
}
|
2022-05-24 21:41:14 -04:00
|
|
|
|
2022-07-12 01:28:01 -04:00
|
|
|
// While this doesn't note which nonce/basepoint this is for, those are expected to be
|
|
|
|
|
// static. Beyond that, they're committed to in the DLEq proof transcripts, ensuring
|
|
|
|
|
// consistency. While this is suboptimal, it maintains IETF compliance, and Algorithm is
|
|
|
|
|
// documented accordingly
|
2022-07-13 02:38:29 -04:00
|
|
|
let transcript = |t: &mut A::Transcript, commitments: [C::G; 2]| {
|
|
|
|
|
t.append_message(b"commitment_D", commitments[0].to_bytes().as_ref());
|
|
|
|
|
t.append_message(b"commitment_E", commitments[1].to_bytes().as_ref());
|
|
|
|
|
};
|
|
|
|
|
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
if *l == params.keys.params().i {
|
2022-07-13 02:38:29 -04:00
|
|
|
for nonce_commitments in &our_preprocess.commitments {
|
|
|
|
|
for commitments in nonce_commitments {
|
|
|
|
|
transcript(params.algorithm.transcript(), *commitments);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
B.insert(*l, (our_preprocess.commitments.clone(), C::F::zero()));
|
|
|
|
|
params.algorithm.process_addendum(
|
|
|
|
|
¶ms.view,
|
|
|
|
|
*l,
|
2022-07-15 01:26:07 -04:00
|
|
|
&mut Cursor::new(our_preprocess.addendum.clone()),
|
2022-07-13 02:38:29 -04:00
|
|
|
)?;
|
|
|
|
|
} else {
|
|
|
|
|
let mut cursor = commitments.remove(l).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut commitments = Vec::with_capacity(nonces.len());
|
|
|
|
|
for (n, nonce_generators) in nonces.clone().iter_mut().enumerate() {
|
|
|
|
|
commitments.push(Vec::with_capacity(nonce_generators.len()));
|
2022-07-13 23:29:48 -04:00
|
|
|
for _ in 0 .. nonce_generators.len() {
|
2022-07-13 02:38:29 -04:00
|
|
|
commitments[n].push(read_D_E::<_, C>(&mut cursor, *l)?);
|
|
|
|
|
transcript(params.algorithm.transcript(), commitments[n][commitments[n].len() - 1]);
|
2022-07-13 23:29:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nonce_generators.len() >= 2 {
|
|
|
|
|
let mut transcript = nonce_transcript::<A::Transcript>();
|
2022-07-13 02:38:29 -04:00
|
|
|
for de in 0 .. 2 {
|
2022-07-15 01:26:07 -04:00
|
|
|
DLEqProof::deserialize(&mut cursor)
|
|
|
|
|
.map_err(|_| FrostError::InvalidCommitment(*l))?
|
|
|
|
|
.verify(
|
|
|
|
|
&mut transcript,
|
2022-07-22 02:34:36 -04:00
|
|
|
nonce_generators,
|
2022-07-15 01:26:07 -04:00
|
|
|
&commitments[n].iter().map(|commitments| commitments[de]).collect::<Vec<_>>(),
|
|
|
|
|
)
|
|
|
|
|
.map_err(|_| FrostError::InvalidCommitment(*l))?;
|
2022-07-13 02:38:29 -04:00
|
|
|
}
|
2022-07-12 01:28:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
B.insert(*l, (commitments, C::F::zero()));
|
|
|
|
|
params.algorithm.process_addendum(¶ms.view, *l, &mut cursor)?;
|
2022-07-12 01:28:01 -04:00
|
|
|
}
|
2022-05-24 21:41:14 -04:00
|
|
|
}
|
|
|
|
|
|
2022-07-12 02:45:18 -04:00
|
|
|
// Re-format into the FROST-expected rho transcript
|
|
|
|
|
let mut rho_transcript = A::Transcript::new(b"FROST_rho");
|
2022-07-22 02:34:36 -04:00
|
|
|
rho_transcript.append_message(b"message", &C::hash_msg(msg));
|
2022-07-12 03:38:59 -04:00
|
|
|
// This won't just be the commitments, yet the full existing transcript if used in an extended
|
|
|
|
|
// protocol
|
2022-07-12 02:45:18 -04:00
|
|
|
rho_transcript.append_message(
|
|
|
|
|
b"commitments",
|
2022-07-15 01:26:07 -04:00
|
|
|
&C::hash_msg(params.algorithm.transcript().challenge(b"commitments").as_ref()),
|
2022-07-12 02:45:18 -04:00
|
|
|
);
|
2022-08-12 23:17:31 -04:00
|
|
|
|
2022-07-12 02:45:18 -04:00
|
|
|
// Include the offset, if one exists
|
|
|
|
|
// While this isn't part of the FROST-expected rho transcript, the offset being here coincides
|
2022-08-12 23:17:31 -04:00
|
|
|
// with another specification (despite the transcript format being distinct)
|
2022-07-12 02:45:18 -04:00
|
|
|
if let Some(offset) = params.keys.offset {
|
2022-08-12 23:17:31 -04:00
|
|
|
// Transcript as a point
|
|
|
|
|
// Under a coordinated model, the coordinater can be the only party to know the discrete log
|
|
|
|
|
// of the offset. This removes the ability for any signer to provide the discrete log,
|
|
|
|
|
// proving a key is related to another, slightly increasing security
|
|
|
|
|
// While further code edits would still be required for such a model (having the offset
|
|
|
|
|
// communicated as a point along with only a single party applying the offset), this means it
|
|
|
|
|
// wouldn't require a transcript change as well
|
2022-08-13 05:07:07 -04:00
|
|
|
rho_transcript.append_message(b"offset", (C::generator() * offset).to_bytes().as_ref());
|
2022-07-12 02:45:18 -04:00
|
|
|
}
|
2022-05-24 21:41:14 -04:00
|
|
|
|
2022-07-12 02:45:18 -04:00
|
|
|
// Generate the per-signer binding factors
|
|
|
|
|
for (l, commitments) in B.iter_mut() {
|
|
|
|
|
let mut rho_transcript = rho_transcript.clone();
|
|
|
|
|
rho_transcript.append_message(b"participant", &l.to_be_bytes());
|
|
|
|
|
commitments.1 = C::hash_binding_factor(rho_transcript.challenge(b"rho").as_ref());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Merge the rho transcript back into the global one to ensure its advanced while committing to
|
|
|
|
|
// everything
|
2022-07-15 01:26:07 -04:00
|
|
|
params
|
|
|
|
|
.algorithm
|
|
|
|
|
.transcript()
|
|
|
|
|
.append_message(b"rho_transcript", rho_transcript.challenge(b"merge").as_ref());
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
2022-07-12 01:28:01 -04:00
|
|
|
let mut Rs = Vec::with_capacity(nonces.len());
|
|
|
|
|
for n in 0 .. nonces.len() {
|
|
|
|
|
Rs.push(vec![C::G::identity(); nonces[n].len()]);
|
|
|
|
|
for g in 0 .. nonces[n].len() {
|
2022-07-12 03:21:22 -04:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
let mut D = C::G::identity();
|
|
|
|
|
let mut statements = Vec::with_capacity(B.len());
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
for (B, binding) in B.values() {
|
|
|
|
|
D += B[n][g][0];
|
|
|
|
|
statements.push((*binding, B[n][g][1]));
|
|
|
|
|
}
|
|
|
|
|
Rs[n][g] = D + multiexp_vartime(&statements);
|
2022-07-12 01:28:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
let mut nonces = our_preprocess
|
|
|
|
|
.nonces
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|nonces| nonces[0] + (nonces[1] * B[¶ms.keys.params().i()].1))
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
let share = params.algorithm.sign_share(¶ms.view, &Rs, &nonces, msg);
|
|
|
|
|
nonces.zeroize();
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
Ok((Package { B, Rs, share }, share.to_repr().as_ref().to_vec()))
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
fn complete<Re: Read, C: Curve, A: Algorithm<C>>(
|
2022-04-21 21:36:18 -04:00
|
|
|
sign_params: &Params<C, A>,
|
|
|
|
|
sign: Package<C>,
|
2022-07-13 02:38:29 -04:00
|
|
|
mut shares: HashMap<u16, Re>,
|
2022-04-21 21:36:18 -04:00
|
|
|
) -> Result<A::Signature, FrostError> {
|
|
|
|
|
let params = sign_params.multisig_params();
|
2022-07-13 02:38:29 -04:00
|
|
|
validate_map(&mut shares, &sign_params.view.included, params.i)?;
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-05-24 21:41:14 -04:00
|
|
|
let mut responses = HashMap::new();
|
|
|
|
|
let mut sum = C::F::zero();
|
|
|
|
|
for l in &sign_params.view.included {
|
2022-07-13 02:38:29 -04:00
|
|
|
let part = if *l == params.i {
|
|
|
|
|
sign.share
|
|
|
|
|
} else {
|
|
|
|
|
C::read_F(shares.get_mut(l).unwrap()).map_err(|_| FrostError::InvalidShare(*l))?
|
|
|
|
|
};
|
2022-04-21 21:36:18 -04:00
|
|
|
sum += part;
|
2022-05-24 21:41:14 -04:00
|
|
|
responses.insert(*l, part);
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Perform signature validation instead of individual share validation
|
|
|
|
|
// For the success route, which should be much more frequent, this should be faster
|
|
|
|
|
// It also acts as an integrity check of this library's signing function
|
2022-07-12 01:28:01 -04:00
|
|
|
let res = sign_params.algorithm.verify(sign_params.view.group_key, &sign.Rs, sum);
|
2022-05-18 01:08:54 -04:00
|
|
|
if let Some(res) = res {
|
|
|
|
|
return Ok(res);
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-05-30 01:46:30 -04:00
|
|
|
// Find out who misbehaved. It may be beneficial to randomly sort this to have detection be
|
|
|
|
|
// within n / 2 on average, and not gameable to n, though that should be minor
|
2022-05-24 21:41:14 -04:00
|
|
|
for l in &sign_params.view.included {
|
|
|
|
|
if !sign_params.algorithm.verify_share(
|
|
|
|
|
sign_params.view.verification_share(*l),
|
2022-07-15 01:26:07 -04:00
|
|
|
&sign.B[l]
|
|
|
|
|
.0
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|nonces| {
|
|
|
|
|
nonces.iter().map(|commitments| commitments[0] + (commitments[1] * sign.B[l].1)).collect()
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
|
responses[l],
|
2022-05-24 21:41:14 -04:00
|
|
|
) {
|
|
|
|
|
Err(FrostError::InvalidShare(*l))?;
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If everyone has a valid share and there were enough participants, this should've worked
|
2022-07-15 01:26:07 -04:00
|
|
|
Err(FrostError::InternalError("everyone had a valid share yet the signature was still invalid"))
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
pub trait PreprocessMachine {
|
2022-05-25 00:21:01 -04:00
|
|
|
type Signature: Clone + PartialEq + fmt::Debug;
|
2022-06-24 08:40:14 -04:00
|
|
|
type SignMachine: SignMachine<Self::Signature>;
|
2022-04-29 22:36:43 -04:00
|
|
|
|
|
|
|
|
/// Perform the preprocessing round required in order to sign
|
|
|
|
|
/// Returns a byte vector which must be transmitted to all parties selected for this signing
|
|
|
|
|
/// process, over an authenticated channel
|
2022-07-15 01:26:07 -04:00
|
|
|
fn preprocess<R: RngCore + CryptoRng>(self, rng: &mut R) -> (Self::SignMachine, Vec<u8>);
|
2022-06-24 08:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait SignMachine<S> {
|
|
|
|
|
type SignatureMachine: SignatureMachine<S>;
|
2022-04-29 22:36:43 -04:00
|
|
|
|
|
|
|
|
/// Sign a message
|
|
|
|
|
/// Takes in the participant's commitments, which are expected to be in a Vec where participant
|
|
|
|
|
/// index = Vec index. None is expected at index 0 to allow for this. None is also expected at
|
|
|
|
|
/// index i which is locally handled. Returns a byte vector representing a share of the signature
|
|
|
|
|
/// for every other participant to receive, over an authenticated channel
|
2022-07-13 02:38:29 -04:00
|
|
|
fn sign<Re: Read>(
|
2022-06-24 08:40:14 -04:00
|
|
|
self,
|
2022-07-13 02:38:29 -04:00
|
|
|
commitments: HashMap<u16, Re>,
|
2022-04-29 22:36:43 -04:00
|
|
|
msg: &[u8],
|
2022-06-24 08:40:14 -04:00
|
|
|
) -> Result<(Self::SignatureMachine, Vec<u8>), FrostError>;
|
|
|
|
|
}
|
2022-04-29 22:36:43 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
pub trait SignatureMachine<S> {
|
2022-04-29 22:36:43 -04:00
|
|
|
/// Complete signing
|
|
|
|
|
/// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index =
|
|
|
|
|
/// Vec index with None at index 0 and index i. Returns a byte vector representing the serialized
|
|
|
|
|
/// signature
|
2022-07-13 02:38:29 -04:00
|
|
|
fn complete<Re: Read>(self, shares: HashMap<u16, Re>) -> Result<S, FrostError>;
|
2022-04-29 22:36:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// State machine which manages signing for an arbitrary signature algorithm
|
|
|
|
|
pub struct AlgorithmMachine<C: Curve, A: Algorithm<C>> {
|
2022-07-15 01:26:07 -04:00
|
|
|
params: Params<C, A>,
|
2022-06-24 08:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct AlgorithmSignMachine<C: Curve, A: Algorithm<C>> {
|
2022-04-21 21:36:18 -04:00
|
|
|
params: Params<C, A>,
|
2022-06-24 08:40:14 -04:00
|
|
|
preprocess: PreprocessPackage<C>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct AlgorithmSignatureMachine<C: Curve, A: Algorithm<C>> {
|
|
|
|
|
params: Params<C, A>,
|
|
|
|
|
sign: Package<C>,
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-04-29 22:36:43 -04:00
|
|
|
impl<C: Curve, A: Algorithm<C>> AlgorithmMachine<C, A> {
|
2022-04-21 21:36:18 -04:00
|
|
|
/// Creates a new machine to generate a key for the specified curve in the specified multisig
|
2022-04-29 22:36:43 -04:00
|
|
|
pub fn new(
|
|
|
|
|
algorithm: A,
|
Utilize zeroize (#76)
* Apply Zeroize to nonces used in Bulletproofs
Also makes bit decomposition constant time for a given amount of
outputs.
* Fix nonce reuse for single-signer CLSAG
* Attach Zeroize to most structures in Monero, and ZOnDrop to anything with private data
* Zeroize private keys and nonces
* Merge prepare_outputs and prepare_transactions
* Ensure CLSAG is constant time
* Pass by borrow where needed, bug fixes
The past few commitments have been one in-progress chunk which I've
broken up as best read.
* Add Zeroize to FROST structs
Still needs to zeroize internally, yet next step. Not quite as
aggressive as Monero, partially due to the limitations of HashMaps,
partially due to less concern about metadata, yet does still delete a
few smaller items of metadata (group key, context string...).
* Remove Zeroize from most Monero multisig structs
These structs largely didn't have private data, just fields with private
data, yet those fields implemented ZeroizeOnDrop making them already
covered. While there is still traces of the transaction left in RAM,
fully purging that was never the intent.
* Use Zeroize within dleq
bitvec doesn't offer Zeroize, so a manual zeroing has been implemented.
* Use Zeroize for random_nonce
It isn't perfect, due to the inability to zeroize the digest, and due to
kp256 requiring a few transformations. It does the best it can though.
Does move the per-curve random_nonce to a provided one, which is allowed
as of https://github.com/cfrg/draft-irtf-cfrg-frost/pull/231.
* Use Zeroize on FROST keygen/signing
* Zeroize constant time multiexp.
* Correct when FROST keygen zeroizes
* Move the FROST keys Arc into FrostKeys
Reduces amount of instances in memory.
* Manually implement Debug for FrostCore to not leak the secret share
* Misc bug fixes
* clippy + multiexp test bug fixes
* Correct FROST key gen share summation
It leaked our own share for ourself.
* Fix cross-group DLEq tests
2022-08-03 03:25:18 -05:00
|
|
|
keys: FrostKeys<C>,
|
2022-05-24 21:41:14 -04:00
|
|
|
included: &[u16],
|
2022-04-29 22:36:43 -04:00
|
|
|
) -> Result<AlgorithmMachine<C, A>, FrostError> {
|
2022-06-24 08:40:14 -04:00
|
|
|
Ok(AlgorithmMachine { params: Params::new(algorithm, keys, included)? })
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
2022-06-03 01:25:46 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
pub(crate) fn unsafe_override_preprocess(
|
|
|
|
|
self,
|
2022-07-15 01:26:07 -04:00
|
|
|
preprocess: PreprocessPackage<C>,
|
2022-07-13 02:38:29 -04:00
|
|
|
) -> AlgorithmSignMachine<C, A> {
|
|
|
|
|
AlgorithmSignMachine { params: self.params, preprocess }
|
2022-06-03 01:25:46 -04:00
|
|
|
}
|
2022-04-29 22:36:43 -04:00
|
|
|
}
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
impl<C: Curve, A: Algorithm<C>> PreprocessMachine for AlgorithmMachine<C, A> {
|
2022-04-29 22:36:43 -04:00
|
|
|
type Signature = A::Signature;
|
2022-06-24 08:40:14 -04:00
|
|
|
type SignMachine = AlgorithmSignMachine<C, A>;
|
2022-04-29 22:36:43 -04:00
|
|
|
|
2022-07-15 01:26:07 -04:00
|
|
|
fn preprocess<R: RngCore + CryptoRng>(self, rng: &mut R) -> (Self::SignMachine, Vec<u8>) {
|
2022-06-24 08:40:14 -04:00
|
|
|
let mut params = self.params;
|
2022-07-13 02:38:29 -04:00
|
|
|
let (preprocess, serialized) = preprocess::<R, C, A>(rng, &mut params);
|
2022-06-24 08:40:14 -04:00
|
|
|
(AlgorithmSignMachine { params, preprocess }, serialized)
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
2022-06-24 08:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachine<C, A> {
|
|
|
|
|
type SignatureMachine = AlgorithmSignatureMachine<C, A>;
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
fn sign<Re: Read>(
|
2022-06-24 08:40:14 -04:00
|
|
|
self,
|
2022-07-13 02:38:29 -04:00
|
|
|
commitments: HashMap<u16, Re>,
|
2022-07-15 01:26:07 -04:00
|
|
|
msg: &[u8],
|
2022-06-24 08:40:14 -04:00
|
|
|
) -> Result<(Self::SignatureMachine, Vec<u8>), FrostError> {
|
|
|
|
|
let mut params = self.params;
|
|
|
|
|
let (sign, serialized) = sign_with_share(&mut params, self.preprocess, commitments, msg)?;
|
|
|
|
|
Ok((AlgorithmSignatureMachine { params, sign }, serialized))
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
2022-06-24 08:40:14 -04:00
|
|
|
}
|
2022-04-21 21:36:18 -04:00
|
|
|
|
2022-07-15 01:26:07 -04:00
|
|
|
impl<C: Curve, A: Algorithm<C>> SignatureMachine<A::Signature> for AlgorithmSignatureMachine<C, A> {
|
2022-07-13 02:38:29 -04:00
|
|
|
fn complete<Re: Read>(self, shares: HashMap<u16, Re>) -> Result<A::Signature, FrostError> {
|
2022-06-24 08:40:14 -04:00
|
|
|
complete(&self.params, self.sign, shares)
|
2022-04-21 21:36:18 -04:00
|
|
|
}
|
|
|
|
|
}
|