mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
Fully document crypto/
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
# Discrete Log Equality
|
||||
|
||||
Implementation of discrete log equality proofs for curves implementing
|
||||
`ff`/`group`. There is also a highly experimental cross-group DLEq proof, under
|
||||
`ff`/`group`.
|
||||
|
||||
There is also a highly experimental cross-group DLEq proof, under
|
||||
the `experimental` feature, which has no formal proofs available yet is
|
||||
available here regardless.
|
||||
|
||||
This library, except for the `experimental` feature, was
|
||||
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/74924095e1a0f266b58181b539d9e74fa35dc37a/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf),
|
||||
culminating in commit 669d2dbffc1dafb82a09d9419ea182667115df06. Any subsequent
|
||||
changes have not undergone auditing.
|
||||
[audited by Cypher Stack in March 2023](https://github.com/serai-dex/serai/raw/e1bb2c191b7123fd260d008e31656d090d559d21/audits/Cypher%20Stack%20crypto%20March%202023/Audit.pdf),
|
||||
culminating in commit
|
||||
[669d2dbffc1dafb82a09d9419ea182667115df06](https://github.com/serai-dex/serai/tree/669d2dbffc1dafb82a09d9419ea182667115df06).
|
||||
Any subsequent changes have not undergone auditing.
|
||||
|
||||
### Cross-Group DLEq
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ use group::{
|
||||
};
|
||||
use multiexp::BatchVerifier;
|
||||
|
||||
/// Scalar utilities.
|
||||
pub mod scalar;
|
||||
use scalar::{scalar_convert, mutual_scalar_from_bytes};
|
||||
|
||||
@@ -63,15 +64,25 @@ pub(crate) fn read_point<R: Read, G: PrimeGroup>(r: &mut R) -> std::io::Result<G
|
||||
Ok(point.unwrap())
|
||||
}
|
||||
|
||||
/// A pair of generators, one committing to values (primary), one blinding (alt), for an elliptic
|
||||
/// curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Generators<G: PrimeGroup> {
|
||||
/// The generator used to commit to values.
|
||||
///
|
||||
/// This should likely be the curve's traditional 'basepoint'.
|
||||
pub primary: G,
|
||||
/// The generator used to blind values. This must be distinct from the primary generator.
|
||||
pub alt: G,
|
||||
}
|
||||
|
||||
impl<G: PrimeGroup> Generators<G> {
|
||||
pub fn new(primary: G, alt: G) -> Generators<G> {
|
||||
Generators { primary, alt }
|
||||
/// Create a new set of generators.
|
||||
pub fn new(primary: G, alt: G) -> Option<Generators<G>> {
|
||||
if primary == alt {
|
||||
None?;
|
||||
}
|
||||
Some(Generators { primary, alt })
|
||||
}
|
||||
|
||||
fn transcript<T: Transcript>(&self, transcript: &mut T) {
|
||||
@@ -81,14 +92,19 @@ impl<G: PrimeGroup> Generators<G> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error for cross-group DLEq proofs.
|
||||
#[derive(Error, PartialEq, Eq, Debug)]
|
||||
pub enum DLEqError {
|
||||
/// Invalid proof of knowledge.
|
||||
#[error("invalid proof of knowledge")]
|
||||
InvalidProofOfKnowledge,
|
||||
/// Invalid proof length.
|
||||
#[error("invalid proof length")]
|
||||
InvalidProofLength,
|
||||
/// Invalid challenge.
|
||||
#[error("invalid challenge")]
|
||||
InvalidChallenge,
|
||||
/// Invalid proof.
|
||||
#[error("invalid proof")]
|
||||
InvalidProof,
|
||||
}
|
||||
@@ -115,7 +131,8 @@ pub struct __DLEqProof<
|
||||
}
|
||||
|
||||
macro_rules! dleq {
|
||||
($name: ident, $signature: expr, $remainder: literal) => {
|
||||
($doc_str: expr, $name: ident, $signature: expr, $remainder: literal,) => {
|
||||
#[doc = $doc_str]
|
||||
pub type $name<G0, G1> = __DLEqProof<
|
||||
G0,
|
||||
G1,
|
||||
@@ -141,21 +158,50 @@ macro_rules! dleq {
|
||||
// over both scalar fields, hence its application here as well. This is mainly here as a point of
|
||||
// reference for the following DLEq proofs, all which use merged challenges, and isn't performant
|
||||
// in comparison to the others
|
||||
dleq!(ClassicLinearDLEq, BitSignature::ClassicLinear, false);
|
||||
dleq!(
|
||||
"The DLEq proof described in MRL-0010.",
|
||||
ClassicLinearDLEq,
|
||||
BitSignature::ClassicLinear,
|
||||
false,
|
||||
);
|
||||
|
||||
// Proves for 2-bits at a time to save 3/7 elements of every other bit
|
||||
// <9% smaller than CompromiseLinear, yet ~12% slower
|
||||
dleq!(ConciseLinearDLEq, BitSignature::ConciseLinear, true);
|
||||
dleq!(
|
||||
"A DLEq proof modified from MRL-0010, proving for two bits at a time to save on space.",
|
||||
ConciseLinearDLEq,
|
||||
BitSignature::ConciseLinear,
|
||||
true,
|
||||
);
|
||||
|
||||
// Uses AOS signatures of the form R, s, to enable the final step of the ring signature to be
|
||||
// batch verified, at the cost of adding an additional element per bit
|
||||
dleq!(EfficientLinearDLEq, BitSignature::EfficientLinear, false);
|
||||
dleq!(
|
||||
"
|
||||
A DLEq proof modified from MRL-0010, using R, s forms instead of c, s forms to enable batch
|
||||
verification at the cost of space usage.
|
||||
",
|
||||
EfficientLinearDLEq,
|
||||
BitSignature::EfficientLinear,
|
||||
false,
|
||||
);
|
||||
|
||||
// Proves for 2-bits at a time while using the R, s form. This saves 3/7 elements of every other
|
||||
// bit, while adding 1 element to every bit, and is more efficient than ConciseLinear yet less
|
||||
// efficient than EfficientLinear due to having more ring signature steps which aren't batched
|
||||
// >25% smaller than EfficientLinear and just 11% slower, making it the recommended option
|
||||
dleq!(CompromiseLinearDLEq, BitSignature::CompromiseLinear, true);
|
||||
dleq!(
|
||||
"
|
||||
A DLEq proof modified from MRL-0010, using R, s forms instead of c, s forms, while proving for
|
||||
two bits at a time, to enable batch verification and take advantage of space savings.
|
||||
|
||||
This isn't quite as efficient as EfficientLinearDLEq, and isn't as compact as
|
||||
ConciseLinearDLEq, yet strikes a strong balance of performance and conciseness.
|
||||
",
|
||||
CompromiseLinearDLEq,
|
||||
BitSignature::CompromiseLinear,
|
||||
true,
|
||||
);
|
||||
|
||||
impl<
|
||||
G0: PrimeGroup + Zeroize,
|
||||
@@ -297,10 +343,13 @@ where
|
||||
(proof, f)
|
||||
}
|
||||
|
||||
/// Prove the cross-Group Discrete Log Equality for the points derived from the scalar created as
|
||||
/// the output of the passed in Digest. Given the non-standard requirements to achieve
|
||||
/// uniformity, needing to be < 2^x instead of less than a prime moduli, this is the simplest way
|
||||
/// to safely and securely generate a Scalar, without risk of failure, nor bias.
|
||||
/// Prove the Cross-Group Discrete Log Equality for the points derived from the scalar created as
|
||||
/// the output of the passed in Digest.
|
||||
///
|
||||
/// Given the non-standard requirements to achieve uniformity, needing to be < 2^x instead of
|
||||
/// less than a prime moduli, this is the simplest way to safely and securely generate a Scalar,
|
||||
/// without risk of failure nor bias.
|
||||
///
|
||||
/// It also ensures a lack of determinable relation between keys, guaranteeing security in the
|
||||
/// currently expected use case for this, atomic swaps, where each swap leaks the key. Knowing
|
||||
/// the relationship between keys would allow breaking all swaps after just one.
|
||||
@@ -323,9 +372,11 @@ where
|
||||
Self::prove_internal(rng, transcript, generators, f)
|
||||
}
|
||||
|
||||
/// Prove the cross-Group Discrete Log Equality for the points derived from the scalar passed in,
|
||||
/// failing if it's not mutually valid. This allows for rejection sampling externally derived
|
||||
/// scalars until they're safely usable, as needed.
|
||||
/// Prove the Cross-Group Discrete Log Equality for the points derived from the scalar passed in,
|
||||
/// failing if it's not mutually valid.
|
||||
///
|
||||
/// This allows for rejection sampling externally derived scalars until they're safely usable,
|
||||
/// as needed.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn prove_without_bias<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
rng: &mut R,
|
||||
@@ -337,7 +388,7 @@ where
|
||||
.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.
|
||||
/// Verify a Cross-Group Discrete Log Equality proof, returning the points proven for.
|
||||
pub fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
@@ -386,6 +437,7 @@ where
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
/// Write a Cross-Group Discrete Log Equality proof to a type satisfying std::io::Write.
|
||||
#[cfg(feature = "serialize")]
|
||||
pub fn write<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
for bit in &self.bits {
|
||||
@@ -398,6 +450,7 @@ where
|
||||
self.poks.1.write(w)
|
||||
}
|
||||
|
||||
/// Read a Cross-Group Discrete Log Equality proof from a type satisfying std::io::Read.
|
||||
#[cfg(feature = "serialize")]
|
||||
pub fn read<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||
let capacity = usize::try_from(G0::Scalar::CAPACITY.min(G1::Scalar::CAPACITY)).unwrap();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use core::ops::Deref;
|
||||
|
||||
@@ -15,6 +16,8 @@ use group::prime::PrimeGroup;
|
||||
#[cfg(feature = "serialize")]
|
||||
use std::io::{self, ErrorKind, Error, Read, Write};
|
||||
|
||||
/// 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;
|
||||
|
||||
@@ -96,6 +99,7 @@ fn read_scalar<R: Read, F: PrimeField>(r: &mut R) -> io::Result<F> {
|
||||
/// Error for DLEq proofs.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum DLEqError {
|
||||
/// The proof was invalid.
|
||||
InvalidProof,
|
||||
}
|
||||
|
||||
@@ -201,6 +205,7 @@ impl<G: PrimeGroup> DLEqProof<G> {
|
||||
}
|
||||
|
||||
/// A proof that multiple series of points each have a single discrete logarithm across generators.
|
||||
///
|
||||
/// 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")]
|
||||
|
||||
@@ -40,14 +40,16 @@ pub(crate) fn generators() -> (Generators<G0>, Generators<G1>) {
|
||||
&(hex!("0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0").into()),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
Generators::new(
|
||||
EdwardsPoint::generator(),
|
||||
EdwardsPoint::from_bytes(&hex!(
|
||||
"8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"
|
||||
))
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user