mod sigma;
use curve25519_dalek::constants;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use hmac::{Hmac, Mac};
use rand_core::{CryptoRng, RngCore};
use sha2::Sha512;
use sigma::OrDLogProver;
const CHALLENGE_CONTEXT: &[u8] = b"deevee challenge context 2022-06-17";
fn challenge(
big_x0: &CompressedRistretto,
big_x1: &CompressedRistretto,
dh_result: &CompressedRistretto,
big_k0: &CompressedRistretto,
big_k1: &CompressedRistretto,
m: &[u8],
) -> Scalar {
let mut hmac = Hmac::<Sha512>::new_from_slice(dh_result.as_bytes()).unwrap();
hmac.update(CHALLENGE_CONTEXT);
hmac.update(big_x0.as_bytes());
hmac.update(big_x1.as_bytes());
hmac.update(big_k0.as_bytes());
hmac.update(big_k1.as_bytes());
hmac.update(m);
Scalar::from_bytes_mod_order_wide(&hmac.finalize().into_bytes().into())
}
struct RawSignature {
e: Scalar,
e0: Scalar,
s0: Scalar,
s1: Scalar,
}
fn raw_sign<R: RngCore + CryptoRng>(
rng: &mut R,
x0: &Scalar,
big_x1: &RistrettoPoint,
m: &[u8],
) -> RawSignature {
let big_x0 = x0 * &constants::RISTRETTO_BASEPOINT_TABLE;
let prover = OrDLogProver::create(rng, x0, big_x1);
let (big_k0, big_k1) = prover.commit();
let dh_result = x0 * big_x1;
let e = challenge(
&big_x0.compress(),
&big_x1.compress(),
&dh_result.compress(),
&big_k0.compress(),
&big_k1.compress(),
m,
);
let ((e0, _), (s0, s1)) = prover.respond(&e);
RawSignature { e, e0, s0, s1 }
}
fn raw_forge<R: RngCore + CryptoRng>(
rng: &mut R,
x1: &Scalar,
big_x0: &RistrettoPoint,
m: &[u8],
) -> RawSignature {
let big_x1 = x1 * &constants::RISTRETTO_BASEPOINT_TABLE;
let prover = OrDLogProver::create(rng, x1, big_x0);
let (big_k1, big_k0) = prover.commit();
let dh_result = x1 * big_x0;
let e = challenge(
&big_x0.compress(),
&big_x1.compress(),
&dh_result.compress(),
&big_k0.compress(),
&big_k1.compress(),
m,
);
let ((_, e0), (s1, s0)) = prover.respond(&e);
RawSignature { e, e0, s0, s1 }
}
fn raw_verify(big_x0: &RistrettoPoint, x1: &Scalar, sig: &RawSignature, m: &[u8]) -> bool {
let big_x1 = x1 * &constants::RISTRETTO_BASEPOINT_TABLE;
let dh_result = x1 * big_x0;
let e1 = sig.e - sig.e0;
let (big_k0, big_k1) =
OrDLogProver::recompute((big_x0, &big_x1), (&sig.e0, &e1), (&sig.s0, &sig.s1));
let e = challenge(
&big_x0.compress(),
&big_x1.compress(),
&dh_result.compress(),
&big_k0.compress(),
&big_k1.compress(),
m,
);
e == sig.e
}
const SIGNATURE_LENGTH: usize = 128;
#[derive(Clone, Copy, Debug)]
pub struct Signature {
data: [u8; SIGNATURE_LENGTH],
}
impl Signature {
fn from_raw(raw: &RawSignature) -> Self {
let mut data = [0u8; SIGNATURE_LENGTH];
data[0..32].copy_from_slice(raw.e.as_bytes());
data[32..64].copy_from_slice(raw.e0.as_bytes());
data[64..96].copy_from_slice(raw.s0.as_bytes());
data[96..128].copy_from_slice(raw.s1.as_bytes());
Self::new(data)
}
fn as_raw(&self) -> Option<RawSignature> {
let e = Scalar::from_canonical_bytes(self.data[0..32].try_into().unwrap())?;
let e0 = Scalar::from_canonical_bytes(self.data[32..64].try_into().unwrap())?;
let s0 = Scalar::from_canonical_bytes(self.data[64..96].try_into().unwrap())?;
let s1 = Scalar::from_canonical_bytes(self.data[96..128].try_into().unwrap())?;
Some(RawSignature { e, e0, s0, s1 })
}
pub fn new(data: [u8; SIGNATURE_LENGTH]) -> Self {
Self { data }
}
pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
self.data
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PublicKey(RistrettoPoint);
impl PublicKey {
pub fn from_bytes(data: [u8; 32]) -> Option<Self> {
Some(Self(CompressedRistretto(data).decompress()?))
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0.compress().0
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct PrivateKey(Scalar);
impl PrivateKey {
pub fn from_bytes(data: [u8; 32]) -> Option<Self> {
let scalar = Scalar::from_canonical_bytes(data)?;
Some(Self(scalar))
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
pub fn sign<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
designee: &PublicKey,
message: &[u8],
) -> Signature {
let raw = raw_sign(rng, &self.0, &designee.0, message);
Signature::from_raw(&raw)
}
pub fn forge<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
signer: &PublicKey,
message: &[u8],
) -> Signature {
let raw = raw_forge(rng, &self.0, &signer.0, message);
Signature::from_raw(&raw)
}
pub fn verify(&self, signer: &PublicKey, message: &[u8], signature: &Signature) -> bool {
let raw_sig = match signature.as_raw() {
None => return false,
Some(s) => s,
};
raw_verify(&signer.0, &self.0, &raw_sig, message)
}
}
pub fn generate_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (PrivateKey, PublicKey) {
let scalar = Scalar::random(rng);
(
PrivateKey(scalar),
PublicKey(&scalar * &constants::RISTRETTO_BASEPOINT_TABLE),
)
}