1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
// Library definitions for rust-elgamal.
// Copyright 2021 Eleanor McMurtry
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![no_std]
#![cfg_attr(feature = "nightly", feature(external_doc))]
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]

mod ciphertext;
mod decrypt;
mod encrypt;
pub mod util;

use curve25519_dalek::constants::{RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_TABLE};
use curve25519_dalek::ristretto::RistrettoBasepointTable;

pub use curve25519_dalek::scalar::Scalar;
pub use curve25519_dalek::ristretto::RistrettoPoint;

pub use ciphertext::Ciphertext;
pub use decrypt::DecryptionKey;
pub use encrypt::EncryptionKey;

/// The group generator as a single point.
/// If you're trying to create a scalar multiple of the generator, you probably want
/// [GENERATOR_TABLE](crate::GENERATOR_TABLE) instead.
pub const GENERATOR_POINT: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;

/// The group generator as a table of precomputed multiples. This is the most efficient way to
/// produce a scalar multiple of the generator.
pub const GENERATOR_TABLE: RistrettoBasepointTable = RISTRETTO_BASEPOINT_TABLE;

#[cfg(test)]
mod tests {
    use rand::prelude::StdRng;
    use rand_core::SeedableRng;

    use crate::DecryptionKey;
    use crate::util::random_point;

    // Test that encrypting a point and decrypting the result does not change a point.
    #[test]
    fn encrypt_decrypt() {
        const N: usize = 100;

        let mut rng = StdRng::from_entropy();
        let dk = DecryptionKey::new(&mut rng);
        let ek = dk.encryption_key();

        for _ in 0..N {
            let m = random_point(&mut rng);
            let ct = ek.encrypt(m, &mut rng);
            let decrypted = dk.decrypt(ct);
            assert_eq!(m, decrypted);
        }
    }

    // Test that re-randomising an encrypted point does not change the decrypted result.
    #[test]
    fn rerandomisation() {
        const N: usize = 100;

        let mut rng = StdRng::from_entropy();
        let dk = DecryptionKey::new(&mut rng);
        let ek = dk.encryption_key();
        let m = random_point(&mut rng);
        let ct = ek.encrypt(m, &mut rng);

        for _ in 0..N {
            let ct = ek.rerandomise(ct, &mut rng);
            let decrypted = dk.decrypt(ct);
            assert_eq!(m, decrypted);
        }
    }

    // Test that the decrypted sum of two ciphertexts is equal to the sum of the original points.
    #[test]
    fn homomorphism() {
        const N: usize = 100;

        let mut rng = StdRng::from_entropy();
        let dk = DecryptionKey::new(&mut rng);
        let ek = dk.encryption_key();

        for _ in 0..N {
            let m1 = random_point(&mut rng);
            let m2 = random_point(&mut rng);
            let sum = m1 + m2;
            let ct1 = ek.encrypt(m1, &mut rng);
            let ct2 = ek.encrypt(m2, &mut rng);
            let ct_sum = ct1 + ct2;
            let decrypted = dk.decrypt(ct_sum);
            assert_eq!(sum, decrypted);
        }
    }
}