Files
serai/crypto/dleq/src/lib.rs

337 lines
10 KiB
Rust
Raw Normal View History

2022-09-29 04:47:55 -04:00
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
2023-03-20 20:10:00 -04:00
#![doc = include_str!("../README.md")]
use core::ops::Deref;
use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, Zeroizing};
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 transcript::Transcript;
use ff::{Field, PrimeField};
use group::prime::PrimeGroup;
#[cfg(feature = "serialize")]
use std::io::{self, ErrorKind, Error, Read, Write};
2023-03-20 20:10:00 -04:00
/// A cross-group DLEq proof capable of proving that two public keys, across two different curves,
/// share a private key.
#[cfg(feature = "experimental")]
pub mod cross_group;
#[cfg(test)]
mod tests;
2023-02-24 04:37:20 -05:00
// Produce a non-biased challenge from the transcript in the specified field
pub(crate) fn challenge<T: Transcript, F: PrimeField>(transcript: &mut T) -> F {
// From here, there are three ways to get a scalar under the ff/group API
// 1: Scalar::random(ChaCha20Rng::from_seed(self.transcript.rng_seed(b"challenge")))
// 2: Grabbing a UInt library to perform reduction by the modulus, then determining endianess
// and loading it in
// 3: Iterating over each byte and manually doubling/adding. This is simplest
let mut challenge = F::ZERO;
// Get a wide amount of bytes to safely reduce without bias
// In most cases, <=1.5x bytes is enough. 2x is still standard and there's some theoretical
// groups which may technically require more than 1.5x bytes for this to work as intended
let target_bytes = ((usize::try_from(F::NUM_BITS).unwrap() + 7) / 8) * 2;
let mut challenge_bytes = transcript.challenge(b"challenge");
let challenge_bytes_len = challenge_bytes.as_ref().len();
// If the challenge is 32 bytes, and we need 64, we need two challenges
let needed_challenges = (target_bytes + (challenge_bytes_len - 1)) / challenge_bytes_len;
// The following algorithm should be equivalent to a wide reduction of the challenges,
// interpreted as concatenated, big-endian byte string
let mut handled_bytes = 0;
'outer: for _ in 0 ..= needed_challenges {
// Cursor of which byte of the challenge to use next
let mut b = 0;
while b < challenge_bytes_len {
// Get the next amount of bytes to attempt
// Only grabs the needed amount of bytes, up to 8 at a time (u64), so long as they're
// available in the challenge
let chunk_bytes = (target_bytes - handled_bytes).min(8).min(challenge_bytes_len - b);
let mut chunk = 0;
for _ in 0 .. chunk_bytes {
chunk <<= 8;
chunk |= u64::from(challenge_bytes.as_ref()[b]);
b += 1;
}
// Add this chunk
challenge += F::from(chunk);
handled_bytes += chunk_bytes;
// If we've reached the target amount of bytes, break
if handled_bytes == target_bytes {
break 'outer;
}
// Shift over by however many bits will be in the next chunk
let next_chunk_bytes = (target_bytes - handled_bytes).min(8).min(challenge_bytes_len);
for _ in 0 .. (next_chunk_bytes * 8) {
challenge = challenge.double();
}
}
// Secure thanks to the Transcript trait having a bound of updating on challenge
challenge_bytes = transcript.challenge(b"challenge_extension");
}
challenge
}
2023-02-24 04:37:20 -05:00
// Helper function to read a scalar
#[cfg(feature = "serialize")]
fn read_scalar<R: Read, F: PrimeField>(r: &mut R) -> io::Result<F> {
let mut repr = F::Repr::default();
r.read_exact(repr.as_mut())?;
let scalar = F::from_repr(repr);
if scalar.is_none().into() {
Err(Error::new(ErrorKind::Other, "invalid scalar"))?;
}
Ok(scalar.unwrap())
}
2023-02-24 04:37:20 -05:00
/// Error for DLEq proofs.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DLEqError {
2023-03-20 20:10:00 -04:00
/// The proof was invalid.
2022-07-15 01:26:07 -04:00
InvalidProof,
}
2023-02-24 04:37:20 -05:00
/// A proof that points have the same discrete logarithm across generators.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct DLEqProof<G: PrimeGroup>
where
G::Scalar: Zeroize,
{
c: G::Scalar,
2022-07-15 01:26:07 -04:00
s: G::Scalar,
}
#[allow(non_snake_case)]
impl<G: PrimeGroup> DLEqProof<G>
where
G::Scalar: Zeroize,
{
fn transcript<T: Transcript>(transcript: &mut T, generator: G, nonce: G, point: G) {
transcript.append_message(b"generator", generator.to_bytes());
transcript.append_message(b"nonce", nonce.to_bytes());
transcript.append_message(b"point", point.to_bytes());
}
2023-02-24 04:37:20 -05:00
/// Prove that the points created by `scalar * G`, for each specified generator, share a discrete
/// logarithm.
pub fn prove<R: RngCore + CryptoRng, T: Transcript>(
rng: &mut R,
transcript: &mut T,
generators: &[G],
scalar: &Zeroizing<G::Scalar>,
) -> DLEqProof<G> {
let r = Zeroizing::new(G::Scalar::random(rng));
transcript.domain_separate(b"dleq");
for generator in generators {
DKG Blame (#196) * Standardize the DLEq serialization function naming They mismatched from the rest of the project. This commit is technically incomplete as it doesn't update the dkg crate. * Rewrite DKG encryption to enable per-message decryption without side effects This isn't technically true as I already know a break in this which I'll correct for shortly. Does update documentation to explain the new scheme. Required for blame. * Add a verifiable system for blame during the FROST DKG Previously, if sent an invalid key share, the participant would realize that and could accuse the sender. Without further evidence, either the accuser or the accused could be guilty. Now, the accuser has a proof the accused is in the wrong. Reworks KeyMachine to return BlameMachine. This explicitly acknowledges how locally complete keys still need group acknowledgement before the protocol can be complete and provides a way for others to verify blame, even after a locally successful run. If any blame is cast, the protocol is no longer considered complete-able (instead aborting). Further accusations of blame can still be handled however. Updates documentation on network behavior. Also starts to remove "OnDrop". We now use Zeroizing for anything which should be zeroized on drop. This is a lot more piece-meal and reduces clones. * Tweak Zeroizing and Debug impls Expands Zeroizing to be more comprehensive. Also updates Zeroizing<CachedPreprocess([u8; 32])> to CachedPreprocess(Zeroizing<[u8; 32]>) so zeroizing is the first thing done and last step before exposing the copy-able [u8; 32]. Removes private keys from Debug. * Fix a bug where adversaries could claim to be using another user's encryption keys to learn their messages Mentioned a few commits ago, now fixed. This wouldn't have affected Serai, which aborts on failure, nor any DKG currently supported. It's just about ensuring the DKG encryption is robust and proper. * Finish moving dleq from ser/deser to write/read * Add tests for dkg blame * Add a FROST test for invalid signature shares * Batch verify encrypted messages' ephemeral keys' PoP
2023-01-01 01:54:18 -05:00
// R, A
Self::transcript(transcript, *generator, *generator * r.deref(), *generator * scalar.deref());
}
let c = challenge(transcript);
DKG Blame (#196) * Standardize the DLEq serialization function naming They mismatched from the rest of the project. This commit is technically incomplete as it doesn't update the dkg crate. * Rewrite DKG encryption to enable per-message decryption without side effects This isn't technically true as I already know a break in this which I'll correct for shortly. Does update documentation to explain the new scheme. Required for blame. * Add a verifiable system for blame during the FROST DKG Previously, if sent an invalid key share, the participant would realize that and could accuse the sender. Without further evidence, either the accuser or the accused could be guilty. Now, the accuser has a proof the accused is in the wrong. Reworks KeyMachine to return BlameMachine. This explicitly acknowledges how locally complete keys still need group acknowledgement before the protocol can be complete and provides a way for others to verify blame, even after a locally successful run. If any blame is cast, the protocol is no longer considered complete-able (instead aborting). Further accusations of blame can still be handled however. Updates documentation on network behavior. Also starts to remove "OnDrop". We now use Zeroizing for anything which should be zeroized on drop. This is a lot more piece-meal and reduces clones. * Tweak Zeroizing and Debug impls Expands Zeroizing to be more comprehensive. Also updates Zeroizing<CachedPreprocess([u8; 32])> to CachedPreprocess(Zeroizing<[u8; 32]>) so zeroizing is the first thing done and last step before exposing the copy-able [u8; 32]. Removes private keys from Debug. * Fix a bug where adversaries could claim to be using another user's encryption keys to learn their messages Mentioned a few commits ago, now fixed. This wouldn't have affected Serai, which aborts on failure, nor any DKG currently supported. It's just about ensuring the DKG encryption is robust and proper. * Finish moving dleq from ser/deser to write/read * Add tests for dkg blame * Add a FROST test for invalid signature shares * Batch verify encrypted messages' ephemeral keys' PoP
2023-01-01 01:54:18 -05:00
// r + ca
let s = (c * scalar.deref()) + r.deref();
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
DLEqProof { c, s }
}
// Transcript a specific generator/nonce/point (G/R/A), as used when verifying a proof.
// This takes in the generator/point, and then the challenge and solution to calculate the nonce.
fn verify_statement<T: Transcript>(
transcript: &mut T,
generator: G,
point: G,
c: G::Scalar,
s: G::Scalar,
) {
// s = r + ca
// sG - cA = R
// R, A
Self::transcript(transcript, generator, (generator * s) - (point * c), point);
}
2023-02-24 04:37:20 -05:00
/// Verify the specified points share a discrete logarithm across the specified generators.
pub fn verify<T: Transcript>(
&self,
transcript: &mut T,
generators: &[G],
2022-07-15 01:26:07 -04:00
points: &[G],
) -> Result<(), DLEqError> {
if generators.len() != points.len() {
Err(DLEqError::InvalidProof)?;
}
transcript.domain_separate(b"dleq");
for (generator, point) in generators.iter().zip(points) {
Self::verify_statement(transcript, *generator, *point, self.c, self.s);
}
if self.c != challenge(transcript) {
Err(DLEqError::InvalidProof)?;
}
Ok(())
}
2023-02-24 04:37:20 -05:00
/// Write a DLEq proof to something implementing Write.
#[cfg(feature = "serialize")]
DKG Blame (#196) * Standardize the DLEq serialization function naming They mismatched from the rest of the project. This commit is technically incomplete as it doesn't update the dkg crate. * Rewrite DKG encryption to enable per-message decryption without side effects This isn't technically true as I already know a break in this which I'll correct for shortly. Does update documentation to explain the new scheme. Required for blame. * Add a verifiable system for blame during the FROST DKG Previously, if sent an invalid key share, the participant would realize that and could accuse the sender. Without further evidence, either the accuser or the accused could be guilty. Now, the accuser has a proof the accused is in the wrong. Reworks KeyMachine to return BlameMachine. This explicitly acknowledges how locally complete keys still need group acknowledgement before the protocol can be complete and provides a way for others to verify blame, even after a locally successful run. If any blame is cast, the protocol is no longer considered complete-able (instead aborting). Further accusations of blame can still be handled however. Updates documentation on network behavior. Also starts to remove "OnDrop". We now use Zeroizing for anything which should be zeroized on drop. This is a lot more piece-meal and reduces clones. * Tweak Zeroizing and Debug impls Expands Zeroizing to be more comprehensive. Also updates Zeroizing<CachedPreprocess([u8; 32])> to CachedPreprocess(Zeroizing<[u8; 32]>) so zeroizing is the first thing done and last step before exposing the copy-able [u8; 32]. Removes private keys from Debug. * Fix a bug where adversaries could claim to be using another user's encryption keys to learn their messages Mentioned a few commits ago, now fixed. This wouldn't have affected Serai, which aborts on failure, nor any DKG currently supported. It's just about ensuring the DKG encryption is robust and proper. * Finish moving dleq from ser/deser to write/read * Add tests for dkg blame * Add a FROST test for invalid signature shares * Batch verify encrypted messages' ephemeral keys' PoP
2023-01-01 01:54:18 -05:00
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_all(self.c.to_repr().as_ref())?;
w.write_all(self.s.to_repr().as_ref())
}
2023-02-24 04:37:20 -05:00
/// Read a DLEq proof from something implementing Read.
#[cfg(feature = "serialize")]
DKG Blame (#196) * Standardize the DLEq serialization function naming They mismatched from the rest of the project. This commit is technically incomplete as it doesn't update the dkg crate. * Rewrite DKG encryption to enable per-message decryption without side effects This isn't technically true as I already know a break in this which I'll correct for shortly. Does update documentation to explain the new scheme. Required for blame. * Add a verifiable system for blame during the FROST DKG Previously, if sent an invalid key share, the participant would realize that and could accuse the sender. Without further evidence, either the accuser or the accused could be guilty. Now, the accuser has a proof the accused is in the wrong. Reworks KeyMachine to return BlameMachine. This explicitly acknowledges how locally complete keys still need group acknowledgement before the protocol can be complete and provides a way for others to verify blame, even after a locally successful run. If any blame is cast, the protocol is no longer considered complete-able (instead aborting). Further accusations of blame can still be handled however. Updates documentation on network behavior. Also starts to remove "OnDrop". We now use Zeroizing for anything which should be zeroized on drop. This is a lot more piece-meal and reduces clones. * Tweak Zeroizing and Debug impls Expands Zeroizing to be more comprehensive. Also updates Zeroizing<CachedPreprocess([u8; 32])> to CachedPreprocess(Zeroizing<[u8; 32]>) so zeroizing is the first thing done and last step before exposing the copy-able [u8; 32]. Removes private keys from Debug. * Fix a bug where adversaries could claim to be using another user's encryption keys to learn their messages Mentioned a few commits ago, now fixed. This wouldn't have affected Serai, which aborts on failure, nor any DKG currently supported. It's just about ensuring the DKG encryption is robust and proper. * Finish moving dleq from ser/deser to write/read * Add tests for dkg blame * Add a FROST test for invalid signature shares * Batch verify encrypted messages' ephemeral keys' PoP
2023-01-01 01:54:18 -05:00
pub fn read<R: Read>(r: &mut R) -> io::Result<DLEqProof<G>> {
Ok(DLEqProof { c: read_scalar(r)?, s: read_scalar(r)? })
}
DKG Blame (#196) * Standardize the DLEq serialization function naming They mismatched from the rest of the project. This commit is technically incomplete as it doesn't update the dkg crate. * Rewrite DKG encryption to enable per-message decryption without side effects This isn't technically true as I already know a break in this which I'll correct for shortly. Does update documentation to explain the new scheme. Required for blame. * Add a verifiable system for blame during the FROST DKG Previously, if sent an invalid key share, the participant would realize that and could accuse the sender. Without further evidence, either the accuser or the accused could be guilty. Now, the accuser has a proof the accused is in the wrong. Reworks KeyMachine to return BlameMachine. This explicitly acknowledges how locally complete keys still need group acknowledgement before the protocol can be complete and provides a way for others to verify blame, even after a locally successful run. If any blame is cast, the protocol is no longer considered complete-able (instead aborting). Further accusations of blame can still be handled however. Updates documentation on network behavior. Also starts to remove "OnDrop". We now use Zeroizing for anything which should be zeroized on drop. This is a lot more piece-meal and reduces clones. * Tweak Zeroizing and Debug impls Expands Zeroizing to be more comprehensive. Also updates Zeroizing<CachedPreprocess([u8; 32])> to CachedPreprocess(Zeroizing<[u8; 32]>) so zeroizing is the first thing done and last step before exposing the copy-able [u8; 32]. Removes private keys from Debug. * Fix a bug where adversaries could claim to be using another user's encryption keys to learn their messages Mentioned a few commits ago, now fixed. This wouldn't have affected Serai, which aborts on failure, nor any DKG currently supported. It's just about ensuring the DKG encryption is robust and proper. * Finish moving dleq from ser/deser to write/read * Add tests for dkg blame * Add a FROST test for invalid signature shares * Batch verify encrypted messages' ephemeral keys' PoP
2023-01-01 01:54:18 -05:00
2023-03-07 05:34:29 -05:00
/// Serialize a DLEq proof to a `Vec<u8>`.
DKG Blame (#196) * Standardize the DLEq serialization function naming They mismatched from the rest of the project. This commit is technically incomplete as it doesn't update the dkg crate. * Rewrite DKG encryption to enable per-message decryption without side effects This isn't technically true as I already know a break in this which I'll correct for shortly. Does update documentation to explain the new scheme. Required for blame. * Add a verifiable system for blame during the FROST DKG Previously, if sent an invalid key share, the participant would realize that and could accuse the sender. Without further evidence, either the accuser or the accused could be guilty. Now, the accuser has a proof the accused is in the wrong. Reworks KeyMachine to return BlameMachine. This explicitly acknowledges how locally complete keys still need group acknowledgement before the protocol can be complete and provides a way for others to verify blame, even after a locally successful run. If any blame is cast, the protocol is no longer considered complete-able (instead aborting). Further accusations of blame can still be handled however. Updates documentation on network behavior. Also starts to remove "OnDrop". We now use Zeroizing for anything which should be zeroized on drop. This is a lot more piece-meal and reduces clones. * Tweak Zeroizing and Debug impls Expands Zeroizing to be more comprehensive. Also updates Zeroizing<CachedPreprocess([u8; 32])> to CachedPreprocess(Zeroizing<[u8; 32]>) so zeroizing is the first thing done and last step before exposing the copy-able [u8; 32]. Removes private keys from Debug. * Fix a bug where adversaries could claim to be using another user's encryption keys to learn their messages Mentioned a few commits ago, now fixed. This wouldn't have affected Serai, which aborts on failure, nor any DKG currently supported. It's just about ensuring the DKG encryption is robust and proper. * Finish moving dleq from ser/deser to write/read * Add tests for dkg blame * Add a FROST test for invalid signature shares * Batch verify encrypted messages' ephemeral keys' PoP
2023-01-01 01:54:18 -05:00
#[cfg(feature = "serialize")]
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
res
}
}
2023-02-24 04:37:20 -05:00
/// A proof that multiple series of points each have a single discrete logarithm across generators.
2023-03-20 20:10:00 -04:00
///
2023-02-24 04:37:20 -05:00
/// This is effectively n distinct DLEq proofs, one for each discrete logarithm and its points
/// across some generators, yet with a smaller overall proof size.
#[cfg(feature = "std")]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct MultiDLEqProof<G: PrimeGroup>
where
G::Scalar: Zeroize,
{
c: G::Scalar,
s: Vec<G::Scalar>,
}
#[cfg(feature = "std")]
#[allow(non_snake_case)]
impl<G: PrimeGroup> MultiDLEqProof<G>
where
G::Scalar: Zeroize,
{
2023-02-24 04:37:20 -05:00
/// Prove for each scalar that the series of points created by multiplying it against its
/// matching generators share a discrete logarithm.
/// This function panics if `generators.len() != scalars.len()`.
pub fn prove<R: RngCore + CryptoRng, T: Transcript>(
rng: &mut R,
transcript: &mut T,
generators: &[Vec<G>],
scalars: &[Zeroizing<G::Scalar>],
) -> MultiDLEqProof<G> {
assert_eq!(
generators.len(),
scalars.len(),
"amount of series of generators doesn't match the amount of scalars"
);
transcript.domain_separate(b"multi_dleq");
let mut nonces = vec![];
for (i, (scalar, generators)) in scalars.iter().zip(generators).enumerate() {
// Delineate between discrete logarithms
transcript.append_message(b"discrete_logarithm", i.to_le_bytes());
let nonce = Zeroizing::new(G::Scalar::random(&mut *rng));
for generator in generators {
DLEqProof::transcript(
transcript,
*generator,
*generator * nonce.deref(),
*generator * scalar.deref(),
);
}
nonces.push(nonce);
}
let c = challenge(transcript);
let mut s = vec![];
for (scalar, nonce) in scalars.iter().zip(nonces) {
s.push((c * scalar.deref()) + nonce.deref());
}
MultiDLEqProof { c, s }
}
2023-02-24 04:37:20 -05:00
/// Verify each series of points share a discrete logarithm against their matching series of
/// generators.
pub fn verify<T: Transcript>(
&self,
transcript: &mut T,
generators: &[Vec<G>],
points: &[Vec<G>],
) -> Result<(), DLEqError> {
if points.len() != generators.len() {
Err(DLEqError::InvalidProof)?;
}
if self.s.len() != generators.len() {
Err(DLEqError::InvalidProof)?;
}
transcript.domain_separate(b"multi_dleq");
for (i, (generators, points)) in generators.iter().zip(points).enumerate() {
if points.len() != generators.len() {
Err(DLEqError::InvalidProof)?;
}
transcript.append_message(b"discrete_logarithm", i.to_le_bytes());
for (generator, point) in generators.iter().zip(points) {
DLEqProof::verify_statement(transcript, *generator, *point, self.c, self.s[i]);
}
}
if self.c != challenge(transcript) {
Err(DLEqError::InvalidProof)?;
}
Ok(())
}
2023-02-24 04:37:20 -05:00
/// Write a multi-DLEq proof to something implementing Write.
#[cfg(feature = "serialize")]
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
w.write_all(self.c.to_repr().as_ref())?;
for s in &self.s {
w.write_all(s.to_repr().as_ref())?;
}
Ok(())
}
2023-02-24 04:37:20 -05:00
/// Read a multi-DLEq proof from something implementing Read.
#[cfg(feature = "serialize")]
pub fn read<R: Read>(r: &mut R, discrete_logs: usize) -> io::Result<MultiDLEqProof<G>> {
let c = read_scalar(r)?;
let mut s = vec![];
for _ in 0 .. discrete_logs {
s.push(read_scalar(r)?);
}
Ok(MultiDLEqProof { c, s })
}
2023-03-07 05:34:29 -05:00
/// Serialize a multi-DLEq proof to a `Vec<u8>`.
#[cfg(feature = "serialize")]
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
res
}
}