1#![warn(missing_docs)]
7
8use hacspec_lib::Randomness;
9use p256::{p256_point_mul, p256_point_mul_base, point_add, random_scalar, P256Point, P256Scalar};
10
11use std::ops::Neg;
12
13const DST_KEYGEN: &[u8] = b"ElGamal-KeyGeneration";
15const DST_ENCRYPT: &[u8] = b"ElGamal-Encryption";
17const DST_RERANDOMIZE: &[u8] = b"ElGamal-Rerandomize";
19
20pub type Ciphertext = (P256Point, P256Point);
24
25pub type Plaintext = P256Point;
27
28#[derive(Debug)]
29pub enum Error {
31 CurveError,
33 SamplingError,
35}
36
37impl From<p256::Error> for Error {
38 fn from(_value: p256::Error) -> Self {
39 Self::CurveError
40 }
41}
42
43pub type DecryptionKey = P256Scalar;
45pub type EncryptionKey = P256Point;
47
48pub fn generate_keys(randomness: &mut Randomness) -> Result<(DecryptionKey, EncryptionKey), Error> {
51 let dec_key = random_scalar(randomness, DST_KEYGEN)?;
52 let enc_key = p256_point_mul_base(dec_key)?;
53
54 Ok((dec_key, enc_key))
55}
56
57pub fn encrypt(
62 enc_key: P256Point,
63 message: Plaintext,
64 randomness: &mut Randomness,
65) -> Result<Ciphertext, Error> {
66 let randomizer = random_scalar(randomness, DST_ENCRYPT)?;
67
68 let blinded_message = p256::point_add(message, p256::p256_point_mul(randomizer, enc_key)?)?;
69 let auxillary = p256::p256_point_mul_base(randomizer)?;
70 Ok((auxillary, blinded_message))
71}
72
73pub fn decrypt(dec_key: P256Scalar, ctx: Ciphertext) -> Result<Plaintext, Error> {
75 let blinding: P256Point = p256::p256_point_mul(dec_key, ctx.0)?;
76
77 p256::point_add(ctx.1, blinding.neg()).map_err(|e| e.into())
78}
79
80pub fn rerandomize(
85 enc_key: P256Point,
86 ctx: Ciphertext,
87 randomness: &mut Randomness,
88) -> Result<Ciphertext, Error> {
89 let randomizer = random_scalar(randomness, DST_RERANDOMIZE)?;
90 let updated_blinding = p256_point_mul(randomizer, enc_key)?;
91 let updated_blinded_message = point_add(updated_blinding, ctx.1)?;
92
93 let updated_auxillary = point_add(p256_point_mul_base(randomizer)?, ctx.0)?;
94 Ok((updated_auxillary, updated_blinded_message))
95}
96
97pub fn add_ciphertexts(lhs: Ciphertext, rhs: Ciphertext) -> Result<Ciphertext, Error> {
103 let sum_blinded_messages = p256::point_add(lhs.0, rhs.0)?;
104 let sum_auxillaries = p256::point_add(lhs.1, rhs.1)?;
105
106 Ok((sum_blinded_messages, sum_auxillaries))
107}
108
109pub fn scalar_mul_ciphertext(scalar: P256Scalar, ctx: Ciphertext) -> Result<Ciphertext, Error> {
112 let multiple_blinded_message = p256_point_mul(scalar, ctx.0)?;
113 let multiple_auxillary = p256_point_mul(scalar, ctx.1)?;
114
115 Ok((multiple_blinded_message, multiple_auxillary))
116}
117
118#[cfg(test)]
119mod test {
120 use super::*;
121 use p256::random_scalar;
122 use rand::RngCore;
123
124 #[test]
125 fn test_correctness() {
126 let msg = random_element().unwrap();
127 let mut uniform_bytes = vec![0u8; 32];
128 rand::thread_rng().fill_bytes(&mut uniform_bytes);
129 let mut uniform_bytes = Randomness::new(uniform_bytes);
130 let (dk, ek) = generate_keys(&mut uniform_bytes).unwrap();
131
132 let ctx = encrypt(ek, msg, &mut Randomness::new(vec![0xab; 32])).unwrap();
133 let decryption = decrypt(dk, ctx).unwrap();
134
135 debug_assert_eq!(msg, decryption);
136 }
137
138 #[test]
139 fn test_rerandomize() {
140 let msg = random_element().unwrap();
141
142 let mut uniform_bytes = vec![0u8; 32];
143 rand::thread_rng().fill_bytes(&mut uniform_bytes);
144 let mut uniform_bytes = Randomness::new(uniform_bytes);
145 let (dk, ek) = generate_keys(&mut uniform_bytes).unwrap();
146
147 let ctx = encrypt(ek, msg, &mut Randomness::new(vec![0xab; 32])).unwrap();
148 let rctx = rerandomize(ek, ctx, &mut Randomness::new(vec![0xab; 32])).unwrap();
149 let decryption = decrypt(dk, rctx).unwrap();
150
151 debug_assert_eq!(msg, decryption);
152 assert_ne!(ctx, rctx);
153 }
154
155 fn random_element() -> Result<P256Point, Error> {
156 let rand = random_scalar(&mut Randomness::new(vec![0xab; 32]), b"ElGamal-Test").unwrap();
157 let res = p256_point_mul_base(rand)?;
158
159 Ok(res)
160 }
161}