deevee/
lib.rs

1//! This crate provides an implementation of designated verifier signatures.
2//! This is like a normal signature scheme, except that the signer also
3//! designates a verifier for their signature.
4//!
5//! Only this verifier can validate the signature. Furthermore, this verifier
6//! can't convince anyone else of the validity of the signature, because they
7//! can forge signatures which designate them as the verifier.
8//!
9//! Here's an example which illustrates all of this functionality:
10//!
11//! ```rust
12//! use deevee::*;
13//! use rand_core::OsRng;
14//!
15//! let (privA, pubA) = generate_keypair(&mut OsRng);
16//! let (privB, pubB) = generate_keypair(&mut OsRng);
17//! let (privC, pubC) = generate_keypair(&mut OsRng);
18//!
19//! let sig = privA.sign(&mut OsRng, &pubB, b"I like cats");
20//! // The signature verifies, because the designee matches
21//! assert!(privB.verify(&pubA, b"I like cats", &sig));
22//! // If we change the message, verification fails
23//! assert!(!privB.verify(&pubA, b"I don't like cats", &sig));
24//! // The signer won't verify with a different signer either
25//! assert!(!privB.verify(&pubC, b"I like cats", &sig));
26//! // The wrong verifier can't validate the signature either
27//! assert!(!privC.verify(&pubA, b"I like cats", &sig));
28//! // Finally, the verifier can forge a valid signature for themselves
29//! let forged = privB.forge(&mut OsRng, &pubA, b"I don't like cats");
30//! assert!(privB.verify(&pubA, b"I don't like cats", &forged));
31//! ```
32mod sigma;
33
34use curve25519_dalek::constants;
35use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
36use curve25519_dalek::scalar::Scalar;
37use hmac::{Hmac, Mac};
38use rand_core::{CryptoRng, RngCore};
39use sha2::Sha512;
40use sigma::OrDLogProver;
41
42const CHALLENGE_CONTEXT: &[u8] = b"deevee challenge context 2022-06-17";
43
44/// Generate the challenge for the signature.
45///
46/// We need the two public points, their secret Diffie-Hellman exchange, and
47/// the two nonce commitments.
48fn challenge(
49    big_x0: &CompressedRistretto,
50    big_x1: &CompressedRistretto,
51    dh_result: &CompressedRistretto,
52    big_k0: &CompressedRistretto,
53    big_k1: &CompressedRistretto,
54    m: &[u8],
55) -> Scalar {
56    // The idea is that we hash all of this into a scalar. To avoid length
57    // extension attacks we hash the dh_result separately, and then add the two
58    // scalars together to get our challenge.
59
60    let mut hmac = Hmac::<Sha512>::new_from_slice(dh_result.as_bytes()).unwrap();
61
62    hmac.update(CHALLENGE_CONTEXT);
63    hmac.update(big_x0.as_bytes());
64    hmac.update(big_x1.as_bytes());
65    hmac.update(big_k0.as_bytes());
66    hmac.update(big_k1.as_bytes());
67    hmac.update(m);
68
69    Scalar::from_bytes_mod_order_wide(&hmac.finalize().into_bytes().into())
70}
71
72struct RawSignature {
73    e: Scalar,
74    e0: Scalar,
75    s0: Scalar,
76    s1: Scalar,
77}
78
79fn raw_sign<R: RngCore + CryptoRng>(
80    rng: &mut R,
81    x0: &Scalar,
82    big_x1: &RistrettoPoint,
83    m: &[u8],
84) -> RawSignature {
85    let big_x0 = x0 * &constants::RISTRETTO_BASEPOINT_TABLE;
86
87    let prover = OrDLogProver::create(rng, x0, big_x1);
88    let (big_k0, big_k1) = prover.commit();
89
90    let dh_result = x0 * big_x1;
91
92    let e = challenge(
93        &big_x0.compress(),
94        &big_x1.compress(),
95        &dh_result.compress(),
96        &big_k0.compress(),
97        &big_k1.compress(),
98        m,
99    );
100
101    let ((e0, _), (s0, s1)) = prover.respond(&e);
102
103    RawSignature { e, e0, s0, s1 }
104}
105
106fn raw_forge<R: RngCore + CryptoRng>(
107    rng: &mut R,
108    x1: &Scalar,
109    big_x0: &RistrettoPoint,
110    m: &[u8],
111) -> RawSignature {
112    let big_x1 = x1 * &constants::RISTRETTO_BASEPOINT_TABLE;
113
114    let prover = OrDLogProver::create(rng, x1, big_x0);
115    let (big_k1, big_k0) = prover.commit();
116
117    let dh_result = x1 * big_x0;
118
119    let e = challenge(
120        &big_x0.compress(),
121        &big_x1.compress(),
122        &dh_result.compress(),
123        &big_k0.compress(),
124        &big_k1.compress(),
125        m,
126    );
127
128    let ((_, e0), (s1, s0)) = prover.respond(&e);
129
130    RawSignature { e, e0, s0, s1 }
131}
132
133fn raw_verify(big_x0: &RistrettoPoint, x1: &Scalar, sig: &RawSignature, m: &[u8]) -> bool {
134    let big_x1 = x1 * &constants::RISTRETTO_BASEPOINT_TABLE;
135    let dh_result = x1 * big_x0;
136
137    let e1 = sig.e - sig.e0;
138    let (big_k0, big_k1) =
139        OrDLogProver::recompute((big_x0, &big_x1), (&sig.e0, &e1), (&sig.s0, &sig.s1));
140    let e = challenge(
141        &big_x0.compress(),
142        &big_x1.compress(),
143        &dh_result.compress(),
144        &big_k0.compress(),
145        &big_k1.compress(),
146        m,
147    );
148    e == sig.e
149}
150
151/// The length of signatures, in bytes.
152const SIGNATURE_LENGTH: usize = 128;
153
154/// Represents a signature designated for a specific verifier.
155///
156/// Only that verifier can check that this signature is valid, and that verifier
157/// can in fact forge valid signatures which designate them as the verifier.
158#[derive(Clone, Copy, Debug)]
159pub struct Signature {
160    data: [u8; SIGNATURE_LENGTH],
161}
162
163impl Signature {
164    fn from_raw(raw: &RawSignature) -> Self {
165        let mut data = [0u8; SIGNATURE_LENGTH];
166        data[0..32].copy_from_slice(raw.e.as_bytes());
167        data[32..64].copy_from_slice(raw.e0.as_bytes());
168        data[64..96].copy_from_slice(raw.s0.as_bytes());
169        data[96..128].copy_from_slice(raw.s1.as_bytes());
170
171        Self::new(data)
172    }
173
174    fn as_raw(&self) -> Option<RawSignature> {
175        let e = Scalar::from_canonical_bytes(self.data[0..32].try_into().unwrap())?;
176        let e0 = Scalar::from_canonical_bytes(self.data[32..64].try_into().unwrap())?;
177        let s0 = Scalar::from_canonical_bytes(self.data[64..96].try_into().unwrap())?;
178        let s1 = Scalar::from_canonical_bytes(self.data[96..128].try_into().unwrap())?;
179
180        Some(RawSignature { e, e0, s0, s1 })
181    }
182
183    /// Create a Signature from raw bytes.
184    pub fn new(data: [u8; SIGNATURE_LENGTH]) -> Self {
185        Self { data }
186    }
187
188    /// Convert a signature to the raw bytes which make up that signature.
189    pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
190        self.data
191    }
192}
193
194/// PublicKey represents an identity.
195///
196/// This identity is used to represent the person designated to verify a signature,
197/// or the person that signed a message.
198#[derive(Clone, Copy, Debug, PartialEq)]
199pub struct PublicKey(RistrettoPoint);
200
201impl PublicKey {
202    /// Attempt to create a PublicKey from bytes.
203    ///
204    /// This can fail if this public key is not valid.
205    pub fn from_bytes(data: [u8; 32]) -> Option<Self> {
206        Some(Self(CompressedRistretto(data).decompress()?))
207    }
208
209    /// Marshall this PublicKey into bytes.
210    pub fn to_bytes(&self) -> [u8; 32] {
211        self.0.compress().0
212    }
213}
214
215/// Represents a private key.
216///
217/// The private key allows creating designated verifier signatures. These
218/// signatures require the private key of that verifier to be validated.
219/// Furthermore, the verifier can use their private key to forge valid signatures
220/// which designate them.
221#[derive(Clone, Copy, PartialEq)]
222pub struct PrivateKey(Scalar);
223
224impl PrivateKey {
225    /// Attempt to unmarshall bytes into a private key.
226    ///
227    /// This can fail if the bytes don't represent a valid private key.
228    pub fn from_bytes(data: [u8; 32]) -> Option<Self> {
229        let scalar = Scalar::from_canonical_bytes(data)?;
230        Some(Self(scalar))
231    }
232
233    /// Marshall this private key into bytes.
234    pub fn to_bytes(&self) -> [u8; 32] {
235        self.0.to_bytes()
236    }
237
238    /// Sign a message, designated to a specific verifier.
239    pub fn sign<R: RngCore + CryptoRng>(
240        &self,
241        rng: &mut R,
242        designee: &PublicKey,
243        message: &[u8],
244    ) -> Signature {
245        let raw = raw_sign(rng, &self.0, &designee.0, message);
246        Signature::from_raw(&raw)
247    }
248
249    /// As a verifier, forge a message from a signer, designated to yourself.
250    pub fn forge<R: RngCore + CryptoRng>(
251        &self,
252        rng: &mut R,
253        signer: &PublicKey,
254        message: &[u8],
255    ) -> Signature {
256        let raw = raw_forge(rng, &self.0, &signer.0, message);
257        Signature::from_raw(&raw)
258    }
259
260    /// Verify that a signer's signature on a message is valid.
261    ///
262    /// You must have been designated the verifier for this to work.
263    pub fn verify(&self, signer: &PublicKey, message: &[u8], signature: &Signature) -> bool {
264        let raw_sig = match signature.as_raw() {
265            None => return false,
266            Some(s) => s,
267        };
268        raw_verify(&signer.0, &self.0, &raw_sig, message)
269    }
270}
271
272/// Generate a new private key, along with its associated public key.
273pub fn generate_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (PrivateKey, PublicKey) {
274    let scalar = Scalar::random(rng);
275    (
276        PrivateKey(scalar),
277        PublicKey(&scalar * &constants::RISTRETTO_BASEPOINT_TABLE),
278    )
279}