├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── sike-bench.rs ├── examples ├── kem434.rs ├── kem503.rs ├── kem610.rs ├── kem751.rs ├── pke434.rs ├── pke503.rs ├── pke610.rs └── pke751.rs └── src ├── constants ├── cs_p434.rs ├── cs_p503.rs ├── cs_p610.rs ├── cs_p751.rs └── mod.rs ├── ff ├── ff_p434.rs ├── ff_p503.rs ├── ff_p610.rs ├── ff_p751.rs └── mod.rs ├── isogeny ├── curve.rs ├── mod.rs ├── point.rs ├── publickey.rs ├── publicparams.rs └── secretkey.rs ├── kem └── mod.rs ├── lib.rs ├── pke └── mod.rs └── utils ├── conversion.rs ├── mod.rs ├── shake.rs └── strategy.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Remove bench results 13 | **/*.svg 14 | **/perf.data 15 | **/perf.data.old -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sike" 3 | version = "0.2.1" 4 | authors = ["Rémi Géraud-Stewart ", "Edmond de Roffignac "] 5 | edition = "2018" 6 | description = "Implementation of the key encapsulation mechanism (KEM) and public-key encryption (pke) schemes of the Supersingular Isogeny Key Encapsulation (SIKE) protocol" 7 | license = "MIT" 8 | readme = "README.md" 9 | keywords = ["crypto", "isogeny", "SIDH", "pke", "kem"] 10 | categories = ["cryptography", "science"] 11 | repository = "https://github.com/rust-crypto-labs/rust-sike" 12 | 13 | [dependencies] 14 | bitvec = "0.17" 15 | getrandom = "0.1" 16 | hex = "0.4" 17 | once_cell = "1.4" 18 | rug = { version = "1.10", features = ["integer"], default-features = false } 19 | sha3 = "0.9" 20 | 21 | [dev-dependencies] 22 | criterion = "0.3" 23 | flamegraph = "0.4" 24 | 25 | [profile.release] 26 | opt-level = 3 27 | debug = false 28 | lto = true 29 | 30 | [[bench]] 31 | name = "sike-bench" 32 | harness = false 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Edmond de Roffignac 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-sike 2 | 3 | `rust-sike` is a Rust implementation of the SIKE isogeny-based key encapsulation suite (SIKE [1]), a post-quantum candidate submitted to the NIST standardization process [2]. 4 | 5 | **WARNING: As of July 30th 2022, SIDH (supersingular Diffie-Hellman), the underlying quantum-resistant mechanism of SIKE, has been found to be vulnerable to an efficient key recovery attacks [3] (recovery time ranging from one hour to less than a day depending on the security level on a single core). Therefore SIDH/SIKE should not be considered as secure and `rust-sike` should only be used for test or research purpose and under no circumstances for any production code** 6 | 7 | ## Why `rust-sike`? 8 | 9 | The SIKE submission already comes with reference implementations, including optimised versions for different platforms. Additional implementations by Microsoft and Cloudflare are available. All these libraries are written in C, with occasional platform-specific assembly instructions, which allows them to reach maximum performance. At the time of writing these implementations match an older version of the SIKE specification. 10 | 11 | `rust-sike` is concerned with providing high *correctness* guarantees: adherence to the SIKE specification, memory and type safety, and reproducibility across platforms. Extensive testing and documentation is desired. Performance matters but is a longer-term concern. 12 | 13 | ## Status 14 | 15 | ### Supported features and algorithms 16 | 17 | * Key encapsulation mechanism (`KEM`) 18 | * Public-key encryption (`PKE`) 19 | * All the parameters described in the NIST submission: `p434`, `p503`, `p610`, and `p751`. 20 | * Optimised tree-traversal strategies 21 | 22 | The updated specification (April 17th 2019) is used as a basis for implementation. 23 | 24 | ### Unsupported features and caveats 25 | 26 | * Key compression and decompression are currently not supported (future work) 27 | * The implementation is not guaranteed to be constant time 28 | * The implementation is not `no_std` compatible (for non-essential reasons) 29 | 30 | ## References and documentation 31 | 32 | * 33 | * 34 | * 35 | 36 | [1]: https://sike.org/ 37 | [2]: https://csrc.nist.gov/Projects/Post-Quantum-Cryptography 38 | [3]: https://eprint.iacr.org/2022/975 39 | -------------------------------------------------------------------------------- /benches/sike-bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | 3 | use rust_sike::{ 4 | kem::KEM, 5 | pke::{Message, PKE}, 6 | sike_p434_params, sike_p503_params, sike_p610_params, sike_p751_params, 7 | strategy::*, 8 | }; 9 | 10 | pub fn bench_p434_pke_std(c: &mut Criterion) { 11 | let params = sike_p434_params(None, None).unwrap(); 12 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 13 | let pke = PKE::setup(params); 14 | 15 | let mut group = c.benchmark_group("SIKEp434 PKE (no opti)"); 16 | let (sk, pk) = pke.gen().unwrap(); 17 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 18 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()); 19 | 20 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 21 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 22 | group.bench_function("Decryption", |b| { 23 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 24 | }); 25 | 26 | group.finish(); 27 | } 28 | 29 | pub fn bench_p434_pke_optim(c: &mut Criterion) { 30 | let params = sike_p434_params( 31 | Some(P434_TWO_TORSION_STRATEGY.to_vec()), 32 | Some(P434_THREE_TORSION_STRATEGY.to_vec()), 33 | ) 34 | .unwrap(); 35 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 36 | let pke = PKE::setup(params); 37 | 38 | let mut group = c.benchmark_group("SIKEp434 PKE (opti)"); 39 | let (sk, pk) = pke.gen().unwrap(); 40 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 41 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()); 42 | 43 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 44 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 45 | group.bench_function("Decryption", |b| { 46 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 47 | }); 48 | 49 | group.finish(); 50 | } 51 | 52 | pub fn bench_p434_kem_std(c: &mut Criterion) { 53 | let params = sike_p434_params(None, None).unwrap(); 54 | let kem = KEM::setup(params); 55 | 56 | let mut group = c.benchmark_group("SIKEp434 KEM (no opti)"); 57 | let (s, sk3, pk3) = kem.keygen().unwrap(); 58 | let (c, _k) = kem.encaps(&pk3).unwrap(); 59 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 60 | 61 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 62 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 63 | group.bench_function("Decapsulation", |b| { 64 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 65 | }); 66 | 67 | group.finish(); 68 | } 69 | 70 | pub fn bench_p434_kem_optim(c: &mut Criterion) { 71 | let params = sike_p434_params( 72 | Some(P434_TWO_TORSION_STRATEGY.to_vec()), 73 | Some(P434_THREE_TORSION_STRATEGY.to_vec()), 74 | ) 75 | .unwrap(); 76 | let kem = KEM::setup(params); 77 | 78 | let mut group = c.benchmark_group("SIKEp434 KEM (opti)"); 79 | let (s, sk3, pk3) = kem.keygen().unwrap(); 80 | let (c, _k) = kem.encaps(&pk3).unwrap(); 81 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 82 | 83 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 84 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 85 | group.bench_function("Decapsulation", |b| { 86 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 87 | }); 88 | 89 | group.finish(); 90 | } 91 | 92 | pub fn bench_p503_pke_std(c: &mut Criterion) { 93 | let params = sike_p503_params(None, None).unwrap(); 94 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 95 | let pke = PKE::setup(params); 96 | 97 | let mut group = c.benchmark_group("SIKEp503 PKE (no opti)"); 98 | let (sk, pk) = pke.gen().unwrap(); 99 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 100 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()).unwrap(); 101 | 102 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 103 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 104 | group.bench_function("Decryption", |b| { 105 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 106 | }); 107 | 108 | group.finish(); 109 | } 110 | 111 | pub fn bench_p503_pke_optim(c: &mut Criterion) { 112 | let params = sike_p503_params( 113 | Some(P503_TWO_TORSION_STRATEGY.to_vec()), 114 | Some(P503_THREE_TORSION_STRATEGY.to_vec()), 115 | ) 116 | .unwrap(); 117 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 118 | let pke = PKE::setup(params); 119 | 120 | let mut group = c.benchmark_group("SIKEp503 PKE (opti)"); 121 | let (sk, pk) = pke.gen().unwrap(); 122 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 123 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()).unwrap(); 124 | 125 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 126 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 127 | group.bench_function("Decryption", |b| { 128 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 129 | }); 130 | 131 | group.finish(); 132 | } 133 | 134 | pub fn bench_p503_kem_std(c: &mut Criterion) { 135 | let params = sike_p503_params(None, None).unwrap(); 136 | let kem = KEM::setup(params); 137 | 138 | let mut group = c.benchmark_group("SIKEp503 KEM (no opti)"); 139 | let (s, sk3, pk3) = kem.keygen().unwrap(); 140 | let (c, _k) = kem.encaps(&pk3).unwrap(); 141 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 142 | 143 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 144 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 145 | group.bench_function("Decapsulation", |b| { 146 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 147 | }); 148 | 149 | group.finish(); 150 | } 151 | 152 | pub fn bench_p503_kem_optim(c: &mut Criterion) { 153 | let params = sike_p503_params( 154 | Some(P503_TWO_TORSION_STRATEGY.to_vec()), 155 | Some(P503_THREE_TORSION_STRATEGY.to_vec()), 156 | ) 157 | .unwrap(); 158 | let kem = KEM::setup(params); 159 | 160 | let mut group = c.benchmark_group("SIKEp503 KEM (opti)"); 161 | let (s, sk3, pk3) = kem.keygen().unwrap(); 162 | let (c, _k) = kem.encaps(&pk3).unwrap(); 163 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 164 | 165 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 166 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 167 | group.bench_function("Decapsulation", |b| { 168 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 169 | }); 170 | 171 | group.finish(); 172 | } 173 | 174 | pub fn bench_p610_pke_std(c: &mut Criterion) { 175 | let params = sike_p610_params(None, None).unwrap(); 176 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 177 | let pke = PKE::setup(params); 178 | 179 | let mut group = c.benchmark_group("SIKEp610 PKE (no opti)"); 180 | let (sk, pk) = pke.gen().unwrap(); 181 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 182 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()).unwrap(); 183 | 184 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 185 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 186 | group.bench_function("Decryption", |b| { 187 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 188 | }); 189 | 190 | group.finish(); 191 | } 192 | 193 | pub fn bench_p610_pke_optim(c: &mut Criterion) { 194 | let params = sike_p610_params( 195 | Some(P610_TWO_TORSION_STRATEGY.to_vec()), 196 | Some(P610_THREE_TORSION_STRATEGY.to_vec()), 197 | ) 198 | .unwrap(); 199 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 200 | let pke = PKE::setup(params); 201 | 202 | let mut group = c.benchmark_group("SIKEp610 PKE (opti)"); 203 | let (sk, pk) = pke.gen().unwrap(); 204 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 205 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()).unwrap(); 206 | 207 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 208 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 209 | group.bench_function("Decryption", |b| { 210 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 211 | }); 212 | 213 | group.finish(); 214 | } 215 | 216 | pub fn bench_p610_kem_std(c: &mut Criterion) { 217 | let params = sike_p610_params(None, None).unwrap(); 218 | let kem = KEM::setup(params); 219 | 220 | let mut group = c.benchmark_group("SIKEp610 KEM (no opti)"); 221 | let (s, sk3, pk3) = kem.keygen().unwrap(); 222 | let (c, _k) = kem.encaps(&pk3).unwrap(); 223 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 224 | 225 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 226 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 227 | group.bench_function("Decapsulation", |b| { 228 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 229 | }); 230 | 231 | group.finish(); 232 | } 233 | 234 | pub fn bench_p610_kem_optim(c: &mut Criterion) { 235 | let params = sike_p610_params( 236 | Some(P610_TWO_TORSION_STRATEGY.to_vec()), 237 | Some(P610_THREE_TORSION_STRATEGY.to_vec()), 238 | ) 239 | .unwrap(); 240 | let kem = KEM::setup(params); 241 | 242 | let mut group = c.benchmark_group("SIKEp610 KEM (opti)"); 243 | let (s, sk3, pk3) = kem.keygen().unwrap(); 244 | let (c, _k) = kem.encaps(&pk3).unwrap(); 245 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()); 246 | 247 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 248 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 249 | group.bench_function("Decapsulation", |b| { 250 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 251 | }); 252 | 253 | group.finish(); 254 | } 255 | 256 | pub fn bench_p751_pke_std(c: &mut Criterion) { 257 | let params = sike_p751_params(None, None).unwrap(); 258 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 259 | let pke = PKE::setup(params); 260 | 261 | let mut group = c.benchmark_group("SIKEp751 PKE (no opti)"); 262 | let (sk, pk) = pke.gen().unwrap(); 263 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 264 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()).unwrap(); 265 | 266 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 267 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 268 | group.bench_function("Decryption", |b| { 269 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 270 | }); 271 | 272 | group.finish(); 273 | } 274 | 275 | pub fn bench_p751_pke_optim(c: &mut Criterion) { 276 | let params = sike_p751_params( 277 | Some(P751_TWO_TORSION_STRATEGY.to_vec()), 278 | Some(P751_THREE_TORSION_STRATEGY.to_vec()), 279 | ) 280 | .unwrap(); 281 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 282 | let pke = PKE::setup(params); 283 | 284 | let mut group = c.benchmark_group("SIKEp751 PKE (opti)"); 285 | let (sk, pk) = pke.gen().unwrap(); 286 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 287 | let _msg_recovered = pke.dec(&sk, ciphertext.clone()).unwrap(); 288 | 289 | group.bench_function("Keygen", |b| b.iter(|| pke.gen())); 290 | group.bench_function("Encryption", |b| b.iter(|| pke.enc(&pk, msg.clone()))); 291 | group.bench_function("Decryption", |b| { 292 | b.iter(|| pke.dec(&sk, ciphertext.clone())) 293 | }); 294 | 295 | group.finish(); 296 | } 297 | 298 | pub fn bench_p751_kem_std(c: &mut Criterion) { 299 | let params = sike_p751_params(None, None).unwrap(); 300 | let kem = KEM::setup(params); 301 | 302 | let mut group = c.benchmark_group("SIKEp751 KEM (no opti)"); 303 | let (s, sk3, pk3) = kem.keygen().unwrap(); 304 | let (c, _k) = kem.encaps(&pk3).unwrap(); 305 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 306 | 307 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 308 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 309 | group.bench_function("Decapsulation", |b| { 310 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 311 | }); 312 | 313 | group.finish(); 314 | } 315 | 316 | pub fn bench_p751_kem_optim(c: &mut Criterion) { 317 | let params = sike_p751_params( 318 | Some(P751_TWO_TORSION_STRATEGY.to_vec()), 319 | Some(P751_THREE_TORSION_STRATEGY.to_vec()), 320 | ) 321 | .unwrap(); 322 | let kem = KEM::setup(params); 323 | 324 | let mut group = c.benchmark_group("SIKEp751 KEM (opti)"); 325 | let (s, sk3, pk3) = kem.keygen().unwrap(); 326 | let (c, _k) = kem.encaps(&pk3).unwrap(); 327 | let _k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 328 | 329 | group.bench_function("Keygen", |b| b.iter(|| kem.keygen())); 330 | group.bench_function("Encapsulation", |b| b.iter(|| kem.encaps(&pk3))); 331 | group.bench_function("Decapsulation", |b| { 332 | b.iter(|| kem.decaps(&s, &sk3, &pk3, c.clone())) 333 | }); 334 | 335 | group.finish(); 336 | } 337 | 338 | pub fn config() -> Criterion { 339 | Criterion::default().sample_size(10) 340 | } 341 | 342 | criterion_group! { 343 | name = p434; 344 | config = config(); 345 | targets = bench_p434_pke_std, bench_p434_pke_optim, bench_p434_kem_std, bench_p434_kem_optim 346 | } 347 | 348 | criterion_group! { 349 | name = p503; 350 | config = config(); 351 | targets = bench_p503_pke_std, bench_p503_pke_optim, bench_p503_kem_std, bench_p503_kem_optim 352 | } 353 | criterion_group! { 354 | name = p610; 355 | config = config(); 356 | targets = bench_p610_pke_std, bench_p610_pke_optim, bench_p610_kem_std, bench_p610_kem_optim 357 | } 358 | criterion_group! { 359 | name = p751; 360 | config = config(); 361 | targets = bench_p751_pke_std, bench_p751_pke_optim, bench_p751_kem_std, bench_p751_kem_optim 362 | } 363 | 364 | criterion_group! { 365 | name = pke; 366 | config = config(); 367 | targets = bench_p434_pke_optim, bench_p503_pke_optim, bench_p610_pke_optim, bench_p751_pke_optim 368 | } 369 | 370 | criterion_group! { 371 | name = kem; 372 | config = config(); 373 | targets = bench_p434_kem_optim, bench_p503_kem_optim, bench_p610_kem_optim, bench_p751_kem_optim 374 | } 375 | 376 | criterion_main!(kem); 377 | -------------------------------------------------------------------------------- /examples/kem434.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | kem::KEM, 3 | sike_p434_params, 4 | strategy::{P434_THREE_TORSION_STRATEGY, P434_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p434_params( 9 | Some(P434_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P434_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let kem = KEM::setup(params); 14 | 15 | let (s, sk3, pk3) = kem.keygen().unwrap(); 16 | let (c, _k) = kem.encaps(&pk3).unwrap(); 17 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 18 | 19 | println!("{:?}", k_recovered) 20 | } 21 | -------------------------------------------------------------------------------- /examples/kem503.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | kem::KEM, 3 | sike_p503_params, 4 | strategy::{P503_THREE_TORSION_STRATEGY, P503_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p503_params( 9 | Some(P503_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P503_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let kem = KEM::setup(params); 14 | 15 | let (s, sk3, pk3) = kem.keygen().unwrap(); 16 | let (c, _k) = kem.encaps(&pk3).unwrap(); 17 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 18 | 19 | println!("{:?}", k_recovered) 20 | } 21 | -------------------------------------------------------------------------------- /examples/kem610.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | kem::KEM, 3 | sike_p610_params, 4 | strategy::{P610_THREE_TORSION_STRATEGY, P610_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p610_params( 9 | Some(P610_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P610_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let kem = KEM::setup(params); 14 | 15 | let (s, sk3, pk3) = kem.keygen().unwrap(); 16 | let (c, _k) = kem.encaps(&pk3).unwrap(); 17 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 18 | 19 | println!("{:?}", k_recovered) 20 | } 21 | -------------------------------------------------------------------------------- /examples/kem751.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | kem::KEM, 3 | sike_p751_params, 4 | strategy::{P751_THREE_TORSION_STRATEGY, P751_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p751_params( 9 | Some(P751_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P751_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let kem = KEM::setup(params); 14 | 15 | let (s, sk3, pk3) = kem.keygen().unwrap(); 16 | let (c, _k) = kem.encaps(&pk3).unwrap(); 17 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c.clone()).unwrap(); 18 | 19 | println!("{:?}", k_recovered) 20 | } 21 | -------------------------------------------------------------------------------- /examples/pke434.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | pke::{Message, PKE}, 3 | sike_p434_params, 4 | strategy::{P434_THREE_TORSION_STRATEGY, P434_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p434_params( 9 | Some(P434_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P434_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 14 | let pke = PKE::setup(params); 15 | 16 | let (sk, pk) = pke.gen().unwrap(); 17 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 18 | let msg_recovered = pke.dec(&sk, ciphertext.clone()); 19 | 20 | msg_recovered.unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /examples/pke503.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | pke::{Message, PKE}, 3 | sike_p503_params, 4 | strategy::{P503_THREE_TORSION_STRATEGY, P503_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p503_params( 9 | Some(P503_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P503_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 14 | let pke = PKE::setup(params); 15 | 16 | let (sk, pk) = pke.gen().unwrap(); 17 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 18 | let msg_recovered = pke.dec(&sk, ciphertext.clone()); 19 | 20 | msg_recovered.unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /examples/pke610.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | pke::{Message, PKE}, 3 | sike_p610_params, 4 | strategy::{P610_THREE_TORSION_STRATEGY, P610_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p610_params( 9 | Some(P610_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P610_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 14 | let pke = PKE::setup(params); 15 | 16 | let (sk, pk) = pke.gen().unwrap(); 17 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 18 | let msg_recovered = pke.dec(&sk, ciphertext.clone()); 19 | 20 | msg_recovered.unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /examples/pke751.rs: -------------------------------------------------------------------------------- 1 | use rust_sike::{ 2 | pke::{Message, PKE}, 3 | sike_p751_params, 4 | strategy::{P751_THREE_TORSION_STRATEGY, P751_TWO_TORSION_STRATEGY}, 5 | }; 6 | 7 | fn main() { 8 | let params = sike_p751_params( 9 | Some(P751_TWO_TORSION_STRATEGY.to_vec()), 10 | Some(P751_THREE_TORSION_STRATEGY.to_vec()), 11 | ) 12 | .unwrap(); 13 | let msg = Message::from_bytes(vec![0; params.clone().secparam / 8]); 14 | let pke = PKE::setup(params); 15 | 16 | let (sk, pk) = pke.gen().unwrap(); 17 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 18 | let msg_recovered = pke.dec(&sk, ciphertext.clone()); 19 | 20 | msg_recovered.unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /src/constants/cs_p434.rs: -------------------------------------------------------------------------------- 1 | //! Constants for SIKE P434 2 | 3 | /// Exponent e2 of the prime 4 | pub const SIKE_P434_E2: &str = "000000D8"; 5 | 6 | /// Exponent e3 of the prime 7 | pub const SIKE_P434_E3: &str = "00000089"; 8 | 9 | /// Size of sk2 10 | pub const SIKE_P434_NKS2: &str = "0000001B"; 11 | 12 | /// Size of sk3 13 | pub const SIKE_P434_NKS3: &str = "0000001C"; 14 | 15 | /// Prime p = 2^e2 3^e3 + 1 16 | pub const SIKE_P434_P: &str = "0002341F271773446CFC5FD681C520567BC65C783158AEA3FDC1767AE2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; 17 | 18 | /// x-coordinate for point P2 19 | pub const SIKE_P434_XP20: &str = "00003CCFC5E1F050030363E6920A0F7A4C6C71E63DE63A0E6475AF621995705F7C84500CB2BB61E950E19EAB8661D25C4A50ED279646CB48"; 20 | /// x-coordinate for point P2 21 | pub const SIKE_P434_XP21: &str = "0001AD1C1CAE7840EDDA6D8A924520F60E573D3B9DFAC6D189941CB22326D284A8816CC4249410FE80D68047D823C97D705246F869E3EA50"; 22 | 23 | /// x-coordinate for point Q2 24 | pub const SIKE_P434_XQ20: &str = "0000C7461738340EFCF09CE388F666EB38F7F3AFD42DC0B664D9F461F31AA2EDC6B4AB71BD42F4D7C058E13F64B237EF7DDD2ABC0DEB0C6C"; 25 | /// x-coordinate for point Q2 26 | pub const SIKE_P434_XQ21: &str = "000025DE37157F50D75D320DD0682AB4A67E471586FBC2D31AA32E6957FA2B2614C4CD40A1E27283EAAF4272AE517847197432E2D61C85F5"; 27 | 28 | /// x-coordinate for point R2 29 | pub const SIKE_P434_XR20: &str = "0000F37AB34BA0CEAD94F43CDC50DE06AD19C67CE4928346E829CB92580DA84D7C36506A2516696BBE3AEB523AD7172A6D239513C5FD2516"; 30 | /// x-coordinate for point R2 31 | pub const SIKE_P434_XR21: &str = "000196CA2ED06A657E90A73543F3902C208F410895B49CF84CD89BE9ED6E4EE7E8DF90B05F3FDB8BDFE489D1B3558E987013F9806036C5AC"; 32 | 33 | /// x-coordinate for point P3 34 | pub const SIKE_P434_XP30: &str = "00008664865EA7D816F03B31E223C26D406A2C6CD0C3D667466056AAE85895EC37368BFC009DFAFCB3D97E639F65E9E45F46573B0637B7A9"; 35 | /// x-coordinate for point P3 36 | pub const SIKE_P434_XP31: &str = "00000000"; 37 | 38 | /// x-coordinate for point Q3 39 | pub const SIKE_P434_XQ30: &str = "00012E84D7652558E694BF84C1FBDAAF99B83B4266C32EC65B10457BCAF94C63EB063681E8B1E7398C0B241C19B9665FDB9E1406DA3D3846"; 40 | /// x-coordinate for point Q3 41 | pub const SIKE_P434_XQ31: &str = "00000000"; 42 | 43 | /// x-coordinate for point R3 44 | pub const SIKE_P434_XR30: &str = "0001CD28597256D4FFE7E002E87870752A8F8A64A1CC78B5A2122074783F51B4FDE90E89C48ED91A8F4A0CCBACBFA7F51A89CE518A52B76C"; 45 | /// x-coordinate for point R3 46 | pub const SIKE_P434_XR31: &str = "000147073290D78DD0CC8420B1188187D1A49DBFA24F26AAD46B2D9BB547DBB6F63A760ECB0C2B20BE52FB77BD2776C3D14BCBC404736AE4"; 47 | -------------------------------------------------------------------------------- /src/constants/cs_p503.rs: -------------------------------------------------------------------------------- 1 | //! Constants for SIKE P503 2 | 3 | /// Exponent e2 of the prime 4 | pub const SIKE_P503_E2: &str = "000000FA"; 5 | 6 | /// Exponent e3 of the prime 7 | pub const SIKE_P503_E3: &str = "0000009F"; 8 | 9 | /// Size of sk2 10 | pub const SIKE_P503_NKS2: &str = "00000020"; 11 | 12 | /// Size of sk3 13 | pub const SIKE_P503_NKS3: &str = "00000020"; 14 | 15 | /// Prime p = 2^e2 3^e3 + 1 16 | pub const SIKE_P503_P: &str = "004066F541811E1E6045C6BDDA77A4D01B9BF6C87B7E7DAF13085BDA2211E7A0ABFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; 17 | 18 | /// x-coordinate for point P2 19 | pub const SIKE_P503_XP20: &str = "0002ED31A03825FA14BC1D92C503C061D843223E611A92D7C5FBEC0F2C915EE7EEE73374DF6A1161EA00CDCB786155E21FD38220C3772CE670BC68274B851678"; 20 | /// x-coordinate for point P2 21 | pub const SIKE_P503_XP21: &str = "001EE4E4E9448FBBAB4B5BAEF280A99B7BF86A1CE05D55BD603C3BA9D7C08FD8DE7968B49A78851FFBC6D0A17CB2FA1B57F3BABEF87720DD9A489B5581F915D2"; 22 | 23 | /// x-coordinate for point Q2 24 | pub const SIKE_P503_XQ20: &str = "00325CF6A8E2C6183A8B9932198039A7F965BA8587B67925D08D809DBF9A69DE1B621F7F134FA2DAB82FF5A2615F92CC71419FFFAAF86A290D604AB167616461"; 25 | /// x-coordinate for point Q2 26 | pub const SIKE_P503_XQ21: &str = "003E7B0494C8E60A8B72308AE09ED34845B34EA0911E356B77A11872CF7FEEFF745D98D0624097BC1AD7CD2ADF7FFC2C1AA5BA3C6684B964FA555A0715E57DB1"; 27 | 28 | /// x-coordinate for point R2 29 | pub const SIKE_P503_XR20: &str = "003D24CF1F347F1DA54C1696442E6AFC192CEE5E320905E0EAB3C9D3FB595CA26C154F39427A0416A9F36337354CF1E6E5AEDD73DF80C710026D49550AC8CE9F"; 30 | /// x-coordinate for point R2 31 | pub const SIKE_P503_XR21: &str = "0006869EA28E4CEE05DCEE8B08ACD59775D03DAA0DC8B094C85156C212C23C72CB2AB2D2D90D46375AA6D66E58E44F8F219431D3006FDED7993F51649C029498"; 32 | 33 | /// x-coordinate for point P3 34 | pub const SIKE_P503_XP30: &str = "0032D03FD1E99ED0CB05C0707AF74617CBEA5AC6B75905B4B54B1B0C2D73697840155E7B1005EFB02B5D02797A8B66A5D258C76A3C9EF745CECE11E9A178BADF"; 35 | /// x-coordinate for point P3 36 | pub const SIKE_P503_XP31: &str = "00000000"; 37 | 38 | /// x-coordinate for point Q3 39 | pub const SIKE_P503_XQ30: &str = "0039014A74763076675D24CF3FA28318DAC75BCB04E54ADDC6494693F72EBB7DA7DC6A3BBCD188DAD5BECE9D6BB4ABDD05DB38C5FBE52D985DCAF74422C24D53"; 40 | /// x-coordinate for point Q3 41 | pub const SIKE_P503_XQ31: &str = "00000000"; 42 | 43 | /// x-coordinate for point R3 44 | pub const SIKE_P503_XR30: &str = "0000C1465FD048FFB8BF2158ED57F0CFFF0C4D5A4397C7542D722567700FDBB8B2825CAB4B725764F5F528294B7F95C17D560E25660AD3D07AB011D95B2CB522"; 45 | 46 | /// x-coordinate for point R3 47 | pub const SIKE_P503_XR31: &str = "00288165466888BE1E78DB339034E2B8C7BDF0483BFA7AB943DFA05B2D1712317916690F5E713740E7C7D4838296E67357DC34E3460A95C330D5169721981758"; 48 | -------------------------------------------------------------------------------- /src/constants/cs_p610.rs: -------------------------------------------------------------------------------- 1 | //! Constants for SIKE P610 2 | 3 | /// Exponent e2 of the prime 4 | pub const SIKE_P610_E2: &str = "00000131"; 5 | 6 | /// Exponent e3 of the prime 7 | pub const SIKE_P610_E3: &str = "000000C0"; 8 | 9 | /// Size of sk2 10 | pub const SIKE_P610_NKS2: &str = "00000027"; 11 | 12 | /// Size of sk3 13 | pub const SIKE_P610_NKS3: &str = "00000026"; 14 | 15 | /// Prime p = 2^e2 3^e3 + 1 16 | pub const SIKE_P610_P: &str = "000000027BF6A768819010C251E7D88CB255B2FA10C4252A9AE7BF45048FF9ABB1784DE8AA5AB02E6E01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; 17 | 18 | /// x-coordinate for point P2 19 | pub const SIKE_P610_XP20: &str = "00000001B368BC6019B46CD802129209B3E65B98BC64A92BC4DB2F9F3AC96B97A1B9C124DF549B528F18BEECB1666D27D47530435E84221272F3A97FB80527D8F8A359F8F1598D365744CA3070A5F26C"; 20 | /// x-coordinate for point P2 21 | pub const SIKE_P610_XP21: &str = "00000001459685DCA7112D1F6030DBC98F2C9CBB41617B6AD913E6523416CCBD8ED9C7841D97DF83092B9B3F2AF00D62E08DAD8FA743CBCCCC1782BE0186A3432D3C97C37CA16873BEDE01F0637C1AA2"; 22 | 23 | /// x-coordinate for point Q2 24 | pub const SIKE_P610_XQ20: &str = "25DA39EC90CDFB9BC0F772CDA52CB8B5A9F478D7AF8DBBA0AEB3E52432822DD88C38F4E3AEC0746E56149F1FE89707C77F8BA4134568629724F4A8E34B06BFE5C5E66E0867EC38B283798B8A"; 25 | /// x-coordinate for point Q2 26 | pub const SIKE_P610_XQ21: &str = "00000002250E1959256AE502428338CB4715399551AEC78D8935B2DC73FCDCFBDB1A0118A2D3EF03489BA6F637B1C7FEE7E5F31340A1A537B76B5B736B4CDD284918918E8C986FC02741FB8C98F0A0ED"; 27 | 28 | /// x-coordinate for point R2 29 | pub const SIKE_P610_XR20: &str = "00000001B36A006D05F9E370D5078CCA54A16845B2BFF737C865368707C0DBBE9F5A62A9B9C79ADF11932A9FA4806210E25C92DB019CC146706DFBC7FA2638ECC4343C1E390426FAA7F2F07FDA163FB5"; 30 | /// x-coordinate for point R2 31 | pub const SIKE_P610_XR21: &str = "0000000183C9ABF2297CA69699357F58FED92553436BBEBA2C3600D89522E7009D19EA5D6C18CFF993AA3AA33923ED93592B0637ED0B33ADF12388AE912BC4AE4749E2DF3C3292994DCF37747518A992"; 32 | 33 | /// x-coordinate for point P3 34 | pub const SIKE_P610_XP30: &str = "000000014E647CB19B7EAAAC640A9C26B9C26DB7DEDA8FC9399F4F8CE620D2B2200480F4338755AE16D0E090F15EA1882166836A478C6E161C938E4EB8C2DD779B45FFDD17DCDF158AF48DE126B3A047"; 35 | /// x-coordinate for point P3 36 | pub const SIKE_P610_XP31: &str = "00000000"; 37 | 38 | /// x-coordinate for point Q3 39 | pub const SIKE_P610_XQ30: &str = "00000001587822E647707ED4313D3BE6A811A694FB201561111838A0816BFB5DEC625D23772DE48A26D78C04EEB26CA4A571C67CE4DC4C620282876B2F2FC2633CA548C3AB0C45CC991417A56F7FEFEB"; 40 | /// x-coordinate for point Q3 41 | pub const SIKE_P610_XQ31: &str = "00000000"; 42 | 43 | /// x-coordinate for point R3 44 | pub const SIKE_P610_XR30: &str = "00000001DB73BC2DE666D24E59AF5E23B79251BA0D189629EF87E56C38778A448FACE312D08EDFB876C3FD45ECF3746D96E2CADBBA08B1A206C47DDD93137059E34C90E2E42E10F30F6E5F52DED74222"; 45 | /// x-coordinate for point R3 46 | pub const SIKE_P610_XR31: &str = "00000001B2C30180DAF5D91871555CE8EFEC76A4D521F877B754311228C7180A3E2318B4E7A00341FF99F34E35BF7A1053CA76FD77C0AFAE38E2091862AB4F1DD4C8D9C83DE37ACBA6646EDB4C238B48"; 47 | -------------------------------------------------------------------------------- /src/constants/cs_p751.rs: -------------------------------------------------------------------------------- 1 | //! Constants for SIKE P751 2 | 3 | /// Exponent e2 of the prime 4 | pub const SIKE_P751_E2: &str = "00000174"; 5 | 6 | /// Exponent e3 of the prime 7 | pub const SIKE_P751_E3: &str = "000000EF"; 8 | 9 | /// Size of sk2 10 | pub const SIKE_P751_NKS2: &str = "0000002F"; 11 | 12 | /// Size of sk3 13 | pub const SIKE_P751_NKS3: &str = "00000030"; 14 | 15 | /// Prime p = 2^e2 3^e3 + 1 16 | pub const SIKE_P751_P: &str = "00006FE5D541F71C0E12909F97BADC668562B5045CB25748084E9867D6EBE876DA959B1A13F7CC76E3EC968549F878A8EEAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; 17 | 18 | /// x-coordinate for point P2 19 | pub const SIKE_P751_XP20: &str = "00004514F8CC94B140F24874F8B87281FA6004CA5B3637C68AC0C0BDB29838051F385FBBCC300BBB24BFBBF6710D7DC8B29ACB81E429BD1BD5629AD0ECAD7C90622F6BB801D0337EE6BC78A7F12FDCB09DECFAE8BFD643C89C3BAC1D87F8B6FA"; 20 | /// x-coordinate for point P2 21 | pub const SIKE_P751_XP21: &str = "0000158ABF500B5914B3A96CED5FDB37D6DD925F2D6E4F7FEA3CC16E1085754077737EA6F8CC74938D971DA289DCF2435BCAC1897D2627693F9BB167DC01BE34AC494C60B8A0F65A28D7A31EA0D54640653A8099CE5A84E4F0168D818AF02041"; 22 | 23 | /// x-coordinate for point Q2 24 | pub const SIKE_P751_XQ20: &str = "00001723D2BFA01A78BF4E39E3A333F8A7E0B415A17F208D3419E7591D59D8ABDB7EE6D2B2DFCB21AC29A40F837983C0F057FD041AD93237704F1597D87F074F682961A38B5489D1019924F8A0EF5E4F1B2E64A7BA536E219F5090F76276290E"; 25 | /// x-coordinate for point Q2 26 | pub const SIKE_P751_XQ21: &str = "00002569D7EAFB6C60B244EF49E05B5E23F73C4F44169A7E02405E90CEB680CB0756054AC0E3DCE95E2950334262CC973235C2F87D89500BCD465B078BD0DEBDF322A2F86AEDFDCFEE65C09377EFBA0C5384DD837BEDB710209FBC8DDB8C35C7"; 27 | 28 | /// x-coordinate for point R2 29 | pub const SIKE_P751_XR20: &str = "00006066E07F3C0D964E8BC963519FAC8397DF477AEA9A067F3BE343BC53C883AF29CCF008E5A30719A29357A8C33EB3600CD078AF1C40ED5792763A4D213EBDE44CC623195C387E0201E7231C529A15AF5AB743EE9E7C9C37AF3051167525BB"; 30 | /// x-coordinate for point R2 31 | pub const SIKE_P751_XR21: &str = "000050E30C2C06494249BC4A144EB5F31212BD05A2AF0CB3064C322FC3604FC5F5FE3A08FB3A02B05A48557E15C992254FFC8910B72B8E1328B4893CDCFBFC003878881CE390D909E39F83C5006E0AE979587775443483D13C65B107FADA5165"; 32 | 33 | /// x-coordinate for point P3 34 | pub const SIKE_P751_XP30: &str = "0000605D4697A245C394B98024A5554746DC12FF56D0C6F15D2F48123B6D9C498EEE98E8F7CD6E216E2F1FF7CE0C969CCA29CAA2FAA57174EF985AC0A504260018760E9FDF67467E20C13982FF5B49B8BEAB05F6023AF873F827400E453432FE"; 35 | /// x-coordinate for point P3 36 | pub const SIKE_P751_XP31: &str = "00000000"; 37 | 38 | /// x-coordinate for point Q3 39 | pub const SIKE_P751_XQ30: &str = "00005BF9544781803CBD7E0EA8B96D934C5CBCA970F9CC327A0A7E4DAD931EC29BAA8A854B8A9FDE5409AF96C5426FA375D99C68E9AE714172D7F04502D45307FA4839F39A28338BBAFD54A461A535408367D5132E6AA0D3DA6973360F8CD0F1"; 40 | /// x-coordinate for point Q3 41 | pub const SIKE_P751_XQ31: &str = "00000000"; 42 | 43 | /// x-coordinate for point R3 44 | pub const SIKE_P751_XR30: &str = "000055E5124A05D4809585F67FE9EA1F02A06CD411F38588BB631BF789C3F98D1C3325843BB53D9B011D8BD1F682C0E4D8A5E723364364E40DAD1B7A476716AC7D1BA705CCDD680BFD4FE4739CC21A9A59ED544B82566BF633E8950186A79FE3"; 45 | /// x-coordinate for point R3 46 | pub const SIKE_P751_XR31: &str = "00005AC57EAFD6CC7569E8B53A148721953262C5B404C143380ADCC184B6C21F0CAFE095B7E9C79CA88791F9A72F1B2F3121829B2622515B694A16875ED637F421B539E66F2FEF1CE8DCEFC8AEA608055E9C44077266AB64611BF851BA06C821"; 47 | -------------------------------------------------------------------------------- /src/constants/mod.rs: -------------------------------------------------------------------------------- 1 | //! Constants used in SIKE computation 2 | 3 | pub mod cs_p434; 4 | pub mod cs_p503; 5 | pub mod cs_p610; 6 | pub mod cs_p751; 7 | -------------------------------------------------------------------------------- /src/ff/ff_p434.rs: -------------------------------------------------------------------------------- 1 | //! Finite field for SIKEp434 2 | //! 3 | //! Implementation of the finite field of order SIKE_P434_P used in SIKEp434 4 | 5 | use crate::constants::cs_p434::SIKE_P434_P; 6 | use crate::ff::FiniteField; 7 | use hex; 8 | 9 | use once_cell::sync::Lazy; 10 | 11 | use std::fmt::Debug; 12 | 13 | use rug::{integer::Order::MsfBe, Integer}; 14 | 15 | // Parsing a constant value, tests ensure no panic 16 | static P434_PRIME: Lazy = Lazy::new(|| Integer::from_str_radix(SIKE_P434_P, 16).unwrap()); 17 | 18 | /// Finite field defined by the prime number SIKE_P434_P 19 | #[derive(Clone, PartialEq)] 20 | pub struct PrimeFieldP434 { 21 | val: Integer, 22 | } 23 | 24 | impl PrimeFieldP434 { 25 | /// Parse a string into and element of the finite field 26 | pub fn from_string(s: &str) -> Result { 27 | Integer::from_str_radix(&s, 16) 28 | .or_else(|_| Err(String::from("Cannot parse from string"))) 29 | .and_then(|val| Ok(Self { val })) 30 | } 31 | } 32 | 33 | impl Debug for PrimeFieldP434 { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | let bytes = self.val.to_string_radix(16); 36 | write!(f, "{:?}", bytes) 37 | } 38 | } 39 | 40 | impl PrimeFieldP434 { 41 | #[inline] 42 | fn order() -> &'static Integer { 43 | &*P434_PRIME 44 | } 45 | } 46 | 47 | impl FiniteField for PrimeFieldP434 { 48 | #[inline] 49 | fn is_zero(&self) -> bool { 50 | self.val == Self::zero().val 51 | } 52 | 53 | #[inline] 54 | fn dimension() -> usize { 55 | 1 56 | } 57 | 58 | #[inline] 59 | fn zero() -> Self { 60 | Self { 61 | val: Integer::new(), 62 | } 63 | } 64 | 65 | #[inline] 66 | fn one() -> Self { 67 | Self { 68 | val: Integer::from(1), 69 | } 70 | } 71 | 72 | #[inline] 73 | fn neg(&self) -> Self { 74 | Self { 75 | val: Integer::from(Self::order() - &self.val), 76 | } 77 | } 78 | 79 | #[inline] 80 | fn inv(&self) -> Result { 81 | Integer::from(&self.val) 82 | .invert(Self::order()) 83 | .or_else(|_| Err(String::from("Cannot invert"))) 84 | .and_then(|val| Ok(Self { val })) 85 | } 86 | 87 | #[inline] 88 | fn add(&self, other: &Self) -> Self { 89 | Self { 90 | val: Integer::from(&self.val + &other.val) % Self::order(), 91 | } 92 | } 93 | 94 | #[inline] 95 | fn sub(&self, other: &Self) -> Self { 96 | self.add(&other.neg()) 97 | } 98 | 99 | #[inline] 100 | fn mul(&self, other: &Self) -> Self { 101 | Self { 102 | val: Integer::from(&self.val * &other.val) % Self::order(), 103 | } 104 | } 105 | 106 | #[inline] 107 | fn div(&self, other: &Self) -> Result { 108 | Ok(self.mul(&other.inv()?)) 109 | } 110 | 111 | #[inline] 112 | fn equals(&self, other: &Self) -> bool { 113 | self.sub(&other).is_zero() 114 | } 115 | 116 | fn into_bytes(self) -> Vec { 117 | self.val.to_digits::(MsfBe) 118 | } 119 | 120 | fn from_bytes(bytes: &[u8]) -> Result { 121 | let s = hex::encode(bytes); 122 | 123 | Integer::from_str_radix(&s, 16) 124 | .or_else(|_| Err(String::from("Cannot parse from bytes"))) 125 | .and_then(|val| Ok(Self { val })) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/ff/ff_p503.rs: -------------------------------------------------------------------------------- 1 | //! Finite field for SIKEp503 2 | //! 3 | //! Implementation of the finite field of order SIKE_P503_P used in SIKEp503 4 | 5 | use crate::constants::cs_p503::SIKE_P503_P; 6 | use crate::ff::FiniteField; 7 | 8 | use hex; 9 | 10 | use once_cell::sync::Lazy; 11 | 12 | use std::fmt::Debug; 13 | 14 | use rug::{integer::Order::MsfBe, Integer}; 15 | 16 | // Parsing a constant value, tests ensure no panic 17 | static P503_PRIME: Lazy = Lazy::new(|| Integer::from_str_radix(SIKE_P503_P, 16).unwrap()); 18 | 19 | /// Finite field defined by the prime number SIKE_P503_P 20 | #[derive(Clone, PartialEq)] 21 | pub struct PrimeFieldP503 { 22 | val: Integer, 23 | } 24 | 25 | impl PrimeFieldP503 { 26 | /// Parse a string into and element of the finite field 27 | pub fn from_string(s: &str) -> Result { 28 | Integer::from_str_radix(&s, 16) 29 | .or_else(|_| Err(String::from("Cannot parse from string"))) 30 | .and_then(|val| Ok(Self { val })) 31 | } 32 | } 33 | 34 | impl Debug for PrimeFieldP503 { 35 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 36 | let bytes = self.val.to_string_radix(16); 37 | write!(f, "{:?}", bytes) 38 | } 39 | } 40 | 41 | impl PrimeFieldP503 { 42 | #[inline] 43 | fn order() -> &'static Integer { 44 | &*P503_PRIME 45 | } 46 | } 47 | 48 | impl FiniteField for PrimeFieldP503 { 49 | #[inline] 50 | fn is_zero(&self) -> bool { 51 | self.val == Self::zero().val 52 | } 53 | 54 | #[inline] 55 | fn dimension() -> usize { 56 | 1 57 | } 58 | 59 | #[inline] 60 | fn zero() -> Self { 61 | Self { 62 | val: Integer::new(), 63 | } 64 | } 65 | 66 | #[inline] 67 | fn one() -> Self { 68 | Self { 69 | val: Integer::from(1), 70 | } 71 | } 72 | 73 | #[inline] 74 | fn neg(&self) -> Self { 75 | Self { 76 | val: Integer::from(Self::order() - &self.val), 77 | } 78 | } 79 | 80 | #[inline] 81 | fn inv(&self) -> Result { 82 | Integer::from(&self.val) 83 | .invert(Self::order()) 84 | .or_else(|_| Err(String::from("Cannot invert"))) 85 | .and_then(|val| Ok(Self { val })) 86 | } 87 | 88 | #[inline] 89 | fn add(&self, other: &Self) -> Self { 90 | Self { 91 | val: Integer::from(&self.val + &other.val) % Self::order(), 92 | } 93 | } 94 | 95 | #[inline] 96 | fn sub(&self, other: &Self) -> Self { 97 | self.add(&other.neg()) 98 | } 99 | 100 | #[inline] 101 | fn mul(&self, other: &Self) -> Self { 102 | Self { 103 | val: Integer::from(&self.val * &other.val) % Self::order(), 104 | } 105 | } 106 | 107 | #[inline] 108 | fn div(&self, other: &Self) -> Result { 109 | Ok(self.mul(&other.inv()?)) 110 | } 111 | 112 | #[inline] 113 | fn equals(&self, other: &Self) -> bool { 114 | self.sub(&other).is_zero() 115 | } 116 | 117 | fn into_bytes(self) -> Vec { 118 | self.val.to_digits::(MsfBe) 119 | } 120 | 121 | fn from_bytes(bytes: &[u8]) -> Result { 122 | let s = hex::encode(bytes); 123 | 124 | Integer::from_str_radix(&s, 16) 125 | .or_else(|_| Err(String::from("Cannot parse from bytes"))) 126 | .and_then(|val| Ok(Self { val })) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/ff/ff_p610.rs: -------------------------------------------------------------------------------- 1 | //! Finite field for SIKEp610 2 | //! 3 | //! Implementation of the finite field of order SIKE_P610_P used in SIKEp610 4 | 5 | use crate::constants::cs_p610::SIKE_P610_P; 6 | use crate::ff::FiniteField; 7 | use hex; 8 | 9 | use once_cell::sync::Lazy; 10 | 11 | use std::fmt::Debug; 12 | 13 | use rug::{integer::Order::MsfBe, Integer}; 14 | 15 | // Parsing a constant value, tests ensure no panic 16 | static P610_PRIME: Lazy = Lazy::new(|| Integer::from_str_radix(SIKE_P610_P, 16).unwrap()); 17 | 18 | /// Finite field defined by the prime number SIKE_P610_P 19 | #[derive(Clone, PartialEq)] 20 | pub struct PrimeFieldP610 { 21 | val: Integer, 22 | } 23 | 24 | impl PrimeFieldP610 { 25 | /// Parse a string into and element of the finite field 26 | pub fn from_string(s: &str) -> Result { 27 | Integer::from_str_radix(&s, 16) 28 | .or_else(|_| Err(String::from("Cannot parse from string"))) 29 | .and_then(|val| Ok(Self { val })) 30 | } 31 | } 32 | 33 | impl Debug for PrimeFieldP610 { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | let bytes = self.val.to_string_radix(16); 36 | write!(f, "{:?}", bytes) 37 | } 38 | } 39 | 40 | impl PrimeFieldP610 { 41 | #[inline] 42 | fn order() -> &'static Integer { 43 | &*P610_PRIME 44 | } 45 | } 46 | 47 | impl FiniteField for PrimeFieldP610 { 48 | #[inline] 49 | fn is_zero(&self) -> bool { 50 | self.val == Self::zero().val 51 | } 52 | 53 | #[inline] 54 | fn dimension() -> usize { 55 | 1 56 | } 57 | 58 | #[inline] 59 | fn zero() -> Self { 60 | Self { 61 | val: Integer::new(), 62 | } 63 | } 64 | 65 | #[inline] 66 | fn one() -> Self { 67 | Self { 68 | val: Integer::from(1), 69 | } 70 | } 71 | 72 | #[inline] 73 | fn neg(&self) -> Self { 74 | Self { 75 | val: Integer::from(Self::order() - &self.val), 76 | } 77 | } 78 | 79 | #[inline] 80 | fn inv(&self) -> Result { 81 | Integer::from(&self.val) 82 | .invert(Self::order()) 83 | .or_else(|_| Err(String::from("Cannot invert"))) 84 | .and_then(|val| Ok(Self { val })) 85 | } 86 | 87 | #[inline] 88 | fn add(&self, other: &Self) -> Self { 89 | Self { 90 | val: Integer::from(&self.val + &other.val) % Self::order(), 91 | } 92 | } 93 | 94 | #[inline] 95 | fn sub(&self, other: &Self) -> Self { 96 | self.add(&other.neg()) 97 | } 98 | 99 | #[inline] 100 | fn mul(&self, other: &Self) -> Self { 101 | Self { 102 | val: Integer::from(&self.val * &other.val) % Self::order(), 103 | } 104 | } 105 | 106 | #[inline] 107 | fn div(&self, other: &Self) -> Result { 108 | Ok(self.mul(&other.inv()?)) 109 | } 110 | 111 | #[inline] 112 | fn equals(&self, other: &Self) -> bool { 113 | self.sub(&other).is_zero() 114 | } 115 | 116 | fn into_bytes(self) -> Vec { 117 | self.val.to_digits::(MsfBe) 118 | } 119 | 120 | fn from_bytes(bytes: &[u8]) -> Result { 121 | let s = hex::encode(bytes); 122 | 123 | Integer::from_str_radix(&s, 16) 124 | .or_else(|_| Err(String::from("Cannot parse from bytes"))) 125 | .and_then(|val| Ok(Self { val })) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/ff/ff_p751.rs: -------------------------------------------------------------------------------- 1 | //! Finite field for SIKEp751 2 | //! 3 | //! Implementation of the finite field of order SIKE_P751_P used in SIKEp751 4 | 5 | use crate::constants::cs_p751::SIKE_P751_P; 6 | 7 | use crate::ff::FiniteField; 8 | use hex; 9 | 10 | use once_cell::sync::Lazy; 11 | 12 | use std::fmt::Debug; 13 | 14 | use rug::{integer::Order::MsfBe, Integer}; 15 | 16 | // Parsing a constant value, tests ensure no panic 17 | static P751_PRIME: Lazy = Lazy::new(|| Integer::from_str_radix(SIKE_P751_P, 16).unwrap()); 18 | 19 | /// Finite field defined by the prime number SIKE_P751_P 20 | #[derive(Clone, PartialEq)] 21 | pub struct PrimeFieldP751 { 22 | val: Integer, 23 | } 24 | 25 | impl PrimeFieldP751 { 26 | /// Parse a string into and element of the finite field 27 | pub fn from_string(s: &str) -> Result { 28 | Integer::from_str_radix(&s, 16) 29 | .or_else(|_| Err(String::from("Cannot parse from string"))) 30 | .and_then(|val| Ok(Self { val })) 31 | } 32 | } 33 | 34 | impl Debug for PrimeFieldP751 { 35 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 36 | let bytes = self.val.to_string_radix(16); 37 | write!(f, "{:?}", bytes) 38 | } 39 | } 40 | 41 | impl PrimeFieldP751 { 42 | #[inline] 43 | fn order() -> &'static Integer { 44 | &*P751_PRIME 45 | } 46 | } 47 | 48 | impl FiniteField for PrimeFieldP751 { 49 | #[inline] 50 | fn is_zero(&self) -> bool { 51 | self.val == Self::zero().val 52 | } 53 | 54 | #[inline] 55 | fn dimension() -> usize { 56 | 1 57 | } 58 | 59 | #[inline] 60 | fn zero() -> Self { 61 | Self { 62 | val: Integer::new(), 63 | } 64 | } 65 | 66 | #[inline] 67 | fn one() -> Self { 68 | Self { 69 | val: Integer::from(1), 70 | } 71 | } 72 | 73 | #[inline] 74 | fn neg(&self) -> Self { 75 | Self { 76 | val: Integer::from(Self::order() - &self.val), 77 | } 78 | } 79 | 80 | #[inline] 81 | fn inv(&self) -> Result { 82 | Integer::from(&self.val) 83 | .invert(Self::order()) 84 | .or_else(|_| Err(String::from("Cannot invert"))) 85 | .and_then(|val| Ok(Self { val })) 86 | } 87 | 88 | #[inline] 89 | fn add(&self, other: &Self) -> Self { 90 | Self { 91 | val: Integer::from(&self.val + &other.val) % Self::order(), 92 | } 93 | } 94 | 95 | #[inline] 96 | fn sub(&self, other: &Self) -> Self { 97 | self.add(&other.neg()) 98 | } 99 | 100 | #[inline] 101 | fn mul(&self, other: &Self) -> Self { 102 | Self { 103 | val: Integer::from(&self.val * &other.val) % Self::order(), 104 | } 105 | } 106 | 107 | #[inline] 108 | fn div(&self, other: &Self) -> Result { 109 | Ok(self.mul(&other.inv()?)) 110 | } 111 | 112 | #[inline] 113 | fn equals(&self, other: &Self) -> bool { 114 | self.sub(&other).is_zero() 115 | } 116 | 117 | fn into_bytes(self) -> Vec { 118 | self.val.to_digits::(MsfBe) 119 | } 120 | 121 | fn from_bytes(bytes: &[u8]) -> Result { 122 | let s = hex::encode(bytes); 123 | 124 | Integer::from_str_radix(&s, 16) 125 | .or_else(|_| Err(String::from("Cannot parse from bytes"))) 126 | .and_then(|val| Ok(Self { val })) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/ff/mod.rs: -------------------------------------------------------------------------------- 1 | //! Finite fields 2 | //! 3 | //! Provides the standard structure for finite fields and their quadratic extensions. 4 | //! It also includes specific finite fields implementation used for SIKE 5 | 6 | use std::fmt::Debug; 7 | 8 | pub mod ff_p434; 9 | pub mod ff_p503; 10 | pub mod ff_p610; 11 | pub mod ff_p751; 12 | 13 | pub use crate::ff::{ 14 | ff_p434::PrimeFieldP434, ff_p503::PrimeFieldP503, ff_p610::PrimeFieldP610, 15 | ff_p751::PrimeFieldP751, 16 | }; 17 | 18 | /// Finite field element 19 | pub trait FiniteField: Sized { 20 | /// Check if the element is the additive identity of the field 21 | fn is_zero(&self) -> bool; 22 | 23 | /// Returns the dimension of the finite field 24 | fn dimension() -> usize; 25 | 26 | /// Returns the additive identity of the field 27 | fn zero() -> Self; 28 | 29 | /// Returns the multiplicative identity of the field 30 | fn one() -> Self; 31 | 32 | /// Returns the additive inverse of the element 33 | fn neg(&self) -> Self; 34 | 35 | /// Returns the multiplicative inverse of the element 36 | fn inv(&self) -> Result; 37 | 38 | /// Defines the addition of two elements 39 | fn add(&self, other: &Self) -> Self; 40 | 41 | /// Defines the substraction of two elements 42 | fn sub(&self, other: &Self) -> Self; 43 | 44 | /// Defines the multiplication of two elements 45 | fn mul(&self, other: &Self) -> Self; 46 | 47 | /// Defines the divison of two elements 48 | fn div(&self, other: &Self) -> Result; 49 | 50 | /// Checks if two elements are equal 51 | fn equals(&self, other: &Self) -> bool; 52 | 53 | /// Converts the element to a bytes representation 54 | fn into_bytes(self) -> Vec; 55 | 56 | /// Converts a bytes representation to an element of the finite field 57 | fn from_bytes(bytes: &[u8]) -> Result; 58 | } 59 | 60 | /// Given a specific finite field 𝔽ₚ, represents an element of 61 | /// its quadratic extension 𝔽ₚ(i) as `x = a + ib`, (`i² = -1`) 62 | #[derive(Clone, Copy, PartialEq)] 63 | pub struct QuadraticExtension { 64 | a: F, 65 | b: F, 66 | } 67 | 68 | impl Debug for QuadraticExtension { 69 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 70 | write!(f, "{:?} + i {:?}", self.a, self.b) 71 | } 72 | } 73 | 74 | impl QuadraticExtension { 75 | /// Generates an element of the quadratic extension given two elements of the base field: `z = a + i b`. 76 | pub fn from(a: F, b: F) -> Self { 77 | Self { a, b } 78 | } 79 | } 80 | 81 | impl FiniteField for QuadraticExtension { 82 | fn is_zero(&self) -> bool { 83 | self.a.is_zero() && self.b.is_zero() 84 | } 85 | 86 | fn dimension() -> usize { 87 | 2 * F::dimension() 88 | } 89 | 90 | fn zero() -> Self { 91 | Self { 92 | a: F::zero(), 93 | b: F::zero(), 94 | } 95 | } 96 | 97 | fn one() -> Self { 98 | Self { 99 | a: F::one(), 100 | b: F::zero(), 101 | } 102 | } 103 | 104 | fn neg(&self) -> Self { 105 | Self { 106 | a: self.a.neg(), 107 | b: self.b.neg(), 108 | } 109 | } 110 | 111 | fn add(&self, other: &Self) -> Self { 112 | Self { 113 | a: self.a.add(&other.a), 114 | b: self.b.add(&other.b), 115 | } 116 | } 117 | 118 | fn sub(&self, other: &Self) -> Self { 119 | self.add(&other.neg()) 120 | } 121 | 122 | fn div(&self, other: &Self) -> Result { 123 | Ok(self.mul(&other.inv()?)) 124 | } 125 | 126 | fn mul(&self, other: &Self) -> Self { 127 | let m1 = self.a.mul(&other.a); 128 | let m2 = self.b.mul(&other.b); 129 | 130 | let m3 = self.a.mul(&other.b); 131 | let m4 = other.a.mul(&self.b); 132 | 133 | Self { 134 | a: m1.sub(&m2), 135 | b: m3.add(&m4), 136 | } 137 | } 138 | 139 | fn inv(&self) -> Result { 140 | let asq = self.a.mul(&self.a); 141 | let bsq = self.b.mul(&self.b); 142 | let inv_norm = asq.add(&bsq).inv()?; 143 | 144 | Ok(Self { 145 | a: inv_norm.mul(&self.a), 146 | b: inv_norm.mul(&self.b.neg()), 147 | }) 148 | } 149 | 150 | fn equals(&self, other: &Self) -> bool { 151 | self.a.equals(&other.a) && self.b.equals(&other.b) 152 | } 153 | 154 | fn into_bytes(self) -> Vec { 155 | use crate::utils::conversion::concatenate; 156 | 157 | let part1 = self.a.into_bytes(); 158 | let part2 = self.b.into_bytes(); 159 | 160 | // Left padding to the nearest power of 2 161 | let p21 = part1.len().next_power_of_two(); 162 | let p22 = part2.len().next_power_of_two(); 163 | let len = std::cmp::max(p21, p22); 164 | 165 | let pad1 = vec![0; len - part1.len()]; 166 | let pad2 = vec![0; len - part2.len()]; 167 | 168 | concatenate(&[&pad1, &part1, &pad2, &part2]) 169 | } 170 | 171 | /// Element from byte representation (ref `ostofp2` Algorithm 1.2.4.) 172 | fn from_bytes(bytes: &[u8]) -> Result { 173 | let n = bytes.len() / 2; 174 | let a = F::from_bytes(&bytes[..n])?; 175 | let b = F::from_bytes(&bytes[n..])?; 176 | Ok(Self::from(a, b)) 177 | } 178 | } 179 | 180 | #[cfg(test)] 181 | mod tests { 182 | 183 | use super::*; 184 | use crate::constants::cs_p434::{SIKE_P434_XP20, SIKE_P434_XP21}; 185 | 186 | #[test] 187 | fn test_conversion_ff434_bytes() { 188 | let num = PrimeFieldP434::from_string(SIKE_P434_XP20).unwrap(); 189 | 190 | let b = num.clone().into_bytes(); 191 | let num_recovered = PrimeFieldP434::from_bytes(&b).unwrap(); 192 | 193 | println!("{:?}", num); 194 | println!("{:?}", num_recovered); 195 | 196 | assert!(num.equals(&num_recovered)); 197 | } 198 | 199 | #[test] 200 | fn test_conversion_quadratic_bytes() { 201 | let num1 = PrimeFieldP434::from_string(SIKE_P434_XP20).unwrap(); 202 | let num2 = PrimeFieldP434::from_string(SIKE_P434_XP21).unwrap(); 203 | 204 | let q = QuadraticExtension::from(num1, num2); 205 | let b = q.clone().into_bytes(); 206 | let q_recovered = QuadraticExtension::from_bytes(&b).unwrap(); 207 | 208 | println!("{:?}", q); 209 | println!("{:?}", q_recovered); 210 | 211 | assert!(q.equals(&q_recovered)); 212 | } 213 | 214 | #[test] 215 | fn test_ff() { 216 | let one = PrimeFieldP434::one(); 217 | let two = one.add(&one); 218 | let three = two.add(&one); 219 | let four1 = two.add(&two); 220 | let four2 = two.mul(&two); 221 | let zero = one.sub(&one); 222 | 223 | println!("zero = {:?}", zero); 224 | println!("one = {:?}", one); 225 | println!("two = {:?}", two); 226 | println!("three = {:?}", three); 227 | println!("four1 = {:?}", four1); 228 | println!("four2 = {:?}", four2); 229 | } 230 | 231 | #[test] 232 | fn test_qff() { 233 | let one = PrimeFieldP434::one(); 234 | let two = one.add(&one); 235 | let x = QuadraticExtension::from(two.clone(), two.clone()); 236 | 237 | let eight_i = x.mul(&x); 238 | 239 | println!("eight_i = {:?}", eight_i); 240 | 241 | let two_plus_two_i = eight_i.div(&x).unwrap(); 242 | 243 | println!("two_plus_two_i = {:?}", two_plus_two_i); 244 | 245 | assert_eq!(two_plus_two_i, x) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/isogeny/curve.rs: -------------------------------------------------------------------------------- 1 | //! Montgomery curve 2 | 3 | use crate::{ff::FiniteField, isogeny::PublicKey}; 4 | 5 | /// Montgomery M_{A,1} Curve defined by (A : C) in projective cooridnates 6 | pub struct Curve { 7 | /// Coefficient A 8 | pub a: K, 9 | 10 | /// Coefficient C 11 | pub c: K, 12 | } 13 | 14 | impl Curve { 15 | /// Clone the curve 16 | pub fn clone(&self) -> Self { 17 | Self { 18 | a: self.a.clone(), 19 | c: self.c.clone(), 20 | } 21 | } 22 | 23 | /// Build a curve from coefficients 24 | #[inline] 25 | pub fn from_coeffs(a: K, c: K) -> Self { 26 | Self { a, c } 27 | } 28 | 29 | /// Curve with equation y² = x³ + 6x² + x (ref 1.3.2) 30 | #[inline] 31 | pub fn starting_curve() -> Curve { 32 | let one = K::one(); 33 | let two = one.add(&one); 34 | let three = two.add(&one); 35 | let six = three.add(&three); 36 | 37 | Curve::from_coeffs(six, one) 38 | } 39 | 40 | /// Convert a curve (A : C) to (A' : C') ~ (A + 2C : 4C) 41 | #[inline] 42 | pub fn curve_plus(&self) -> Curve { 43 | let one = K::one(); 44 | let two = one.add(&one); 45 | let four = two.add(&two); 46 | 47 | let a = two.mul(&self.c).add(&self.a); 48 | let c = four.mul(&self.c); 49 | 50 | Curve::from_coeffs(a, c) 51 | } 52 | 53 | /// Given convert a curve (A : C) to (A' : C') ~ (A + 2C : A - 2C) 54 | #[inline] 55 | pub fn curve_plus_minus(&self) -> Curve { 56 | let one = K::one(); 57 | let two = one.add(&one); 58 | 59 | let a = two.mul(&self.c).add(&self.a); 60 | let c = self.a.sub(&two.mul(&self.c)); 61 | 62 | Curve::from_coeffs(a, c) 63 | } 64 | 65 | /// Montgomery j-invariant (ref Algorithm 9 p.56) 66 | #[inline] 67 | pub fn j_invariant(&self) -> Result { 68 | let j = self.a.mul(&self.a); // 1. 69 | let t1 = self.c.mul(&self.c); //2. 70 | let t0 = t1.add(&t1); // 3. 71 | let t0 = j.sub(&t0); // 4. 72 | let t0 = t0.sub(&t1); //5. 73 | 74 | let j = t0.sub(&t1); // 6. 75 | let t1 = t1.mul(&t1); //7. 76 | let j = j.mul(&t1); // 8. 77 | let t0 = t0.add(&t0); // 9. 78 | let t0 = t0.add(&t0); // 10. 79 | 80 | let t1 = t0.mul(&t0); // 11. 81 | let t0 = t0.mul(&t1); // 12. 82 | let t0 = t0.add(&t0); // 13. 83 | let t0 = t0.add(&t0); // 14. 84 | 85 | t0.div(&j) // 15. 86 | } 87 | 88 | /// Generates a curve from three elements of 𝔽ₚ(i), or returns None 89 | /// (ref `cfpk` Algorithm 1.2.1 ) 90 | #[inline] 91 | pub fn from_public_key(pk: &PublicKey) -> Result, String> { 92 | let (x_p, x_q, x_r) = (&pk.x1, &pk.x2, &pk.x3); 93 | 94 | // 1. 95 | if x_p.is_zero() || x_q.is_zero() || x_r.is_zero() { 96 | return Err(String::from("Incorrect public key!")); 97 | } 98 | 99 | // 2. 100 | let one = K::one(); 101 | let two = one.add(&one); 102 | let four = two.add(&two); 103 | 104 | let num = K::one() 105 | .sub(&x_p.mul(&x_q)) 106 | .sub(&x_p.mul(&x_r)) 107 | .sub(&x_q.mul(&x_r)); 108 | let num = num.mul(&num); 109 | let denom = four.mul(&x_p).mul(&x_q).mul(&x_r); 110 | let frac = num.div(&denom)?; 111 | let a = frac.sub(&x_p).sub(&x_q).sub(&x_r); 112 | let c = one; 113 | 114 | // 3, 4. 115 | Ok(Curve::from_coeffs(a, c)) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/isogeny/mod.rs: -------------------------------------------------------------------------------- 1 | //! Tools for isogeny computations 2 | 3 | use bitvec::prelude::*; 4 | use std::{collections::VecDeque, convert::TryInto, fmt::Debug}; 5 | 6 | mod curve; 7 | mod point; 8 | mod publickey; 9 | mod publicparams; 10 | mod secretkey; 11 | 12 | use crate::{ff::FiniteField, isogeny::point::Point}; 13 | 14 | pub use crate::isogeny::{ 15 | curve::Curve, publickey::PublicKey, publicparams::*, secretkey::SecretKey, 16 | }; 17 | 18 | type ThreePoints = (Point, Point, Point); 19 | 20 | /// SIKE structure for computing isogenies 21 | pub struct CurveIsogenies { 22 | params: PublicParameters, 23 | } 24 | 25 | impl CurveIsogenies { 26 | /// Initialise the SIKE structure with given parameters 27 | pub fn init(params: PublicParameters) -> Self { 28 | Self { params } 29 | } 30 | 31 | /// Coordinate doubling (ref. `xDBL`, Algorithm 3 p. 54) 32 | /// * Input: P. Output: [2]P 33 | #[inline] 34 | fn double(p: &Point, curve: &Curve) -> Point { 35 | let a_24_plus = &curve.a; 36 | let c_24 = &curve.c; 37 | 38 | let t0 = p.x.sub(&p.z); // 1. 39 | let t1 = p.x.add(&p.z); // 2. 40 | let t0 = t0.mul(&t0); // 3. 41 | let t1 = t1.mul(&t1); // 4. 42 | let z = c_24.mul(&t0); // 5. 43 | let x = z.mul(&t1); // 6. 44 | let t1 = t1.sub(&t0); // 7. 45 | let t0 = a_24_plus.mul(&t1); // 8. 46 | let z = z.add(&t0); // 9. 47 | let z = z.mul(&t1); // 10. 48 | 49 | Point { x, z } 50 | } 51 | 52 | /// Repeated coordinate doubling (ref `xDBLe` Algorithm 4 p.55) 53 | /// Input: P, e. Output : [2^e]P 54 | #[inline] 55 | fn ndouble(p: Point, e: u64, curve: &Curve) -> Point { 56 | if e == 0 { 57 | return p; 58 | } 59 | let mut point = p; 60 | for _ in 1..=e { 61 | point = Self::double(&point, curve); 62 | } 63 | point 64 | } 65 | 66 | /// Combined coordinate doubling and differential addition (ref `xDBLADD` Algorithm 5 p.55) 67 | /// * Input: P, Q, Q - P, a_24_plus. Output: 2P, P+Q. 68 | #[inline] 69 | fn double_and_add( 70 | p: &Point, 71 | q: &Point, 72 | qmp: &Point, 73 | a_24_plus: &K, 74 | ) -> (Point, Point) { 75 | let t0 = p.x.add(&p.z); //1. 76 | let t1 = p.x.sub(&p.z); // 2. 77 | let x2 = t0.mul(&t0); // 3. 78 | let t2 = q.x.sub(&q.z); // 4. 79 | let xpq = q.x.add(&q.z); // 5. 80 | let t0 = t0.mul(&t2); // 6. 81 | let z2 = t1.mul(&t1); // 7. 82 | 83 | let t1 = t1.mul(&xpq); // 8. 84 | let t2 = x2.sub(&z2); // 9. 85 | let x2 = x2.mul(&z2); // 10. 86 | let xpq = t2.mul(a_24_plus); // 11. 87 | let zpq = t0.sub(&t1); // 12. 88 | let z2 = xpq.add(&z2); // 13. 89 | let xpq = t0.add(&t1); // 14. 90 | 91 | let z2 = z2.mul(&t2); // 15. 92 | let zpq = zpq.mul(&zpq); // 16. 93 | let xpq = xpq.mul(&xpq); // 17. 94 | let zpq = qmp.x.mul(&zpq); // 18. 95 | let xpq = qmp.z.mul(&xpq); // 19. 96 | 97 | let two_p = Point { x: x2, z: z2 }; 98 | let p_plus_q = Point { x: xpq, z: zpq }; 99 | 100 | (two_p, p_plus_q) 101 | } 102 | 103 | /// Coordinate tripling (ref `xTPL` Algorithm 6 p.55) 104 | /// * Input: P. Output: [3]P 105 | #[inline] 106 | fn triple(p: &Point, curve: &Curve) -> Point { 107 | let a_24_plus = &curve.a; 108 | let a_24_minus = &curve.c; 109 | 110 | let t0 = p.x.sub(&p.z); // 1. 111 | let t2 = t0.mul(&t0); // 2. 112 | let t1 = p.x.add(&p.z); // 3. 113 | let t3 = t1.mul(&t1); // 4. 114 | let t4 = t1.add(&t0); // 5. 115 | let t0 = t1.sub(&t0); // 6. 116 | 117 | let t1 = t4.mul(&t4); // 7. 118 | let t1 = t1.sub(&t3); // 8. 119 | let t1 = t1.sub(&t2); // 9. 120 | let t5 = t3.mul(&a_24_plus); // 10. 121 | let t3 = t5.mul(&t3); // 11. 122 | let t6 = t2.mul(&a_24_minus); // 12. 123 | 124 | let t2 = t2.mul(&t6); // 13. 125 | let t3 = t2.sub(&t3); // 14. 126 | let t2 = t5.sub(&t6); // 15. 127 | let t1 = t2.mul(&t1); // 16. 128 | let t2 = t3.add(&t1); // 17. 129 | let t2 = t2.mul(&t2); // 18. 130 | 131 | let x = t2.mul(&t4); // 19. 132 | let t1 = t3.sub(&t1); // 20. 133 | let t1 = t1.mul(&t1); // 21. 134 | let z = t1.mul(&t0); // 22. 135 | 136 | Point { x, z } 137 | } 138 | 139 | /// Repeated point tripling (ref `xTPLe` Algorithm 7 p.56) 140 | /// * Input: P, e. Output: [E^e]P 141 | #[inline] 142 | fn ntriple(p: Point, e: u64, curve: &Curve) -> Point { 143 | if e == 0 { 144 | return p; 145 | } 146 | let mut point = p; 147 | for _ in 1..=e { 148 | point = Self::triple(&point, curve); 149 | } 150 | point 151 | } 152 | 153 | /// Three point ladder (ref `Ladder3pt` Algorithm 8 p.56) 154 | /// * Input: m (binary), x_p, x_q, x_(Q-P) 155 | /// * Output: P + [m]Q 156 | #[inline] 157 | fn three_pts_ladder( 158 | m: &BitSlice, 159 | x_p: K, 160 | x_q: K, 161 | x_qmp: K, 162 | curve: &Curve, 163 | ) -> Result, String> { 164 | let mut p0 = Point::from_x(x_q); 165 | let mut p1 = Point::from_x(x_p); 166 | let mut p2 = Point::from_x(x_qmp); 167 | 168 | let one = K::one(); 169 | let two = one.add(&one); 170 | let four = two.add(&two); 171 | 172 | let a_24_plus = &curve.a.add(&two).div(&four)?; 173 | 174 | // Start with low weight bits 175 | for &m_i in m.iter().rev() { 176 | if m_i { 177 | let (p0v, p1v) = Self::double_and_add(&p0, &p1, &p2, a_24_plus); 178 | p0 = p0v; 179 | p1 = p1v; 180 | } else { 181 | let (p0v, p2v) = Self::double_and_add(&p0, &p2, &p1, a_24_plus); 182 | p0 = p0v; 183 | p2 = p2v; 184 | } 185 | } 186 | 187 | Ok(p1) 188 | } 189 | 190 | /// Recovering Montgomery curve coefficient (ref `get_A`, Algorithm 10 p. 57) 191 | /// * Input: x_p, x_q, x_(Q-P) 192 | /// * Output: A 193 | fn _from_points(x_p: K, x_q: K, x_qmp: K) -> Result, String> { 194 | let t1 = x_p.add(&x_q); //1. 195 | let t0 = x_p.mul(&x_q); //2. 196 | let a = x_qmp.mul(&t1); //3. 197 | let a = a.add(&t0); //4. 198 | 199 | let t0 = t0.mul(&x_qmp); //5. 200 | let a = a.sub(&K::one()); //6. 201 | let t0 = t0.add(&t0); //7. 202 | let t1 = t1.add(&x_qmp); //8. 203 | 204 | let t0 = t0.add(&t0); //9. 205 | let a = a.mul(&a); // 10. 206 | let a = a.div(&t0)?; // 11 and 12. 207 | 208 | let a = a.sub(&t1); // 13. 209 | 210 | Ok(Curve::from_coeffs(a, K::one())) 211 | } 212 | 213 | /// Computing the two-isogenous curve (ref `2_iso_curve` Algorithm 11 p.57) 214 | /// * Input: P of order 2 on the curve 215 | /// * Output: E/

216 | #[inline] 217 | fn two_isogenous_curve(p: &Point) -> Curve { 218 | let a = p.x.mul(&p.x); // 1. 219 | let c = p.z.mul(&p.z); // 2. 220 | let a = c.sub(&a); //3. 221 | 222 | Curve::from_coeffs(a, c) 223 | } 224 | 225 | /// Evaluate the two-isogeny at a point (ref `2_iso_eval` Algorithm 12 p.57) 226 | /// * Input: P of order 2, Q, both on the curve 227 | /// * Output: Q' on a 2-iso curve 228 | #[inline] 229 | fn two_isogeny_eval(p: &Point, q: &Point) -> Point { 230 | let t0 = p.x.add(&p.z); // 1. 231 | let t1 = p.x.sub(&p.z); // 2. 232 | let t2 = q.x.add(&q.z); // 3. 233 | let t3 = q.x.sub(&q.z); // 4. 234 | let t0 = t0.mul(&t3); // 5. 235 | let t1 = t1.mul(&t2); // 6. 236 | let t2 = t0.add(&t1); // 7. 237 | let t3 = t0.sub(&t1); // 8. 238 | let x = q.x.mul(&t2); // 9. 239 | let z = q.z.mul(&t3); // 10. 240 | 241 | Point { x, z } 242 | } 243 | 244 | /// Computing the four-isogenous curve (ref `4_iso_curve` Algorithm 13 p.57) 245 | /// * Input: P of order 4. 246 | /// * Output: E/

and constants k1, k2, k3 247 | #[inline] 248 | fn four_isogenous_curve(p: &Point) -> (Curve, K, K, K) { 249 | let k2 = p.x.sub(&p.z); // 1. 250 | let k3 = p.x.add(&p.z); // 2. 251 | let k1 = p.z.mul(&p.z); // 3. 252 | let k1 = k1.add(&k1); // 4. 253 | let c = k1.mul(&k1); // 5. 254 | let k1 = k1.add(&k1); // 6. 255 | let a = p.x.mul(&p.x); // 7. 256 | let a = a.add(&a); // 8. 257 | let a = a.mul(&a); // 9. 258 | 259 | (Curve::from_coeffs(a, c), k1, k2, k3) 260 | } 261 | 262 | /// Evaluate the four-isogeny at a point (ref `4_iso_eval` Algorithm 14 p. 58) 263 | /// * Input: (k1, k2, k3), Q 264 | /// * Output: Q' on a 4-isogenous curve 265 | #[inline] 266 | pub fn four_isogeny_eval(k1: &K, k2: &K, k3: &K, q: &Point) -> Point { 267 | let t0 = q.x.add(&q.z); // 1. 268 | let t1 = q.x.sub(&q.z); // 2. 269 | let x = t0.mul(&k2); // 3. 270 | let z = t1.mul(&k3); // 4. 271 | 272 | let t0 = t0.mul(&t1); // 5. 273 | let t0 = t0.mul(&k1); // 6. 274 | let t1 = x.add(&z); // 7. 275 | let z = x.sub(&z); // 8. 276 | 277 | let t1 = t1.mul(&t1); // 9. 278 | let z = z.mul(&z); // 10. 279 | let x = t0.add(&t1); // 11. 280 | let t0 = z.sub(&t0); // 12. 281 | 282 | let x = x.mul(&t1); // 13. 283 | let z = z.mul(&t0); // 14. 284 | 285 | Point { x, z } 286 | } 287 | 288 | /// Computing the three-isogenous curve (ref `3_iso_curve` Algorithm 15 p.58) 289 | /// Input; P of order 3 290 | /// Output E/

and constants k1, k2 291 | #[inline] 292 | fn three_isogenous_curve(p: &Point) -> (Curve, K, K) { 293 | let k1 = p.x.sub(&p.z); // 1. 294 | let t0 = k1.mul(&k1); // 2. 295 | let k2 = p.x.add(&p.z); // 3. 296 | let t1 = k2.mul(&k2); // 4. 297 | let t2 = t0.add(&t1); // 5. 298 | let t3 = k1.add(&k2); // 6. 299 | 300 | let t3 = t3.mul(&t3); // 7. 301 | let t3 = t3.sub(&t2); // 8. 302 | let t2 = t1.add(&t3); // 9. 303 | let t3 = t3.add(&t0); // 10. 304 | let t4 = t3.add(&t0); // 11. 305 | let t4 = t4.add(&t4); // 12. 306 | 307 | let t4 = t1.add(&t4); // 13. 308 | let c = t2.mul(&t4); // 14. 309 | let t4 = t1.add(&t2); // 15. 310 | let t4 = t4.add(&t4); // 16. 311 | let t4 = t0.add(&t4); // 17. 312 | let t4 = t3.mul(&t4); // 18. 313 | 314 | let t0 = t4.sub(&c); // 19. 315 | let a = c.add(&t0); // 20. 316 | 317 | (Curve::from_coeffs(a, c), k1, k2) 318 | } 319 | /// Evaluate the three-isogeny at a point (ref `3_iso_eval` Algorithm 16 p.58) 320 | /// * Input: k1, k2, Q 321 | /// * Output: Q' on the 3-isogenous curve 322 | #[inline] 323 | pub fn three_isogeny_eval(q: &Point, k1: &K, k2: &K) -> Point { 324 | let t0 = q.x.add(&q.z); // 1. 325 | let t1 = q.x.sub(&q.z); // 2. 326 | let t0 = k1.mul(&t0); // 3. 327 | let t1 = k2.mul(&t1); // 4. 328 | let t2 = t0.add(&t1); // 5. 329 | let t0 = t1.sub(&t0); // 6. 330 | let t2 = t2.mul(&t2); // 7. 331 | let t0 = t0.mul(&t0); // 8. 332 | let x = q.x.mul(&t2); // 9. 333 | let z = q.z.mul(&t0); // 10. 334 | 335 | Point { x, z } 336 | } 337 | 338 | /// Computing and evaluating the 2^e isogeny, simple version (ref `2_e_iso` Algorithm 17 p.59) 339 | /// * Input: S of order 2^(e_2) 340 | /// Optional input: three points on the curve 341 | /// * Output: E/ 342 | /// Optional output: three points on the new curve 343 | #[inline] 344 | fn two_e_iso( 345 | &self, 346 | s: Point, 347 | mut opt: Option>, 348 | curve: &Curve, 349 | ) -> (Curve, Option>) { 350 | let mut c = curve.clone(); 351 | let mut s = s; 352 | let mut e2 = self.params.e2; 353 | 354 | if e2 % 2 == 1 { 355 | e2 -= 1; 356 | let t = Self::ndouble(s.clone(), e2, &c); 357 | 358 | // 3. 359 | c = Self::two_isogenous_curve(&t); 360 | 361 | // 4. 362 | s = Self::two_isogeny_eval(&t, &s); 363 | 364 | // 5 and 6. 365 | opt = opt.map(|(p1, p2, p3)| { 366 | ( 367 | Self::two_isogeny_eval(&t, &p1), 368 | Self::two_isogeny_eval(&t, &p2), 369 | Self::two_isogeny_eval(&t, &p3), 370 | ) 371 | }); 372 | } 373 | 374 | // 1. 375 | for e in (0..=e2 - 2).rev().step_by(2) { 376 | // 2. 377 | let t = Self::ndouble(s.clone(), e, &c); 378 | 379 | // 3. 380 | let (new_c, k1, k2, k3) = Self::four_isogenous_curve(&t); 381 | c = new_c; 382 | 383 | // 4. 384 | s = Self::four_isogeny_eval(&k1, &k2, &k3, &s); 385 | 386 | // 5 and 6. 387 | opt = opt.map(|(p1, p2, p3)| { 388 | ( 389 | Self::four_isogeny_eval(&k1, &k2, &k3, &p1), 390 | Self::four_isogeny_eval(&k1, &k2, &k3, &p2), 391 | Self::four_isogeny_eval(&k1, &k2, &k3, &p3), 392 | ) 393 | }); 394 | } 395 | 396 | // 7. 397 | (c, opt) 398 | } 399 | 400 | /// Computing & evaluating 2^e-isogeny, optimised version (ref `2_e_iso` Algorithm 19 p. 60) 401 | /// * Input: S of order 2^(e_2), curve, strategy 402 | /// Optional input: three points on the curve 403 | /// * Output: E/ 404 | /// Optional output: three points on the new curve 405 | #[inline] 406 | fn two_e_iso_optim( 407 | &self, 408 | s: Point, 409 | mut opt: Option>, 410 | curve_plus: &Curve, 411 | strategy: &[usize], 412 | ) -> Result<(Curve, Option>), String> { 413 | if self.params.e2 as usize / 2 - 1 != strategy.len() { 414 | return Err(String::from("Invalid strategy")); 415 | } 416 | 417 | let mut curve = curve_plus.clone(); 418 | let mut s = s; 419 | let mut e2 = self.params.e2; 420 | 421 | if e2 % 2 == 1 { 422 | e2 -= 1; 423 | let t = Self::ndouble(s.clone(), e2, &curve); 424 | 425 | // 3. 426 | curve = Self::two_isogenous_curve(&t); 427 | 428 | // 4. 429 | s = Self::two_isogeny_eval(&t, &s); 430 | 431 | // 5 and 6. 432 | opt = opt.map(|(p1, p2, p3)| { 433 | ( 434 | Self::two_isogeny_eval(&t, &p1), 435 | Self::two_isogeny_eval(&t, &p2), 436 | Self::two_isogeny_eval(&t, &p3), 437 | ) 438 | }) 439 | } 440 | 441 | // 1. 442 | let mut queue = VecDeque::new(); 443 | 444 | // 2. 445 | queue.push_back((self.params.e2 / 2, s)); 446 | 447 | // 3. 448 | let mut i = 1; 449 | 450 | // 4. 451 | while !queue.is_empty() { 452 | let s_i = if i <= strategy.len() { 453 | // Valid conversion on 32 ad 64 bits arch, cannot panic 454 | strategy[i - 1].try_into().unwrap() 455 | } else { 456 | 1 457 | }; 458 | 459 | // 5. 460 | // Queue is not empty, cannot panic 461 | let (h, p) = queue.pop_back().unwrap(); 462 | 463 | // 6. 464 | if h == 1 { 465 | // 7. 466 | let (new_curve, k1, k2, k3) = Self::four_isogenous_curve(&p); 467 | curve = new_curve; 468 | 469 | // 8. 470 | let mut tmp_queue = VecDeque::new(); 471 | 472 | // 9. 473 | while !queue.is_empty() { 474 | // 10. 475 | // Queue is not empty, cannot panic 476 | let (h_prime, p_prime) = queue.pop_front().unwrap(); 477 | 478 | // 11. 479 | let p_prime = Self::four_isogeny_eval(&k1, &k2, &k3, &p_prime); 480 | 481 | // 12. 482 | tmp_queue.push_back((h_prime - 1, p_prime)); 483 | } 484 | 485 | // 13. 486 | queue = tmp_queue; 487 | 488 | // 14 and 15. 489 | opt = opt.map(|(p1, p2, p3)| { 490 | ( 491 | Self::four_isogeny_eval(&k1, &k2, &k3, &p1), 492 | Self::four_isogeny_eval(&k1, &k2, &k3, &p2), 493 | Self::four_isogeny_eval(&k1, &k2, &k3, &p3), 494 | ) 495 | }) 496 | } else if h > s_i { 497 | // 17. 498 | queue.push_back((h, p.clone())); 499 | 500 | // 18. 501 | let p_prime = Self::ndouble(p, 2 * s_i, &curve); 502 | 503 | // 19. 504 | queue.push_back((h - s_i, p_prime)); 505 | 506 | // 20. 507 | i += 1; 508 | } else { 509 | // 22. 510 | return Err(String::from("Invalid strategy !")); 511 | } 512 | } 513 | 514 | // 23. 515 | Ok((curve, opt)) 516 | } 517 | 518 | /// Computing and evaluating the 3^e isogeny, simple version (ref `3_e_iso` Algorithm 18 p.59) 519 | /// * Input: S of order 3^(e_3) on the curve 520 | /// Optional input : three points on the curve 521 | /// * Output: E/ 522 | /// Optional output: three points on the new curve 523 | #[inline] 524 | fn three_e_iso( 525 | &self, 526 | s: Point, 527 | mut opt: Option>, 528 | curve: &Curve, 529 | ) -> (Curve, Option>) { 530 | let mut c = curve.clone(); 531 | let mut s = s; 532 | 533 | for e in (0..self.params.e3).rev() { 534 | // 2. 535 | let t = Self::ntriple(s.clone(), e, &c); 536 | 537 | // 3. 538 | let (new_c, k1, k2) = Self::three_isogenous_curve(&t); 539 | c = new_c; 540 | 541 | // 4. 542 | s = Self::three_isogeny_eval(&s, &k1, &k2); 543 | 544 | // 5 and 6. 545 | opt = opt.map(|(p1, p2, p3)| { 546 | ( 547 | Self::three_isogeny_eval(&p1, &k1, &k2), 548 | Self::three_isogeny_eval(&p2, &k1, &k2), 549 | Self::three_isogeny_eval(&p3, &k1, &k2), 550 | ) 551 | }) 552 | } 553 | 554 | (c, opt) 555 | } 556 | 557 | /// Computing & evaluating 3^e-isogeny, optimised version (ref `3_e_iso` Algorithm 20 p. 61) 558 | /// * Input: S of order 2^(e_2), curve, strategy 559 | /// Optional input: three points on the curve 560 | /// * Output: E/ 561 | /// Optional output: three points on the new curve 562 | #[inline] 563 | fn three_e_iso_optim( 564 | &self, 565 | s: Point, 566 | mut opt: Option>, 567 | curve_pm: &Curve, 568 | strategy: &[usize], 569 | ) -> Result<(Curve, Option>), String> { 570 | if self.params.e3 as usize - 1 != strategy.len() { 571 | return Err(String::from("Invalid strategy")); 572 | } 573 | 574 | let mut curve = curve_pm.clone(); 575 | 576 | // 1. 577 | let mut queue = VecDeque::new(); 578 | 579 | // 2. 580 | queue.push_back((self.params.e3, s)); 581 | 582 | // 3. 583 | let mut i = 1; 584 | 585 | // 4. 586 | while !queue.is_empty() { 587 | let s_i = if i <= strategy.len() { 588 | // Valid conversion on 32 ad 64 bits arch, cannot panic 589 | strategy[i - 1].try_into().unwrap() 590 | } else { 591 | 1 592 | }; 593 | 594 | // 5. 595 | // Queue is not empty, cannot panic 596 | let (h, p) = queue.pop_back().unwrap(); 597 | 598 | // 6. 599 | if h == 1 { 600 | // 7. 601 | let (new_curve, k1, k2) = Self::three_isogenous_curve(&p); 602 | curve = new_curve; 603 | 604 | // 8. 605 | let mut tmp_queue = VecDeque::new(); 606 | 607 | // 9. 608 | while !queue.is_empty() { 609 | // 10. 610 | // Queue is not empty, cannot panic 611 | let (h_prime, p_prime) = queue.pop_front().unwrap(); 612 | 613 | // 11. 614 | let p_prime = Self::three_isogeny_eval(&p_prime, &k1, &k2); 615 | 616 | // 12. 617 | tmp_queue.push_back((h_prime - 1, p_prime)); 618 | } 619 | 620 | // 13. 621 | queue = tmp_queue; 622 | 623 | // 14 and 15. 624 | opt = opt.map(|(p1, p2, p3)| { 625 | ( 626 | Self::three_isogeny_eval(&p1, &k1, &k2), 627 | Self::three_isogeny_eval(&p2, &k1, &k2), 628 | Self::three_isogeny_eval(&p3, &k1, &k2), 629 | ) 630 | }) 631 | } else if h > s_i { 632 | // 17. 633 | queue.push_back((h, p.clone())); 634 | 635 | // 18. 636 | let p_prime = Self::ntriple(p, s_i, &curve); 637 | 638 | // 19. 639 | queue.push_back((h - s_i, p_prime)); 640 | 641 | // 20. 642 | i += 1; 643 | } else { 644 | // 22. 645 | return Err(String::from("Invalid strategy !")); 646 | } 647 | } 648 | 649 | // 23. 650 | Ok((curve, opt)) 651 | } 652 | 653 | /// Computing public key on the 2-torsion (ref `isogen_2` Algo 21 p.62) 654 | /// * Input: secret key, [tree traversal strategy] 655 | /// * Output: public key 656 | /// 657 | #[inline] 658 | pub fn isogen2(&self, sk: &SecretKey) -> Result, String> { 659 | // 1. 660 | let curve = Curve::starting_curve(); 661 | let curve_plus = curve.curve_plus(); 662 | 663 | // 2. 664 | let xp3 = self.params.xp3.clone(); 665 | let p1 = Point::from_x(xp3); 666 | let xq3 = self.params.xq3.clone(); 667 | let p2 = Point::from_x(xq3); 668 | let xr3 = self.params.xr3.clone(); 669 | let p3 = Point::from_x(xr3); 670 | 671 | // 3. 672 | let xp2 = self.params.xp2.clone(); 673 | let xq2 = self.params.xq2.clone(); 674 | let xr2 = self.params.xr2.clone(); 675 | let s = Self::three_pts_ladder(&sk.to_bits(), xp2, xq2, xr2, &curve)?; 676 | 677 | // 4. 678 | let opt = Some((p1, p2, p3)); 679 | 680 | let (_, opt) = match &self.params.e2_strategy { 681 | Some(strat) => self.two_e_iso_optim(s, opt, &curve_plus, &strat)?, 682 | None => self.two_e_iso(s, opt, &curve_plus), 683 | }; 684 | 685 | // 5. 686 | let (p1, p2, p3) = match opt { 687 | Some(p) => p, 688 | None => return Err(String::from("No points where supplied")), 689 | }; 690 | let x1 = p1.x.div(&p1.z)?; 691 | let x2 = p2.x.div(&p2.z)?; 692 | let x3 = p3.x.div(&p3.z)?; 693 | 694 | // 6. 695 | Ok(PublicKey { x1, x2, x3 }) 696 | } 697 | 698 | /// Computing public key on the 3-torsion (ref `isogen_3` Algorithm 22 p.62) 699 | /// * Input: secret key 700 | /// * Output: public key 701 | #[inline] 702 | pub fn isogen3(&self, sk: &SecretKey) -> Result, String> { 703 | // 1. 704 | let curve = Curve::starting_curve(); 705 | let curve_pm = curve.curve_plus_minus(); 706 | 707 | // 2. 708 | let xp2 = self.params.xp2.clone(); 709 | let p1 = Point::from_x(xp2); 710 | 711 | let xq2 = self.params.xq2.clone(); 712 | let p2 = Point::from_x(xq2); 713 | 714 | let xr2 = self.params.xr2.clone(); 715 | let p3 = Point::from_x(xr2); 716 | 717 | // 3. 718 | let xp3 = self.params.xp3.clone(); 719 | let xq3 = self.params.xq3.clone(); 720 | let xr3 = self.params.xr3.clone(); 721 | 722 | let s = Self::three_pts_ladder(&sk.to_bits(), xp3, xq3, xr3, &curve)?; 723 | 724 | // 4. 725 | let opt = Some((p1, p2, p3)); 726 | 727 | let (_, opt) = match &self.params.e3_strategy { 728 | Some(strat) => self.three_e_iso_optim(s, opt, &curve_pm, &strat)?, 729 | None => self.three_e_iso(s, opt, &curve_pm), 730 | }; 731 | 732 | // 5. 733 | let (p1, p2, p3) = match opt { 734 | Some(p) => p, 735 | None => return Err(String::from("No points where supplied")), 736 | }; 737 | let x1 = p1.x.div(&p1.z)?; 738 | let x2 = p2.x.div(&p2.z)?; 739 | let x3 = p3.x.div(&p3.z)?; 740 | 741 | // 6. 742 | Ok(PublicKey { x1, x2, x3 }) 743 | } 744 | 745 | /// Establishing shared keys on the 2-torsion, (ref `isoex_2` Algorithm 23 p.63) 746 | /// * Input: secret key, public key, [tree traversal strategy] 747 | /// * Output: j-invariant 748 | #[inline] 749 | pub fn isoex2(&self, sk: &SecretKey, pk: &PublicKey) -> Result { 750 | let one = K::one(); 751 | let two = one.add(&one); 752 | let four = two.add(&two); 753 | 754 | // 1. 755 | let curve = Curve::from_public_key(pk)?; 756 | 757 | // 2. 758 | let (x1, x2, x3) = (&pk.x1, &pk.x2, &pk.x3); 759 | let s = Self::three_pts_ladder(&sk.to_bits(), x1.clone(), x2.clone(), x3.clone(), &curve)?; 760 | 761 | // 3. 762 | let curve_plus = Curve::from_coeffs(curve.a.add(&two), four.clone()); 763 | 764 | // 4. 765 | let (curve_plus, _) = match &self.params.e2_strategy { 766 | Some(strat) => self.two_e_iso_optim(s, None, &curve_plus, &strat)?, 767 | None => self.two_e_iso(s, None, &curve_plus), 768 | }; 769 | 770 | // 5. 771 | let curve = Curve::from_coeffs( 772 | curve_plus.a.mul(&four).sub(&curve_plus.c.mul(&two)), 773 | curve_plus.c, 774 | ); 775 | 776 | // 6, 7. 777 | Ok(curve.j_invariant()?) 778 | } 779 | 780 | /// Establishing shared keys on the 3-torsion (ref `isoex_3` Algorithm 24 p.63) 781 | /// * Input: secret key, public key, [tree traversal strategy] 782 | /// * Output: a j-invariant 783 | #[inline] 784 | pub fn isoex3(&self, sk: &SecretKey, pk: &PublicKey) -> Result { 785 | let one = K::one(); 786 | let two = one.add(&one); 787 | 788 | // 1. 789 | let curve = Curve::from_public_key(pk)?; 790 | 791 | // 2. 792 | let (x1, x2, x3) = (&pk.x1, &pk.x2, &pk.x3); 793 | let s = Self::three_pts_ladder(&sk.to_bits(), x1.clone(), x2.clone(), x3.clone(), &curve)?; 794 | 795 | // 3. 796 | let curve_pm = Curve::from_coeffs(curve.a.add(&two), curve.a.sub(&two)); 797 | 798 | // 4. 799 | let (curve_pm, _) = match &self.params.e3_strategy { 800 | Some(strat) => self.three_e_iso_optim(s, None, &curve_pm, &strat)?, 801 | None => self.three_e_iso(s, None, &curve_pm), 802 | }; 803 | 804 | // 5. 805 | let curve = Curve::from_coeffs( 806 | two.mul(&curve_pm.a.add(&curve_pm.c)), 807 | curve_pm.a.sub(&curve_pm.c), 808 | ); 809 | 810 | // 6, 7. 811 | Ok(curve.j_invariant()?) 812 | } 813 | } 814 | 815 | #[cfg(test)] 816 | mod tests { 817 | use crate::{ 818 | constants::cs_p434::{SIKE_P434_NKS2, SIKE_P434_NKS3}, 819 | ff::{PrimeFieldP434, QuadraticExtension}, 820 | isogeny::publicparams::sike_p434_params, 821 | utils::{ 822 | conversion::{str_to_p434, str_to_u64}, 823 | strategy::{P434_THREE_TORSION_STRATEGY, P434_TWO_TORSION_STRATEGY}, 824 | }, 825 | }; 826 | 827 | use super::*; 828 | 829 | #[test] 830 | fn test_iso_eval() { 831 | let one: QuadraticExtension = QuadraticExtension::one(); 832 | let two = one.add(&one); 833 | let k1 = two.add(&one).mul(&two); 834 | let k2 = two.add(&two).mul(&two); 835 | let k3 = two.add(&two); 836 | 837 | let x = one.add(&one).add(&two).add(&two); 838 | 839 | let pt = Point::from_x(x); 840 | 841 | println!("Before {:?}", pt.x.div(&pt.z)); 842 | 843 | let pt2 = CurveIsogenies::four_isogeny_eval(&k1, &k2, &k3, &pt); 844 | 845 | let pt3 = CurveIsogenies::three_isogeny_eval(&pt, &k1, &k2); 846 | 847 | println!("After 4isoeval {:?}", pt2.x.div(&pt2.z)); 848 | println!("After 3isoeval {:?}", pt3.x.div(&pt3.z)); 849 | 850 | assert_ne!(pt, pt3) 851 | } 852 | 853 | #[test] 854 | fn test_isoex_isogen() { 855 | let nks3 = str_to_u64(SIKE_P434_NKS3).unwrap(); 856 | let nks2 = str_to_u64(SIKE_P434_NKS2).unwrap(); 857 | 858 | let params = sike_p434_params(None, None).unwrap(); 859 | 860 | let iso = CurveIsogenies::init(params); 861 | 862 | let sk3 = SecretKey::get_random_secret_key(nks3 as usize).unwrap(); 863 | let sk2 = SecretKey::get_random_secret_key(nks2 as usize).unwrap(); 864 | 865 | let pk3 = iso.isogen3(&sk3).unwrap(); 866 | let pk2 = iso.isogen2(&sk2).unwrap(); 867 | 868 | let j_a = iso.isoex2(&sk2, &pk3).unwrap(); 869 | let j_b = iso.isoex3(&sk3, &pk2).unwrap(); 870 | 871 | println!("j_A = {:?}", j_a); 872 | println!("j_B = {:?}", j_b); 873 | 874 | assert!(j_a.equals(&j_b)); 875 | } 876 | 877 | #[test] 878 | fn test_isogen2() { 879 | let nks2 = str_to_u64(SIKE_P434_NKS2).unwrap(); 880 | let sk = SecretKey::get_random_secret_key(nks2 as usize).unwrap(); 881 | let strat = Some(P434_TWO_TORSION_STRATEGY.to_vec()); 882 | 883 | let params = sike_p434_params(strat, None).unwrap(); 884 | 885 | let iso = CurveIsogenies::init(params); 886 | let pk = iso.isogen2(&sk).unwrap(); 887 | let pk_2 = iso.isogen2(&sk).unwrap(); 888 | 889 | assert_eq!(pk, pk_2); 890 | } 891 | 892 | #[test] 893 | fn test_isogen3() { 894 | let nks3 = str_to_u64(SIKE_P434_NKS3).unwrap(); 895 | let sk = SecretKey::get_random_secret_key(nks3 as usize).unwrap(); 896 | let strat = Some(P434_THREE_TORSION_STRATEGY.to_vec()); 897 | 898 | let params = sike_p434_params(None, strat).unwrap(); 899 | 900 | let iso = CurveIsogenies::init(params); 901 | let pk = iso.isogen3(&sk).unwrap(); 902 | let pk_2 = iso.isogen3(&sk).unwrap(); 903 | 904 | assert_eq!(pk, pk_2); 905 | } 906 | 907 | #[test] 908 | fn test_conversion_secretkey_bytes() { 909 | let k = SecretKey::get_random_secret_key(256).unwrap(); 910 | let b = k.clone().to_bytes(); 911 | let k_recovered = SecretKey::from_bytes(&b); 912 | 913 | assert_eq!(k, k_recovered); 914 | } 915 | 916 | #[test] 917 | fn test_conversion_publickey_bytes() { 918 | let nks3 = str_to_u64(SIKE_P434_NKS3).unwrap(); 919 | let sk = SecretKey::get_random_secret_key(nks3 as usize).unwrap(); 920 | let strat = Some(P434_THREE_TORSION_STRATEGY.to_vec()); 921 | 922 | let params = sike_p434_params(None, strat).unwrap(); 923 | let iso = CurveIsogenies::init(params); 924 | let pk = iso.isogen3(&sk).unwrap(); 925 | let (b0, b1, b2) = pk.clone().into_bytes(); 926 | 927 | let pk_recovered = PublicKey::from_bytes(&b0, &b1, &b2).unwrap(); 928 | 929 | assert_eq!(pk, pk_recovered) 930 | } 931 | 932 | #[test] 933 | fn test_j_invariant() { 934 | use crate::{ 935 | ff::{ff_p434::PrimeFieldP434, QuadraticExtension}, 936 | isogeny::Curve, 937 | }; 938 | let curve = Curve::starting_curve(); 939 | 940 | let j: QuadraticExtension = curve.j_invariant().unwrap(); 941 | 942 | // 287496 + 0i 943 | assert_eq!(j, str_to_p434("00046308", "00000000").unwrap()) 944 | } 945 | } 946 | -------------------------------------------------------------------------------- /src/isogeny/point.rs: -------------------------------------------------------------------------------- 1 | //! Points in projective coordinates 2 | 3 | use crate::ff::FiniteField; 4 | use std::fmt::{Debug, Formatter, Result}; 5 | 6 | /// Point defined by (X: Z) in projective coordinates 7 | #[derive(Clone)] 8 | pub struct Point { 9 | /// X coordinate in projective space 10 | pub x: K, 11 | /// Z coordinate in projective space 12 | pub z: K, 13 | } 14 | 15 | impl Debug for Point { 16 | /// A point is represented as (x : z) 17 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 18 | write!(f, "({:?}:{:?})", self.x, self.z) 19 | } 20 | } 21 | 22 | impl Point { 23 | /// Returns the points (x : 1) 24 | #[inline] 25 | pub fn from_x(x: K) -> Self { 26 | Self { x, z: K::one() } 27 | } 28 | } 29 | 30 | impl PartialEq for Point { 31 | /// Two points are equal if (z != 0 and x/z) match, or if z = 0 for both 32 | fn eq(&self, other: &Self) -> bool { 33 | let other_zero = other.z.is_zero(); 34 | if self.z.is_zero() { 35 | other_zero 36 | } else if other_zero { 37 | false 38 | } else { 39 | // Z / Z' are not zero and operation take place on finite field, div cannot panic 40 | let ratio = self.x.div(&self.z).unwrap(); 41 | let other_ratio = &other.x.div(&other.z).unwrap(); 42 | ratio.equals(&other_ratio) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/isogeny/publickey.rs: -------------------------------------------------------------------------------- 1 | //! Public key material 2 | 3 | use crate::ff::FiniteField; 4 | 5 | /// Public key 6 | /// 7 | /// The public key is a curve, but can be represented as a triple of points, of which only 8 | /// the x-coordinate is stored. 9 | #[derive(Clone)] 10 | pub struct PublicKey { 11 | /// First point 12 | pub x1: K, 13 | 14 | /// Second point 15 | pub x2: K, 16 | 17 | /// Third point 18 | pub x3: K, 19 | } 20 | 21 | impl std::fmt::Debug for PublicKey { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | write!(f, "{:?}, {:?}, {:?}", self.x1, self.x2, self.x3) 24 | } 25 | } 26 | 27 | impl PublicKey { 28 | /// Converts the public key to a sequence of bytes (for each point) 29 | pub fn into_bytes(self) -> (Vec, Vec, Vec) { 30 | ( 31 | self.x1.into_bytes(), 32 | self.x2.into_bytes(), 33 | self.x3.into_bytes(), 34 | ) 35 | } 36 | 37 | /// Creates a new public key for given three points (represented as bytes) 38 | pub fn from_bytes(part1: &[u8], part2: &[u8], part3: &[u8]) -> Result { 39 | Ok(Self { 40 | x1: K::from_bytes(part1)?, 41 | x2: K::from_bytes(part2)?, 42 | x3: K::from_bytes(part3)?, 43 | }) 44 | } 45 | } 46 | 47 | impl std::cmp::PartialEq for PublicKey { 48 | fn eq(&self, other: &Self) -> bool { 49 | self.x1.equals(&other.x1) && self.x2.equals(&other.x2) && self.x3.equals(&other.x3) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/isogeny/publicparams.rs: -------------------------------------------------------------------------------- 1 | //! Public parameters 2 | 3 | use crate::constants::{cs_p434::*, cs_p503::*, cs_p610::*, cs_p751::*}; 4 | use crate::ff::{ 5 | QuadraticExtension, 6 | { 7 | ff_p434::PrimeFieldP434, ff_p503::PrimeFieldP503, ff_p610::PrimeFieldP610, 8 | ff_p751::PrimeFieldP751, 9 | }, 10 | }; 11 | use crate::utils::{conversion::*, strategy}; 12 | 13 | /// Public parameters 14 | #[derive(Clone)] 15 | pub struct PublicParameters { 16 | /// Security parameter (curve-dependent) 17 | pub secparam: usize, 18 | 19 | /// Size of K_2 keyspace for sk2 generation; 20 | pub keyspace2: u64, 21 | 22 | /// Size of K_3 keyspace fir sk3 generation; 23 | pub keyspace3: u64, 24 | 25 | /// Tree-traversal strategy for the 2-torsion 26 | pub e2_strategy: Option, 27 | 28 | /// Tree-traversal strategy for the 3-torsion 29 | pub e3_strategy: Option, 30 | 31 | /// Exponent of 2 in the prime modulus 32 | pub e2: u64, 33 | 34 | /// Exponent of 3 in the prime modulus 35 | pub e3: u64, 36 | 37 | /// x-coordinate of the point P2 38 | pub xp2: K, 39 | 40 | /// x-coordinate of the point Q2 41 | pub xq2: K, 42 | 43 | /// x-coordinate of the point R2 44 | pub xr2: K, 45 | 46 | /// x-coordinate of the point P3 47 | pub xp3: K, 48 | 49 | /// x-coordinate of the point Q3 50 | pub xq3: K, 51 | 52 | /// x-coordinate of the point R3 53 | pub xr3: K, 54 | } 55 | 56 | /// Load params for SIKE_p434 57 | pub fn sike_p434_params( 58 | strat2tor: Option, 59 | strat3tor: Option, 60 | ) -> Result>, String> { 61 | Ok(PublicParameters { 62 | secparam: 128, 63 | keyspace2: str_to_u64(SIKE_P434_NKS2)?, 64 | keyspace3: str_to_u64(SIKE_P434_NKS3)?, 65 | e2_strategy: strat2tor, 66 | e3_strategy: strat3tor, 67 | e2: str_to_u64(SIKE_P434_E2)?, 68 | e3: str_to_u64(SIKE_P434_E3)?, 69 | xp2: str_to_p434(SIKE_P434_XP20, SIKE_P434_XP21)?, 70 | xq2: str_to_p434(SIKE_P434_XQ20, SIKE_P434_XQ21)?, 71 | xr2: str_to_p434(SIKE_P434_XR20, SIKE_P434_XR21)?, 72 | xp3: str_to_p434(SIKE_P434_XP30, SIKE_P434_XP31)?, 73 | xq3: str_to_p434(SIKE_P434_XQ30, SIKE_P434_XQ31)?, 74 | xr3: str_to_p434(SIKE_P434_XR30, SIKE_P434_XR31)?, 75 | }) 76 | } 77 | 78 | /// Load params for SIKE_p503 79 | pub fn sike_p503_params( 80 | strat2tor: Option, 81 | strat3tor: Option, 82 | ) -> Result>, String> { 83 | Ok(PublicParameters { 84 | secparam: 192, 85 | keyspace2: str_to_u64(SIKE_P503_NKS2)?, 86 | keyspace3: str_to_u64(SIKE_P503_NKS3)?, 87 | e2_strategy: strat2tor, 88 | e3_strategy: strat3tor, 89 | e2: str_to_u64(SIKE_P503_E2)?, 90 | e3: str_to_u64(SIKE_P503_E3)?, 91 | xp2: str_to_p503(SIKE_P503_XP20, SIKE_P503_XP21)?, 92 | xq2: str_to_p503(SIKE_P503_XQ20, SIKE_P503_XQ21)?, 93 | xr2: str_to_p503(SIKE_P503_XR20, SIKE_P503_XR21)?, 94 | xp3: str_to_p503(SIKE_P503_XP30, SIKE_P503_XP31)?, 95 | xq3: str_to_p503(SIKE_P503_XQ30, SIKE_P503_XQ31)?, 96 | xr3: str_to_p503(SIKE_P503_XR30, SIKE_P503_XR31)?, 97 | }) 98 | } 99 | 100 | /// Load params for SIKE_p610 101 | pub fn sike_p610_params( 102 | strat2tor: Option, 103 | strat3tor: Option, 104 | ) -> Result>, String> { 105 | Ok(PublicParameters { 106 | secparam: 192, 107 | keyspace2: str_to_u64(SIKE_P610_NKS2)?, 108 | keyspace3: str_to_u64(SIKE_P610_NKS3)?, 109 | e2_strategy: strat2tor, 110 | e3_strategy: strat3tor, 111 | e2: str_to_u64(SIKE_P610_E2)?, 112 | e3: str_to_u64(SIKE_P610_E3)?, 113 | xp2: str_to_p610(SIKE_P610_XP20, SIKE_P610_XP21)?, 114 | xq2: str_to_p610(SIKE_P610_XQ20, SIKE_P610_XQ21)?, 115 | xr2: str_to_p610(SIKE_P610_XR20, SIKE_P610_XR21)?, 116 | xp3: str_to_p610(SIKE_P610_XP30, SIKE_P610_XP31)?, 117 | xq3: str_to_p610(SIKE_P610_XQ30, SIKE_P610_XQ31)?, 118 | xr3: str_to_p610(SIKE_P610_XR30, SIKE_P610_XR31)?, 119 | }) 120 | } 121 | 122 | /// Load params for SIKE_p751 123 | pub fn sike_p751_params( 124 | strat2tor: Option, 125 | strat3tor: Option, 126 | ) -> Result>, String> { 127 | Ok(PublicParameters { 128 | secparam: 256, 129 | keyspace2: str_to_u64(SIKE_P751_NKS2)?, 130 | keyspace3: str_to_u64(SIKE_P751_NKS3)?, 131 | e2_strategy: strat2tor, 132 | e3_strategy: strat3tor, 133 | e2: str_to_u64(SIKE_P751_E2)?, 134 | e3: str_to_u64(SIKE_P751_E3)?, 135 | xp2: str_to_p751(SIKE_P751_XP20, SIKE_P751_XP21)?, 136 | xq2: str_to_p751(SIKE_P751_XQ20, SIKE_P751_XQ21)?, 137 | xr2: str_to_p751(SIKE_P751_XR20, SIKE_P751_XR21)?, 138 | xp3: str_to_p751(SIKE_P751_XP30, SIKE_P751_XP31)?, 139 | xq3: str_to_p751(SIKE_P751_XQ30, SIKE_P751_XQ31)?, 140 | xr3: str_to_p751(SIKE_P751_XR30, SIKE_P751_XR31)?, 141 | }) 142 | } 143 | -------------------------------------------------------------------------------- /src/isogeny/secretkey.rs: -------------------------------------------------------------------------------- 1 | //! Secret key 2 | use bitvec::prelude::*; 3 | 4 | #[derive(Clone, PartialEq)] 5 | /// Secret key 6 | pub struct SecretKey { 7 | bytes: Vec, 8 | } 9 | 10 | impl std::fmt::Debug for SecretKey { 11 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 12 | write!(f, "{:?}", self.bytes) 13 | } 14 | } 15 | 16 | impl SecretKey { 17 | /// Get a random secret key of given `size` in bytes 18 | /// 19 | /// # Examples 20 | /// 21 | /// ```rust 22 | /// use rust_sike::pke::SecretKey; 23 | /// let key = SecretKey::get_random_secret_key(64); 24 | /// println!("{:?}", key); 25 | /// ``` 26 | pub fn get_random_secret_key(size: usize) -> Result { 27 | let mut bytes = vec![0; size]; 28 | if let Err(_e) = getrandom::getrandom(&mut bytes) { 29 | return Err(String::from("RNG Error")); 30 | }; 31 | Ok(Self::from_bytes(&bytes)) 32 | } 33 | 34 | /// Converts the secret key into a sequence of bits 35 | /// 36 | /// Note: The format is big endian 37 | pub fn to_bits(&self) -> BitVec { 38 | // We reverse the order of the bytes 39 | // such that bits are properly ordered 40 | // Ex : [1, 0] -> [00000000, 00000001] 41 | let bytes = self.bytes.iter().rev().copied().collect(); 42 | BitVec::::from_vec(bytes) 43 | } 44 | 45 | /// Converts the secret key to bytes 46 | pub fn to_bytes(&self) -> Vec { 47 | self.bytes.clone() 48 | } 49 | 50 | /// Build a secret key from bytes 51 | pub fn from_bytes(bytes: &[u8]) -> Self { 52 | Self { 53 | bytes: bytes.to_vec(), 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/kem/mod.rs: -------------------------------------------------------------------------------- 1 | //! Key encapsulation mechanism 2 | //! 3 | //! # Examples 4 | //! ```rust 5 | //! use rust_sike::{self, KEM}; 6 | //! let params = rust_sike::sike_p434_params(None, None); 7 | //! 8 | //! let kem = KEM::setup(params); 9 | //! 10 | //! // Alice runs keygen, publishes pk3. Values s and sk3 are secret 11 | //! let (s, sk3, pk3) = kem.keygen(); 12 | //! 13 | //! // Bob uses pk3 to derive a key k and encapsulation c 14 | //! let (c, k) = kem.encaps(&pk3); 15 | //! 16 | //! // Bob sends c to Alice 17 | //! // Alice uses s, c, sk3 and pk3 to recover k 18 | //! let k_recovered = kem.decaps(&s, &sk3, &pk3, c); 19 | //! 20 | //! assert_eq!(k, k_recovered); 21 | //! ``` 22 | 23 | use crate::{ 24 | ff::FiniteField, 25 | isogeny::{PublicKey, PublicParameters, SecretKey}, 26 | pke::{Ciphertext, Message, PKE}, 27 | utils::{conversion, shake}, 28 | }; 29 | 30 | use std::fmt::Debug; 31 | 32 | /// Key-encapsulation mechanism (ref Algorithm 2, Section 1.3.10) 33 | pub struct KEM { 34 | params: PublicParameters, 35 | pke: PKE, 36 | n: usize, 37 | } 38 | 39 | impl KEM { 40 | /// Initialise the KEM 41 | #[inline] 42 | pub fn setup(params: PublicParameters) -> Self { 43 | Self { 44 | pke: PKE::setup(params.clone()), 45 | n: params.secparam, 46 | params, 47 | } 48 | } 49 | 50 | /// Generate a secret and a keypair 51 | #[inline] 52 | pub fn keygen(&self) -> Result<(Vec, SecretKey, PublicKey), String> { 53 | let sk3 = SecretKey::get_random_secret_key(self.params.keyspace3 as usize)?; 54 | let pk3 = self.pke.isogenies.isogen3(&sk3)?; 55 | let s = Self::random_string(self.n); 56 | 57 | Ok((s, sk3, pk3)) 58 | } 59 | 60 | /// Encapsulate the shared secret using the PKE encryption 61 | #[inline] 62 | pub fn encaps(&self, pk: &PublicKey) -> Result<(Ciphertext, Vec), String> { 63 | let message = Message::from_bytes(Self::random_string(self.n / 8)); 64 | let r = self.hash_function_g(&message.clone(), &pk); 65 | let det_sk = SecretKey::from_bytes(&r); 66 | 67 | let c0: PublicKey = self.pke.isogenies.isogen2(&det_sk)?; 68 | 69 | let j_inv = self.pke.isogenies.isoex2(&det_sk, &pk)?; 70 | let h = self.pke.hash_function_f(j_inv); 71 | 72 | if h.len() != message.bytes.len() { 73 | return Err(String::from("Incorrect Hash")); 74 | } 75 | 76 | let c1_bytes = PKE::::xor(&message.bytes, &h); 77 | 78 | let (part1, part2, part3) = c0.into_bytes(); 79 | let cipher = Ciphertext { 80 | bytes00: part1, 81 | bytes01: part2, 82 | bytes02: part3, 83 | bytes1: c1_bytes, 84 | }; 85 | 86 | let k = self.hash_function_h(&message, &cipher); 87 | Ok((cipher, k)) 88 | } 89 | 90 | /// Decapsulate the shared secret using the PKE decryption 91 | #[inline] 92 | pub fn decaps( 93 | &self, 94 | s: &[u8], 95 | sk: &SecretKey, 96 | pk: &PublicKey, 97 | c: Ciphertext, 98 | ) -> Result, String> { 99 | let m = self.pke.dec(&sk, c.clone())?; 100 | let s = Message::from_bytes(s.to_vec()); 101 | let r = self.hash_function_g(&m.clone(), &pk); 102 | 103 | let c0 = PublicKey::from_bytes(&c.bytes00, &c.bytes01, &c.bytes02)?; 104 | let rsk = SecretKey::from_bytes(&r); 105 | 106 | let c0p = self.pke.isogenies.isogen2(&rsk)?; 107 | 108 | if c0p == c0 { 109 | Ok(self.hash_function_h(&m, &c)) 110 | } else { 111 | Ok(self.hash_function_h(&s, &c)) 112 | } 113 | } 114 | 115 | fn random_string(size: usize) -> Vec { 116 | let mut result = vec![0; size]; 117 | getrandom::getrandom(&mut result).unwrap(); 118 | result 119 | } 120 | 121 | fn hash_function_g(&self, m: &Message, pk: &PublicKey) -> Vec { 122 | let (part1, part2, part3) = pk.clone().into_bytes(); 123 | let msg_bytes = m.clone().into_bytes(); 124 | let input = conversion::concatenate(&[&msg_bytes, &part1, &part2, &part3]); 125 | 126 | let n: usize = self.params.secparam; 127 | 128 | shake::shake256(&input, n / 8) 129 | } 130 | 131 | fn hash_function_h(&self, m: &Message, c: &Ciphertext) -> Vec { 132 | let input = conversion::concatenate(&[ 133 | &m.clone().into_bytes(), 134 | &c.bytes00, 135 | &c.bytes01, 136 | &c.bytes02, 137 | &c.bytes1, 138 | ]); 139 | 140 | let n = self.params.secparam; 141 | 142 | shake::shake256(&input, n / 8) 143 | } 144 | } 145 | 146 | #[cfg(test)] 147 | mod tests { 148 | use super::*; 149 | use crate::{ 150 | isogeny::{sike_p434_params, sike_p503_params, sike_p610_params, sike_p751_params}, 151 | utils::strategy::*, 152 | }; 153 | 154 | #[test] 155 | fn test_kem_p434() { 156 | let params = sike_p434_params(None, None).unwrap(); 157 | 158 | let kem = KEM::setup(params); 159 | 160 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 161 | let (s, sk3, pk3) = kem.keygen().unwrap(); 162 | 163 | // Bob uses pk3 to derive a key k and encapsulation c 164 | let (c, k) = kem.encaps(&pk3).unwrap(); 165 | 166 | // Bob sends c to Alice 167 | // Alice uses s, c, sk3 and pk3 to recover k 168 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 169 | 170 | assert_eq!(k, k_recovered); 171 | } 172 | 173 | #[test] 174 | fn test_kem_p503() { 175 | let params = sike_p503_params(None, None).unwrap(); 176 | 177 | let kem = KEM::setup(params); 178 | 179 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 180 | let (s, sk3, pk3) = kem.keygen().unwrap(); 181 | 182 | // Bob uses pk3 to derive a key k and encapsulation c 183 | let (c, k) = kem.encaps(&pk3).unwrap(); 184 | 185 | // Bob sends c to Alice 186 | // Alice uses s, c, sk3 and pk3 to recover k 187 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 188 | 189 | assert_eq!(k, k_recovered); 190 | } 191 | 192 | #[test] 193 | fn test_kem_p610() { 194 | let params = sike_p610_params(None, None).unwrap(); 195 | 196 | let kem = KEM::setup(params); 197 | 198 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 199 | let (s, sk3, pk3) = kem.keygen().unwrap(); 200 | 201 | // Bob uses pk3 to derive a key k and encapsulation c 202 | let (c, k) = kem.encaps(&pk3).unwrap(); 203 | 204 | // Bob sends c to Alice 205 | // Alice uses s, c, sk3 and pk3 to recover k 206 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 207 | 208 | assert_eq!(k, k_recovered); 209 | } 210 | 211 | #[test] 212 | fn test_kem_p751() { 213 | let params = sike_p751_params(None, None).unwrap(); 214 | 215 | let kem = KEM::setup(params); 216 | 217 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 218 | let (s, sk3, pk3) = kem.keygen().unwrap(); 219 | 220 | // Bob uses pk3 to derive a key k and encapsulation c 221 | let (c, k) = kem.encaps(&pk3).unwrap(); 222 | 223 | // Bob sends c to Alice 224 | // Alice uses s, c, sk3 and pk3 to recover k 225 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 226 | 227 | assert_eq!(k, k_recovered); 228 | } 229 | 230 | #[test] 231 | fn test_kem_optim_p434() { 232 | let params = sike_p434_params( 233 | Some(P434_TWO_TORSION_STRATEGY.to_vec()), 234 | Some(P434_THREE_TORSION_STRATEGY.to_vec()), 235 | ) 236 | .unwrap(); 237 | 238 | let kem = KEM::setup(params); 239 | 240 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 241 | let (s, sk3, pk3) = kem.keygen().unwrap(); 242 | 243 | // Bob uses pk3 to derive a key k and encapsulation c 244 | let (c, k) = kem.encaps(&pk3).unwrap(); 245 | 246 | // Bob sends c to Alice 247 | // Alice uses s, c, sk3 and pk3 to recover k 248 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 249 | 250 | assert_eq!(k, k_recovered); 251 | } 252 | 253 | #[test] 254 | fn test_kem_optim_p503() { 255 | let params = sike_p503_params( 256 | Some(P503_TWO_TORSION_STRATEGY.to_vec()), 257 | Some(P503_THREE_TORSION_STRATEGY.to_vec()), 258 | ) 259 | .unwrap(); 260 | 261 | let kem = KEM::setup(params); 262 | 263 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 264 | let (s, sk3, pk3) = kem.keygen().unwrap(); 265 | 266 | // Bob uses pk3 to derive a key k and encapsulation c 267 | let (c, k) = kem.encaps(&pk3).unwrap(); 268 | 269 | // Bob sends c to Alice 270 | // Alice uses s, c, sk3 and pk3 to recover k 271 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 272 | 273 | assert_eq!(k, k_recovered); 274 | } 275 | 276 | #[test] 277 | fn test_kem_optim_p610() { 278 | let params = sike_p610_params( 279 | Some(P610_TWO_TORSION_STRATEGY.to_vec()), 280 | Some(P610_THREE_TORSION_STRATEGY.to_vec()), 281 | ) 282 | .unwrap(); 283 | 284 | let kem = KEM::setup(params); 285 | 286 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 287 | let (s, sk3, pk3) = kem.keygen().unwrap(); 288 | 289 | // Bob uses pk3 to derive a key k and encapsulation c 290 | let (c, k) = kem.encaps(&pk3).unwrap(); 291 | 292 | // Bob sends c to Alice 293 | // Alice uses s, c, sk3 and pk3 to recover k 294 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 295 | 296 | assert_eq!(k, k_recovered); 297 | } 298 | 299 | #[test] 300 | fn test_kem_optim_p751() { 301 | let params = sike_p751_params( 302 | Some(P751_TWO_TORSION_STRATEGY.to_vec()), 303 | Some(P751_THREE_TORSION_STRATEGY.to_vec()), 304 | ) 305 | .unwrap(); 306 | 307 | let kem = KEM::setup(params); 308 | 309 | // Alice runs keygen, publishes pk3. Values s and sk3 are secret 310 | let (s, sk3, pk3) = kem.keygen().unwrap(); 311 | 312 | // Bob uses pk3 to derive a key k and encapsulation c 313 | let (c, k) = kem.encaps(&pk3).unwrap(); 314 | 315 | // Bob sends c to Alice 316 | // Alice uses s, c, sk3 and pk3 to recover k 317 | let k_recovered = kem.decaps(&s, &sk3, &pk3, c).unwrap(); 318 | 319 | assert_eq!(k, k_recovered); 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is documentation for the `rust-sike` crate. 2 | //! 3 | //! # Introduction 4 | //! `rust-sike` is an implementation of the supersingular isogeny primitives for SIKE, a post-quantum 5 | //! candidate submitted to NIST for standardization. 6 | //! 7 | //! This crate provides public-key encryption (`PKE`) and key encapsulation (`KEM`). 8 | //! 9 | //! # Examples 10 | //! 11 | //! ```rust 12 | //! use rust_sike::{self, KEM}; 13 | //! let params = rust_sike::sike_p434_params(None, None); 14 | //! 15 | //! let kem = KEM::setup(params); 16 | //! 17 | //! // Alice runs keygen, publishes pk3. Values s and sk3 are secret 18 | //! let (s, sk3, pk3) = kem.keygen(); 19 | //! 20 | //! // Bob uses pk3 to derive a key k and encapsulation c 21 | //! let (c, k) = kem.encaps(&pk3); 22 | //! 23 | //! // Bob sends c to Alice 24 | //! // Alice uses s, c, sk3 and pk3 to recover k 25 | //! let k_recovered = kem.decaps(&s, &sk3, &pk3, c); 26 | //! 27 | //! assert_eq!(k, k_recovered); 28 | //! ``` 29 | 30 | #![warn(missing_docs)] 31 | #![deny(clippy::mem_forget)] 32 | #[forbid(unsafe_code)] 33 | mod constants; 34 | mod ff; 35 | mod isogeny; 36 | mod utils; 37 | 38 | pub mod kem; 39 | pub mod pke; 40 | pub use {kem::KEM, pke::PKE}; 41 | 42 | pub use utils::strategy::{ 43 | compute_strategy, P434_THREE_TORSION_STRATEGY, P434_TWO_TORSION_STRATEGY, 44 | P503_THREE_TORSION_STRATEGY, P503_TWO_TORSION_STRATEGY, P610_THREE_TORSION_STRATEGY, 45 | P610_TWO_TORSION_STRATEGY, P751_THREE_TORSION_STRATEGY, P751_TWO_TORSION_STRATEGY, 46 | }; 47 | 48 | pub use crate::{ 49 | isogeny::{sike_p434_params, sike_p503_params, sike_p610_params, sike_p751_params}, 50 | utils::strategy, 51 | }; 52 | -------------------------------------------------------------------------------- /src/pke/mod.rs: -------------------------------------------------------------------------------- 1 | //! Public-key cryptosystem: 2 | //! 3 | //! A `Message` is encrypted using the public key, and decrypted using the corresponding private key. 4 | //! 5 | //! # Examples 6 | //! ```rust 7 | //! use rust_sike::{self, pke::{PKE, Message}}; 8 | //! let params = rust_sike::sike_p434_params( 9 | //! Some(rust_sike::P434_TWO_TORSION_STRATEGY.to_vec()), 10 | //! Some(rust_sike::P434_THREE_TORSION_STRATEGY.to_vec()), 11 | //! ); 12 | //! 13 | //! let pke = PKE::setup(params.clone()); 14 | //! 15 | //! // Alice generates a keypair, she publishes her pk 16 | //! let (sk, pk) = pke.gen(); 17 | //! 18 | //! // Bob writes a message 19 | //! let msg = Message::from_bytes(vec![0; params.secparam / 8]); 20 | //! // Bob encrypts the message using Alice's pk 21 | //! let ciphertext = pke.enc(&pk, msg.clone()); 22 | //! 23 | //! // Bob sends the ciphertext to Alice 24 | //! // Alice decrypts the message using her sk 25 | //! let msg_recovered = pke.dec(&sk, ciphertext); 26 | //! 27 | //! // Alice should correctly recover Bob's plaintext message 28 | //! assert_eq!(msg_recovered.to_bytes(), msg.to_bytes()); 29 | //! ``` 30 | 31 | use crate::{ 32 | ff::FiniteField, 33 | isogeny::{CurveIsogenies, PublicParameters}, 34 | utils::shake, 35 | }; 36 | 37 | pub use crate::isogeny::{PublicKey, SecretKey}; 38 | 39 | use std::fmt::Debug; 40 | 41 | /// `Message` 42 | #[derive(Clone)] 43 | pub struct Message { 44 | /// Contents of the message 45 | pub bytes: Vec, 46 | } 47 | 48 | impl Message { 49 | /// Build a `Message` from a sequence of bytes 50 | pub fn from_bytes(bytes: Vec) -> Self { 51 | Self { bytes } 52 | } 53 | 54 | /// Obtain bytes from a `Message` 55 | pub fn into_bytes(self) -> Vec { 56 | self.bytes 57 | } 58 | } 59 | 60 | /// `Ciphertext` 61 | /// 62 | /// We decompose the ciphertext in subarrays for convenience 63 | #[derive(Clone)] 64 | pub struct Ciphertext { 65 | /// Ciphertext, part 0, subpart 0 66 | pub bytes00: Vec, 67 | 68 | /// Ciphertext, part 0, subpart 1 69 | pub bytes01: Vec, 70 | 71 | /// Ciphertext, part 0, subpart 2 72 | pub bytes02: Vec, 73 | 74 | /// Ciphertext, part 1 75 | pub bytes1: Vec, 76 | } 77 | 78 | /// Public-key cryptosystem (ref Algorithm 1, Section 1.3.9) 79 | pub struct PKE { 80 | /// Instance of the SIKE problem for this PKE 81 | pub isogenies: CurveIsogenies, 82 | params: PublicParameters, 83 | } 84 | 85 | impl PKE { 86 | /// Initialise cryptosystem with parameters `params` 87 | #[inline] 88 | pub fn setup(params: PublicParameters) -> Self { 89 | Self { 90 | isogenies: CurveIsogenies::init(params.clone()), 91 | params, 92 | } 93 | } 94 | 95 | /// Generate a keypair 96 | #[inline] 97 | pub fn gen(&self) -> Result<(SecretKey, PublicKey), String> { 98 | // 1. 99 | let sk3 = SecretKey::get_random_secret_key(self.params.keyspace3 as usize)?; 100 | 101 | // 2. 102 | let pk3 = self.isogenies.isogen3(&sk3)?; 103 | 104 | // 3. 105 | Ok((sk3, pk3)) 106 | } 107 | 108 | /// Encrypt a message 109 | #[inline] 110 | pub fn enc(&self, pk: &PublicKey, m: Message) -> Result { 111 | // 4. 112 | let sk2 = SecretKey::get_random_secret_key(self.params.keyspace2 as usize)?; 113 | 114 | // 5. 115 | let c0: PublicKey = self.isogenies.isogen2(&sk2)?; 116 | 117 | // 6. 118 | let j = self.isogenies.isoex2(&sk2, &pk)?; 119 | 120 | // 7. 121 | let h = self.hash_function_f(j); 122 | 123 | // 8. 124 | if h.len() != m.bytes.len() { 125 | return Err(String::from("Incorrect Hash")); 126 | } 127 | 128 | let c1_bytes = Self::xor(&m.bytes, &h); 129 | 130 | // 9. 131 | let (part1, part2, part3) = c0.into_bytes(); 132 | Ok(Ciphertext { 133 | bytes00: part1, 134 | bytes01: part2, 135 | bytes02: part3, 136 | bytes1: c1_bytes, 137 | }) 138 | } 139 | 140 | /// Decrypts a message 141 | #[inline] 142 | pub fn dec(&self, sk: &SecretKey, c: Ciphertext) -> Result { 143 | // 10. 144 | let c0 = &PublicKey::from_bytes(&c.bytes00, &c.bytes01, &c.bytes02)?; 145 | 146 | let j: K = self.isogenies.isoex3(sk, c0)?; 147 | 148 | // 11. 149 | let h = self.hash_function_f(j); 150 | 151 | // 12. 152 | if h.len() != c.bytes1.len() { 153 | return Err(String::from("Incorrect Hash")); 154 | } 155 | 156 | let m = Self::xor(&h, &c.bytes1); 157 | 158 | // 13. 159 | Ok(Message { bytes: m }) 160 | } 161 | 162 | /// Computes the F function 163 | pub fn hash_function_f(&self, j: K) -> Vec { 164 | shake::shake256(&j.into_bytes(), self.params.secparam / 8) 165 | } 166 | 167 | /// Computes the bitwise XOR between two sequences 168 | pub fn xor(input1: &[u8], input2: &[u8]) -> Vec { 169 | input1 170 | .iter() 171 | .zip(input2.iter()) 172 | .map(|(x, y)| x ^ y) 173 | .collect() 174 | } 175 | } 176 | 177 | #[cfg(test)] 178 | mod tests { 179 | use super::*; 180 | use crate::{ 181 | isogeny::{sike_p434_params, sike_p503_params, sike_p610_params, sike_p751_params}, 182 | utils::strategy::*, 183 | }; 184 | 185 | #[test] 186 | fn test_pke_optim_p434() { 187 | let params = sike_p434_params( 188 | Some(P434_TWO_TORSION_STRATEGY.to_vec()), 189 | Some(P434_THREE_TORSION_STRATEGY.to_vec()), 190 | ) 191 | .unwrap(); 192 | 193 | let pke = PKE::setup(params.clone()); 194 | 195 | // Alice generates a keypair, she published her pk 196 | println!("[Debug] Key generation"); 197 | let (sk, pk) = pke.gen().unwrap(); 198 | 199 | // Bob writes a message 200 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 201 | // Bob encrypts the message using Alice's pk 202 | println!("[Debug] Encryption"); 203 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 204 | 205 | // Bob sends the ciphertext to Alice 206 | // Alice decrypts the message using her sk 207 | println!("[Debug] Decryption"); 208 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 209 | 210 | // Alice should correctly recover Bob's plaintext message 211 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 212 | } 213 | 214 | #[test] 215 | fn test_pke_optim_p503() { 216 | let params = sike_p503_params( 217 | Some(P503_TWO_TORSION_STRATEGY.to_vec()), 218 | Some(P503_THREE_TORSION_STRATEGY.to_vec()), 219 | ) 220 | .unwrap(); 221 | 222 | let pke = PKE::setup(params.clone()); 223 | 224 | // Alice generates a keypair, she published her pk 225 | println!("[Debug] Key generation"); 226 | let (sk, pk) = pke.gen().unwrap(); 227 | 228 | // Bob writes a message 229 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 230 | // Bob encrypts the message using Alice's pk 231 | println!("[Debug] Encryption"); 232 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 233 | 234 | // Bob sends the ciphertext to Alice 235 | // Alice decrypts the message using her sk 236 | println!("[Debug] Decryption"); 237 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 238 | 239 | // Alice should correctly recover Bob's plaintext message 240 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 241 | } 242 | 243 | #[test] 244 | fn test_pke_optim_p610() { 245 | let params = sike_p610_params( 246 | Some(P610_TWO_TORSION_STRATEGY.to_vec()), 247 | Some(P610_THREE_TORSION_STRATEGY.to_vec()), 248 | ) 249 | .unwrap(); 250 | 251 | let pke = PKE::setup(params.clone()); 252 | 253 | // Alice generates a keypair, she published her pk 254 | println!("[Debug] Key generation"); 255 | let (sk, pk) = pke.gen().unwrap(); 256 | 257 | // Bob writes a message 258 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 259 | // Bob encrypts the message using Alice's pk 260 | println!("[Debug] Encryption"); 261 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 262 | 263 | // Bob sends the ciphertext to Alice 264 | // Alice decrypts the message using her sk 265 | println!("[Debug] Decryption"); 266 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 267 | 268 | // Alice should correctly recover Bob's plaintext message 269 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 270 | } 271 | 272 | #[test] 273 | fn test_pke_optim_p751() { 274 | let params = sike_p751_params( 275 | Some(P751_TWO_TORSION_STRATEGY.to_vec()), 276 | Some(P751_THREE_TORSION_STRATEGY.to_vec()), 277 | ) 278 | .unwrap(); 279 | 280 | let pke = PKE::setup(params.clone()); 281 | 282 | // Alice generates a keypair, she published her pk 283 | println!("[Debug] Key generation"); 284 | let (sk, pk) = pke.gen().unwrap(); 285 | 286 | // Bob writes a message 287 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 288 | // Bob encrypts the message using Alice's pk 289 | println!("[Debug] Encryption"); 290 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 291 | 292 | // Bob sends the ciphertext to Alice 293 | // Alice decrypts the message using her sk 294 | println!("[Debug] Decryption"); 295 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 296 | 297 | // Alice should correctly recover Bob's plaintext message 298 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 299 | } 300 | 301 | #[test] 302 | fn test_pke_p434() { 303 | let params = sike_p434_params(None, None).unwrap(); 304 | 305 | let pke = PKE::setup(params.clone()); 306 | 307 | // Alice generates a keypair, she published her pk 308 | println!("[Debug] Key generation"); 309 | let (sk, pk) = pke.gen().unwrap(); 310 | 311 | // Bob writes a message 312 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 313 | // Bob encrypts the message using Alice's pk 314 | println!("[Debug] Encryption"); 315 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 316 | 317 | // Bob sends the ciphertext to Alice 318 | // Alice decrypts the message using her sk 319 | println!("[Debug] Decryption"); 320 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 321 | 322 | // Alice should correctly recover Bob's plaintext message 323 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 324 | } 325 | 326 | #[test] 327 | fn test_pke_p503() { 328 | let params = sike_p503_params(None, None).unwrap(); 329 | 330 | let pke = PKE::setup(params.clone()); 331 | 332 | // Alice generates a keypair, she published her pk 333 | println!("[Debug] Key generation"); 334 | let (sk, pk) = pke.gen().unwrap(); 335 | 336 | // Bob writes a message 337 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 338 | // Bob encrypts the message using Alice's pk 339 | println!("[Debug] Encryption"); 340 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 341 | 342 | // Bob sends the ciphertext to Alice 343 | // Alice decrypts the message using her sk 344 | println!("[Debug] Decryption"); 345 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 346 | 347 | // Alice should correctly recover Bob's plaintext message 348 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 349 | } 350 | 351 | #[test] 352 | fn test_pke_p610() { 353 | let params = sike_p610_params(None, None).unwrap(); 354 | 355 | let pke = PKE::setup(params.clone()); 356 | 357 | // Alice generates a keypair, she published her pk 358 | println!("[Debug] Key generation"); 359 | let (sk, pk) = pke.gen().unwrap(); 360 | 361 | // Bob writes a message 362 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 363 | // Bob encrypts the message using Alice's pk 364 | println!("[Debug] Encryption"); 365 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 366 | 367 | // Bob sends the ciphertext to Alice 368 | // Alice decrypts the message using her sk 369 | println!("[Debug] Decryption"); 370 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 371 | 372 | // Alice should correctly recover Bob's plaintext message 373 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 374 | } 375 | 376 | #[test] 377 | fn test_pke_p751() { 378 | let params = sike_p751_params(None, None).unwrap(); 379 | 380 | let pke = PKE::setup(params.clone()); 381 | 382 | // Alice generates a keypair, she published her pk 383 | println!("[Debug] Key generation"); 384 | let (sk, pk) = pke.gen().unwrap(); 385 | 386 | // Bob writes a message 387 | let msg = Message::from_bytes(vec![0; params.secparam / 8]); 388 | // Bob encrypts the message using Alice's pk 389 | println!("[Debug] Encryption"); 390 | let ciphertext = pke.enc(&pk, msg.clone()).unwrap(); 391 | 392 | // Bob sends the ciphertext to Alice 393 | // Alice decrypts the message using her sk 394 | println!("[Debug] Decryption"); 395 | let msg_recovered = pke.dec(&sk, ciphertext).unwrap(); 396 | 397 | // Alice should correctly recover Bob's plaintext message 398 | assert_eq!(msg_recovered.into_bytes(), msg.into_bytes()); 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /src/utils/conversion.rs: -------------------------------------------------------------------------------- 1 | //! Utils for conversions 2 | 3 | use crate::ff::{ 4 | PrimeFieldP434, PrimeFieldP503, PrimeFieldP610, PrimeFieldP751, QuadraticExtension, 5 | }; 6 | 7 | /// String to `u64` conversion 8 | pub fn str_to_u64(s: &str) -> Result { 9 | u64::from_str_radix(s, 16).or_else(|_| Err(String::from("Cannot parse from string"))) 10 | } 11 | 12 | /// String to an element of the quadratic extension field conversion 13 | pub fn str_to_p434(s0: &str, s1: &str) -> Result, String> { 14 | Ok(QuadraticExtension::from( 15 | PrimeFieldP434::from_string(s0)?, 16 | PrimeFieldP434::from_string(s1)?, 17 | )) 18 | } 19 | 20 | /// String to an element of the quadratic extension field conversion 21 | pub fn str_to_p503(s0: &str, s1: &str) -> Result, String> { 22 | Ok(QuadraticExtension::from( 23 | PrimeFieldP503::from_string(s0)?, 24 | PrimeFieldP503::from_string(s1)?, 25 | )) 26 | } 27 | 28 | /// String to an element of the quadratic extension field conversion 29 | pub fn str_to_p751(s0: &str, s1: &str) -> Result, String> { 30 | Ok(QuadraticExtension::from( 31 | PrimeFieldP751::from_string(s0)?, 32 | PrimeFieldP751::from_string(s1)?, 33 | )) 34 | } 35 | 36 | /// String to an element of the quadratic extension field conversion 37 | pub fn str_to_p610(s0: &str, s1: &str) -> Result, String> { 38 | Ok(QuadraticExtension::from( 39 | PrimeFieldP610::from_string(s0)?, 40 | PrimeFieldP610::from_string(s1)?, 41 | )) 42 | } 43 | 44 | /// Concatenates a list of arrays into one array 45 | // Example 46 | // ``` 47 | // let a = [1, 2]; 48 | // let b = [3, 4]; 49 | // let a_and_b = conversion::concatenate(&[&a, &b]); 50 | // assert_eq!(a_and_b, [1, 2, 3, 4]); 51 | // ``` 52 | pub fn concatenate(arrays: &[&[u8]]) -> Vec { 53 | let mut result = vec![]; 54 | for &array in arrays { 55 | result.extend(array); 56 | } 57 | result 58 | } 59 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | //! Misc utilities 2 | 3 | pub mod conversion; 4 | pub mod shake; 5 | pub mod strategy; 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use super::{ 10 | conversion::concatenate, 11 | shake::shake256, 12 | strategy::{compute_strategy, P434_THREE_TORSION_STRATEGY, P434_TWO_TORSION_STRATEGY}, 13 | }; 14 | 15 | fn compare_arrays(array1: &[T], array2: &[T]) -> bool 16 | where 17 | T: PartialEq + std::fmt::Debug, 18 | { 19 | let couples = array1.iter().zip(array2.iter()); 20 | 21 | for (x, y) in couples { 22 | if x != y { 23 | println!("[!] Arrays differ"); 24 | println!("{:?}", array1); 25 | println!("{:?}", array2); 26 | 27 | return false; 28 | } 29 | } 30 | 31 | true 32 | } 33 | 34 | #[test] 35 | fn test_concatenate() { 36 | let a = vec![1, 2, 3, 4, 5]; 37 | let b = vec![6, 7, 8]; 38 | let c = vec![1, 2, 3, 4, 5, 6, 7, 8]; 39 | 40 | let d = concatenate(&[&a, &b]); 41 | 42 | assert_eq!(c, d) 43 | } 44 | 45 | #[test] 46 | fn test_shake256_0bit() { 47 | let msg = vec![]; 48 | let output = shake256(&msg, 512); 49 | 50 | // NIST test vector for SHAKE-256 on a zero-bit input 51 | let reference: [u8; 512] = [ 52 | 0x46, 0xB9, 0xDD, 0x2B, 0x0B, 0xA8, 0x8D, 0x13, 0x23, 0x3B, 0x3F, 0xEB, 0x74, 0x3E, 53 | 0xEB, 0x24, 0x3F, 0xCD, 0x52, 0xEA, 0x62, 0xB8, 0x1B, 0x82, 0xB5, 0x0C, 0x27, 0x64, 54 | 0x6E, 0xD5, 0x76, 0x2F, 0xD7, 0x5D, 0xC4, 0xDD, 0xD8, 0xC0, 0xF2, 0x00, 0xCB, 0x05, 55 | 0x01, 0x9D, 0x67, 0xB5, 0x92, 0xF6, 0xFC, 0x82, 0x1C, 0x49, 0x47, 0x9A, 0xB4, 0x86, 56 | 0x40, 0x29, 0x2E, 0xAC, 0xB3, 0xB7, 0xC4, 0xBE, 0x14, 0x1E, 0x96, 0x61, 0x6F, 0xB1, 57 | 0x39, 0x57, 0x69, 0x2C, 0xC7, 0xED, 0xD0, 0xB4, 0x5A, 0xE3, 0xDC, 0x07, 0x22, 0x3C, 58 | 0x8E, 0x92, 0x93, 0x7B, 0xEF, 0x84, 0xBC, 0x0E, 0xAB, 0x86, 0x28, 0x53, 0x34, 0x9E, 59 | 0xC7, 0x55, 0x46, 0xF5, 0x8F, 0xB7, 0xC2, 0x77, 0x5C, 0x38, 0x46, 0x2C, 0x50, 0x10, 60 | 0xD8, 0x46, 0xC1, 0x85, 0xC1, 0x51, 0x11, 0xE5, 0x95, 0x52, 0x2A, 0x6B, 0xCD, 0x16, 61 | 0xCF, 0x86, 0xF3, 0xD1, 0x22, 0x10, 0x9E, 0x3B, 0x1F, 0xDD, 0x94, 0x3B, 0x6A, 0xEC, 62 | 0x46, 0x8A, 0x2D, 0x62, 0x1A, 0x7C, 0x06, 0xC6, 0xA9, 0x57, 0xC6, 0x2B, 0x54, 0xDA, 63 | 0xFC, 0x3B, 0xE8, 0x75, 0x67, 0xD6, 0x77, 0x23, 0x13, 0x95, 0xF6, 0x14, 0x72, 0x93, 64 | 0xB6, 0x8C, 0xEA, 0xB7, 0xA9, 0xE0, 0xC5, 0x8D, 0x86, 0x4E, 0x8E, 0xFD, 0xE4, 0xE1, 65 | 0xB9, 0xA4, 0x6C, 0xBE, 0x85, 0x47, 0x13, 0x67, 0x2F, 0x5C, 0xAA, 0xAE, 0x31, 0x4E, 66 | 0xD9, 0x08, 0x3D, 0xAB, 0x4B, 0x09, 0x9F, 0x8E, 0x30, 0x0F, 0x01, 0xB8, 0x65, 0x0F, 67 | 0x1F, 0x4B, 0x1D, 0x8F, 0xCF, 0x3F, 0x3C, 0xB5, 0x3F, 0xB8, 0xE9, 0xEB, 0x2E, 0xA2, 68 | 0x03, 0xBD, 0xC9, 0x70, 0xF5, 0x0A, 0xE5, 0x54, 0x28, 0xA9, 0x1F, 0x7F, 0x53, 0xAC, 69 | 0x26, 0x6B, 0x28, 0x41, 0x9C, 0x37, 0x78, 0xA1, 0x5F, 0xD2, 0x48, 0xD3, 0x39, 0xED, 70 | 0xE7, 0x85, 0xFB, 0x7F, 0x5A, 0x1A, 0xAA, 0x96, 0xD3, 0x13, 0xEA, 0xCC, 0x89, 0x09, 71 | 0x36, 0xC1, 0x73, 0xCD, 0xCD, 0x0F, 0xAB, 0x88, 0x2C, 0x45, 0x75, 0x5F, 0xEB, 0x3A, 72 | 0xED, 0x96, 0xD4, 0x77, 0xFF, 0x96, 0x39, 0x0B, 0xF9, 0xA6, 0x6D, 0x13, 0x68, 0xB2, 73 | 0x08, 0xE2, 0x1F, 0x7C, 0x10, 0xD0, 0x4A, 0x3D, 0xBD, 0x4E, 0x36, 0x06, 0x33, 0xE5, 74 | 0xDB, 0x4B, 0x60, 0x26, 0x01, 0xC1, 0x4C, 0xEA, 0x73, 0x7D, 0xB3, 0xDC, 0xF7, 0x22, 75 | 0x63, 0x2C, 0xC7, 0x78, 0x51, 0xCB, 0xDD, 0xE2, 0xAA, 0xF0, 0xA3, 0x3A, 0x07, 0xB3, 76 | 0x73, 0x44, 0x5D, 0xF4, 0x90, 0xCC, 0x8F, 0xC1, 0xE4, 0x16, 0x0F, 0xF1, 0x18, 0x37, 77 | 0x8F, 0x11, 0xF0, 0x47, 0x7D, 0xE0, 0x55, 0xA8, 0x1A, 0x9E, 0xDA, 0x57, 0xA4, 0xA2, 78 | 0xCF, 0xB0, 0xC8, 0x39, 0x29, 0xD3, 0x10, 0x91, 0x2F, 0x72, 0x9E, 0xC6, 0xCF, 0xA3, 79 | 0x6C, 0x6A, 0xC6, 0xA7, 0x58, 0x37, 0x14, 0x30, 0x45, 0xD7, 0x91, 0xCC, 0x85, 0xEF, 80 | 0xF5, 0xB2, 0x19, 0x32, 0xF2, 0x38, 0x61, 0xBC, 0xF2, 0x3A, 0x52, 0xB5, 0xDA, 0x67, 81 | 0xEA, 0xF7, 0xBA, 0xAE, 0x0F, 0x5F, 0xB1, 0x36, 0x9D, 0xB7, 0x8F, 0x3A, 0xC4, 0x5F, 82 | 0x8C, 0x4A, 0xC5, 0x67, 0x1D, 0x85, 0x73, 0x5C, 0xDD, 0xDB, 0x09, 0xD2, 0xB1, 0xE3, 83 | 0x4A, 0x1F, 0xC0, 0x66, 0xFF, 0x4A, 0x16, 0x2C, 0xB2, 0x63, 0xD6, 0x54, 0x12, 0x74, 84 | 0xAE, 0x2F, 0xCC, 0x86, 0x5F, 0x61, 0x8A, 0xBE, 0x27, 0xC1, 0x24, 0xCD, 0x8B, 0x07, 85 | 0x4C, 0xCD, 0x51, 0x63, 0x01, 0xB9, 0x18, 0x75, 0x82, 0x4D, 0x09, 0x95, 0x8F, 0x34, 86 | 0x1E, 0xF2, 0x74, 0xBD, 0xAB, 0x0B, 0xAE, 0x31, 0x63, 0x39, 0x89, 0x43, 0x04, 0xE3, 87 | 0x58, 0x77, 0xB0, 0xC2, 0x8A, 0x9B, 0x1F, 0xD1, 0x66, 0xC7, 0x96, 0xB9, 0xCC, 0x25, 88 | 0x8A, 0x06, 0x4A, 0x8F, 0x57, 0xE2, 0x7F, 0x2A, 89 | ]; 90 | 91 | assert!(compare_arrays(&reference, &output)) 92 | } 93 | 94 | #[test] 95 | fn test_strategy_2tor() { 96 | let n4 = 107; 97 | let p4 = 5633; 98 | let q4 = 5461; 99 | 100 | let p434strat = compute_strategy(n4, p4, q4).unwrap(); 101 | assert!(compare_arrays(&p434strat, &P434_TWO_TORSION_STRATEGY)); 102 | } 103 | 104 | #[test] 105 | fn test_strategy_3tor() { 106 | let n3 = 136; 107 | let p3 = 5322; 108 | let q3 = 5282; 109 | 110 | let p434strat = compute_strategy(n3, p3, q3).unwrap(); 111 | 112 | assert!(compare_arrays(&p434strat, &P434_THREE_TORSION_STRATEGY)); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/utils/shake.rs: -------------------------------------------------------------------------------- 1 | //! Utils for SHAKE 2 | 3 | use sha3::digest::{ExtendableOutput, Update, XofReader}; 4 | use sha3::Shake256; 5 | 6 | /// SHAKE-256 wrapper 7 | /// * Input: `input` string and `length` of the desired output 8 | /// * Output: an array of length `len`. 9 | // 10 | // # Example 11 | // ``` 12 | // let result = shake256(&[1, 2, 3, 4, 5], 32); 13 | // println!("{:?}", result); 14 | // ``` 15 | #[inline] 16 | pub fn shake256(input: &[u8], len: usize) -> Vec { 17 | let mut buffer = vec![0; len]; 18 | let mut shake = Shake256::default(); 19 | shake.update(input); 20 | shake.finalize_xof().read(&mut buffer); 21 | buffer 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/strategy.rs: -------------------------------------------------------------------------------- 1 | //! Utils for tree traversal strategies 2 | 3 | /// 2-torsion tree-traversal strategy 4 | pub type Torsion2Strategy = Vec; 5 | 6 | /// 3-torsion tree-traversal strategy 7 | pub type Torsion3Strategy = Vec; 8 | 9 | /// 2-torsion reference strategy for SIKEp434 (ref C.1.1.) 10 | pub const P434_TWO_TORSION_STRATEGY: [usize; 107] = [ 11 | 48, 28, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 12 | 1, 1, 13, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 4, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 21, 12, 13 | 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 9, 5, 3, 2, 1, 1, 1, 14 | 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 15 | ]; 16 | 17 | /// 3-torsion reference strategy for SIKEp434 (ref C.1.2.) 18 | pub const P434_THREE_TORSION_STRATEGY: [usize; 136] = [ 19 | 66, 33, 17, 9, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 1, 2, 1, 20 | 1, 4, 2, 1, 1, 2, 1, 1, 16, 8, 4, 2, 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 21 | 1, 1, 4, 2, 1, 1, 2, 1, 1, 32, 16, 8, 4, 3, 1, 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 22 | 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 23 | 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 24 | ]; 25 | 26 | /// 2-torsion reference strategy for SIKEp503 (ref C.2.1.) 27 | pub const P503_TWO_TORSION_STRATEGY: [usize; 124] = [ 28 | 61, 32, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 29 | 1, 1, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 30 | 1, 1, 29, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 31 | 2, 1, 1, 13, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 5, 4, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 32 | ]; 33 | 34 | /// 3-torsion reference strategy for SIKEp503 (ref C.2.2.) 35 | pub const P503_THREE_TORSION_STRATEGY: [usize; 158] = [ 36 | 71, 38, 21, 13, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 5, 4, 2, 1, 1, 2, 1, 1, 2, 1, 1, 37 | 1, 9, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 17, 9, 5, 3, 2, 1, 1, 1, 1, 2, 38 | 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 33, 17, 9, 5, 39 | 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 40 | 1, 1, 16, 8, 4, 2, 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 41 | 2, 1, 1, 42 | ]; 43 | 44 | /// 2-torsion reference strategy for SIKEp610 (ref C.3.1.) 45 | pub const P610_TWO_TORSION_STRATEGY: [usize; 151] = [ 46 | 67, 37, 21, 12, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 9, 47 | 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 16, 9, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 48 | 1, 4, 2, 1, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 33, 16, 8, 5, 2, 1, 1, 49 | 1, 2, 1, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 16, 8, 4, 2, 50 | 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 51 | ]; 52 | 53 | /// 3-torsion reference strategy for SIKEp610 (ref C.3.2.) 54 | pub const P610_THREE_TORSION_STRATEGY: [usize; 191] = [ 55 | 86, 48, 27, 15, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 56 | 1, 1, 1, 12, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 21, 12, 57 | 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 9, 5, 3, 2, 1, 1, 1, 58 | 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 38, 21, 12, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 59 | 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 9, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 17, 60 | 9, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 1, 2, 1, 1, 4, 2, 1, 61 | 1, 2, 1, 1, 62 | ]; 63 | 64 | /// 2-torsion reference strategy for SIKEp751 (ref C.4.1.) 65 | pub const P751_TWO_TORSION_STRATEGY: [usize; 185] = [ 66 | 80, 48, 27, 15, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 67 | 1, 1, 1, 12, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 21, 12, 68 | 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 9, 5, 3, 2, 1, 1, 1, 69 | 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 33, 20, 12, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 5, 70 | 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 8, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 2, 1, 1, 16, 8, 71 | 4, 2, 1, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 72 | ]; 73 | 74 | /// 3-torsion reference strategy for SIKEp751 (ref C.4.2.) 75 | pub const P751_THREE_TORSION_STRATEGY: [usize; 238] = [ 76 | 112, 63, 32, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 77 | 1, 2, 1, 1, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 78 | 1, 2, 1, 1, 31, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 79 | 1, 1, 2, 1, 1, 15, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 7, 4, 2, 1, 1, 2, 1, 1, 3, 2, 80 | 1, 1, 1, 1, 49, 31, 16, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 8, 4, 2, 1, 1, 2, 1, 1, 4, 81 | 2, 1, 1, 2, 1, 1, 15, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 7, 4, 2, 1, 1, 2, 1, 1, 3, 82 | 2, 1, 1, 1, 1, 21, 12, 8, 4, 2, 1, 1, 2, 1, 1, 4, 2, 1, 1, 2, 1, 1, 5, 3, 2, 1, 1, 1, 1, 2, 1, 83 | 1, 1, 9, 5, 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 84 | ]; 85 | 86 | /// Computing optimised strategy (ref `compute_strategy`, Algorithm 46 p. 75). 87 | /// * Input: strategy size `n`, parameters `p`, `q` 88 | /// * Output: optimal strategy of size `n` 89 | /// 90 | /// # Examples 91 | /// ```rust 92 | /// use rust_sike::compute_strategy; 93 | /// let strat = compute_strategy(12, 13, 14); 94 | /// println!("{:?}", strat); 95 | /// ``` 96 | pub fn compute_strategy(n: usize, p: u64, q: u64) -> Result, &'static str> { 97 | // 1. 98 | let mut strategies = vec![vec![]]; 99 | 100 | // 2. 101 | let mut cost = vec![0]; 102 | 103 | let eval = |c: &Vec, i: u64, b: u64| { 104 | c[(i - b) as usize - 1] + c[b as usize - 1] + b * p + (i - b) * q 105 | }; 106 | 107 | // 3. 108 | for i in 2..=(n as u64 + 1) { 109 | // 4. 110 | let mut min_val = eval(&cost, i, 1); 111 | let mut b = 1; 112 | for b_val in 2..i { 113 | let c_val = eval(&cost, i, b_val); 114 | if c_val < min_val { 115 | min_val = c_val; 116 | b = b_val; 117 | } 118 | } 119 | 120 | // 5. 121 | let mut new_strat = vec![b as usize]; 122 | new_strat.extend(&strategies[(i - b - 1) as usize]); 123 | new_strat.extend(&strategies[b as usize - 1]); 124 | strategies.push(new_strat); 125 | 126 | // 6. 127 | cost.push(min_val); 128 | } 129 | 130 | // 7. 131 | match strategies.last() { 132 | Some(s) => Ok(s.to_vec()), 133 | None => Err("No valid strategy found"), 134 | } 135 | } 136 | --------------------------------------------------------------------------------