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
193macro_rules! osrng {
194    () => {
195        &mut aead::OsRng
196    };
197}
198
199/// Returns a random Ristretto point, mainly for examples.
200///
201/// If you're immediately encrypting this point, consider
202/// using [PublicKey::encrypt_random] instead.
203pub fn random_point() -> RistrettoPoint {
204    RistrettoPoint::random(osrng!())
205}
206
207/// Returns a random scalar, mainly for examples.
208pub fn random_scalar() -> Scalar {
209    Scalar::random(osrng!())
210}
211
212/// Private key - load using [`PrivateKey::from_hex`] or generate with [`PrivateKey::random`].
213///
214/// Caches the associated [`PublicKey`], which means that loading a [`PrivateKey`] involves a base
215/// point multiplication.
216#[derive(Clone, PartialEq, Eq, Debug, zeroize::ZeroizeOnDrop)]
217pub struct PrivateKey {
218    /// underlying scalar
219    scalar: Scalar,
220
221    /// associated public key, stored for efficiency
222    #[zeroize(skip)]
223    public_key: PublicKey,
224}
225
226impl PrivateKey {
227    /// Returns reference to underlying scalar.
228    pub fn as_scalar(&self) -> &Scalar {
229        &self.scalar
230    }
231
232    pub fn random() -> Self {
233        random_scalar().into()
234    }
235
236    pub fn public_key(&self) -> &PublicKey {
237        &self.public_key
238    }
239
240    /// Computes the [PublicKey] associated with the product of two [PrivateKey]s given only one
241    /// private key.
242    pub fn scale(&self, pk: &PublicKey) -> PublicKey {
243        (self.scalar * pk.point).into()
244    }
245
246    /// Creates a Diffie-Hellman-type shared secret between this [`PrivateKey`] and the [`PublicKey`].
247    pub fn shared_secret(&self, pk: &PublicKey) -> SharedSecret {
248        SharedSecret {
249            inner: self.scale(pk).to_bytes(),
250        }
251    }
252}
253
254impl From<Scalar> for PrivateKey {
255    fn from(scalar: Scalar) -> Self {
256        PrivateKey {
257            scalar,
258            public_key: (&scalar * B).into(),
259        }
260    }
261}
262
263/// Public key - obtained using [`PublicKey::from_hex`] or [`PrivateKey::public_key`].
264#[derive(Clone, PartialEq, Eq, Debug)]
265pub struct PublicKey {
266    point: RistrettoPoint,
267    compressed: CompressedRistretto,
268}
269
270impl AsRef<[u8]> for PublicKey {
271    /// Returns a reference to the compressed encoding of this public key
272    fn as_ref(&self) -> &[u8] {
273        self.compressed.as_bytes().as_slice()
274    }
275}
276
277impl PublicKey {
278    /// Turns a 64 digit hex string into a [`PublicKey`].
279    ///
280    /// Returns `None` when the hex-encoding is invalid or when the hex-encoding does not encode a
281    /// valid Ristretto point.
282    pub fn from_hex(hexstr: &str) -> Option<Self> {
283        CompressedRistretto::from_hex(hexstr)?.try_into().ok()
284    }
285
286    /// The identity element, which encodes as 32 zero bytes.
287    pub fn zero() -> Self {
288        use curve25519_dalek::traits::Identity as _;
289        RistrettoPoint::identity().into()
290    }
291
292    /// Encrypts the given `plaintext` for this public key.
293    /// If the plaintext is a random point, consider using [Self::encrypt_random].
294    pub fn encrypt(&self, plaintext: RistrettoPoint) -> Triple {
295        self.encrypt_with_random(random_scalar(), plaintext)
296    }
297
298    /// Like [`Self::encrypt`], but you can specify the random scalar used - which you shouldn't
299    /// except to make deterministic tests.
300    pub fn encrypt_with_random(&self, r: Scalar, plaintext: RistrettoPoint) -> Triple {
301        Triple {
302            ek: &r * B,
303            ct: plaintext + r * self.point,
304            pk: self.point,
305        }
306    }
307
308    /// Effectively encrypts a random plaintext for this public key.
309    ///
310    /// Instead of picking random Ristretto point M and random scalar r and computing
311    ///   `(rB, r * pk + M, self)`
312    /// we pick Ristretto points ek and ct randomly and return
313    ///   `(ek, ct, sekf)`.
314    /// since this is more efficient, and yields the same distribution.
315    pub fn encrypt_random(&self) -> Triple {
316        Triple {
317            ek: random_point(),
318            ct: random_point(),
319            pk: self.point,
320        }
321    }
322}
323
324impl From<RistrettoPoint> for PublicKey {
325    fn from(point: RistrettoPoint) -> Self {
326        Self {
327            point,
328            compressed: point.compress(),
329        }
330    }
331}
332
333impl TryFrom<CompressedRistretto> for PublicKey {
334    type Error = ();
335
336    fn try_from(compressed: CompressedRistretto) -> Result<Self, Self::Error> {
337        Ok(Self {
338            point: compressed.decompress().ok_or(())?,
339            compressed,
340        })
341    }
342}
343
344/// Adds encoding and decoding methods to [`PrivateKey`], [`PublicKey`], [`Triple`], [`Scalar`]
345/// and [`RistrettoPoint`] which can all be represented as `[u8; N]`s for some `N`.  
346///
347/// Not all arrays of the form `[u8; N]` may be a valid representation of the type of object in question, though.
348pub trait Encoding<const N: usize>
349where
350    Self: Sized,
351{
352    /// Decodes `Some(object)` from `bytes` if `bytes` encodes some `object` of type `Self`;
353    /// otherwise returns `None`.
354    fn from_bytes(bytes: [u8; N]) -> Option<Self>;
355
356    /// Encodes `self` as `[u8; N]`.
357    fn to_bytes(&self) -> [u8; N];
358
359    /// Like [Self::from_bytes], but reads `[u8; N]` from `slice`.  Returns `None` if `slice.len()!=N`
360    /// or when the slice is not a valid encoding.
361    fn from_slice(slice: &[u8]) -> Option<Self> {
362        if slice.len() != N {
363            return None;
364        }
365
366        let mut buf = [0u8; N];
367        buf.copy_from_slice(slice);
368
369        Self::from_bytes(buf)
370    }
371
372    /// Copies the encoding of `self` into `slice`.  Returns `None` when `slice.len()!=N`.
373    fn copy_to_slice(&self, slice: &mut [u8]) -> Option<()> {
374        if slice.len() != N {
375            return None;
376        }
377
378        slice.copy_from_slice(&self.to_bytes());
379
380        Some(())
381    }
382
383    /// Like [Self::from_bytes], but reads the `[u8; N]` from the 2*N-digit hex string `hex`.
384    /// The case of the hex digits is ignored.
385    fn from_hex(hex: &str) -> Option<Self> {
386        let hex: &[u8] = hex.as_bytes();
387
388        if hex.len() != 2 * N {
389            return None;
390        }
391
392        let mut buf = [0u8; N];
393
394        base16ct::mixed::decode(hex, &mut buf).ok()?;
395        Self::from_bytes(buf)
396    }
397
398    /// Returns the `2*N`-digit lower-case hex representation of `self`.
399    fn to_hex(&self) -> String {
400        base16ct::lower::encode_string(&self.to_bytes())
401    }
402
403    /// Loads object from the `N`-byte buffer pointed to by `ptr`.
404    ///
405    /// # Safety
406    /// The caller must make sure that `ptr` is properly alligned,
407    /// the `N`-byte buffer is readable, and isn't modified for the duration of the call.
408    ///
409    /// See the 'Safety' section of [core::slice::from_raw_parts] for more details.
410    unsafe fn from_ptr(ptr: *const u8) -> Option<Self> {
411        Self::from_slice(unsafe { core::slice::from_raw_parts(ptr, N) })
412    }
413
414    /// Writes the `N`-byte representation of this object to the memory location `ptr`.
415    ///
416    /// # Safety
417    /// The caller must make sure that `ptr` is properly alligned,
418    /// the `N`-byte buffer is writable, and isn't modified for the duration of the call.
419    ///
420    /// See the 'Safety' section of [core::slice::from_raw_parts_mut] for more details.
421    unsafe fn copy_to_ptr(self, ptr: *mut u8) {
422        self.copy_to_slice(unsafe { core::slice::from_raw_parts_mut(ptr, N) })
423            .unwrap()
424        // Note: `copy_to_slice` only fails when the provided slice has the incorrect size (not `N`)
425        // which is not the case here.
426    }
427}
428
429impl Encoding<32> for Scalar {
430    fn from_bytes(bytes: [u8; 32]) -> Option<Scalar> {
431        Scalar::from_canonical_bytes(bytes).into()
432    }
433
434    fn to_bytes(&self) -> [u8; 32] {
435        Scalar::to_bytes(self)
436    }
437}
438
439impl Encoding<32> for CompressedRistretto {
440    fn from_bytes(bytes: [u8; 32]) -> Option<CompressedRistretto> {
441        Some(CompressedRistretto(bytes))
442    }
443
444    fn to_bytes(&self) -> [u8; 32] {
445        self.to_bytes()
446    }
447}
448
449impl Encoding<32> for RistrettoPoint {
450    fn from_bytes(bytes: [u8; 32]) -> Option<RistrettoPoint> {
451        CompressedRistretto(bytes).decompress()
452    }
453
454    fn to_bytes(&self) -> [u8; 32] {
455        self.compress().to_bytes()
456    }
457}
458
459impl Encoding<32> for PrivateKey {
460    fn from_bytes(bytes: [u8; 32]) -> Option<PrivateKey> {
461        Scalar::from_bytes(bytes).map(PrivateKey::from)
462    }
463
464    fn to_bytes(&self) -> [u8; 32] {
465        self.scalar.to_bytes()
466    }
467}
468
469impl Encoding<32> for PublicKey {
470    fn from_bytes(bytes: [u8; 32]) -> Option<PublicKey> {
471        CompressedRistretto::from_bytes(bytes)?.try_into().ok()
472    }
473
474    fn to_bytes(&self) -> [u8; 32] {
475        self.compressed.to_bytes()
476    }
477}
478
479impl Encoding<96> for Triple {
480    fn from_bytes(bytes: [u8; 96]) -> Option<Triple> {
481        let ek: RistrettoPoint = RistrettoPoint::from_slice(&bytes[..32])?;
482        let ct: RistrettoPoint = RistrettoPoint::from_slice(&bytes[32..64])?;
483        let pk: RistrettoPoint = RistrettoPoint::from_slice(&bytes[64..])?;
484
485        Some(Triple { ek, ct, pk })
486    }
487
488    fn to_bytes(&self) -> [u8; 96] {
489        let mut result = [0u8; 96];
490
491        // Note: `copy_to_slice` only fails when the slice's size is not 32, which it won't below
492        self.ek.copy_to_slice(&mut result[..32]).unwrap();
493        self.ct.copy_to_slice(&mut result[32..64]).unwrap();
494        self.pk.copy_to_slice(&mut result[64..]).unwrap();
495
496        result
497    }
498}
499
500mod serde_impls {
501    use super::*;
502    use crate::misc::serde_ext;
503    use serde::de::Error as _;
504
505    /// Implements [`serde::Serialize`] and [`serde::Deserialize`] using [`serde_ext::ByteArray`] and hex
506    /// encoding
507    macro_rules! serde_impl {
508        { $type:ident, $n:literal } => {
509
510            impl<'de> serde::Deserialize<'de> for $type {
511                fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
512                    let byte_array : serde_ext::ByteArray<$n>  =
513                        serde_ext::bytes_wrapper::B16::<serde_ext::ByteArray<$n>>::deserialize(d)?.into_inner();
514                    $type::from_bytes(byte_array.into()).ok_or_else(|| D::Error::custom(concat!("invalid ", stringify!($type))))
515                }
516            }
517
518            impl<'de> serde::Serialize for $type {
519                fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
520                    let byte_array = serde_ext::ByteArray::<$n>::from(self.to_bytes());
521                    serde_ext::bytes_wrapper::B16::<serde_ext::ByteArray<$n>>::from(byte_array)
522                        .serialize(s)
523                }
524            }
525        }
526    }
527
528    serde_impl! { PrivateKey, 32 }
529    serde_impl! { PublicKey, 32 }
530    serde_impl! { Triple, 96 }
531}
532
533/// Shared secret created by combining a [`PrivateKey`] with a [`PublicKey`], which, although it is
534/// basically the encoding of a [`RistrettoPoint`], is given a separate interface to limit its
535/// usage.
536#[derive(Clone, Debug, zeroize::ZeroizeOnDrop)]
537pub struct SharedSecret {
538    inner: [u8; 32],
539}
540
541impl crate::common::secret::DigestibleSecret for SharedSecret {
542    fn as_bytes(&self) -> &[u8] {
543        &self.inner
544    }
545}
546
547impl crate::common::secret::DigestibleSecret for PrivateKey {
548    fn as_bytes(&self) -> &[u8] {
549        self.scalar.as_bytes().as_slice()
550    }
551}
552
553///// Application binary interface
554//pub mod abi {
555//    use super::*;
556//
557//    /// Decrypts the given `ciphertext` using the given `private_key` and stores the result in
558//    /// `plaintext`.
559//    ///
560//    ///   * `plaintext` - pointer to a writable 32-byte buffer
561//    ///   * `ciperhtext` - pointer to a 96-byte buffer holding the result of [Triple::to_bytes]
562//    ///   * `private_key` - pointer to a 32-byte buffer holding the result of [Scalar::to_bytes]
563//    ///
564//    /// # Safety
565//    /// The caller must make sure the pointers are aligned, point to valid memory regions,
566//    /// are readable, and plaintext is writable, and are not otherwise modified.
567//    ///
568//    /// For more details, see [core::slice::from_raw_parts] and [core::slice::from_raw_parts_mut].
569//    #[unsafe(no_mangle)]
570//    pub unsafe extern "C" fn decrypt(
571//        plaintext: *mut u8,
572//        ciphertext: *const u8,
573//        private_key: *const u8,
574//    ) -> DecryptResult {
575//        let pk = match unsafe { PrivateKey::from_ptr(private_key) } {
576//            Some(pk) => pk,
577//            None => return DecryptResult::InvalidPrivateKey,
578//        };
579//
580//        let ct = match unsafe { Triple::from_ptr(ciphertext) } {
581//            Some(ct) => ct,
582//            None => return DecryptResult::InvalidTriple,
583//        };
584//
585//        let pt = match ct.decrypt_and_check_pk(&pk) {
586//            Some(pt) => pt,
587//            None => return DecryptResult::WrongPublicKey,
588//        };
589//
590//        unsafe { pt.copy_to_ptr(plaintext) }
591//
592//        DecryptResult::Ok
593//    }
594//
595//    /// Result of [decrypt].
596//    #[repr(u8)]
597//    pub enum DecryptResult {
598//        Ok = 1,
599//        WrongPublicKey = 2,
600//        InvalidTriple = 3,
601//        InvalidPrivateKey = 4,
602//    }
603//}