Skip to main content

pubhubs/common/
elgamal.rs

1//! The ElGamal cryptosystem, as used in PEP
2
3use curve25519_dalek::{
4    constants::RISTRETTO_BASEPOINT_TABLE as B,
5    ristretto::{CompressedRistretto, RistrettoPoint},
6    scalar::Scalar,
7};
8
9/// ElGamal ciphertext - the result of [`PublicKey::encrypt`].
10///
11/// The associated public key is remembered to allow rerandomization, but this public key is
12/// not authenticated in any way.  This means that anyone intercepting a triple may
13/// modify the public key without detection (but this does not cause the
14/// triple to be decryptable to the same plaintext by another public key.)
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Triple {
17    /// Ephemeral key
18    ek: RistrettoPoint,
19
20    /// Ciphertext,
21    ct: RistrettoPoint,
22
23    /// Public key
24    pk: RistrettoPoint,
25}
26
27impl Triple {
28    /// Decrypts the triple using the given private key `sk`.  If the triple was encrypted
29    /// for a different private key, the result is a random point.
30    pub fn decrypt(self, sk: &PrivateKey) -> RistrettoPoint {
31        self.ct - sk.scalar * self.ek
32    }
33
34    /// Decrypts the triple using the given private key `sk` if the triple claims to be encrypted
35    /// for the associated public key;  returns `None` otherwise.
36    ///
37    /// **Warning** This function can't check whether the triple's public key `pk` has been
38    /// tampered with.  
39    ///
40    /// While tampering cannot be prevented, the plaintext of a triple with spoofed `pk` can be
41    /// garbled, using [Self::rerandomize].
42    ///
43    pub fn decrypt_and_check_pk(self, sk: &PrivateKey) -> Option<RistrettoPoint> {
44        if self.pk == B * &sk.scalar {
45            Some(self.decrypt(sk))
46        } else {
47            None
48        }
49    }
50
51    /// Changes the public key of this triple, likely resulting in garbage down the road.
52    ///
53    /// Used for demonstration purposes.
54    pub fn spoof_pk(self, pk: PublicKey) -> Triple {
55        Triple {
56            ek: self.ek,
57            ct: self.ct,
58            pk: pk.point,
59        }
60    }
61
62    /// Changes the appearance of the ciphertext, but leaves the plaintext and the target
63    /// public key unaltered.  If the public key was spoofed, the plaintext is garbled.
64    /// ```
65    /// use pubhubs::common::elgamal::{PrivateKey, random_point, random_scalar};
66    /// use curve25519_dalek::{
67    ///     ristretto::RistrettoPoint,
68    ///     constants::RISTRETTO_BASEPOINT_TABLE as B,
69    /// };
70    ///
71    /// let M = random_point();
72    /// let sk = PrivateKey::random();
73    /// let pk = sk.public_key();
74    ///
75    /// let r1 = random_scalar();
76    /// let r2 = random_scalar();
77    ///
78    /// // Rerandomization leaves the plaintext unchanged:
79    /// let trip = pk.encrypt_with_random(r1, M).rerandomize_with_random(r2);
80    /// assert_eq!(trip, pk.encrypt_with_random(r1+r2,M));
81    ///
82    /// // But if the public key was spoofed, the plaintext is garbled:
83    /// let sk2 = PrivateKey::random();
84    /// let pk2 = sk2.public_key().clone();
85    /// let trip = pk.encrypt_with_random(r1, M).spoof_pk(pk2).rerandomize_with_random(r2);
86    ///
87    /// assert_eq!(trip.clone().decrypt_and_check_pk(&sk2),
88    ///     Some(M + B * &(r1 * (sk.as_scalar()-sk2.as_scalar()))));
89    ///
90    /// // Indeed, if sk =/= sk2, then  r1(sk - sk2)B will be some random unknowable Ristretto
91    /// // point, because r1 should be a random scalar that has been thrown away.
92    /// ```
93    pub fn rerandomize(self) -> Triple {
94        self.rerandomize_with_random(random_scalar())
95    }
96
97    /// Like [Self::rerandomize], but you can specify the random scalar used -
98    /// which you shouldn't except to make deterministic tests.
99    pub fn rerandomize_with_random(self, r: Scalar) -> Triple {
100        Triple {
101            ek: self.ek + &r * B,
102            ct: self.ct + r * self.pk,
103            pk: self.pk,
104        }
105    }
106
107    /// Like [rsk] but taking the parameters `s` and `k` thusly: `rsk_with_s(s).and_k(k)`.
108    pub fn rsk_with_s(self, s: &Scalar) -> rsk::WithS<'_> {
109        rsk::WithS { t: self, s }
110    }
111
112    /// Changes the given ciphertext according to the `params` provided:
113    ///
114    ///  - Multiplies the underlying plaintext by `params.s()`;
115    ///  - Multiplies the target public/private key by `params.k()`;
116    ///  - Rerandomizes the ciphertext using the scalar `params.r()`.
117    ///    
118    ///    If the public key `self.pk` was spoofed, the resulting plaintext is garbled,
119    ///    provided the scalar `params.r()` was random.
120    ///
121    /// If you only need to specify `s` and `k`, use `triple.rsk_with_s(s).and_k(k)` instead.
122    pub fn rsk(self, params: impl rsk::Params) -> Triple {
123        let r: Scalar = params.r();
124        let kpk = self.pk * params.k();
125
126        Triple {
127            ek: params.s_over_k() * self.ek + &r * B,
128            ct: params.s() * self.ct + r * kpk,
129            pk: kpk,
130        }
131    }
132}
133
134/// Utilities for [Triple::rsk]
135pub mod rsk {
136    use super::*;
137
138    /// Implementation of the [Params] trait given the parameters `s` and `k`.
139    pub struct SAndK<'s, 'k> {
140        s: &'s Scalar,
141        k: &'k Scalar,
142    }
143
144    impl Params for SAndK<'_, '_> {
145        fn s(&self) -> &Scalar {
146            self.s
147        }
148
149        fn k(&self) -> &Scalar {
150            self.k
151        }
152    }
153
154    /// The result of [Triple::rsk_with_s]. You should call [WithS::and_k] on it.
155    pub struct WithS<'a> {
156        pub(crate) t: Triple,
157        pub(crate) s: &'a Scalar,
158    }
159
160    impl WithS<'_> {
161        pub fn and_k(self, k: &Scalar) -> Triple {
162            self.t.rsk(SAndK { s: self.s, k })
163        }
164    }
165
166    /// Utilities for the [Triple::rsk] operation.
167    pub trait Params {
168        /// Multiply the encrypted plaintext ristretto point by this scalar.
169        fn s(&self) -> &Scalar;
170
171        /// Multiply the target public/private key by this scalar.
172        fn k(&self) -> &Scalar;
173
174        /// Returns `1/k`.
175        fn k_inv(&self) -> Scalar {
176            self.k().invert()
177        }
178
179        /// Returns `s/k`.
180        fn s_over_k(&self) -> Scalar {
181            self.s() * self.k_inv()
182        }
183
184        /// Returns the scalar used for rerandomisation.
185        ///
186        /// **Warning:** only override this method for the purpose of making deterministic test.
187        fn r(&self) -> Scalar {
188            random_scalar()
189        }
190    }
191}
192
193/// Returns a random Ristretto point, mainly for examples.
194///
195/// If you're immediately encrypting this point, consider
196/// using [PublicKey::encrypt_random] instead.
197pub fn random_point() -> RistrettoPoint {
198    // NOTE: what `RistrettoPoint::random` does, but without needing a rand_core v0.6 RNG.
199    RistrettoPoint::from_uniform_bytes(&crate::misc::crypto::random_64_bytes())
200}
201
202/// Returns a random scalar, mainly for examples.
203pub fn random_scalar() -> Scalar {
204    // NOTE: what `Scalar::random` does, but without needing a rand_core v0.6 RNG.
205    Scalar::from_bytes_mod_order_wide(&crate::misc::crypto::random_64_bytes())
206}
207
208/// Private key - load using [`PrivateKey::from_hex`] or generate with [`PrivateKey::random`].
209///
210/// Caches the associated [`PublicKey`], which means that loading a [`PrivateKey`] involves a base
211/// point multiplication.
212#[derive(Clone, PartialEq, Eq, Debug, zeroize::ZeroizeOnDrop)]
213pub struct PrivateKey {
214    /// underlying scalar
215    scalar: Scalar,
216
217    /// associated public key, stored for efficiency
218    #[zeroize(skip)]
219    public_key: PublicKey,
220}
221
222impl PrivateKey {
223    /// Returns reference to underlying scalar.
224    pub fn as_scalar(&self) -> &Scalar {
225        &self.scalar
226    }
227
228    pub fn random() -> Self {
229        random_scalar().into()
230    }
231
232    pub fn public_key(&self) -> &PublicKey {
233        &self.public_key
234    }
235
236    /// Computes the [PublicKey] associated with the product of two [PrivateKey]s given only one
237    /// private key.
238    pub fn scale(&self, pk: &PublicKey) -> PublicKey {
239        (self.scalar * pk.point).into()
240    }
241
242    /// Creates a Diffie-Hellman-type shared secret between this [`PrivateKey`] and the [`PublicKey`].
243    pub fn shared_secret(&self, pk: &PublicKey) -> SharedSecret {
244        SharedSecret {
245            inner: self.scale(pk).to_bytes(),
246        }
247    }
248}
249
250impl From<Scalar> for PrivateKey {
251    fn from(scalar: Scalar) -> Self {
252        PrivateKey {
253            scalar,
254            public_key: (&scalar * B).into(),
255        }
256    }
257}
258
259/// Public key - obtained using [`PublicKey::from_hex`] or [`PrivateKey::public_key`].
260#[derive(Clone, PartialEq, Eq, Debug)]
261pub struct PublicKey {
262    point: RistrettoPoint,
263    compressed: CompressedRistretto,
264}
265
266impl AsRef<[u8]> for PublicKey {
267    /// Returns a reference to the compressed encoding of this public key
268    fn as_ref(&self) -> &[u8] {
269        self.compressed.as_bytes().as_slice()
270    }
271}
272
273impl PublicKey {
274    /// Turns a 64 digit hex string into a [`PublicKey`].
275    ///
276    /// Returns `None` when the hex-encoding is invalid or when the hex-encoding does not encode a
277    /// valid Ristretto point.
278    pub fn from_hex(hexstr: &str) -> Option<Self> {
279        CompressedRistretto::from_hex(hexstr)?.try_into().ok()
280    }
281
282    /// The identity element, which encodes as 32 zero bytes.
283    pub fn zero() -> Self {
284        use curve25519_dalek::traits::Identity as _;
285        RistrettoPoint::identity().into()
286    }
287
288    /// Encrypts the given `plaintext` for this public key.
289    /// If the plaintext is a random point, consider using [Self::encrypt_random].
290    pub fn encrypt(&self, plaintext: RistrettoPoint) -> Triple {
291        self.encrypt_with_random(random_scalar(), plaintext)
292    }
293
294    /// Like [`Self::encrypt`], but you can specify the random scalar used - which you shouldn't
295    /// except to make deterministic tests.
296    pub fn encrypt_with_random(&self, r: Scalar, plaintext: RistrettoPoint) -> Triple {
297        Triple {
298            ek: &r * B,
299            ct: plaintext + r * self.point,
300            pk: self.point,
301        }
302    }
303
304    /// Effectively encrypts a random plaintext for this public key.
305    ///
306    /// Instead of picking random Ristretto point M and random scalar r and computing
307    ///   `(rB, r * pk + M, self)`
308    /// we pick Ristretto points ek and ct randomly and return
309    ///   `(ek, ct, sekf)`.
310    /// since this is more efficient, and yields the same distribution.
311    pub fn encrypt_random(&self) -> Triple {
312        Triple {
313            ek: random_point(),
314            ct: random_point(),
315            pk: self.point,
316        }
317    }
318}
319
320impl From<RistrettoPoint> for PublicKey {
321    fn from(point: RistrettoPoint) -> Self {
322        Self {
323            point,
324            compressed: point.compress(),
325        }
326    }
327}
328
329impl TryFrom<CompressedRistretto> for PublicKey {
330    type Error = ();
331
332    fn try_from(compressed: CompressedRistretto) -> Result<Self, Self::Error> {
333        Ok(Self {
334            point: compressed.decompress().ok_or(())?,
335            compressed,
336        })
337    }
338}
339
340/// Adds encoding and decoding methods to [`PrivateKey`], [`PublicKey`], [`Triple`], [`Scalar`]
341/// and [`RistrettoPoint`] which can all be represented as `[u8; N]`s for some `N`.  
342///
343/// Not all arrays of the form `[u8; N]` may be a valid representation of the type of object in question, though.
344pub trait Encoding<const N: usize>
345where
346    Self: Sized,
347{
348    /// Decodes `Some(object)` from `bytes` if `bytes` encodes some `object` of type `Self`;
349    /// otherwise returns `None`.
350    fn from_bytes(bytes: [u8; N]) -> Option<Self>;
351
352    /// Encodes `self` as `[u8; N]`.
353    fn to_bytes(&self) -> [u8; N];
354
355    /// Like [Self::from_bytes], but reads `[u8; N]` from `slice`.  Returns `None` if `slice.len()!=N`
356    /// or when the slice is not a valid encoding.
357    fn from_slice(slice: &[u8]) -> Option<Self> {
358        if slice.len() != N {
359            return None;
360        }
361
362        let mut buf = [0u8; N];
363        buf.copy_from_slice(slice);
364
365        Self::from_bytes(buf)
366    }
367
368    /// Copies the encoding of `self` into `slice`.  Returns `None` when `slice.len()!=N`.
369    fn copy_to_slice(&self, slice: &mut [u8]) -> Option<()> {
370        if slice.len() != N {
371            return None;
372        }
373
374        slice.copy_from_slice(&self.to_bytes());
375
376        Some(())
377    }
378
379    /// Like [Self::from_bytes], but reads the `[u8; N]` from the 2*N-digit hex string `hex`.
380    /// The case of the hex digits is ignored.
381    fn from_hex(hex: &str) -> Option<Self> {
382        let hex: &[u8] = hex.as_bytes();
383
384        if hex.len() != 2 * N {
385            return None;
386        }
387
388        let mut buf = [0u8; N];
389
390        base16ct::mixed::decode(hex, &mut buf).ok()?;
391        Self::from_bytes(buf)
392    }
393
394    /// Returns the `2*N`-digit lower-case hex representation of `self`.
395    fn to_hex(&self) -> String {
396        base16ct::lower::encode_string(&self.to_bytes())
397    }
398
399    /// Loads object from the `N`-byte buffer pointed to by `ptr`.
400    ///
401    /// # Safety
402    /// The caller must make sure that `ptr` is properly alligned,
403    /// the `N`-byte buffer is readable, and isn't modified for the duration of the call.
404    ///
405    /// See the 'Safety' section of [core::slice::from_raw_parts] for more details.
406    unsafe fn from_ptr(ptr: *const u8) -> Option<Self> {
407        Self::from_slice(unsafe { core::slice::from_raw_parts(ptr, N) })
408    }
409
410    /// Writes the `N`-byte representation of this object to the memory location `ptr`.
411    ///
412    /// # Safety
413    /// The caller must make sure that `ptr` is properly alligned,
414    /// the `N`-byte buffer is writable, and isn't modified for the duration of the call.
415    ///
416    /// See the 'Safety' section of [core::slice::from_raw_parts_mut] for more details.
417    unsafe fn copy_to_ptr(self, ptr: *mut u8) {
418        self.copy_to_slice(unsafe { core::slice::from_raw_parts_mut(ptr, N) })
419            .unwrap()
420        // Note: `copy_to_slice` only fails when the provided slice has the incorrect size (not `N`)
421        // which is not the case here.
422    }
423}
424
425impl Encoding<32> for Scalar {
426    fn from_bytes(bytes: [u8; 32]) -> Option<Scalar> {
427        Scalar::from_canonical_bytes(bytes).into()
428    }
429
430    fn to_bytes(&self) -> [u8; 32] {
431        Scalar::to_bytes(self)
432    }
433}
434
435impl Encoding<32> for CompressedRistretto {
436    fn from_bytes(bytes: [u8; 32]) -> Option<CompressedRistretto> {
437        Some(CompressedRistretto(bytes))
438    }
439
440    fn to_bytes(&self) -> [u8; 32] {
441        self.to_bytes()
442    }
443}
444
445impl Encoding<32> for RistrettoPoint {
446    fn from_bytes(bytes: [u8; 32]) -> Option<RistrettoPoint> {
447        CompressedRistretto(bytes).decompress()
448    }
449
450    fn to_bytes(&self) -> [u8; 32] {
451        self.compress().to_bytes()
452    }
453}
454
455impl Encoding<32> for PrivateKey {
456    fn from_bytes(bytes: [u8; 32]) -> Option<PrivateKey> {
457        Scalar::from_bytes(bytes).map(PrivateKey::from)
458    }
459
460    fn to_bytes(&self) -> [u8; 32] {
461        self.scalar.to_bytes()
462    }
463}
464
465impl Encoding<32> for PublicKey {
466    fn from_bytes(bytes: [u8; 32]) -> Option<PublicKey> {
467        CompressedRistretto::from_bytes(bytes)?.try_into().ok()
468    }
469
470    fn to_bytes(&self) -> [u8; 32] {
471        self.compressed.to_bytes()
472    }
473}
474
475impl Encoding<96> for Triple {
476    fn from_bytes(bytes: [u8; 96]) -> Option<Triple> {
477        let ek: RistrettoPoint = RistrettoPoint::from_slice(&bytes[..32])?;
478        let ct: RistrettoPoint = RistrettoPoint::from_slice(&bytes[32..64])?;
479        let pk: RistrettoPoint = RistrettoPoint::from_slice(&bytes[64..])?;
480
481        Some(Triple { ek, ct, pk })
482    }
483
484    fn to_bytes(&self) -> [u8; 96] {
485        let mut result = [0u8; 96];
486
487        // Note: `copy_to_slice` only fails when the slice's size is not 32, which it won't below
488        self.ek.copy_to_slice(&mut result[..32]).unwrap();
489        self.ct.copy_to_slice(&mut result[32..64]).unwrap();
490        self.pk.copy_to_slice(&mut result[64..]).unwrap();
491
492        result
493    }
494}
495
496mod serde_impls {
497    use super::*;
498    use crate::misc::serde_ext;
499    use serde::de::Error as _;
500
501    /// Implements [`serde::Serialize`] and [`serde::Deserialize`] using [`serde_ext::ByteArray`] and hex
502    /// encoding
503    macro_rules! serde_impl {
504        { $type:ident, $n:literal } => {
505
506            impl<'de> serde::Deserialize<'de> for $type {
507                fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
508                    let byte_array : serde_ext::ByteArray<$n>  =
509                        serde_ext::bytes_wrapper::B16::<serde_ext::ByteArray<$n>>::deserialize(d)?.into_inner();
510                    $type::from_bytes(byte_array.into()).ok_or_else(|| D::Error::custom(concat!("invalid ", stringify!($type))))
511                }
512            }
513
514            impl<'de> serde::Serialize for $type {
515                fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
516                    let byte_array = serde_ext::ByteArray::<$n>::from(self.to_bytes());
517                    serde_ext::bytes_wrapper::B16::<serde_ext::ByteArray<$n>>::from(byte_array)
518                        .serialize(s)
519                }
520            }
521        }
522    }
523
524    serde_impl! { PrivateKey, 32 }
525    serde_impl! { PublicKey, 32 }
526    serde_impl! { Triple, 96 }
527}
528
529/// Shared secret created by combining a [`PrivateKey`] with a [`PublicKey`], which, although it is
530/// basically the encoding of a [`RistrettoPoint`], is given a separate interface to limit its
531/// usage.
532#[derive(Clone, Debug, zeroize::ZeroizeOnDrop)]
533pub struct SharedSecret {
534    inner: [u8; 32],
535}
536
537impl crate::common::secret::DigestibleSecret for SharedSecret {
538    fn as_bytes(&self) -> &[u8] {
539        &self.inner
540    }
541}
542
543impl crate::common::secret::DigestibleSecret for PrivateKey {
544    fn as_bytes(&self) -> &[u8] {
545        self.scalar.as_bytes().as_slice()
546    }
547}
548
549///// Application binary interface
550//pub mod abi {
551//    use super::*;
552//
553//    /// Decrypts the given `ciphertext` using the given `private_key` and stores the result in
554//    /// `plaintext`.
555//    ///
556//    ///   * `plaintext` - pointer to a writable 32-byte buffer
557//    ///   * `ciperhtext` - pointer to a 96-byte buffer holding the result of [Triple::to_bytes]
558//    ///   * `private_key` - pointer to a 32-byte buffer holding the result of [Scalar::to_bytes]
559//    ///
560//    /// # Safety
561//    /// The caller must make sure the pointers are aligned, point to valid memory regions,
562//    /// are readable, and plaintext is writable, and are not otherwise modified.
563//    ///
564//    /// For more details, see [core::slice::from_raw_parts] and [core::slice::from_raw_parts_mut].
565//    #[unsafe(no_mangle)]
566//    pub unsafe extern "C" fn decrypt(
567//        plaintext: *mut u8,
568//        ciphertext: *const u8,
569//        private_key: *const u8,
570//    ) -> DecryptResult {
571//        let pk = match unsafe { PrivateKey::from_ptr(private_key) } {
572//            Some(pk) => pk,
573//            None => return DecryptResult::InvalidPrivateKey,
574//        };
575//
576//        let ct = match unsafe { Triple::from_ptr(ciphertext) } {
577//            Some(ct) => ct,
578//            None => return DecryptResult::InvalidTriple,
579//        };
580//
581//        let pt = match ct.decrypt_and_check_pk(&pk) {
582//            Some(pt) => pt,
583//            None => return DecryptResult::WrongPublicKey,
584//        };
585//
586//        unsafe { pt.copy_to_ptr(plaintext) }
587//
588//        DecryptResult::Ok
589//    }
590//
591//    /// Result of [decrypt].
592//    #[repr(u8)]
593//    pub enum DecryptResult {
594//        Ok = 1,
595//        WrongPublicKey = 2,
596//        InvalidTriple = 3,
597//        InvalidPrivateKey = 4,
598//    }
599//}