Luke Parker
2022-11-10 22:35:09 -05:00
parent d714f2202d
commit 84de427d72
32 changed files with 313 additions and 278 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "dleq"
version = "0.1.2"
version = "0.2.0"
description = "Implementation of single and cross-curve Discrete Log Equality proofs"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dleq"

View File

@@ -1,8 +1,10 @@
use core::ops::Deref;
use thiserror::Error;
use rand_core::{RngCore, CryptoRng};
use zeroize::Zeroize;
use zeroize::{Zeroize, Zeroizing};
use digest::{Digest, HashMarker};
@@ -18,7 +20,7 @@ pub mod scalar;
use scalar::{scalar_convert, mutual_scalar_from_bytes};
pub(crate) mod schnorr;
use schnorr::SchnorrPoK;
use self::schnorr::SchnorrPoK;
pub(crate) mod aos;
@@ -185,17 +187,17 @@ where
rng: &mut R,
transcript: &mut T,
generators: (Generators<G0>, Generators<G1>),
f: (G0::Scalar, G1::Scalar),
) -> (Self, (G0::Scalar, G1::Scalar)) {
f: (Zeroizing<G0::Scalar>, Zeroizing<G1::Scalar>),
) -> (Self, (Zeroizing<G0::Scalar>, Zeroizing<G1::Scalar>)) {
Self::transcript(
transcript,
generators,
((generators.0.primary * f.0), (generators.1.primary * f.1)),
((generators.0.primary * f.0.deref()), (generators.1.primary * f.1.deref())),
);
let poks = (
SchnorrPoK::<G0>::prove(rng, transcript, generators.0.primary, f.0),
SchnorrPoK::<G1>::prove(rng, transcript, generators.1.primary, f.1),
SchnorrPoK::<G0>::prove(rng, transcript, generators.0.primary, &f.0),
SchnorrPoK::<G1>::prove(rng, transcript, generators.1.primary, &f.1),
);
let mut blinding_key_total = (G0::Scalar::zero(), G1::Scalar::zero());
@@ -269,7 +271,7 @@ where
let proof = __DLEqProof { bits, remainder, poks };
debug_assert_eq!(
proof.reconstruct_keys(),
(generators.0.primary * f.0, generators.1.primary * f.1)
(generators.0.primary * f.0.deref(), generators.1.primary * f.1.deref())
);
(proof, f)
}
@@ -286,13 +288,17 @@ where
transcript: &mut T,
generators: (Generators<G0>, Generators<G1>),
digest: D,
) -> (Self, (G0::Scalar, G1::Scalar)) {
Self::prove_internal(
rng,
transcript,
generators,
mutual_scalar_from_bytes(digest.finalize().as_ref()),
)
) -> (Self, (Zeroizing<G0::Scalar>, Zeroizing<G1::Scalar>)) {
// This pattern theoretically prevents the compiler from moving it, so our protection against
// a copy remaining un-zeroized is actually what's causing a copy. There's still a feeling of
// safety granted by it, even if there's a loss in performance.
let (mut f0, mut f1) =
mutual_scalar_from_bytes::<G0::Scalar, G1::Scalar>(digest.finalize().as_ref());
let f = (Zeroizing::new(f0), Zeroizing::new(f1));
f0.zeroize();
f1.zeroize();
Self::prove_internal(rng, transcript, generators, f)
}
/// Prove the cross-Group Discrete Log Equality for the points derived from the scalar passed in,
@@ -302,9 +308,10 @@ where
rng: &mut R,
transcript: &mut T,
generators: (Generators<G0>, Generators<G1>),
f0: G0::Scalar,
) -> Option<(Self, (G0::Scalar, G1::Scalar))> {
scalar_convert(f0).map(|f1| Self::prove_internal(rng, transcript, generators, (f0, f1)))
f0: Zeroizing<G0::Scalar>,
) -> Option<(Self, (Zeroizing<G0::Scalar>, Zeroizing<G1::Scalar>))> {
scalar_convert(*f0.deref()) // scalar_convert will zeroize it, though this is unfortunate
.map(|f1| Self::prove_internal(rng, transcript, generators, (f0, Zeroizing::new(f1))))
}
/// Verify a cross-Group Discrete Log Equality statement, returning the points proven for.

View File

@@ -1,6 +1,8 @@
use core::ops::Deref;
use rand_core::{RngCore, CryptoRng};
use zeroize::Zeroize;
use zeroize::{Zeroize, Zeroizing};
use transcript::Transcript;
@@ -44,18 +46,17 @@ where
rng: &mut R,
transcript: &mut T,
generator: G,
mut private_key: G::Scalar,
private_key: &Zeroizing<G::Scalar>,
) -> SchnorrPoK<G> {
let mut nonce = G::Scalar::random(rng);
let nonce = Zeroizing::new(G::Scalar::random(rng));
#[allow(non_snake_case)]
let R = generator * nonce;
let res = SchnorrPoK {
let R = generator * nonce.deref();
SchnorrPoK {
R,
s: nonce + (private_key * SchnorrPoK::hra(transcript, generator, R, generator * private_key)),
};
private_key.zeroize();
nonce.zeroize();
res
s: (SchnorrPoK::hra(transcript, generator, R, generator * private_key.deref()) *
private_key.deref()) +
nonce.deref(),
}
}
pub(crate) fn verify<R: RngCore + CryptoRng, T: Transcript>(

View File

@@ -1,9 +1,11 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
use core::ops::Deref;
use rand_core::{RngCore, CryptoRng};
use zeroize::Zeroize;
use zeroize::{Zeroize, Zeroizing};
use transcript::Transcript;
@@ -79,23 +81,20 @@ impl<G: PrimeGroup> DLEqProof<G> {
rng: &mut R,
transcript: &mut T,
generators: &[G],
mut scalar: G::Scalar,
scalar: &Zeroizing<G::Scalar>,
) -> DLEqProof<G>
where
G::Scalar: Zeroize,
{
let mut r = G::Scalar::random(rng);
let r = Zeroizing::new(G::Scalar::random(rng));
transcript.domain_separate(b"dleq");
for generator in generators {
Self::transcript(transcript, *generator, *generator * r, *generator * scalar);
Self::transcript(transcript, *generator, *generator * r.deref(), *generator * scalar.deref());
}
let c = challenge(transcript);
let s = r + (c * scalar);
scalar.zeroize();
r.zeroize();
let s = (c * scalar.deref()) + r.deref();
DLEqProof { c, s }
}

View File

@@ -1,4 +1,8 @@
use core::ops::Deref;
use hex_literal::hex;
use zeroize::Zeroizing;
use rand_core::{RngCore, OsRng};
use ff::{Field, PrimeField};
@@ -19,7 +23,6 @@ use crate::{
};
mod scalar;
mod schnorr;
mod aos;
type G0 = ProjectivePoint;
@@ -51,8 +54,8 @@ pub(crate) fn generators() -> (Generators<G0>, Generators<G1>) {
macro_rules! verify_and_deserialize {
($type: ty, $proof: ident, $generators: ident, $keys: ident) => {
let public_keys = $proof.verify(&mut OsRng, &mut transcript(), $generators).unwrap();
assert_eq!($generators.0.primary * $keys.0, public_keys.0);
assert_eq!($generators.1.primary * $keys.1, public_keys.1);
assert_eq!($generators.0.primary * $keys.0.deref(), public_keys.0);
assert_eq!($generators.1.primary * $keys.1.deref(), public_keys.1);
#[cfg(feature = "serialize")]
{
@@ -117,8 +120,8 @@ macro_rules! test_dleq {
let mut key;
let mut res;
while {
key = Scalar::random(&mut OsRng);
res = $type::prove_without_bias(&mut OsRng, &mut transcript(), generators, key);
key = Zeroizing::new(Scalar::random(&mut OsRng));
res = $type::prove_without_bias(&mut OsRng, &mut transcript(), generators, key.clone());
res.is_none()
} {}
let res = res.unwrap();
@@ -156,8 +159,13 @@ fn test_rejection_sampling() {
assert!(
// Either would work
EfficientLinearDLEq::prove_without_bias(&mut OsRng, &mut transcript(), generators(), pow_2)
.is_none()
EfficientLinearDLEq::prove_without_bias(
&mut OsRng,
&mut transcript(),
generators(),
Zeroizing::new(pow_2)
)
.is_none()
);
}
@@ -167,13 +175,18 @@ fn test_remainder() {
assert_eq!(Scalar::CAPACITY, 255);
let generators = (generators().0, generators().0);
// This will ignore any unused bits, ensuring every remaining one is set
let keys = mutual_scalar_from_bytes(&[0xFF; 32]);
assert_eq!(keys.0 + Scalar::one(), Scalar::from(2u64).pow_vartime(&[255]));
let keys = mutual_scalar_from_bytes::<Scalar, Scalar>(&[0xFF; 32]);
let keys = (Zeroizing::new(keys.0), Zeroizing::new(keys.1));
assert_eq!(Scalar::one() + keys.0.deref(), Scalar::from(2u64).pow_vartime(&[255]));
assert_eq!(keys.0, keys.1);
let (proof, res) =
ConciseLinearDLEq::prove_without_bias(&mut OsRng, &mut transcript(), generators, keys.0)
.unwrap();
let (proof, res) = ConciseLinearDLEq::prove_without_bias(
&mut OsRng,
&mut transcript(),
generators,
keys.0.clone(),
)
.unwrap();
assert_eq!(keys, res);
verify_and_deserialize!(

View File

@@ -1,3 +1,5 @@
use core::ops::Deref;
use rand_core::OsRng;
use zeroize::Zeroize;
@@ -20,12 +22,12 @@ where
let mut batch = BatchVerifier::new(10);
for _ in 0 .. 10 {
let private = G::Scalar::random(&mut OsRng);
SchnorrPoK::prove(&mut OsRng, &mut transcript.clone(), G::generator(), private).verify(
let private = Zeroizing::new(G::Scalar::random(&mut OsRng));
SchnorrPoK::prove(&mut OsRng, &mut transcript.clone(), G::generator(), &private).verify(
&mut OsRng,
&mut transcript.clone(),
G::generator(),
G::generator() * private,
G::generator() * private.deref(),
&mut batch,
);
}

View File

@@ -1,9 +1,11 @@
#[cfg(feature = "experimental")]
mod cross_group;
use core::ops::Deref;
use hex_literal::hex;
use rand_core::OsRng;
use zeroize::Zeroizing;
use ff::Field;
use group::GroupEncoding;
@@ -13,6 +15,9 @@ use transcript::{Transcript, RecommendedTranscript};
use crate::DLEqProof;
#[cfg(feature = "experimental")]
mod cross_group;
#[test]
fn test_dleq() {
let transcript = || RecommendedTranscript::new(b"DLEq Proof Test");
@@ -39,12 +44,12 @@ fn test_dleq() {
];
for i in 0 .. 5 {
let key = Scalar::random(&mut OsRng);
let proof = DLEqProof::prove(&mut OsRng, &mut transcript(), &generators[.. i], key);
let key = Zeroizing::new(Scalar::random(&mut OsRng));
let proof = DLEqProof::prove(&mut OsRng, &mut transcript(), &generators[.. i], &key);
let mut keys = [ProjectivePoint::GENERATOR; 5];
for k in 0 .. 5 {
keys[k] = generators[k] * key;
keys[k] = generators[k] * key.deref();
}
proof.verify(&mut transcript(), &generators[.. i], &keys[.. i]).unwrap();