├── .gitignore ├── Cargo.toml ├── README.md ├── benches └── main.rs └── src ├── kiltz_vahlis_one.rs ├── lib.rs ├── util.rs ├── waters.rs └── waters_naccache.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ibe" 3 | version = "0.1.4" 4 | description = "Identity Based Encryption schemes on the BLS12-381 pairing-friendly elliptic curve" 5 | authors = ["Wouter Geraedts "] 6 | edition = "2018" 7 | license = "MIT" 8 | repository = "https://github.com/wassasin/ibe" 9 | keywords = ["ibe", "encryption", "ecc", "no_std"] 10 | categories = ["cryptography", "no-std"] 11 | 12 | [dependencies] 13 | rand = "0.7" 14 | tiny-keccak = { version = "2.0", features = ["sha3"] } 15 | arrayref = "0.3" 16 | irmaseal-curve = "0.1" 17 | 18 | [dependencies.subtle] 19 | version = "2.3" 20 | default-features = false 21 | 22 | [dependencies.byteorder] 23 | version = "1.3" 24 | default-features = false 25 | 26 | [dev-dependencies] 27 | criterion = "0.3" 28 | 29 | [lib] 30 | bench = false 31 | 32 | [[bench]] 33 | name = "main" 34 | harness = false 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IBE 2 | Identity Based Encryption schemes on the [BLS12-381 pairing-friendly elliptic curve](https://github.com/zkcrypto/bls12_381) in Rust. 3 | 4 | Implements the following schemes: 5 | * Waters 6 | * Waters-Naccache 7 | * Kiltz-Vahlis IBE1 8 | 9 | You should probably use the Kiltz-Vahlis IBE1 scheme, as it provides the best security properties. 10 | 11 | ## Technical notes 12 | * **This implementation has not (yet) been reviewed or audited. Use at your own risk.** 13 | * Uses [SHA3-512](https://crates.io/crates/tiny-keccak) for hashing to identities. 14 | * Compiles succesfully on Rust Stable. 15 | * Does not use the Rust standard library (no-std). 16 | * The structure of the byte serialisation of the various datastructures is not guaranteed to remain constant between releases of this library. 17 | * All operations in this library are implemented to run in constant time. 18 | 19 | ## TODO's 20 | * The underlying libraries might benefit from running on Rust nightly, which prevents compiler optimizations that could jeopardize constant time operations, but enabling this will require using `subtle/nightly`. 21 | -------------------------------------------------------------------------------- /benches/main.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | 3 | fn criterion_waters_benchmark(criterion: &mut Criterion) { 4 | use ibe::waters::*; 5 | 6 | let mut rng = rand::thread_rng(); 7 | 8 | let id = "email:w.geraedts@sarif.nl".as_bytes(); 9 | let kid = Identity::derive(id); 10 | 11 | let m = Message::generate(&mut rng); 12 | 13 | let (pk, sk) = setup(&mut rng); 14 | let usk = extract_usk(&pk, &sk, &kid, &mut rng); 15 | 16 | let c = encrypt(&pk, &kid, &m, &mut rng); 17 | 18 | criterion.bench_function("waters generate message", |b| { 19 | let mut rng = rand::thread_rng(); 20 | b.iter(|| Message::generate(&mut rng)) 21 | }); 22 | criterion.bench_function("waters setup", |b| { 23 | let mut rng = rand::thread_rng(); 24 | b.iter(|| setup(&mut rng)) 25 | }); 26 | criterion.bench_function("waters derive", move |b| b.iter(|| Identity::derive(id))); 27 | criterion.bench_function("waters extract", move |b| { 28 | let mut rng = rand::thread_rng(); 29 | b.iter(|| extract_usk(black_box(&pk), black_box(&sk), black_box(&kid), &mut rng)) 30 | }); 31 | criterion.bench_function("waters encrypt", move |b| { 32 | let mut rng = rand::thread_rng(); 33 | b.iter(|| encrypt(black_box(&pk), black_box(&kid), black_box(&m), &mut rng)) 34 | }); 35 | criterion.bench_function("waters decrypt", move |b| { 36 | b.iter(|| decrypt(black_box(&usk), black_box(&c))) 37 | }); 38 | } 39 | 40 | fn criterion_waters_naccache_benchmark(criterion: &mut Criterion) { 41 | use ibe::waters_naccache::*; 42 | 43 | let mut rng = rand::thread_rng(); 44 | 45 | let id = "email:w.geraedts@sarif.nl".as_bytes(); 46 | let kid = Identity::derive(id); 47 | 48 | let m = Message::generate(&mut rng); 49 | 50 | let (pk, sk) = setup(&mut rng); 51 | let usk = extract_usk(&pk, &sk, &kid, &mut rng); 52 | 53 | let c = encrypt(&pk, &kid, &m, &mut rng); 54 | 55 | criterion.bench_function("waters_naccache generate message", |b| { 56 | let mut rng = rand::thread_rng(); 57 | b.iter(|| Message::generate(&mut rng)) 58 | }); 59 | criterion.bench_function("waters_naccache setup", |b| { 60 | let mut rng = rand::thread_rng(); 61 | b.iter(|| setup(&mut rng)) 62 | }); 63 | criterion.bench_function("waters_naccache derive", move |b| { 64 | b.iter(|| Identity::derive(id)) 65 | }); 66 | criterion.bench_function("waters_naccache extract", move |b| { 67 | let mut rng = rand::thread_rng(); 68 | b.iter(|| extract_usk(black_box(&pk), black_box(&sk), black_box(&kid), &mut rng)) 69 | }); 70 | criterion.bench_function("waters_naccache encrypt", move |b| { 71 | let mut rng = rand::thread_rng(); 72 | b.iter(|| encrypt(black_box(&pk), black_box(&kid), black_box(&m), &mut rng)) 73 | }); 74 | criterion.bench_function("waters_naccache decrypt", move |b| { 75 | b.iter(|| decrypt(black_box(&usk), black_box(&c))) 76 | }); 77 | } 78 | 79 | fn criterion_kiltz_vahlis_one_benchmark(criterion: &mut Criterion) { 80 | use ibe::kiltz_vahlis_one::*; 81 | 82 | let mut rng = rand::thread_rng(); 83 | 84 | let id = "email:w.geraedts@sarif.nl".as_bytes(); 85 | let kid = Identity::derive(id); 86 | 87 | let (pk, sk) = setup(&mut rng); 88 | let usk = extract_usk(&pk, &sk, &kid, &mut rng); 89 | let ppk = pk.to_bytes(); 90 | 91 | let (c, _k) = encrypt(&pk, &kid, &mut rng); 92 | 93 | criterion.bench_function("kiltz_vahlis_one unpack_pk", |b| { 94 | b.iter(|| PublicKey::from_bytes(&ppk)) 95 | }); 96 | criterion.bench_function("kiltz_vahlis_one setup", |b| { 97 | let mut rng = rand::thread_rng(); 98 | b.iter(|| setup(&mut rng)) 99 | }); 100 | criterion.bench_function("kiltz_vahlis_one derive", move |b| { 101 | b.iter(|| Identity::derive(id)) 102 | }); 103 | criterion.bench_function("kiltz_vahlis_one extract", move |b| { 104 | let mut rng = rand::thread_rng(); 105 | b.iter(|| extract_usk(black_box(&pk), black_box(&sk), black_box(&kid), &mut rng)) 106 | }); 107 | criterion.bench_function("kiltz_vahlis_one encrypt", move |b| { 108 | let mut rng = rand::thread_rng(); 109 | b.iter(|| encrypt(black_box(&pk), black_box(&kid), &mut rng)) 110 | }); 111 | criterion.bench_function("kiltz_vahlis_one decrypt", move |b| { 112 | b.iter(|| decrypt(black_box(&usk), black_box(&c))) 113 | }); 114 | } 115 | 116 | criterion_group!( 117 | benches, 118 | criterion_waters_benchmark, 119 | criterion_waters_naccache_benchmark, 120 | criterion_kiltz_vahlis_one_benchmark, 121 | ); 122 | criterion_main!(benches); 123 | -------------------------------------------------------------------------------- /src/kiltz_vahlis_one.rs: -------------------------------------------------------------------------------- 1 | //! Identity Based Encryption Kiltz-Vahlis IBE1 scheme on the [BLS12-381 pairing-friendly elliptic curve](https://github.com/zkcrypto/bls12_381). 2 | //! * From: "[CCA2 Secure IBE: Standard Model Efficiency through Authenticated Symmetric Encryption](https://link.springer.com/chapter/10.1007/978-3-540-79263-5_14)" 3 | //! * Published in: CT-RSA, 2008 4 | //! 5 | //! Uses [SHA3-512](https://crates.io/crates/tiny-keccak) for hashing to identities. 6 | //! 7 | //! The structure of the byte serialisation of the various datastructures is not guaranteed 8 | //! to remain constant between releases of this library. 9 | //! All operations in this library are implemented to run in constant time. 10 | 11 | use crate::util::*; 12 | use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; 13 | use irmaseal_curve::{G1Affine, G1Projective, G2Affine, Gt, Scalar}; 14 | use rand::Rng; 15 | use subtle::{Choice, ConditionallySelectable, CtOption}; 16 | 17 | const K: usize = 256; 18 | const N: usize = 2 * K; 19 | const N_BYTE_LEN: usize = N / 8; 20 | 21 | const HASH_PARAMETER_SIZE: usize = N * 48; 22 | 23 | const PUBLICKEYSIZE: usize = 96 + 48 + HASH_PARAMETER_SIZE + 48 + 288; 24 | 25 | struct HashParameters([G1Affine; N]); 26 | 27 | /// Public key parameters generated by the PKG used to encrypt messages. 28 | #[derive(Clone, Copy, PartialEq)] 29 | pub struct PublicKey { 30 | g: G2Affine, 31 | hzero: G1Affine, 32 | h: HashParameters, 33 | u: G1Affine, 34 | z: Gt, 35 | } 36 | 37 | /// Secret key parameter generated by the PKG used to extract user secret keys. 38 | #[derive(Clone, Copy, Debug, PartialEq)] 39 | pub struct SecretKey { 40 | alpha: G1Affine, 41 | } 42 | 43 | /// Points on the paired curves that form the user secret key. 44 | #[derive(Clone, Copy, PartialEq, Debug)] 45 | pub struct UserSecretKey { 46 | d1: G1Affine, 47 | d2: G2Affine, 48 | d3: G1Affine, 49 | } 50 | 51 | /// Byte representation of an identity. 52 | /// 53 | /// Can be hashed to the curve together with some parameters from the Public Key. 54 | pub struct Identity([u8; N_BYTE_LEN]); 55 | 56 | /// Encrypted message. Can only be decrypted with an user secret key. 57 | #[derive(Clone, Copy, Debug, PartialEq)] 58 | pub struct CipherText { 59 | c1: G2Affine, 60 | c2: G1Affine, 61 | } 62 | 63 | /// A point on the paired curve that can be encrypted and decrypted. 64 | /// 65 | /// You can use the byte representation to derive an AES key. 66 | #[derive(Clone, Copy, Debug, PartialEq)] 67 | pub struct SymmetricKey(Gt); 68 | 69 | /// Generate a keypair used by the Private Key Generator (PKG). 70 | pub fn setup(rng: &mut R) -> (PublicKey, SecretKey) { 71 | let g: G2Affine = rand_g2(rng).into(); 72 | 73 | let alpha: G1Affine = rand_g1(rng).into(); 74 | let u: G1Affine = rand_g1(rng).into(); 75 | let z = irmaseal_curve::pairing(&alpha, &g); 76 | 77 | let hzero = G1Affine::default(); 78 | let mut h = HashParameters([G1Affine::default(); N]); 79 | for hi in h.0.iter_mut() { 80 | *hi = rand_g1(rng).into(); 81 | } 82 | 83 | let pk = PublicKey { g, hzero, h, u, z }; 84 | let sk = SecretKey { alpha }; 85 | 86 | (pk, sk) 87 | } 88 | 89 | fn hash_to_curve(pk: &PublicKey, v: &Identity) -> G1Projective { 90 | let mut hcoll: G1Projective = pk.hzero.into(); 91 | for (hi, vi) in pk.h.0.iter().zip(bits(&v.0)) { 92 | hcoll = G1Projective::conditional_select(&hcoll, &(hi + hcoll), vi); 93 | } 94 | hcoll 95 | } 96 | 97 | fn hash_g2_to_scalar(x: G2Affine) -> Scalar { 98 | let buf = sha3_512(&x.to_uncompressed()); 99 | Scalar::from_bytes_wide(&buf) 100 | } 101 | 102 | /// Extract an user secret key for a given identity. 103 | pub fn extract_usk( 104 | pk: &PublicKey, 105 | sk: &SecretKey, 106 | v: &Identity, 107 | rng: &mut R, 108 | ) -> UserSecretKey { 109 | let s = rand_scalar(rng); 110 | 111 | let d1 = (sk.alpha + (hash_to_curve(pk, v) * s)).into(); 112 | let d2 = (pk.g * (-s)).into(); 113 | let d3 = (pk.u * s).into(); 114 | 115 | UserSecretKey { d1, d2, d3 } 116 | } 117 | 118 | /// Generate a symmetric key and corresponding CipherText for that key. 119 | pub fn encrypt(pk: &PublicKey, v: &Identity, rng: &mut R) -> (CipherText, SymmetricKey) { 120 | let r = rand_scalar(rng); 121 | 122 | let c1 = (pk.g * r).into(); 123 | let t = hash_g2_to_scalar(c1); 124 | let c2 = ((hash_to_curve(pk, v) + (pk.u * t)) * r).into(); 125 | let k = pk.z * r; 126 | 127 | (CipherText { c1, c2 }, SymmetricKey(k)) 128 | } 129 | 130 | /// Decrypt ciphertext to a SymmetricKey using a user secret key. 131 | pub fn decrypt(usk: &UserSecretKey, c: &CipherText) -> SymmetricKey { 132 | let t = hash_g2_to_scalar(c.c1); 133 | let k1 = irmaseal_curve::pairing(&(usk.d1 + (usk.d3 * t)).into(), &c.c1); 134 | let k2 = irmaseal_curve::pairing(&c.c2, &usk.d2); 135 | 136 | let k = k1 + k2; 137 | 138 | SymmetricKey(k) 139 | } 140 | 141 | impl Identity { 142 | /// Hash a byte slice to a set of Identity parameters, which acts as a user public key. 143 | /// Uses sha3-512 internally. 144 | pub fn derive(b: &[u8]) -> Identity { 145 | Identity(sha3_512(b)) 146 | } 147 | 148 | /// Hash a string slice to a set of Identity parameters. 149 | /// Directly converts characters to UTF-8 byte representation. 150 | pub fn derive_str(s: &str) -> Identity { 151 | Self::derive(s.as_bytes()) 152 | } 153 | } 154 | 155 | impl Clone for Identity { 156 | fn clone(&self) -> Self { 157 | let mut res = [u8::default(); N_BYTE_LEN]; 158 | for (src, dst) in self.0.iter().zip(res.as_mut().iter_mut()) { 159 | *dst = *src; 160 | } 161 | Identity(res) 162 | } 163 | } 164 | 165 | impl Copy for Identity {} 166 | 167 | impl SymmetricKey { 168 | pub fn to_bytes(&self) -> [u8; 288] { 169 | self.0.to_compressed() 170 | } 171 | 172 | pub fn from_bytes(bytes: &[u8; 288]) -> CtOption { 173 | Gt::from_compressed(bytes).map(Self) 174 | } 175 | } 176 | 177 | impl HashParameters { 178 | pub fn to_bytes(&self) -> [u8; HASH_PARAMETER_SIZE] { 179 | let mut res = [0u8; HASH_PARAMETER_SIZE]; 180 | for i in 0..N { 181 | *array_mut_ref![&mut res, i * 48, 48] = self.0[i].to_compressed(); 182 | } 183 | res 184 | } 185 | 186 | pub fn from_bytes(bytes: &[u8; HASH_PARAMETER_SIZE]) -> CtOption { 187 | let mut res = [G1Affine::default(); N]; 188 | let mut is_some = Choice::from(1u8); 189 | for i in 0..N { 190 | // See comment in PublicKey::from_bytes on cofactor. 191 | is_some &= G1Affine::from_compressed_unchecked(array_ref![bytes, i * 48, 48]) 192 | .map(|s| { 193 | res[i] = s; 194 | }) 195 | .is_some(); 196 | } 197 | CtOption::new(HashParameters(res), is_some) 198 | } 199 | } 200 | 201 | impl ConditionallySelectable for HashParameters { 202 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 203 | let mut res = [G1Affine::default(); N]; 204 | for (i, (ai, bi)) in a.0.iter().zip(b.0.iter()).enumerate() { 205 | res[i] = G1Affine::conditional_select(&ai, &bi, choice); 206 | } 207 | HashParameters(res) 208 | } 209 | } 210 | 211 | impl PartialEq for HashParameters { 212 | fn eq(&self, rhs: &HashParameters) -> bool { 213 | self.0.iter().zip(rhs.0.iter()).all(|(x, y)| x.eq(y)) 214 | } 215 | } 216 | 217 | impl Clone for HashParameters { 218 | fn clone(&self) -> Self { 219 | let mut res = [G1Affine::default(); N]; 220 | for (src, dst) in self.0.iter().zip(res.as_mut().iter_mut()) { 221 | *dst = *src; 222 | } 223 | Self(res) 224 | } 225 | } 226 | 227 | impl Copy for HashParameters {} 228 | 229 | impl Default for HashParameters { 230 | fn default() -> Self { 231 | HashParameters([G1Affine::default(); N]) 232 | } 233 | } 234 | 235 | impl PublicKey { 236 | pub fn to_bytes(&self) -> [u8; PUBLICKEYSIZE] { 237 | let mut res = [0u8; PUBLICKEYSIZE]; 238 | 239 | let (g, hzero, h, u, z) = mut_array_refs![&mut res, 96, 48, HASH_PARAMETER_SIZE, 48, 288]; 240 | *g = self.g.to_compressed(); 241 | *hzero = self.hzero.to_compressed(); 242 | *h = self.h.to_bytes(); 243 | *u = self.u.to_compressed(); 244 | *z = self.z.to_compressed(); 245 | res 246 | } 247 | 248 | pub fn from_bytes(bytes: &[u8; PUBLICKEYSIZE]) -> CtOption { 249 | let (g, hzero, h, u, z) = array_refs![&bytes, 96, 48, HASH_PARAMETER_SIZE, 48, 288]; 250 | 251 | // from_compressed_unchecked doesn't check whether the element has 252 | // a cofactor. To mount an attack using a cofactor an attacker 253 | // must be able to manipulate the public parameters. But then the 254 | // attacker can simply use parameters they generated themselves. 255 | // Thus checking for a cofactor is superfluous. 256 | let g = G2Affine::from_compressed_unchecked(g); 257 | let hzero = G1Affine::from_compressed_unchecked(hzero); 258 | let h = HashParameters::from_bytes(h); 259 | let u = G1Affine::from_compressed_unchecked(u); 260 | let z = Gt::from_compressed_unchecked(z); 261 | 262 | g.and_then(|g| { 263 | hzero.and_then(|hzero| { 264 | h.and_then(|h| u.and_then(|u| z.map(|z| PublicKey { g, hzero, h, u, z }))) 265 | }) 266 | }) 267 | } 268 | } 269 | 270 | impl SecretKey { 271 | pub fn to_bytes(&self) -> [u8; 48] { 272 | self.alpha.to_compressed() 273 | } 274 | 275 | pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { 276 | G1Affine::from_compressed(bytes).map(|alpha| SecretKey { alpha }) 277 | } 278 | } 279 | 280 | impl UserSecretKey { 281 | pub fn to_bytes(&self) -> [u8; 192] { 282 | let mut res = [0u8; 192]; 283 | let (d1, d2, d3) = mut_array_refs![&mut res, 48, 96, 48]; 284 | *d1 = self.d1.to_compressed(); 285 | *d2 = self.d2.to_compressed(); 286 | *d3 = self.d3.to_compressed(); 287 | res 288 | } 289 | 290 | pub fn from_bytes(bytes: &[u8; 192]) -> CtOption { 291 | let (d1, d2, d3) = array_refs![bytes, 48, 96, 48]; 292 | 293 | let d1 = G1Affine::from_compressed(d1); 294 | let d2 = G2Affine::from_compressed(d2); 295 | let d3 = G1Affine::from_compressed(d3); 296 | 297 | d1.and_then(|d1| d2.and_then(|d2| d3.map(|d3| UserSecretKey { d1, d2, d3 }))) 298 | } 299 | } 300 | 301 | impl CipherText { 302 | pub fn to_bytes(&self) -> [u8; 144] { 303 | let mut res = [0u8; 144]; 304 | let (c1, c2) = mut_array_refs![&mut res, 96, 48]; 305 | *c1 = self.c1.to_compressed(); 306 | *c2 = self.c2.to_compressed(); 307 | res 308 | } 309 | 310 | pub fn from_bytes(bytes: &[u8; 144]) -> CtOption { 311 | let (c1, c2) = array_refs![bytes, 96, 48]; 312 | 313 | let c1 = G2Affine::from_compressed(c1); 314 | let c2 = G1Affine::from_compressed(c2); 315 | 316 | c1.and_then(|c1| c2.map(|c2| CipherText { c1, c2 })) 317 | } 318 | } 319 | 320 | #[cfg(test)] 321 | mod tests { 322 | use super::*; 323 | 324 | const ID: &'static str = "email:w.geraedts@sarif.nl"; 325 | 326 | #[allow(dead_code)] 327 | struct DefaultSubResults { 328 | kid: Identity, 329 | pk: PublicKey, 330 | sk: SecretKey, 331 | usk: UserSecretKey, 332 | c: CipherText, 333 | k: SymmetricKey, 334 | } 335 | 336 | fn perform_default() -> DefaultSubResults { 337 | let mut rng = rand::thread_rng(); 338 | 339 | let id = ID.as_bytes(); 340 | let kid = Identity::derive(id); 341 | 342 | let (pk, sk) = setup(&mut rng); 343 | let usk = extract_usk(&pk, &sk, &kid, &mut rng); 344 | 345 | let (c, k) = encrypt(&pk, &kid, &mut rng); 346 | 347 | DefaultSubResults { 348 | kid, 349 | pk, 350 | sk, 351 | usk, 352 | c, 353 | k, 354 | } 355 | } 356 | 357 | #[test] 358 | fn eq_encrypt_decrypt() { 359 | let results = perform_default(); 360 | let k2 = decrypt(&results.usk, &results.c); 361 | 362 | assert_eq!(results.k, k2); 363 | } 364 | 365 | #[test] 366 | fn eq_serialize_deserialize() { 367 | let result = perform_default(); 368 | 369 | assert_eq!( 370 | result.k, 371 | SymmetricKey::from_bytes(&result.k.to_bytes()).unwrap() 372 | ); 373 | assert!(result.pk == PublicKey::from_bytes(&result.pk.to_bytes()).unwrap()); 374 | assert_eq!( 375 | result.sk, 376 | SecretKey::from_bytes(&result.sk.to_bytes()).unwrap() 377 | ); 378 | assert_eq!( 379 | result.usk, 380 | UserSecretKey::from_bytes(&result.usk.to_bytes()).unwrap() 381 | ); 382 | assert_eq!( 383 | result.c, 384 | CipherText::from_bytes(&result.c.to_bytes()).unwrap() 385 | ); 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Identity Based Encryption schemes on the [BLS12-381 pairing-friendly elliptic curve](https://github.com/zkcrypto/bls12_381). 2 | //! 3 | //! Implements the following schemes: 4 | //! * Waters 5 | //! * Waters-Naccache 6 | //! * Kiltz-Vahlis IBE1 7 | //! 8 | //! ## How to use 9 | //! The following example is similar for all the schemes. 10 | //! Check the corresponding tests for concrete examples per scheme. 11 | //! 12 | //! ``` 13 | //! use ibe::kiltz_vahlis_one::*; 14 | //! 15 | //! const ID: &'static str = "email:w.geraedts@sarif.nl"; 16 | //! let mut rng = rand::thread_rng(); 17 | //! 18 | //! // Hash the identity to a set of scalars. 19 | //! let kid = Identity::derive(ID.as_bytes()); 20 | //! 21 | //! // Generate a public-private-keypair for a trusted third party. 22 | //! let (pk, sk) = setup(&mut rng); 23 | //! 24 | //! // Extract a private key for an identity / user. 25 | //! let usk = extract_usk(&pk, &sk, &kid, &mut rng); 26 | //! 27 | //! // Generate a random message and encrypt it with the public key and an identity. 28 | //! let (c, k) = encrypt(&pk, &kid, &mut rng); 29 | //! 30 | //! // Decrypt the ciphertext of that message with the private key of the user. 31 | //! let k2 = decrypt(&usk, &c); 32 | //! 33 | //! assert_eq!(k, k2); 34 | //! ``` 35 | 36 | #![no_std] 37 | 38 | #[cfg(test)] 39 | #[macro_use] 40 | extern crate std; 41 | 42 | mod util; 43 | 44 | pub mod kiltz_vahlis_one; 45 | pub mod waters; 46 | pub mod waters_naccache; 47 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use irmaseal_curve::{G1Affine, G1Projective, G2Affine, G2Projective, Gt, Scalar}; 2 | 3 | pub fn rand_scalar(rng: &mut R) -> Scalar { 4 | let mut buf = [0u8; 64]; 5 | rng.fill_bytes(&mut buf); 6 | 7 | Scalar::from_bytes_wide(&buf) 8 | } 9 | 10 | pub fn rand_g1(rng: &mut R) -> G1Projective { 11 | use core::ops::Mul; 12 | let g = G1Projective::generator(); 13 | let x = rand_scalar(rng); 14 | g.mul(x) 15 | } 16 | 17 | pub fn rand_g2(rng: &mut R) -> G2Projective { 18 | use core::ops::Mul; 19 | let g = G2Projective::generator(); 20 | let x = rand_scalar(rng); 21 | g.mul(x) 22 | } 23 | 24 | pub fn rand_gt(rng: &mut R) -> Gt { 25 | let generator = irmaseal_curve::pairing(&G1Affine::generator(), &G2Affine::generator()); 26 | 27 | let r = rand_scalar(rng); 28 | generator * r 29 | } 30 | 31 | pub fn bits<'a>(slice: &'a [u8]) -> impl Iterator + 'a { 32 | slice 33 | .iter() 34 | .rev() 35 | .zip((0..8).rev()) 36 | .map(|(x, i)| subtle::Choice::from((*x >> i) & 1)) 37 | } 38 | 39 | pub fn sha3_256(slice: &[u8]) -> [u8; 32] { 40 | use tiny_keccak::Hasher; 41 | 42 | let mut digest = tiny_keccak::Sha3::v256(); 43 | digest.update(slice); 44 | 45 | let mut buf = [0u8; 32]; 46 | digest.finalize(&mut buf); 47 | 48 | return buf; 49 | } 50 | 51 | pub fn sha3_512(slice: &[u8]) -> [u8; 64] { 52 | use tiny_keccak::Hasher; 53 | 54 | let mut digest = tiny_keccak::Sha3::v512(); 55 | digest.update(slice); 56 | 57 | let mut buf = [0u8; 64]; 58 | digest.finalize(&mut buf); 59 | 60 | return buf; 61 | } 62 | -------------------------------------------------------------------------------- /src/waters.rs: -------------------------------------------------------------------------------- 1 | //! Identity Based Encryption Waters scheme on the [BLS12-381 pairing-friendly elliptic curve](https://github.com/zkcrypto/bls12_381). 2 | //! * From: "[Efficient Identity-Based Encryption Without Random Oracles](https://link.springer.com/chapter/10.1007/11426639_7)" 3 | //! * Published in: EUROCRYPT, 2005 4 | //! 5 | //! Uses [SHA3-256](https://crates.io/crates/tiny-keccak) for hashing to identities. 6 | //! 7 | //! The structure of the byte serialisation of the various datastructures is not guaranteed 8 | //! to remain constant between releases of this library. 9 | //! All operations in this library are implemented to run in constant time. 10 | 11 | use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; 12 | use rand::Rng; 13 | use subtle::{Choice, ConditionallySelectable, CtOption}; 14 | 15 | use crate::util::*; 16 | use irmaseal_curve::{G1Affine, G1Projective, G2Affine, Gt}; 17 | 18 | const HASH_BIT_LEN: usize = 256; 19 | const HASH_BYTE_LEN: usize = HASH_BIT_LEN / 8; 20 | 21 | const CHUNKS: usize = HASH_BIT_LEN; 22 | 23 | const PARAMETERSIZE: usize = CHUNKS * 48; 24 | const PUBLICKEYSIZE: usize = 2 * 48 + 2 * 96 + PARAMETERSIZE; 25 | 26 | /// Public key parameters used for entanglement with identities. 27 | struct Parameters([G1Affine; CHUNKS]); 28 | 29 | /// Public key parameters generated by the PKG used to encrypt messages. 30 | #[derive(Clone, Copy, PartialEq)] 31 | pub struct PublicKey { 32 | g: G2Affine, 33 | g1: G1Affine, 34 | g2: G2Affine, 35 | uprime: G1Affine, 36 | u: Parameters, 37 | } 38 | 39 | /// Secret key parameter generated by the PKG used to extract user secret keys. 40 | #[derive(Clone, Copy, PartialEq, Debug)] 41 | pub struct SecretKey { 42 | g1prime: G1Affine, 43 | } 44 | 45 | /// Points on the paired curves that form the user secret key. 46 | #[derive(Clone, Copy, PartialEq, Debug)] 47 | pub struct UserSecretKey { 48 | d1: G1Affine, 49 | d2: G2Affine, 50 | } 51 | 52 | /// Field parameters for an identity. 53 | /// 54 | /// Effectively a hash of an identity, mapped to the curve field. 55 | /// Together with the public key parameters generated by the PKG forms the user public key. 56 | pub struct Identity([u8; HASH_BYTE_LEN]); 57 | 58 | /// A point on the paired curve that can be encrypted and decrypted. 59 | /// 60 | /// You can use the byte representation to derive an AES key. 61 | #[derive(Clone, Copy, Debug, PartialEq)] 62 | pub struct Message(Gt); 63 | 64 | /// Encrypted message. Can only be decrypted with an user secret key. 65 | #[derive(Clone, Copy, Debug, PartialEq)] 66 | pub struct CipherText { 67 | c1: Gt, 68 | c2: G2Affine, 69 | c3: G1Affine, 70 | } 71 | 72 | /// Generate a keypair used by the Private Key Generator (PKG). 73 | pub fn setup(rng: &mut R) -> (PublicKey, SecretKey) { 74 | let g: G2Affine = rand_g2(rng).into(); 75 | 76 | let alpha = rand_scalar(rng); 77 | let g2 = (g * alpha).into(); 78 | 79 | let g1 = rand_g1(rng).into(); 80 | let uprime = rand_g1(rng).into(); 81 | 82 | let mut u = Parameters([G1Affine::default(); CHUNKS]); 83 | for ui in u.0.iter_mut() { 84 | *ui = rand_g1(rng).into(); 85 | } 86 | 87 | let pk = PublicKey { 88 | g, 89 | g1, 90 | g2, 91 | uprime, 92 | u, 93 | }; 94 | 95 | let g1prime: G1Affine = (g1 * alpha).into(); 96 | 97 | let sk = SecretKey { g1prime }; 98 | 99 | (pk, sk) 100 | } 101 | 102 | /// Common operation used in extraction and encryption to entangle 103 | /// PublicKey with Identity into a point on G1. 104 | fn entangle(pk: &PublicKey, v: &Identity) -> G1Projective { 105 | let mut ucoll: G1Projective = pk.uprime.into(); 106 | for (ui, vi) in pk.u.0.iter().zip(bits(&v.0)) { 107 | ucoll = G1Projective::conditional_select(&ucoll, &(ui + ucoll), vi); 108 | } 109 | ucoll 110 | } 111 | 112 | /// Extract an user secret key for a given identity. 113 | pub fn extract_usk( 114 | pk: &PublicKey, 115 | sk: &SecretKey, 116 | v: &Identity, 117 | rng: &mut R, 118 | ) -> UserSecretKey { 119 | let r = rand_scalar(rng); 120 | let ucoll = entangle(pk, v); 121 | let d1 = (sk.g1prime + (ucoll * r)).into(); 122 | let d2 = (pk.g * r).into(); 123 | 124 | UserSecretKey { d1, d2 } 125 | } 126 | 127 | /// Encrypt a message using the PKG public key and an identity. 128 | pub fn encrypt(pk: &PublicKey, v: &Identity, m: &Message, rng: &mut R) -> CipherText { 129 | let t = rand_scalar(rng); 130 | 131 | let c3coll = entangle(pk, v); 132 | let c1 = irmaseal_curve::pairing(&pk.g1, &pk.g2) * t + m.0; 133 | let c2 = (pk.g * t).into(); 134 | let c3 = (c3coll * t).into(); 135 | 136 | CipherText { c1, c2, c3 } 137 | } 138 | 139 | /// Decrypt ciphertext to a message using a user secret key. 140 | pub fn decrypt(usk: &UserSecretKey, c: &CipherText) -> Message { 141 | let num = irmaseal_curve::pairing(&c.c3, &usk.d2); 142 | let dem = irmaseal_curve::pairing(&usk.d1, &c.c2); 143 | 144 | let m = c.c1 + num - dem; 145 | Message(m) 146 | } 147 | 148 | impl PublicKey { 149 | pub fn to_bytes(&self) -> [u8; PUBLICKEYSIZE] { 150 | let mut res = [0u8; PUBLICKEYSIZE]; 151 | let (g, g1, g2, uprime, u) = mut_array_refs![&mut res, 96, 48, 96, 48, PARAMETERSIZE]; 152 | *g = self.g.to_compressed(); 153 | *g1 = self.g1.to_compressed(); 154 | *g2 = self.g2.to_compressed(); 155 | *uprime = self.uprime.to_compressed(); 156 | *u = self.u.to_bytes(); 157 | res 158 | } 159 | 160 | pub fn from_bytes(bytes: &[u8; PUBLICKEYSIZE]) -> CtOption { 161 | let (g, g1, g2, uprime, u) = array_refs![bytes, 96, 48, 96, 48, PARAMETERSIZE]; 162 | 163 | let g = G2Affine::from_compressed(g); 164 | let g1 = G1Affine::from_compressed(g1); 165 | let g2 = G2Affine::from_compressed(g2); 166 | let uprime = G1Affine::from_compressed(uprime); 167 | let u = Parameters::from_bytes(u); 168 | 169 | g.and_then(|g| { 170 | g1.and_then(|g1| { 171 | g2.and_then(|g2| { 172 | uprime.and_then(|uprime| { 173 | u.map(|u| PublicKey { 174 | g, 175 | g1, 176 | g2, 177 | uprime, 178 | u, 179 | }) 180 | }) 181 | }) 182 | }) 183 | }) 184 | } 185 | } 186 | 187 | impl SecretKey { 188 | pub fn to_bytes(&self) -> [u8; 48] { 189 | self.g1prime.to_compressed() 190 | } 191 | 192 | pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { 193 | G1Affine::from_compressed(bytes).map(|g1prime| SecretKey { g1prime }) 194 | } 195 | } 196 | 197 | impl UserSecretKey { 198 | pub fn to_bytes(&self) -> [u8; 144] { 199 | let mut res = [0u8; 144]; 200 | let (d1, d2) = mut_array_refs![&mut res, 48, 96]; 201 | *d1 = self.d1.to_compressed(); 202 | *d2 = self.d2.to_compressed(); 203 | res 204 | } 205 | 206 | pub fn from_bytes(bytes: &[u8; 144]) -> CtOption { 207 | let (d1, d2) = array_refs![bytes, 48, 96]; 208 | 209 | let d1 = G1Affine::from_compressed(d1); 210 | let d2 = G2Affine::from_compressed(d2); 211 | 212 | d1.and_then(|d1| d2.map(|d2| UserSecretKey { d1, d2 })) 213 | } 214 | } 215 | 216 | impl Message { 217 | /// Generate a random point on the paired curve. 218 | pub fn generate(rng: &mut R) -> Self { 219 | Self(rand_gt(rng)) 220 | } 221 | 222 | pub fn to_bytes(&self) -> [u8; 288] { 223 | self.0.to_compressed() 224 | } 225 | 226 | pub fn from_bytes(bytes: &[u8; 288]) -> CtOption { 227 | Gt::from_compressed(bytes).map(Message) 228 | } 229 | } 230 | 231 | impl Parameters { 232 | pub fn to_bytes(&self) -> [u8; PARAMETERSIZE] { 233 | let mut res = [0u8; PARAMETERSIZE]; 234 | for i in 0..CHUNKS { 235 | *array_mut_ref![&mut res, i * 48, 48] = self.0[i].to_compressed(); 236 | } 237 | res 238 | } 239 | 240 | pub fn from_bytes(bytes: &[u8; PARAMETERSIZE]) -> CtOption { 241 | let mut res = [G1Affine::default(); CHUNKS]; 242 | let mut is_some = Choice::from(1u8); 243 | for i in 0..CHUNKS { 244 | is_some &= G1Affine::from_compressed(array_ref![bytes, i * 48, 48]) 245 | .map(|s| { 246 | res[i] = s; 247 | }) 248 | .is_some(); 249 | } 250 | CtOption::new(Parameters(res), is_some) 251 | } 252 | } 253 | 254 | impl ConditionallySelectable for Parameters { 255 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 256 | let mut res = [G1Affine::default(); CHUNKS]; 257 | for (i, (ai, bi)) in a.0.iter().zip(b.0.iter()).enumerate() { 258 | res[i] = G1Affine::conditional_select(&ai, &bi, choice); 259 | } 260 | Parameters(res) 261 | } 262 | } 263 | 264 | impl Clone for Parameters { 265 | fn clone(&self) -> Self { 266 | let mut res = [G1Affine::default(); CHUNKS]; 267 | for (src, dst) in self.0.iter().zip(res.as_mut().iter_mut()) { 268 | *dst = *src; 269 | } 270 | Parameters(res) 271 | } 272 | } 273 | 274 | impl Copy for Parameters {} 275 | 276 | impl PartialEq for Parameters { 277 | fn eq(&self, rhs: &Self) -> bool { 278 | self.0.iter().zip(rhs.0.iter()).all(|(x, y)| x.eq(y)) 279 | } 280 | } 281 | 282 | impl Default for Parameters { 283 | fn default() -> Self { 284 | Parameters([G1Affine::default(); CHUNKS]) 285 | } 286 | } 287 | 288 | impl Identity { 289 | /// Hash a byte slice to a set of Identity parameters, which acts as a user public key. 290 | /// Uses sha3-256 internally. 291 | pub fn derive(b: &[u8]) -> Identity { 292 | Identity(sha3_256(b)) 293 | } 294 | 295 | /// Hash a string slice to a set of Identity parameters. 296 | /// Directly converts characters to UTF-8 byte representation. 297 | pub fn derive_str(s: &str) -> Identity { 298 | Self::derive(s.as_bytes()) 299 | } 300 | } 301 | 302 | impl Clone for Identity { 303 | fn clone(&self) -> Self { 304 | let mut res = [u8::default(); HASH_BYTE_LEN]; 305 | for (src, dst) in self.0.iter().zip(res.as_mut().iter_mut()) { 306 | *dst = *src; 307 | } 308 | Identity(res) 309 | } 310 | } 311 | 312 | impl Copy for Identity {} 313 | 314 | impl CipherText { 315 | pub fn to_bytes(&self) -> [u8; 432] { 316 | let mut res = [0u8; 432]; 317 | let (c1, c2, c3) = mut_array_refs![&mut res, 288, 96, 48]; 318 | *c1 = self.c1.to_compressed(); 319 | *c2 = self.c2.to_compressed(); 320 | *c3 = self.c3.to_compressed(); 321 | res 322 | } 323 | 324 | pub fn from_bytes(bytes: &[u8; 432]) -> CtOption { 325 | let (c1, c2, c3) = array_refs![bytes, 288, 96, 48]; 326 | 327 | let c1 = Gt::from_compressed(c1); 328 | let c2 = G2Affine::from_compressed(c2); 329 | let c3 = G1Affine::from_compressed(c3); 330 | 331 | c1.and_then(|c1| c2.and_then(|c2| c3.map(|c3| CipherText { c1, c2, c3 }))) 332 | } 333 | } 334 | 335 | #[cfg(test)] 336 | mod tests { 337 | use super::*; 338 | 339 | const ID: &'static str = "email:w.geraedts@sarif.nl"; 340 | 341 | #[allow(dead_code)] 342 | struct DefaultSubResults { 343 | kid: Identity, 344 | m: Message, 345 | pk: PublicKey, 346 | sk: SecretKey, 347 | usk: UserSecretKey, 348 | c: CipherText, 349 | } 350 | 351 | fn perform_default() -> DefaultSubResults { 352 | let mut rng = rand::thread_rng(); 353 | 354 | let id = ID.as_bytes(); 355 | let kid = Identity::derive(id); 356 | 357 | let m = Message::generate(&mut rng); 358 | 359 | let (pk, sk) = setup(&mut rng); 360 | let usk = extract_usk(&pk, &sk, &kid, &mut rng); 361 | 362 | let c = encrypt(&pk, &kid, &m, &mut rng); 363 | 364 | DefaultSubResults { 365 | kid, 366 | m, 367 | pk, 368 | sk, 369 | usk, 370 | c, 371 | } 372 | } 373 | 374 | #[test] 375 | fn eq_encrypt_decrypt() { 376 | let results = perform_default(); 377 | let m2 = decrypt(&results.usk, &results.c); 378 | 379 | assert_eq!(results.m, m2); 380 | } 381 | 382 | #[test] 383 | fn eq_serialize_deserialize() { 384 | let result = perform_default(); 385 | 386 | assert_eq!(result.m, Message::from_bytes(&result.m.to_bytes()).unwrap()); 387 | assert!(result.pk == PublicKey::from_bytes(&result.pk.to_bytes()).unwrap()); 388 | assert_eq!( 389 | result.sk, 390 | SecretKey::from_bytes(&result.sk.to_bytes()).unwrap() 391 | ); 392 | assert_eq!( 393 | result.usk, 394 | UserSecretKey::from_bytes(&result.usk.to_bytes()).unwrap() 395 | ); 396 | assert_eq!( 397 | result.c, 398 | CipherText::from_bytes(&result.c.to_bytes()).unwrap() 399 | ); 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /src/waters_naccache.rs: -------------------------------------------------------------------------------- 1 | //! Identity Based Encryption Waters-Naccache scheme on the [BLS12-381 pairing-friendly elliptic curve](https://github.com/zkcrypto/bls12_381). 2 | //! * Inspired by: [CHARM implementation](https://github.com/JHUISI/charm/blob/dev/charm/schemes/ibenc/ibenc_waters05.py) 3 | //! * From: "[Secure and Practical Identity-Based Encryption](http://eprint.iacr.org/2005/369.pdf)" 4 | //! * Published in: IET Information Security, 2007 5 | //! 6 | //! Uses [SHA3-512](https://crates.io/crates/tiny-keccak) for hashing to identities. 7 | //! 8 | //! The structure of the byte serialisation of the various datastructures is not guaranteed 9 | //! to remain constant between releases of this library. 10 | //! All operations in this library are implemented to run in constant time. 11 | 12 | use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; 13 | use rand::Rng; 14 | use subtle::{Choice, ConditionallySelectable, CtOption}; 15 | 16 | use crate::util::*; 17 | use irmaseal_curve::{G1Affine, G2Affine, G2Projective, Gt, Scalar}; 18 | 19 | const HASH_BIT_LEN: usize = 512; 20 | const HASH_BYTE_LEN: usize = HASH_BIT_LEN / 8; 21 | 22 | const BITSIZE: usize = 32; 23 | const CHUNKSIZE: usize = BITSIZE / 8; 24 | const CHUNKS: usize = HASH_BYTE_LEN / CHUNKSIZE; 25 | 26 | const PARAMETERSIZE: usize = CHUNKS * 96; 27 | const PUBLICKEYSIZE: usize = 2 * 48 + 2 * 96 + PARAMETERSIZE; 28 | 29 | #[derive(Default, Clone, Copy, PartialEq, Debug)] 30 | struct Parameters([G2Affine; CHUNKS]); 31 | 32 | impl ConditionallySelectable for Parameters { 33 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 34 | let mut res = [G2Affine::default(); CHUNKS]; 35 | for (i, (ai, bi)) in a.0.iter().zip(b.0.iter()).enumerate() { 36 | res[i] = G2Affine::conditional_select(&ai, &bi, choice); 37 | } 38 | Parameters(res) 39 | } 40 | } 41 | 42 | impl Parameters { 43 | pub fn to_bytes(&self) -> [u8; PARAMETERSIZE] { 44 | let mut res = [0u8; PARAMETERSIZE]; 45 | for i in 0..CHUNKS { 46 | *array_mut_ref![&mut res, i * 96, 96] = self.0[i].to_compressed(); 47 | } 48 | res 49 | } 50 | 51 | pub fn from_bytes(bytes: &[u8; PARAMETERSIZE]) -> CtOption { 52 | let mut res = [G2Affine::default(); CHUNKS]; 53 | let mut is_some = Choice::from(1u8); 54 | for i in 0..CHUNKS { 55 | is_some &= G2Affine::from_compressed(array_ref![bytes, i * 96, 96]) 56 | .map(|s| { 57 | res[i] = s; 58 | }) 59 | .is_some(); 60 | } 61 | CtOption::new(Parameters(res), is_some) 62 | } 63 | } 64 | 65 | /// Public key parameters generated by the PKG used to encrypt messages. 66 | #[derive(Clone, Copy, PartialEq, Debug)] 67 | pub struct PublicKey { 68 | g: G1Affine, 69 | g1: G1Affine, 70 | g2: G2Affine, 71 | uprime: G2Affine, 72 | u: Parameters, 73 | } 74 | 75 | impl PublicKey { 76 | pub fn to_bytes(&self) -> [u8; PUBLICKEYSIZE] { 77 | let mut res = [0u8; PUBLICKEYSIZE]; 78 | let (g, g1, g2, uprime, u) = mut_array_refs![&mut res, 48, 48, 96, 96, PARAMETERSIZE]; 79 | *g = self.g.to_compressed(); 80 | *g1 = self.g1.to_compressed(); 81 | *g2 = self.g2.to_compressed(); 82 | *uprime = self.uprime.to_compressed(); 83 | *u = self.u.to_bytes(); 84 | res 85 | } 86 | 87 | pub fn from_bytes(bytes: &[u8; PUBLICKEYSIZE]) -> CtOption { 88 | let (g, g1, g2, uprime, u) = array_refs![bytes, 48, 48, 96, 96, PARAMETERSIZE]; 89 | 90 | let g = G1Affine::from_compressed(g); 91 | let g1 = G1Affine::from_compressed(g1); 92 | let g2 = G2Affine::from_compressed(g2); 93 | let uprime = G2Affine::from_compressed(uprime); 94 | let u = Parameters::from_bytes(u); 95 | 96 | g.and_then(|g| { 97 | g1.and_then(|g1| { 98 | g2.and_then(|g2| { 99 | uprime.and_then(|uprime| { 100 | u.map(|u| PublicKey { 101 | g, 102 | g1, 103 | g2, 104 | uprime, 105 | u, 106 | }) 107 | }) 108 | }) 109 | }) 110 | }) 111 | } 112 | } 113 | 114 | /// Field parameters for an identity. 115 | /// 116 | /// Effectively a hash of an identity, mapped to the curve field. 117 | /// Together with the public key parameters generated by the PKG forms the user public key. 118 | #[derive(Clone, Copy, Debug, PartialEq)] 119 | pub struct Identity([Scalar; CHUNKS]); 120 | 121 | /// Secret key parameter generated by the PKG used to extract user secret keys. 122 | #[derive(Clone, Copy, Debug, PartialEq)] 123 | pub struct SecretKey { 124 | g2prime: G2Affine, 125 | } 126 | 127 | impl SecretKey { 128 | pub fn to_bytes(&self) -> [u8; 96] { 129 | self.g2prime.to_compressed() 130 | } 131 | 132 | pub fn from_bytes(bytes: &[u8; 96]) -> CtOption { 133 | G2Affine::from_compressed(bytes).map(|g2prime| SecretKey { g2prime }) 134 | } 135 | } 136 | 137 | /// Points on the paired curves that form the user secret key. 138 | #[derive(Clone, Copy, Debug, PartialEq)] 139 | pub struct UserSecretKey { 140 | d1: G2Affine, 141 | d2: G1Affine, 142 | } 143 | 144 | impl UserSecretKey { 145 | pub fn to_bytes(&self) -> [u8; 144] { 146 | let mut res = [0u8; 144]; 147 | let (d1, d2) = mut_array_refs![&mut res, 96, 48]; 148 | *d1 = self.d1.to_compressed(); 149 | *d2 = self.d2.to_compressed(); 150 | res 151 | } 152 | 153 | pub fn from_bytes(bytes: &[u8; 144]) -> CtOption { 154 | let (d1, d2) = array_refs![bytes, 96, 48]; 155 | 156 | let d1 = G2Affine::from_compressed(d1); 157 | let d2 = G1Affine::from_compressed(d2); 158 | 159 | d1.and_then(|d1| d2.map(|d2| UserSecretKey { d1, d2 })) 160 | } 161 | } 162 | 163 | /// Encrypted message. Can only be decrypted with an user secret key. 164 | #[derive(Clone, Copy, Debug, PartialEq)] 165 | pub struct CipherText { 166 | c1: Gt, 167 | c2: G1Affine, 168 | c3: G2Affine, 169 | } 170 | 171 | impl CipherText { 172 | pub fn to_bytes(&self) -> [u8; 432] { 173 | let mut res = [0u8; 432]; 174 | let (c1, c2, c3) = mut_array_refs![&mut res, 288, 48, 96]; 175 | *c1 = self.c1.to_compressed(); 176 | *c2 = self.c2.to_compressed(); 177 | *c3 = self.c3.to_compressed(); 178 | res 179 | } 180 | 181 | pub fn from_bytes(bytes: &[u8; 432]) -> CtOption { 182 | let (c1, c2, c3) = array_refs![bytes, 288, 48, 96]; 183 | 184 | let c1 = Gt::from_compressed(c1); 185 | let c2 = G1Affine::from_compressed(c2); 186 | let c3 = G2Affine::from_compressed(c3); 187 | 188 | c1.and_then(|c1| c2.and_then(|c2| c3.map(|c3| CipherText { c1, c2, c3 }))) 189 | } 190 | } 191 | 192 | /// A point on the paired curve that can be encrypted and decrypted. 193 | /// 194 | /// You can use the byte representation to derive an AES key. 195 | #[derive(Clone, Copy, Debug, PartialEq)] 196 | pub struct Message(Gt); 197 | 198 | impl Message { 199 | /// Generate a random point on the paired curve. 200 | pub fn generate(rng: &mut R) -> Self { 201 | Self(rand_gt(rng)) 202 | } 203 | 204 | pub fn to_bytes(&self) -> [u8; 288] { 205 | self.0.to_compressed() 206 | } 207 | 208 | pub fn from_bytes(bytes: &[u8; 288]) -> CtOption { 209 | Gt::from_compressed(bytes).map(Message) 210 | } 211 | } 212 | 213 | /// Generate a keypair used by the Private Key Generator (PKG). 214 | pub fn setup(rng: &mut R) -> (PublicKey, SecretKey) { 215 | let g: G1Affine = rand_g1(rng).into(); 216 | 217 | let alpha = rand_scalar(rng); 218 | let g1 = (g * alpha).into(); 219 | 220 | let g2 = rand_g2(rng).into(); 221 | let uprime = rand_g2(rng).into(); 222 | 223 | let mut u = Parameters([G2Affine::default(); CHUNKS]); 224 | for ui in u.0.iter_mut() { 225 | *ui = rand_g2(rng).into(); 226 | } 227 | 228 | let pk = PublicKey { 229 | g, 230 | g1, 231 | g2, 232 | uprime, 233 | u, 234 | }; 235 | 236 | let g2prime: G2Affine = (g2 * alpha).into(); 237 | 238 | let sk = SecretKey { g2prime }; 239 | 240 | (pk, sk) 241 | } 242 | 243 | /// Common operation used in extraction and encryption to entangle 244 | /// PublicKey with Identity into a point on G2. 245 | fn entangle(pk: &PublicKey, v: &Identity) -> G2Projective { 246 | let mut ucoll: G2Projective = pk.uprime.into(); 247 | for (ui, vi) in pk.u.0.iter().zip(&v.0) { 248 | ucoll += ui * vi; 249 | } 250 | ucoll 251 | } 252 | 253 | /// Extract an user secret key for a given identity. 254 | pub fn extract_usk( 255 | pk: &PublicKey, 256 | sk: &SecretKey, 257 | v: &Identity, 258 | rng: &mut R, 259 | ) -> UserSecretKey { 260 | let r = rand_scalar(rng); 261 | let ucoll = entangle(pk, v); 262 | let d1 = (sk.g2prime + (ucoll * r)).into(); 263 | let d2 = (pk.g * r).into(); 264 | 265 | UserSecretKey { d1, d2 } 266 | } 267 | 268 | /// Encrypt a message using the PKG public key and an identity. 269 | pub fn encrypt(pk: &PublicKey, v: &Identity, m: &Message, rng: &mut R) -> CipherText { 270 | let t = rand_scalar(rng); 271 | 272 | let c3coll = entangle(pk, v); 273 | let c1 = irmaseal_curve::pairing(&pk.g1, &pk.g2) * t + m.0; 274 | let c2 = (pk.g * t).into(); 275 | let c3 = (c3coll * t).into(); 276 | 277 | CipherText { c1, c2, c3 } 278 | } 279 | 280 | /// Decrypt ciphertext to a message using a user secret key. 281 | pub fn decrypt(usk: &UserSecretKey, c: &CipherText) -> Message { 282 | let num = irmaseal_curve::pairing(&usk.d2, &c.c3); 283 | let dem = irmaseal_curve::pairing(&c.c2, &usk.d1); 284 | 285 | let m = c.c1 + num - dem; 286 | Message(m) 287 | } 288 | 289 | impl Identity { 290 | /// Hash a byte slice to a set of Identity parameters, which acts as a user public key. 291 | /// Uses sha3-512 internally. 292 | pub fn derive(b: &[u8]) -> Identity { 293 | let hash = sha3_512(b); 294 | 295 | let mut result = [Scalar::zero(); CHUNKS]; 296 | for (i, r) in result.iter_mut().enumerate().take(CHUNKS) { 297 | *r = u64::from(u32::from_le_bytes(*array_ref![ 298 | hash, 299 | i * CHUNKSIZE, 300 | CHUNKSIZE 301 | ])) 302 | .into(); 303 | } 304 | 305 | Identity(result) 306 | } 307 | 308 | /// Hash a string slice to a set of Identity parameters. 309 | /// Directly converts characters to UTF-8 byte representation. 310 | pub fn derive_str(s: &str) -> Identity { 311 | Self::derive(s.as_bytes()) 312 | } 313 | } 314 | 315 | #[cfg(test)] 316 | mod tests { 317 | use super::*; 318 | 319 | const ID: &'static str = "email:w.geraedts@sarif.nl"; 320 | 321 | #[allow(dead_code)] 322 | struct DefaultSubResults { 323 | kid: Identity, 324 | m: Message, 325 | pk: PublicKey, 326 | sk: SecretKey, 327 | usk: UserSecretKey, 328 | c: CipherText, 329 | } 330 | 331 | fn perform_default() -> DefaultSubResults { 332 | let mut rng = rand::thread_rng(); 333 | 334 | let id = ID.as_bytes(); 335 | let kid = Identity::derive(id); 336 | 337 | let m = Message::generate(&mut rng); 338 | 339 | let (pk, sk) = setup(&mut rng); 340 | let usk = extract_usk(&pk, &sk, &kid, &mut rng); 341 | 342 | let c = encrypt(&pk, &kid, &m, &mut rng); 343 | 344 | DefaultSubResults { 345 | kid, 346 | m, 347 | pk, 348 | sk, 349 | usk, 350 | c, 351 | } 352 | } 353 | 354 | #[test] 355 | fn eq_encrypt_decrypt() { 356 | let results = perform_default(); 357 | let m2 = decrypt(&results.usk, &results.c); 358 | 359 | assert_eq!(results.m, m2); 360 | } 361 | 362 | #[test] 363 | fn stability_identity() { 364 | const REFERENCE: &'static [u32; 16] = &[ 365 | 224058892, 3543031066, 2100894308, 1450993543, 380724969, 4144530249, 2749396120, 366 | 320408521, 409248772, 2464563459, 877936958, 2596797041, 3979538376, 3505820338, 367 | 590474010, 189115610, 368 | ]; 369 | 370 | let id = ID.as_bytes(); 371 | let kid = Identity::derive(id); 372 | 373 | for (kidi, ri) in kid.0.iter().zip(REFERENCE) { 374 | let mut buf = [0u8; 32]; 375 | buf[0..4].copy_from_slice(&ri.to_le_bytes()); 376 | 377 | assert_eq!(kidi.to_bytes(), buf); 378 | } 379 | } 380 | 381 | #[test] 382 | fn eq_serialize_deserialize() { 383 | let result = perform_default(); 384 | 385 | assert_eq!(result.m, Message::from_bytes(&result.m.to_bytes()).unwrap()); 386 | assert_eq!( 387 | result.pk, 388 | PublicKey::from_bytes(&result.pk.to_bytes()).unwrap() 389 | ); 390 | assert_eq!( 391 | result.sk, 392 | SecretKey::from_bytes(&result.sk.to_bytes()).unwrap() 393 | ); 394 | assert_eq!( 395 | result.usk, 396 | UserSecretKey::from_bytes(&result.usk.to_bytes()).unwrap() 397 | ); 398 | assert_eq!( 399 | result.c, 400 | CipherText::from_bytes(&result.c.to_bytes()).unwrap() 401 | ); 402 | } 403 | } 404 | --------------------------------------------------------------------------------