diff --git a/attic/flatkv.rs b/attic/flatkv.rs deleted file mode 100644 index 77c0574d7..000000000 --- a/attic/flatkv.rs +++ /dev/null @@ -1,261 +0,0 @@ -// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md. - -use std::borrow::Cow; - -/// A flat key/value store implemented in terms of arrays tuples of (key, value). -#[repr(transparent)] -#[derive(Clone, PartialEq, Eq)] -pub struct FlatKV(Vec<(&'static str, Value)>); - -/// Value variant for FlatKV. -#[derive(Clone, PartialEq, Eq)] -pub enum Value { - N, - KV(FlatKV), - S(Cow<'static, str>), - I(i64), - UI(u64), - B(bool), - Endpoint(crate::vl1::Endpoint), - Identity(crate::vl1::Identity), -} - -impl Into for FlatKV { - #[inline(always)] - fn into(self) -> Value { - Value::KV(self) - } -} - -impl Into for &'static str { - #[inline(always)] - fn into(self) -> Value { - Value::S(Cow::Borrowed(self)) - } -} - -impl Into for String { - #[inline(always)] - fn into(self) -> Value { - Value::S(Cow::Owned(self)) - } -} - -impl Into for i64 { - #[inline(always)] - fn into(self) -> Value { - Value::I(self) - } -} - -impl Into for u64 { - #[inline(always)] - fn into(self) -> Value { - Value::UI(self) - } -} - -impl Into for isize { - #[inline(always)] - fn into(self) -> Value { - Value::I(self as i64) - } -} - -impl Into for usize { - #[inline(always)] - fn into(self) -> Value { - Value::UI(self as u64) - } -} - -impl Into for i32 { - #[inline(always)] - fn into(self) -> Value { - Value::I(self as i64) - } -} - -impl Into for u32 { - #[inline(always)] - fn into(self) -> Value { - Value::UI(self as u64) - } -} - -impl Into for i16 { - #[inline(always)] - fn into(self) -> Value { - Value::I(self as i64) - } -} - -impl Into for u16 { - #[inline(always)] - fn into(self) -> Value { - Value::UI(self as u64) - } -} - -impl Into for i8 { - #[inline(always)] - fn into(self) -> Value { - Value::I(self as i64) - } -} - -impl Into for u8 { - #[inline(always)] - fn into(self) -> Value { - Value::UI(self as u64) - } -} - -impl Into for bool { - #[inline(always)] - fn into(self) -> Value { - Value::B(self) - } -} - -impl Into for crate::vl1::Endpoint { - #[inline(always)] - fn into(self) -> Value { - Value::Endpoint(self) - } -} - -impl Into for crate::vl1::InetAddress { - #[inline(always)] - fn into(self) -> Value { - Value::Endpoint(crate::vl1::Endpoint::IpUdp(self)) - } -} - -impl Into for crate::vl1::Identity { - #[inline(always)] - fn into(self) -> Value { - Value::Identity(self) - } -} - -impl ToString for Value { - fn to_string(&self) -> String { - match self { - Value::N => "(null)".into(), - Value::KV(x) => x.to_string(), - Value::S(x) => x.to_string(), - Value::I(x) => x.to_string(), - Value::UI(x) => x.to_string(), - Value::B(x) => x.to_string(), - Value::Endpoint(x) => x.to_string(), - Value::Identity(x) => x.to_string(), - } - } -} - -impl FlatKV { - #[inline(always)] - pub fn add>(&mut self, k: &'static str, v: T) { - self.0.push((k, v.into())) - } -} - -fn json_escape(src: &str, escaped: &mut String) { - use std::fmt::Write; - let mut utf16_buf = [0u16; 2]; - for c in src.chars() { - match c { - '\x08' => escaped.push_str("\\b"), - '\x0c' => escaped.push_str("\\f"), - '\n' => escaped.push_str("\\n"), - '\r' => escaped.push_str("\\r"), - '\t' => escaped.push_str("\\t"), - '"' => escaped.push_str("\\\""), - '\\' => escaped.push_str("\\\\"), - '/' => escaped.push_str("\\/"), - c if c.is_ascii_graphic() => escaped.push(c), - c => { - let encoded = c.encode_utf16(&mut utf16_buf); - for utf16 in encoded { - write!(escaped, "\\u{:04X}", utf16).unwrap(); - } - } - } - } -} - -impl Default for FlatKV { - #[inline(always)] - fn default() -> Self { - Self(Vec::new()) - } -} - -impl FlatKV { - #[inline(always)] - pub fn new() -> Self { - Self(Vec::new()) - } -} - -impl ToString for FlatKV { - /// Output a JSON formatted map of values or maps. - fn to_string(&self) -> String { - let mut first = true; - let mut tmp = String::new(); - tmp.push_str("{ "); //} //" - for (k, v) in self.0.iter() { - if first { - first = false; - } else { - tmp.push_str(", "); - } - tmp.push('"'); - json_escape(*k, &mut tmp); - tmp.push_str("\": "); - match v { - Value::S(_) | Value::Endpoint(_) | Value::Identity(_) => { - tmp.push('"'); - json_escape(v.to_string().as_str(), &mut tmp); - tmp.push('"'); - } - _ => tmp.push_str(v.to_string().as_str()), - } - } - tmp.push_str("} "); - tmp - } -} - -#[macro_export] -macro_rules! kv { - ($($key:expr => $value:expr,)+) => (kv!($($key => $value),+)); - ( $($key:expr => $value:expr),* ) => { - { - #[allow(unused_mut)] - let mut _kv = crate::util::flatkv::FlatKV(Vec::new()); - $( - _kv.add($key, $value); - )* - _kv - } - }; -} - -#[cfg(test)] -mod tests { - #[test] - fn kv_macro() { - let kv = kv!( - "foo" => 0_u64, - "bar" => "bar", - "baz" => -1_i64, - "lala" => false, - "lol" => kv!( - "boo" => 1_u16, - "far" => 2_u32, - ) - ); - } -} diff --git a/attic/hybridkey.rs b/attic/hybridkey.rs deleted file mode 100644 index a923cf59e..000000000 --- a/attic/hybridkey.rs +++ /dev/null @@ -1,112 +0,0 @@ -// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md. - -use std::io::Write; - -use zerotier_core_crypto::hash::{hmac_sha512, SHA512}; -use zerotier_core_crypto::p384::{P384KeyPair, P384PublicKey, P384_PUBLIC_KEY_SIZE}; -use zerotier_core_crypto::secret::Secret; -use zerotier_core_crypto::x25519::{C25519KeyPair, C25519_PUBLIC_KEY_SIZE}; - -pub const ALGORITHM_C25519: u8 = 0x01; -pub const ALGORITHM_ECC_NIST_P384: u8 = 0x02; - -/// A bundle of key pairs for multiple algorithms that can be used to execute key agreement. -/// -/// These are used in ephemeral session key negotiation. -#[derive(Clone)] -pub struct HybridKeyPair { - pub c25519: C25519KeyPair, - pub p384: P384KeyPair, -} - -impl HybridKeyPair { - pub fn generate() -> HybridKeyPair { - Self { - c25519: C25519KeyPair::generate(), - p384: P384KeyPair::generate(), - } - } - - pub fn public_bytes(&self) -> Vec { - let mut buf: Vec = Vec::with_capacity(1 + C25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE); - buf.push(ALGORITHM_C25519 | ALGORITHM_ECC_NIST_P384); - let _ = buf.write_all(&self.c25519.public_bytes()); - let _ = buf.write_all(self.p384.public_key_bytes()); - buf - } - - /// Execute key agreement using all keys in common between this and the other public key. - /// - /// If there are FIPS/NIST approved keys present these will be used last in a chain of - /// HMAC(previous, next) KDF operations, making the final result FIPS-compliant. Non-FIPS - /// algorithms before it can be considered "salts" input to HKDF for FIPS purposes. - pub fn agree(&self, other_public: &HybridPublicKey) -> Option> { - let mut k: Option> = None; - if other_public.c25519.is_some() { - // k can't have anything in it yet since this is the first checked - let _ = k.insert(Secret(SHA512::hash(self.c25519.agree(other_public.c25519.as_ref().unwrap()).as_bytes()))); - } - if other_public.p384.is_some() { - let p384_secret = self.p384.agree(other_public.p384.as_ref().unwrap()); - if p384_secret.is_none() { - return None; - } - if k.is_some() { - let prev_k = k.take().unwrap(); - let _ = k.insert(Secret(hmac_sha512(prev_k.as_bytes(), p384_secret.unwrap().as_bytes()))); - } else { - let _ = k.insert(Secret(SHA512::hash(p384_secret.unwrap().as_bytes()))); - } - } - return k; - } -} - -unsafe impl Send for HybridKeyPair {} - -/// A public key composed of multiple public keys for multiple algorithms. -/// -/// The key pair above currently always uses every algorithm but the protocol permits -/// mix-and-match. At least one must be present though or agreement will fail. -#[derive(Clone)] -pub struct HybridPublicKey { - pub c25519: Option<[u8; C25519_PUBLIC_KEY_SIZE]>, - pub p384: Option, -} - -impl HybridPublicKey { - pub fn from_bytes(mut b: &[u8]) -> Option { - if !b.is_empty() { - let mut hpk = Self { c25519: None, p384: None }; - let types = b[0]; - b = &b[1..]; - let mut have = false; - if (types & ALGORITHM_C25519) != 0 { - if b.len() < C25519_PUBLIC_KEY_SIZE { - return None; - } - let _ = hpk.c25519.insert((&b[0..C25519_PUBLIC_KEY_SIZE]).try_into().unwrap()); - b = &b[C25519_PUBLIC_KEY_SIZE..]; - have = true; - } - if (types & ALGORITHM_ECC_NIST_P384) != 0 { - if b.len() < P384_PUBLIC_KEY_SIZE { - return None; - } - let pk = P384PublicKey::from_bytes(&b[0..P384_PUBLIC_KEY_SIZE]); - if pk.is_none() { - return None; - } - let _ = hpk.p384.insert(pk.unwrap()); - b = &b[P384_PUBLIC_KEY_SIZE..]; - have = true; - } - if have { - return Some(hpk); - } - } - return None; - } -} - -unsafe impl Send for HybridPublicKey {} diff --git a/attic/sidhp751/LICENSE b/attic/sidhp751/LICENSE deleted file mode 100644 index 80b6b6a0c..000000000 --- a/attic/sidhp751/LICENSE +++ /dev/null @@ -1,86 +0,0 @@ - MIT License - - Copyright (c) Erkan Tairi. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE - - ================================================================================ - - Portions of SIDH-RS is derived from Cloudflare's Go SIDH imlementation, found at - , available under the following license: - - ================================================================================ - - Copyright (c) 2017 Cloudflare. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Cloudflare nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ================================================================================ - - The x64 field arithmetic implementation was derived from the Microsoft Research - SIDH implementation, , available - under the following license: - - ================================================================================ - - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/attic/sidhp751/ORIGINAL-README.md b/attic/sidhp751/ORIGINAL-README.md deleted file mode 100644 index ab622a80a..000000000 --- a/attic/sidhp751/ORIGINAL-README.md +++ /dev/null @@ -1,93 +0,0 @@ -# SIDH-RS [![](https://img.shields.io/crates/v/sidh.svg)](https://crates.io/crates/sidh) [![](https://docs.rs/sidh/badge.svg)](https://docs.rs/sidh/) [![Build Status](https://travis-ci.com/etairi/sidh-rs.svg?branch=master)](https://travis-ci.org/etairi/sidh-rs) - -The **SIDH-RS** library is an efficient supersingular isogeny-based cryptography library written in Rust language. The library includes the ephemeral Diffie-Hellman key exchange (SIDH) as described in [1,2]. This scheme is conjectured to be secure against quantum computer attacks. - -The library provides a generic field arithmetic implementation, therefore, making it compatible with many different architectures (such as x64, x86, and ARM). Significant portions of this code are ported from the [Cloudflare's SIDH library](https://github.com/cloudflare/p751sidh). Also portions of the field arithmetic are ported from the [Microsoft Research implementation](https://github.com/Microsoft/PQCrypto-SIDH). This library follows their naming convention, writing "Alice" for the party -using 2^e-isogenies and "Bob" for the party using 3^e-isogenies. - -This package does **not** implement SIDH key validation, so it should only be -used for ephemeral Diffie-Hellman, i.e. each keypair should be used at most once. - -## Main Features - -- Supports ephemeral Diffie-Hellman key exchange. -- Supports Linux OS, Mac OS and Windows OS. -- Provides basic implementation of the underlying arithmetic functions in Rust to enable support on a wide range of platforms including x64, x86 and ARM. -- Provides optimized implementations of the underlying arithmetic functions for x64 platforms using assembly for Windows and Linux. -- Includes testing and benchmarking code. - -## Warning - -This library is not actively maintained anymore. Furthermore, the code has **not** yet received sufficient peer review by other qualified cryptographers to be considered in any way, shape, or form, safe. The library was developed for experimentation purposes. - -**USE AT YOUR OWN RISK** - -## Installation - -To install, add the following to the dependencies section of your project's `Cargo.toml`: - -```toml -sidh = "^0.2" -``` - -Then, in your library or executable source, add: - - extern crate sidh; - -By default, the benchmarks are not compiled without the `nightly` and `bench` features. To run the benchmarks, do: - -```sh -cargo bench --features="nightly bench" -``` - -## Example - -```rust -extern crate rand; -extern crate sidh; - -use rand::thread_rng; -use sidh::sidh::*; - -fn main() { - let mut rng = thread_rng(); - - let (alice_public, alice_secret) = generate_alice_keypair(&mut rng); - let (bob_public, bob_secret) = generate_bob_keypair(&mut rng); - let alice_shared_secret = alice_secret.shared_secret(&bob_public); - let bob_shared_secret = bob_secret.shared_secret(&alice_public); - - assert!(alice_shared_secret.iter().zip(bob_shared_secret.iter()).all(|(a, b)| a == b)); -} -``` - -## Documentation - -Extensive documentation is available [here](https://docs.rs/sidh). - -## License - -**SIDH-RS** is licensed under the MIT License; see [`LICENSE`](LICENSE) for details. - -Portions of the library are derived from [Cloudflare's Go SIDH imlementation](https://github.com/cloudflare/p751sidh). The x64 field arithmetic implementation is derived from the [Microsoft Research SIDH implementation](https://github.com/Microsoft/PQCrypto-SIDH). - -## References - -[1] Craig Costello, Patrick Longa, and Michael Naehrig, "Efficient algorithms for supersingular isogeny Diffie-Hellman". Advances in Cryptology - CRYPTO 2016, LNCS 9814, pp. 572-601, 2016. -The extended version is available [here](http://eprint.iacr.org/2016/413). - -[2] David Jao and Luca DeFeo, "Towards quantum-resistant cryptosystems from supersingular elliptic curve isogenies". PQCrypto 2011, LNCS 7071, pp. 19-34, 2011. - -[3] Craig Costello, David Jao, Patrick Longa, Michael Naehrig, Joost Renes, and David Urbanik, "Efficient compression of SIDH public keys". Advances in Cryptology - EUROCRYPT 2017, LNCS 10210, pp. 679-706, 2017. -The preprint version is available [here](http://eprint.iacr.org/2016/963). - -[4] Reza Azarderakhsh, Matthew Campagna, Craig Costello, Luca De Feo, Basil Hess, Amir Jalali, David Jao, Brian Koziel, Brian LaMacchia, Patrick Longa, Michael Naehrig, Joost Renes, Vladimir Soukharev, and David Urbanik, "Supersingular Isogeny Key Encapsulation". Submission to the NIST Post-Quantum Standardization project (to appear soon), 2017. - -[5] Craig Costello, and Huseyin Hisil, "A simple and compact algorithm for SIDH with arbitrary degree isogenies". Advances in Cryptology - ASIACRYPT 2017 (to appear), 2017. -The preprint version is available [here](https://eprint.iacr.org/2017/504). - -[6] Armando Faz-Hernández, Julio López, Eduardo Ochoa-Jiménez, and Francisco Rodríguez-Henríquez, "A faster software implementation of the supersingular isogeny Diffie-Hellman key exchange protocol". Cryptology ePrint Archive: Report 2017/1015, 2017. -The preprint version is available [here](https://eprint.iacr.org/2017/1015). - -[7] Gustavo H. M. Zanon, Marcos A. Simplicio Jr., Geovandro C. C. F. Pereira, Javad Doliskani, and Paulo S. L. M. Barreto, "Faster isogeny-based compressed key agreement". Cryptology ePrint Archive: Report 2017/1143, 2017. -The preprint version is available [here](https://eprint.iacr.org/2017/1143). diff --git a/attic/sidhp751/constants.rs b/attic/sidhp751/constants.rs deleted file mode 100644 index 7507838d0..000000000 --- a/attic/sidhp751/constants.rs +++ /dev/null @@ -1,25 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -use crate::sidhp751::field::PrimeFieldElement; -use crate::sidhp751::fp::Fp751Element; - -/// The x-coordinate of `P_A = [3^239](11, oddsqrt(11^3 + 11))` on `E_0(F_p)`. -pub const AFFINE_X_PA: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x27914862, 0xd56fe526, 0x96b5baea, 0x1fad60dc, 0xbf07ab91, 0x1e137d0, 0x52161964, 0x404d3e92, 0xcd09a337, 0x3c5385e4, 0x69e4af73, 0x44764267, 0x989dfe33, 0x9790c6db, 0xd2aa8b5e, 0xe06e1c04, 0xedea73b9, 0x38c08185, 0xa4396ca6, 0xaa41f678, 0x2229e9a0, 0x92b9259b, 0x26818be0, 0x2f93]) }; - -/// The y-coordinate of `P_A = [3^239](11, oddsqrt(11^3 + 11))` on `E_0(F_p)`. -pub const AFFINE_Y_PA: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0xbe3d7739, 0x332bd16f, 0x2319e3db, 0x7e5e20ff, 0xaefbd81b, 0xea856234, 0x6d071283, 0xe016df7d, 0xf73cd34f, 0x8ae42796, 0xa4774575, 0x6364b408, 0x7ce99497, 0xa71c97f1, 0xaa0cbe71, 0xda03cdd9, 0x195bd56f, 0xe52b4fda, 0x1fce0a46, 0xdac41f81, 0x0ee84a61, 0x9333720f, 0xf006e578, 0x1399]) }; - -/// The x-coordinate of `P_B = [2^372](6, oddsqrt(6^3 + 6))` on `E_0(F_p)`. -pub const AFFINE_X_PB: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x7b96c4ab, 0xf1a8c9ed, 0x5178486e, 0x299429da, 0x0cd5c2f4, 0xef4926f2, 0x58b4716a, 0x683b2e28, 0x3cac3eeb, 0xdda2fbcc, 0x3a600460, 0xec055f9f, 0x58c3848b, 0xd5a5a17a, 0xf42eaed5, 0x4652d836, 0x78b3a3b3, 0x2f2e71ed, 0x180add1d, 0xa771c057, 0xd835f512, 0xc780a5d2, 0xa3b55ac1, 0x114e]) }; - -/// The y-coordinate of `P_B = [2^372](6, oddsqrt(6^3 + 6))` on `E_0(F_p)`. -pub const AFFINE_Y_PB: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x73e3736b, 0xd1e14712, 0x4da241fe, 0xf9301ba9, 0x7fef0a85, 0xe14ab3c1, 0x037e9e62, 0xb4ddd26a, 0xb2afeb69, 0x66142df, 0x649d6c9e, 0xe297cb70, 0x8b1a0912, 0x214dfc6e, 0xb01cf859, 0x9f5ba818, 0x07c12828, 0x87d15b49, 0x3a880dbf, 0xa4da70c5, 0x72c8f253, 0xac5df62a, 0xa42ec617, 0x2e26]) }; - -/// The value of `(a+2)/4` for the starting curve `E_0` with `a=0`: this is `1/2`. -pub const E0_A_PLUS2_OVER4: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x124d6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8e00000, 0xc0aa7287, 0x9c8a2434, 0xa9a378a3, 0xa206996c, 0x41a41b52, 0x6876280d, 0x175ce04f, 0xe903b49f, 0x0666d227, 0xf851186, 0x7cff6e7f, 0x4ea0]) }; diff --git a/attic/sidhp751/curve.rs b/attic/sidhp751/curve.rs deleted file mode 100644 index 556923df1..000000000 --- a/attic/sidhp751/curve.rs +++ /dev/null @@ -1,1103 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -//! This module contains internal curve representation and operations -//! for SIDH, which is not part of the public API. - -use crate::sidhp751::fp::{Fp751Element, FP751_NUM_WORDS}; -use crate::sidhp751::field::{PrimeFieldElement, ExtensionFieldElement}; -use crate::sidhp751::constants::*; - -use std::fmt::Debug; -use std::ops::Neg; -use subtle::{ConditionallySelectable, Choice}; - -#[cfg(test)] -use quickcheck::{Gen, Arbitrary}; - -macro_rules! assign{ - {($v1:ident, $v2:ident) = $e:expr} => - { - { - let (v1, v2) = $e; - $v1 = v1; - $v2 = v2; - } - }; -} - -// = 256 -const CONST_256: ExtensionFieldElement = ExtensionFieldElement { - A: Fp751Element([0x249ad67, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7300000, 0x9973da8b, 0x73815496, 0x46718c7f, 0x856657c1, 0xe363a697, 0x461860e4,0xbba838cd, 0xf9fd6510,0x06993c0c, 0x4e1a3c3f, 0xef5b75c7, 0x55ab]), - B: Fp751Element([0_u32; FP751_NUM_WORDS]) -}; - -/// A point on the projective line `P^1(F_{p^2})`. -/// -/// This is used to work projectively with the curve coefficients. -#[allow(non_snake_case)] -#[derive(Copy, Clone, PartialEq)] -pub struct ProjectiveCurveParameters { - pub A: ExtensionFieldElement, - pub C: ExtensionFieldElement, -} - -#[allow(non_snake_case)] -struct CachedCurveParameters { - Aplus2C: ExtensionFieldElement, - C4: ExtensionFieldElement, -} - -#[allow(non_snake_case)] -struct CachedTripleCurveParameters { - Aminus2C: ExtensionFieldElement, - C2: ExtensionFieldElement, -} - -impl Debug for ProjectiveCurveParameters { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "ProjectiveCurveParameters(A: {:?}\nC: {:?})", &self.A, &self.C) - } -} - -#[cfg(test)] -impl Arbitrary for ProjectiveCurveParameters { - fn arbitrary(g: &mut Gen) -> ProjectiveCurveParameters { - let a = ExtensionFieldElement::arbitrary(g); - let c = ExtensionFieldElement::arbitrary(g); - ProjectiveCurveParameters{ A: a, C: c } - } -} - -impl ProjectiveCurveParameters { - pub fn from_affine(a: &ExtensionFieldElement) -> ProjectiveCurveParameters { - ProjectiveCurveParameters{ - A: *a, - C: ExtensionFieldElement::one() - } - } - - /// Recover the curve parameters from three points on the curve. - #[allow(non_snake_case)] - pub fn recover_curve_parameters(affine_xP: &ExtensionFieldElement, affine_xQ: &ExtensionFieldElement, affine_xQmP: &ExtensionFieldElement) -> - ProjectiveCurveParameters - { - let mut t0 = ExtensionFieldElement::one(); // = 1 - let mut t1 = affine_xP * affine_xQ; // = x_P * x_Q - t0 = &t0 - &t1; // = 1 - x_P * x_Q - t1 = affine_xP * affine_xQmP; // = x_P * x_{Q-P} - t0 = &t0 - &t1; // = 1 - x_P * x_Q - x_P * x_{Q-P} - t1 = affine_xQ * affine_xQmP; // = x_Q * x_{Q-P} - t0 = &t0 - &t1; // = 1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P} - let mut a = t0.square(); // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2 - t1 = &t1 * affine_xP; // = x_P * x_Q * x_{Q-P} - t1 = &t1 + &t1; // = 2 * x_P * x_Q * x_{Q-P} - let c = &t1 + &t1; // = 4 * x_P * x_Q * x_{Q-P} - t0 = affine_xP + affine_xQ; // = x_P + x_Q - t0 = &t0 + affine_xQmP; // = x_P + x_Q + x_{Q-P} - t1 = &c * &t0; // = 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P}) - a = &a - &t1; // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2 - 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P}) - - ProjectiveCurveParameters{ A: a, C: c } - } - - /// Compute the j-invariant of the given curve. - pub fn j_invariant(&self) -> ExtensionFieldElement { - let a = &self.A; - let c = &self.C; - let mut v0 = c.square(); // C^2 - let mut v1 = a.square(); // A^2 - let mut v2 = &v0 + &v0; // 2C^2 - let mut v3 = &v2 + &v0; // 3C^2 - v2 = &v2 + &v2; // 4C^2 - v2 = &v1 - &v2; // A^2 - 4C^2 - v1 = &v1 - &v3; // A^2 - 3C^2 - v3 = v1.square(); // (A^2 - 3C^2)^2 - v3 = &v3 * &v1; // (A^2 - 3C^2)^3 - v0 = v0.square(); // C^4 - v3 = &v3 * &CONST_256; // 256(A^2 - 3C^2)^3 - v2 = &v2 * &v0; // C^4(A^2 - 4C^2) - v2 = v2.inv(); // 1/C^4(A^2 - 4C^2) - v0 = &v3 * &v2; // 256(A^2 - 3C^2)^3 / C^4(A^2 - 4C^2) - - v0 - } - - /// Compute cached parameters `A + 2C, 4C`. - #[allow(non_snake_case)] - fn cached_params(&self) -> CachedCurveParameters { - let mut Aplus2C = &self.C + &self.C; // = 2*C - let C4 = &Aplus2C + &Aplus2C; // = 4*C - Aplus2C = &Aplus2C + &self.A; // = 2*C + A - - CachedCurveParameters{ Aplus2C, C4 } - } - - /// Compute cached parameters `A - 2C, 2C`. - #[allow(non_snake_case)] - fn cached_triple_params(&self) -> CachedTripleCurveParameters { - let C2 = &self.C + &self.C; // = 2*C - let Aminus2C = &self.A - &C2; // = A -2*C - - CachedTripleCurveParameters{ Aminus2C, C2 } - } -} - -/// A point on the projective line `P^1(F_{p^2})`. -/// -/// This represents a point on the (Kummer line) of a Montgomery curve. The -/// curve is specified by a ProjectiveCurveParameters struct. -#[allow(non_snake_case)] -#[derive(Copy, Clone, PartialEq)] -pub struct ProjectivePoint { - pub X: ExtensionFieldElement, - pub Z: ExtensionFieldElement, -} - -impl ConditionallySelectable for ProjectivePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ProjectivePoint{ - X: ExtensionFieldElement::conditional_select(&a.X, &b.X, choice), - Z: ExtensionFieldElement::conditional_select(&a.Z, &b.Z, choice) - } - } - - fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { - ExtensionFieldElement::conditional_swap(&mut a.X, &mut b.X, choice); - ExtensionFieldElement::conditional_swap(&mut a.Z, &mut b.Z, choice); - } -} - -impl Debug for ProjectivePoint { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "ProjectivePoint(X: {:?}\nZ: {:?})", &self.X, &self.Z) - } -} - -#[cfg(test)] -impl Arbitrary for ProjectivePoint { - fn arbitrary(g: &mut Gen) -> ProjectivePoint { - let x = ExtensionFieldElement::arbitrary(g); - let z = ExtensionFieldElement::arbitrary(g); - ProjectivePoint{ X: x, Z: z } - } -} - -impl ProjectivePoint { - /// Creates a new zero `ProejctivePoint`. - pub fn new() -> ProjectivePoint { - ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() } - } - - #[allow(non_snake_case)] - pub fn from_affine_prime_field(x: &PrimeFieldElement) -> ProjectivePoint { - let _X = ExtensionFieldElement{ A: x.A, B: ExtensionFieldElement::zero().B }; - ProjectivePoint{ - X: _X, - Z: ExtensionFieldElement::one() - } - } - - pub fn from_affine(x: &ExtensionFieldElement) -> ProjectivePoint { - ProjectivePoint{ - X: *x, - Z: ExtensionFieldElement::one() - } - } - - pub fn to_affine(&self) -> ExtensionFieldElement { - let affine_x = &self.Z.inv() * &self.X; - affine_x - } - - /// Returns true if both sides are equal. Takes variable time. - pub fn vartime_eq(&self, _rhs: &ProjectivePoint) -> bool { - let t0 = &self.X * &_rhs.Z; - let t1 = &self.Z * &_rhs.X; - t0.vartime_eq(&t1) - } - - /// Given `xP = x(P), xQ = x(Q)`, and `xPmQ = x(P-Q)`, compute `xR = x(P+Q)`. - #[allow(non_snake_case)] - fn add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint) -> ProjectivePoint { - let xP = *self; - // Algorithm 1 of Costello-Smith. - let mut v0 = &xP.X + &xP.Z; // X_P + Z_P - let v1 = &(&xQ.X - &xQ.Z) * &v0; // (X_Q - Z_Q)(X_P + Z_P) - v0 = &xP.X - &xP.Z; // X_P - Z_P - let v2 = &(&xQ.X + &xQ.Z) * &v0; // (X_Q + Z_Q)(X_P - Z_P) - let v3 = (&v1 + &v2).square(); // 4(X_Q X_P - Z_Q Z_P)^2 - let v4 = (&v1 - &v2).square(); // 4(X_Q Z_P - Z_Q X_P)^2 - v0 = &xPmQ.Z * &v3; // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2 - let z = &xPmQ.X * &v4; // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2 - let x = v0; - - ProjectivePoint{ X: x, Z: z } - } - - /// Given `xP = x(P)` and cached curve parameters `Aplus2C = A + 2*C, C4 = 4*C`, compute `xQ = x([2]P)`. - #[allow(non_snake_case)] - fn double(&self, curve: &CachedCurveParameters) -> ProjectivePoint { - let xP = *self; - // Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients. - let v1 = (&xP.X + &xP.Z).square(); // (X+Z)^2 - let mut v2 = (&xP.X - &xP.Z).square(); // (X-Z)^2 - let xz4 = &v1 - &v2; // 4XZ = (X+Z)^2 - (X-Z)^2 - v2 = &v2 * &curve.C4; // 4C(X-Z)^2 - let x = &v1 * &v2; // 4C(X+Z)^2(X-Z)^2 - let mut v3 = &xz4 * &curve.Aplus2C; // 4XZ(A + 2C) - v3 = &v3 + &v2; // 4XZ(A + 2C) + 4C(X-Z)^2 - let z = &v3 * &xz4; // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ - // Now (xQ.x : xQ.z) - // = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ ) - // = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ ) - // = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ ) - ProjectivePoint{ X: x, Z: z } - } - - /// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`. - // Params: `C4 = 4*C` and `Aplus2C = (A+2C)` - // Cost: 8M+4S+8A in `Fp2` - #[allow(non_snake_case)] - fn dbl_add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, params: &CachedCurveParameters) -> - (ProjectivePoint, ProjectivePoint) - { - let xP = *self; - let (x1, z1) = (&xPmQ.X, &xPmQ.Z); - let (x2, z2) = (&xP.X, &xP.Z); - let (x3, z3) = (&xQ.X, &xQ.Z); - - let mut t0 = x2 + z2; // A = x2+z2 - let mut t1 = x2 - z2; // B = x2-z2 - let mut t3 = x3 + z3; // C = x3+z3 - let mut t2 = x3 - z3; // D = x3-z3 - t2 = &t2 * &t0; // DA = D*A - t3 = &t3 * &t1; // CB = C*B - - let mut x = &t2 + &t3; // x5 = DA+CB - let mut z = &t2 - &t3; // z5 = DA-CB - x = x.square(); // x5 = (DA+CB)^2 - z = z.square(); // z5 = (DA-CB)^2 - x = &x * z1; // x5 = z1*(DA+CB)^2 - z = &z * x1; // z5 = x1*(DA-CB)^2 - let xPaddQ = ProjectivePoint{ X: x, Z: z }; - - t0 = t0.square(); // t0 = AA = A^2 - t1 = t1.square(); // t1 = BB = B^2 - t2 = &t0 - &t1; // t2 = E = AA-BB - t3 = &t1 * ¶ms.C4; // t3 = (4C)*BB - z = &t2 * ¶ms.Aplus2C; // z4 = (A+2C)*E - z = &z + &t3; // z4 = (4C)*BB+(A+2C)*E - x = &t0 * &t3; // x4 = AA*(4C)*BB - z = &z * &t2; // z4 = E*((4C)*BB+(A+2C)*E) - let x2P = ProjectivePoint{ X: x, Z: z }; - - (x2P, xPaddQ) - } - - /// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([2^k]P)`. - #[allow(non_snake_case)] - pub fn pow2k(&self, curve: &ProjectiveCurveParameters, k: u32) -> ProjectivePoint { - let cached_params = curve.cached_params(); - let mut xQ = *self; - for _ in 0..k { xQ = xQ.double(&cached_params); } - xQ - } - - // Uses the efficient Montgomery tripling formulas from FLOR-SIDH-x64 - // Reference: A faster SW implementation of SIDH (github.com/armfazh/flor-sidh-x64). - /// Given `xP = x(P)` and cached tripling curve parameters `Aminus2C = A - 2*C, C2 = 2*C`, compute `xQ = x([3]P)`. - /// Returns `xQ` to allow chaining. - #[allow(non_snake_case)] - fn triple(&self, curve: &CachedTripleCurveParameters) -> ProjectivePoint { - let xP = *self; - let (x1, z1) = (&xP.X, &xP.Z); - let mut t0 = x1.square(); // t0 = x1^2 - let mut t1 = z1.square(); // t1 = z1^2 - let mut t2 = x1 + z1; // t2 = x1+z1 - t2 = t2.square(); // t2 = t2^2 - let t3 = &t0 + &t1; // t3 = t0+t1 - let mut t4 = &t2 - &t3; // t4 = t2-t3 - let mut t5 = &curve.Aminus2C * &t4; // t5 = (A-2C)*t4 - t2 = &curve.C2 * &t2; // t2 = (2C)*t2 - t5 = &t5 + &t2; // t5 = t2+t5 - t5 = &t5 + &t5; // t5 = t5+t5 - t5 = &t5 + &t5; // t5 = t5+t5 - t0 = &t0 * &t5; // t0 = t0*t5 - t1 = &t1 * &t5; // t1 = t1*t5 - t4 = &t3 - &t4; // t4 = t3-t4 - t2 = &t2 * &t4; // t2 = t2*t4 - t0 = &t2 - &t0; // t0 = t2-t0 - t1 = &t2 - &t1; // t1 = t2-t1 - t0 = t0.square(); // t0 = t0^2 - t1 = t1.square(); // t1 = t1^2 - let x = x1 * &t1; // x3 = x1*t1 - let z = z1 * &t0; // z3 = z1*t0 - - ProjectivePoint{ X: x, Z: z } - } - - /// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([3^k]P)`. - #[allow(non_snake_case)] - pub fn pow3k(&self, curve: &ProjectiveCurveParameters, k: u32) -> ProjectivePoint { - let cached_params = curve.cached_triple_params(); - let mut xQ = *self; - for _ in 0..k { xQ = xQ.triple(&cached_params); } - xQ - } - - /// Given `x(P)` and a scalar `m` in little-endian bytes, compute `x([m]P)` using the - /// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith. - /// - /// This function's execution time is dependent only on the byte-length of the - /// input scalar. All scalars of the same input length execute in uniform time. - /// The scalar can be padded with zero bytes to ensure a uniform length. - #[allow(non_snake_case)] - fn scalar_mul(&self, curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint { - let xP = *self; - let cached_params = curve.cached_params(); - let mut x0 = ProjectivePoint{ X: ExtensionFieldElement::one(), Z: ExtensionFieldElement::zero() }; - let mut x1 = xP; - let mut tmp: ProjectivePoint; - - // Iterate over the bits of the scalar, top to bottom. - let mut prev_bit: u8 = 0; - for i in (0..scalar.len()).rev() { - let scalar_byte = scalar[i]; - for j in (0..8).rev() { - let bit = (scalar_byte >> (j as u32)) & 0x1; - ProjectivePoint::conditional_swap(&mut x0, &mut x1, (bit ^ prev_bit).into()); - tmp = x0.double(&cached_params); - x1 = x0.add(&x1, &xP); - x0 = tmp; - prev_bit = bit; - } - } - // Now prev_bit is the lowest bit of the scalar. - ProjectivePoint::conditional_swap(&mut x0, &mut x1, prev_bit.into()); - let xQ = x0; - xQ - } - - /// Given `P = (x_P, y_P)` in affine coordinates, as well as projective points - /// `x(Q), x(R) = x(P+Q)`, all in the prime-field subgroup of the starting curve - /// `E_0(F_p)`, use the Okeya-Sakurai coordinate recovery strategy to recover `Q = - /// (X_Q : Y_Q : Z_Q)`. - /// - /// This is Algorithm 5 of Costello-Smith, with the constants `a = 0, b = 1` hardcoded. - #[allow(non_snake_case)] - fn okeya_sakurai_coordinate_recovery(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement, - xQ: &ProjectivePrimeFieldPoint, xR: &ProjectivePrimeFieldPoint) -> - (PrimeFieldElement, PrimeFieldElement, PrimeFieldElement) - { - let mut v1 = affine_xP * &xQ.Z; // = x_P*Z_Q - let mut v2 = &xQ.X + &v1; // = X_Q + x_P*Z_Q - let mut v3 = (&xQ.X - &v1).square(); // = (X_Q - x_P*Z_Q)^2 - v3 = &v3 * &xR.X; // = X_R*(X_Q - x_P*Z_Q)^2 - // Skip setting v1 = 2a*Z_Q (step 6) since we hardcode a = 0. - // Skip adding v1 to v2 (step 7) since v1 is zero. - let mut v4 = affine_xP * &xQ.X; // = x_P*X_Q - v4 = &v4 + &xQ.Z; // = x_P*X_Q + Z_Q - v2 = &v2 * &v4; // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q) - // Skip multiplication by v1 (step 11) since v1 is zero. - // Skip subtracting v1 from v2 (step 12) since v1 is zero. - v2 = &v2 * &xR.Z; // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R - let Y_Q = &v2 - &v3; // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R - X_R*(X_Q - x_P*Z_Q)^2 - v1 = affine_yP + affine_yP; // = 2b*y_P - v1 = &(&v1 * &xQ.Z) * &xR.Z; // = 2b*y_P*Z_Q*Z_R - let X_Q = &v1 * &xQ.X; // = 2b*y_P*Z_Q*Z_R*X_Q - let Z_Q = &v1 * &xQ.Z; // = 2b*y_P*Z_Q^2*Z_R - - (X_Q, Y_Q, Z_Q) - } - - /// Given `x(P), x(Q), x(P-Q)`, as well as a scalar m in little-endian bytes, - /// compute `x(P + [m]Q)` using the "three-point ladder" of de Feo, Jao, and Plut. - /// - /// This function's execution time is dependent only on the byte-length of the - /// input scalar. All scalars of the same input length execute in uniform time. - /// The scalar can be padded with zero bytes to ensure a uniform length. - // - // The algorithm, as described in de Feo-Jao-Plut, is as follows: - // - // (x0, x1, x2) <--- (x(O), x(Q), x(P)) - // - // for i = |m| down to 0, indexing the bits of m: - // Invariant: (x0, x1, x2) == (x( [t]Q ), x( [t+1]Q ), x( P + [t]Q )) - // where t = m//2^i is the high bits of m, starting at i - // if m_i == 0: - // (x0, x1, x2) <--- (xDBL(x0), xADD(x1, x0, x(Q)), xADD(x2, x0, x(P))) - // Invariant: (x0, x1, x2) == (x( [2t]Q ), x( [2t+1]Q ), x( P + [2t]Q )) - // == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q )) - // where t' = m//2^{i-1} is the high bits of m, starting at i-1 - // if m_i == 1: - // (x0, x1, x2) <--- (xADD(x1, x0, x(Q)), xDBL(x1), xADD(x2, x1, x(P-Q))) - // Invariant: (x0, x1, x2) == (x( [2t+1]Q ), x( [2t+2]Q ), x( P + [2t+1]Q )) - // == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q )) - // where t' = m//2^{i-1} is the high bits of m, starting at i-1 - // return x2 - // - // Notice that the roles of (x0,x1) and (x(P), x(P-Q)) swap depending on the - // current bit of the scalar. Instead of swapping which operations we do, we - // can swap variable names, producing the following uniform algorithm: - // - // (x0, x1, x2) <--- (x(O), x(Q), x(P)) - // (y0, y1) <--- (x(P), x(P-Q)) - // - // for i = |m| down to 0, indexing the bits of m: - // (x0, x1) <--- SWAP( m_{i+1} xor m_i, (x0,x1) ) - // (y0, y1) <--- SWAP( m_{i+1} xor m_i, (y0,y1) ) - // (x0, x1, x2) <--- ( xDBL(x0), xADD(x1,x0,x(Q)), xADD(x2, x0, y0) ) - // - // return x2 - // - #[allow(non_snake_case)] - pub fn three_point_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, - curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint - { - let cached_params = curve.cached_params(); - - // (x0, x1, x2) <--- (x(O), x(Q), x(P)) - let mut x0 = ProjectivePoint{ X: ExtensionFieldElement::one(), Z: ExtensionFieldElement::zero() }; - let mut x1 = *xQ; - let mut x2 = *xP; - // (y0, y1) <--- (x(P), x(P-Q)) - let mut y0 = *xP; - let mut y1 = *xPmQ; - - // Iterate over the bits of the scalar, top to bottom. - let mut prev_bit: u8 = 0; - for i in (0..scalar.len()).rev() { - let scalar_byte = scalar[i]; - for j in (0..8).rev() { - let bit = (scalar_byte >> (j as u32)) & 0x1; - ProjectivePoint::conditional_swap(&mut x0, &mut x1, (bit ^ prev_bit).into()); - ProjectivePoint::conditional_swap(&mut y0, &mut y1, (bit ^ prev_bit).into()); - x1 = x1.add(&x0, xQ); // = xADD(x1, x0, x(Q)) - assign!{(x0, x2) = x0.dbl_add(&x2, &y0, &cached_params)}; - prev_bit = bit; - } - } - - let xR = x2; - xR - } - - /// Right-to-left point multiplication, which given the x-coordinate - /// of `P, Q` and `P-Q` calculates the x-coordinate of `R=P+[k]Q`. - #[allow(non_snake_case)] - pub fn right_to_left_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, - curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint - { - let cached_params = curve.cached_params(); - let mut R1 = *xP; - let mut R2 = *xPmQ; - let mut R0 = *xQ; - - // Iterate over the bits of the scalar, bottom to top. - let mut prev_bit: u8 = 0; - for i in 0..scalar.len() { - let scalar_byte = scalar[i]; - for j in 0..8 { - let bit = (scalar_byte >> (j as u32)) & 0x1; - ProjectivePoint::conditional_swap(&mut R1, &mut R2, (bit ^ prev_bit).into()); - assign!{(R0, R2) = R0.dbl_add(&R2, &R1, &cached_params)}; - prev_bit = bit; - } - } - ProjectivePoint::conditional_swap(&mut R1, &mut R2, prev_bit.into()); - let xR = R1; - xR - } - - /// Given the affine x-coordinate `affine_xP` of `P`, compute the x-coordinate - /// `x(\tau(P)-P) of \tau(P)-P`. - #[allow(non_snake_case)] - pub fn distort_and_difference(affine_xP: &PrimeFieldElement) -> ProjectivePoint { - let mut t0 = affine_xP.square(); // = x_P^2 - let t1 = &PrimeFieldElement::one() + &t0; // = x_P^2 + 1 - let b = t1.A; // = 0 + (x_P^2 + 1)*i - t0 = affine_xP + affine_xP; // = 2*x_P - let a = t0.A; // = 2*x_P + 0*i - - let x = ExtensionFieldElement{ A: Fp751Element::zero(), B: b }; - let z = ExtensionFieldElement{ A: a, B: Fp751Element::zero() }; - let xR = ProjectivePoint{ X: x, Z: z }; - xR - } - - /// Given an affine point `P = (x_P, y_P)` in the prime-field subgroup of the - /// starting curve `E_0(F_p)`, together with a secret scalar `m`, compute `x(P+[m]Q)`, - /// where `Q = \tau(P)` is the image of `P` under the distortion map. - // - // The computation uses basically the same strategy as the - // Costello-Longa-Naehrig implementation: - // - // 1. Use the standard Montgomery ladder to compute x([m]Q), x([m+1]Q) - // - // 2. Use Okeya-Sakurai coordinate recovery to recover [m]Q from Q, x([m]Q), - // x([m+1]Q) - // - // 3. Use P and [m]Q to compute x(P + [m]Q) - // - // The distortion map \tau is defined as - // - // \tau : E_0(F_{p^2}) ---> E_0(F_{p^2}) - // - // \tau : (x,y) |---> (-x, iy). - // - // The image of the distortion map is the _trace-zero_ subgroup of E_0(F_{p^2}) - // defined by Tr(P) = P + \pi_p(P) = id, where \pi_p((x,y)) = (x^p, y^p) is the - // p-power Frobenius map. To see this, take P = (x,y) \in E_0(F_{p^2}). Then - // Tr(P) = id if and only if \pi_p(P) = -P, so that - // - // -P = (x, -y) = (x^p, y^p) = \pi_p(P); - // - // we have x^p = x if and only if x \in F_p, while y^p = -y if and only if y = - // i*y' for y' \in F_p. - // - // Thus (excepting the identity) every point in the trace-zero subgroup is of - // the form \tau((x,y)) = (-x,i*y) for (x,y) \in E_0(F_p). - // - // Since the Montgomery ladder only uses the x-coordinate, and the x-coordinate - // is always in the prime subfield, we can compute x([m]Q), x([m+1]Q) entirely - // in the prime subfield. - // - // The affine form of the relation for Okeya-Sakurai coordinate recovery is - // given on p. 13 of Costello-Smith: - // - // y_Q = ((x_P*x_Q + 1)*(x_P + x_Q + 2*a) - 2*a - x_R*(x_P - x_Q)^2)/(2*b*y_P), - // - // where R = Q + P and a,b are the Montgomery parameters. In our setting - // (a,b)=(0,1) and our points are P=Q, Q=[m]Q, P+Q=[m+1]Q, so this becomes - // - // y_{mQ} = ((x_Q*x_{mQ} + 1)*(x_Q + x_{mQ}) - x_{m1Q}*(x_Q - x_{mQ})^2)/(2*y_Q) - // - // y_{mQ} = ((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(2*y_P*i) - // - // y_{mQ} = i*((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(-2*y_P) - // - // since (x_Q, y_Q) = (-x_P, y_P*i). In projective coordinates this is - // - // Y_{mQ}' = ((Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q} - // - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2) - // - // with denominator - // - // Z_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*Z_{mQ}. - // - // Setting - // - // X_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*X_{mQ} - // - // gives [m]Q = (X_{mQ}' : i*Y_{mQ}' : Z_{mQ}') with X,Y,Z all in F_p. (Here - // the ' just denotes that we've added extra terms to the denominators during - // the computation of Y) - // - // To compute the x-coordinate x(P+[m]Q) from P and [m]Q, we use the affine - // addition formulas of section 2.2 of Costello-Smith. We're only interested - // in the x-coordinate, giving - // - // X_R = Z_{mQ}*(i*Y_{mQ} - y_P*Z_{mQ})^2 - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 - // - // Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2. - // - // Notice that although X_R \in F_{p^2}, we can split the computation into - // coordinates X_R = X_{R,a} + X_{R,b}*i as - // - // (i*Y_{mQ} - y_P*Z_{mQ})^2 = (y_P*Z_{mQ})^2 - Y_{mQ}^2 - 2*y_P*Z_{mQ}*Y_{mQ}*i, - // - // giving - // - // X_{R,a} = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2) - // - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 - // - // X_{R,b} = -2*y_P*Y_{mQ}*Z_{mQ}^2 - // - // Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2. - // - // These formulas could probably be combined with the formulas for y-recover - // and computed more efficiently, but efficiency isn't the biggest concern - // here, since the bulk of the cost is already in the ladder. - #[allow(non_snake_case)] - pub fn secret_point(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement, scalar: &[u8]) -> ProjectivePoint { - let mut xQ = ProjectivePrimeFieldPoint::from_affine(affine_xP); - xQ.X = (&xQ.X).neg(); - - // Compute x([m]Q) = (X_{mQ} : Z_{mQ}), x([m+1]Q) = (X_{m1Q} : Z_{m1Q}). - let (xmQ, xm1Q) = xQ.scalar_mul_prime_field(&E0_A_PLUS2_OVER4, scalar); - - // Now perform coordinate recovery: - // [m]Q = (X_{mQ} : Y_{mQ}*i : Z_{mQ}) - - // Y_{mQ} = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q} - // - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2 - let mut t0 = affine_xP * &xmQ.X; // = x_P*X_{mQ} - let mut YmQ = &xmQ.Z - &t0; // = Z_{mQ} - x_P*X_{mQ} - let mut t1 = affine_xP * &xmQ.Z; // = x_P*Z_{mQ} - t0 = &xmQ.X - &t1; // = X_{mQ} - x_P*Z_{mQ} - YmQ = &YmQ * &t0; // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ}) - YmQ = &YmQ * &xm1Q.Z; // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q} - t1 = (&t1 + &xmQ.X).square(); // = (X_{mQ} + x_P*Z_{mQ})^2 - t1 = &t1 * &xm1Q.X; // = X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2 - YmQ = &YmQ - &t1; // = Y_{mQ} - - // Z_{mQ} = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P) - t0 = &(&xmQ.Z * &xm1Q.Z) * affine_yP; // = Z_{mQ} * Z_{m1Q} * y_P - t0 = (&t0).neg(); // = -1*(Z_{mQ} * Z_{m1Q} * y_P) - t0 = &t0 + &t0; // = -2*(Z_{mQ} * Z_{m1Q} * y_P) - let ZmQ = &xmQ.Z * &t0; // = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P) - - // We added terms to the denominator Z_{mQ}, so multiply them to X_{mQ}. - // X_{mQ} = -2*X_{mQ}*Z_{mQ}*Z_{m1Q}*y_P - let XmQ = &xmQ.X * &t0; - - // Now compute x(P + [m]Q) = (X_Ra + i*X_Rb : Z_R) - let mut XRb = &ZmQ.square() * &YmQ; // = Y_{mQ} * Z_{mQ}^2 - XRb = &XRb * affine_yP; // = Y_{mQ} * y_P * Z_{mQ}^2 - XRb = &XRb + &XRb; // = 2 * Y_{mQ} * y_P * Z_{mQ}^2 - XRb = (&XRb).neg(); // = -2 * Y_{mQ} * y_P * Z_{mQ}^2 - - t0 = (affine_yP * &ZmQ).square(); // = (y_P * Z_{mQ})^2 - t1 = YmQ.square(); // = Y_{mQ}^2 - let mut XRa = &t0 - &t1; // = (y_P * Z_{mQ})^2 - Y_{mQ}^2 - XRa = &XRa * &ZmQ; // = Z_{mQ}*((y_P * Z_{mQ})^2 - Y_{mQ}^2) - t0 = affine_xP * &ZmQ; // = x_P * Z_{mQ} - t1 = &XmQ + &t0; // = X_{mQ} + x_P*Z_{mQ} - t0 = &XmQ - &t0; // = X_{mQ} - x_P*Z_{mQ} - t0 = t0.square(); // = (X_{mQ} - x_P*Z_{mQ})^2 - t1 = &t1 * &t0; // = (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 - XRa = &XRa - &t1; // = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2) - (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 - - let ZR = &ZmQ * &t0; // = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2 - - let mut xR = ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() }; - xR.X.A = XRa.A; - xR.X.B = XRb.A; - xR.Z.A = ZR.A; - - xR - } -} - -/// A point on the projective line `P^1(F_p)`. -/// -/// This represents a point on the (Kummer line) of the prime-field subgroup of -/// the base curve `E_0(F_p)`, defined by `E_0 : y^2 = x^3 + x`. -#[allow(non_snake_case)] -#[derive(Copy, Clone, PartialEq)] -struct ProjectivePrimeFieldPoint { - X: PrimeFieldElement, - Z: PrimeFieldElement, -} - -impl ConditionallySelectable for ProjectivePrimeFieldPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ProjectivePrimeFieldPoint{ - X: PrimeFieldElement::conditional_select(&a.X, &b.X, choice), - Z: PrimeFieldElement::conditional_select(&a.Z, &b.Z, choice) - } - } - - fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { - PrimeFieldElement::conditional_swap(&mut a.X, &mut b.X, choice); - PrimeFieldElement::conditional_swap(&mut a.Z, &mut b.Z, choice); - } -} - -impl Debug for ProjectivePrimeFieldPoint { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "ProjectivePrimeFieldPoint(X: {:?}\nZ: {:?})", &self.X, &self.Z) - } -} - -#[cfg(test)] -impl Arbitrary for ProjectivePrimeFieldPoint { - fn arbitrary(g: &mut Gen) -> ProjectivePrimeFieldPoint { - let x = PrimeFieldElement::arbitrary(g); - let z = PrimeFieldElement::arbitrary(g); - ProjectivePrimeFieldPoint{ X: x, Z: z } - } -} - -impl ProjectivePrimeFieldPoint { - /// Creates a new zero `ProjectivePrimeFieldPoint`. - pub fn new() -> ProjectivePrimeFieldPoint { - ProjectivePrimeFieldPoint{ X: PrimeFieldElement::zero(), Z: PrimeFieldElement::zero() } - } - - pub fn from_affine(x: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { - ProjectivePrimeFieldPoint{ - X: *x, - Z: PrimeFieldElement::one() - } - } - - pub fn to_affine(&self) -> PrimeFieldElement { - &self.Z.inv() * &self.X - } - - /// Returns true if both sides are equal. Takes variable time. - pub fn vartime_eq(&self, _rhs: &ProjectivePrimeFieldPoint) -> bool { - let t0 = &self.X * &_rhs.Z; - let t1 = &self.Z * &_rhs.X; - t0.vartime_eq(&t1) - } - - /// Given `xP = x(P), xQ = x(Q)`, and `xPmQ = x(P-Q)`, compute `xR = x(P+Q)`. - #[allow(non_snake_case)] - fn add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint) -> - ProjectivePrimeFieldPoint - { - let xP = *self; - // Algorithm 1 of Costello-Smith. - let mut v0 = &xP.X + &xP.Z; // X_P + Z_P - let v1 = &(&xQ.X - &xQ.Z) * &v0; // (X_Q - Z_Q)(X_P + Z_P) - v0 = &xP.X - &xP.Z; // X_P - Z_P - let v2 = &(&xQ.X + &xQ.Z) * &v0; // (X_Q + Z_Q)(X_P - Z_P) - let v3 = (&v1 + &v2).square(); // 4(X_Q X_P - Z_Q Z_P)^2 - let v4 = (&v1 - &v2).square(); // 4(X_Q Z_P - Z_Q X_P)^2 - v0 = &xPmQ.Z * &v3; // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2 - let z = &xPmQ.X * &v4; // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2 - let x = v0; - - ProjectivePrimeFieldPoint{ X: x, Z: z } - } - - /// Given `xP = x(P)` and cached curve parameter `aPlus2Over4 = (a+2)/4, compute xQ = x([2]P)`. - // - // Note that we don't use projective curve coefficients here because we only - // ever use a fixed curve (in our case, the base curve E_0). - #[allow(non_snake_case)] - fn double(&self, aPlus2Over4: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { - let xP = *self; - // Algorithm 2 of Costello-Smith - let v1 = (&xP.X + &xP.Z).square(); // (X+Z)^2 - let v2 = (&xP.X - &xP.Z).square(); // (X-Z)^2 - let xz4 = &v1 - &v2; // 4XZ = (X+Z)^2 - (X-Z)^2 - let x = &v1 * &v2; // (X+Z)^2(X-Z)^2 - let mut v3 = &xz4 * aPlus2Over4; // 4XZ((a+2)/4) - v3 = &v3 + &v2; // 4XZ((a+2)/4) + (X-Z)^2 - let z = &v3 * &xz4; // (4XZ((a+2)/4) + (X-Z)^2)4XZ - // Now (xQ.x : xQ.z) - // = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ ) - ProjectivePrimeFieldPoint{ X: x, Z: z } - } - - /// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`. - // Assumptions: - // aPlus2Over2 = (A+2)/4. - // z(P-Q) = 1, the Z-coordinate of P-Q is equal to 1. - // Cost: 6M+4S+8A in Fp - #[allow(non_snake_case)] - fn dbl_add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint, aPlus2Over4: &PrimeFieldElement) -> - (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) - { - let xP = *self; - let x1 = &xPmQ.X; - let (x2, z2) = (&xP.X, &xP.Z); - let (x3, z3) = (&xQ.X, &xQ.Z); - - let mut t0 = x2 + z2; // A = x2+z2 - let mut t1 = x2 - z2; // B = x2-z2 - let mut t3 = x3 + z3; // C = x3+z3 - let mut t2 = x3 - z3; // D = x3-z3 - t2 = &t2 * &t0; // DA = D*A - t3 = &t3 * &t1; // CB = C*B - - let mut x = &t2 + &t3; // x5 = DA+CB - let mut z = &t2 - &t3; // z5 = DA-CB - x = x.square(); // x5 = (DA+CB)^2 - z = z.square(); // z5 = (DA-CB)^2 - z = &z * x1; // z5 = x1*(DA-CB)^2 - let xPaddQ = ProjectivePrimeFieldPoint{ X: x, Z: z }; - - t0 = t0.square(); // t0 = AA = A^2 - t1 = t1.square(); // t1 = BB = B^2 - x = &t0 * &t1; // x4 = AA*BB - t0 = &t0 - &t1; // t2 = E = AA-BB - z = &t0 * aPlus2Over4; // z4 = ((A+2C)/4)*E - z = &z + &t1; // z4 = BB+((A+2C)/4)*E - z = &z * &t0; // z4 = E*(BB+((A+2C)/4)*E) - let x2P = ProjectivePrimeFieldPoint{ X: x, Z: z }; - - (x2P, xPaddQ) - } - - /// Given `x(P)` and a scalar m in little-endian bytes, compute `x([m]P), x([m+1]P)` - /// using the Montgomery ladder. This is described in Algorithm 8 of Costello-Smith. - /// - /// The extra value `x([m+1]P)` is returned to allow y-coordinate recovery, otherwise, - /// it can be ignored. - /// - /// This function's execution time is dependent only on the byte-length of the input - /// scalar. All scalars of the same input length execute in uniform time. - /// The scalar can be padded with zero bytes to ensure a uniform length. - #[allow(non_snake_case)] - fn scalar_mul_prime_field(&self, aPlus2Over4: &PrimeFieldElement, scalar: &[u8]) -> (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) - { - let xP = *self; - let mut x0 = ProjectivePrimeFieldPoint{ X: PrimeFieldElement::one(), Z: PrimeFieldElement::zero() }; - let mut x1 = xP; - - // Iterate over the bits of the scalar, top to bottom. - let mut prev_bit: u8 = 0; - for i in (0..scalar.len()).rev() { - let scalar_byte = scalar[i]; - for j in (0..8).rev() { - let bit = (scalar_byte >> (j as u32)) & 0x1; - ProjectivePrimeFieldPoint::conditional_swap(&mut x0, &mut x1, (bit ^ prev_bit).into()); - assign!{(x0, x1) = x0.dbl_add(&x1, &xP, aPlus2Over4)}; - prev_bit = bit; - } - } - // Now prev_bit is the lowest bit of the scalar. - ProjectivePrimeFieldPoint::conditional_swap(&mut x0, &mut x1, prev_bit.into()); - (x0, x1) - } -} - -// Sage script for generating test vectors: -// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) -// sage: R. = Fp[] -// sage: Fp2 = Fp.extension(x^2 + 1, 'i') -// sage: i = Fp2.gen() -// sage: A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 -// sage: C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 -// sage: E = EllipticCurve(Fp2, [0,A/C,0,1,0]) -// sage: X, Y, Z = (8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557, 2381174772709336084066332457520782192315178511983342038392622832616744048226360647551642232950959910067260611740876401494529727990031260499974773548012283808741733925525689114517493995359390158666069816204787133942283380884077*i + 5378956232034228335189697969144556552783858755832284194802470922976054645696324118966333158267442767138528227968841257817537239745277092206433048875637709652271370008564179304718555812947398374153513738054572355903547642836171, 1) -// sage: P = E((X,Y,Z)) -// sage: X2, Y2, Z2 = 2*P -// sage: X3, Y3, Z3 = 3*P -// sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 -// -#[allow(non_snake_case)] -#[cfg(test)] -mod test { - use super::*; - use quickcheck::QuickCheck; - - // A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 - const CURVE_A: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xca2c435e, 0x8319eb18, 0x72cd0267, 0x3a93beae, 0x72fd5a84, 0x5e465e1f, 0x50aa7272, 0x8617fa41, 0x99d62a13, 0x887da247, 0x3c7667fe, 0xb079b31b, 0x0fa14f2e, 0xc4661b15, 0x7bc6efd6, 0xd4d2b296, 0xb7239003, 0x854215a8, 0xcba656c2, 0x61c5302c, 0x7d6f97a2, 0xf93194a2, 0x532bca75, 0x1ed9]), - B: Fp751Element([0x0e8c7db6, 0xb6f54104, 0x65342e15, 0x99403e73, 0x7c29cced, 0x457e9cee, 0x073b1d67, 0x8ece72dc, 0x7ad28d28, 0x6e73cef1, 0xca317472, 0x7aed836, 0x54263b54, 0x89e1de94, 0x7aa0071b, 0x74532927, 0x3bc86b9b, 0xf623dfc7, 0xa9245882, 0xb8e3c1d8, 0x17770bec, 0x6ad0b3d3, 0x6e8d502b, 0x5b40]) }; - // C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 - const CURVE_C: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xbf723107, 0x4fb2358b, 0xac79e240, 0x3a791521, 0x7c4c922f, 0x283e24ef, 0x205e33cc, 0xc89baa1, 0xcff6fee1, 0x3031be81, 0x2f6a95c4, 0xaf7a494a, 0xaac83a1d, 0x248d251e, 0xe2550c88, 0xc122fca1, 0x1b6cfd3d, 0xbc0451b1, 0xb046222c, 0x9c0a114a, 0x2f21f6ea, 0x43b957b3, 0x87fa61de, 0x5b9c]), - B: Fp751Element([0xaac15ec6, 0xacf142af, 0x04a071d5, 0xfd1322a5, 0x10f6c5c6, 0x56bb205e, 0x9a97b9bd, 0xe204d284, 0x02fe7f2e, 0x40b01222, 0xafacf2cb, 0xecf72c6f, 0xf869f60a, 0x45dfc681, 0xff4af66c, 0x11814c9a, 0xeea54fe7, 0x9278b0c4, 0xaf7f2e2e, 0x9a633d5b, 0xf1a05112, 0x69a329e6, 0x4ace23e4, 0x1d87]) }; - - const CURVE: ProjectiveCurveParameters = ProjectiveCurveParameters{ A: CURVE_A, C: CURVE_C }; - const AFFINE_XP: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xaac47247, 0xe8d05f30, 0x55441de7, 0x576ec00c, 0xfe558518, 0xbf1a8ec5, 0x77515881, 0xd77cb17f, 0x7ee73ec4, 0x8e985283, 0xd4f44a6b, 0x8159634a, 0x33a798c5, 0x2e4eb55, 0x4d5bc849, 0x9be8c435, 0x06496b84, 0xf47dc618, 0x295120e0, 0x25d0e130, 0x5f8139e3, 0xdbef5409, 0x4f20862c, 0x5a72]), - B: Fp751Element([0x23602e30, 0x3ca30d76, 0xf45f07b7, 0xfb281edd, 0x901a45bc, 0xd2bf62d5, 0x86306dd2, 0xc67c9baf, 0x93f538ca, 0x4e2bd930, 0xc25b9cbe, 0xcfd92075, 0x095bcbab, 0xceafe9a3, 0x80c85414, 0x7d928ad3, 0x2afdc095, 0x37c5f38b, 0xa7b779f4, 0x75325899, 0x49f20fdd, 0xf1305682, 0x264767d1, 0x178f]) }; - const AFFINE_XP2: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0x576ce979, 0x2a77afa8, 0x9b0aeba0, 0xab1360e6, 0xbffad660, 0xd79e3e3c, 0xa10f106b, 0x5fd0175a, 0xce9fbdbc, 0x1800ebaf, 0x2bdd6166, 0x228fc914, 0x314e34c3, 0x867cf907, 0x4c13c31c, 0xa58d18c9, 0x8b11499f, 0x699a5bc7, 0x01f7ccf1, 0xa29fc29a, 0x347eebce, 0x6c69c0c5, 0xcee0cc57, 0x38e]), - B: Fp751Element([0xf4837da0, 0x43607fd5, 0xe27f8f4a, 0x560bad4c, 0x8495b4dd, 0x2164927f, 0xb831a997, 0x621103fd, 0xea7db2db, 0xad740c4e, 0x205096cd, 0x2cde0442, 0xede8324e, 0x2af51a70, 0x0b9f3466, 0x41a4e68, 0x60b8f476, 0x5481f746, 0x56ff4d18, 0xfcb2f3e6, 0x37171acc, 0x42e3ce08, 0x8c30530c, 0x4423]) }; - const AFFINE_XP3: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0x3feca947, 0x2096e3f2, 0xa4ad8634, 0xf36f635a, 0x983c5e9a, 0xdae3b1c6, 0x62cb74b4, 0xe08df6c2, 0x37452d3d, 0xd2ca4edc, 0x2f500c79, 0xfb5f3fe4, 0xabc2b21f, 0x73740aa3, 0x9f914cca, 0xd535fd86, 0x823fb67f, 0x4a558466, 0xe3bfc715, 0x3e50a7a0, 0x183a132f, 0xf43c6da9, 0xa1e1b8b9, 0x61ac]), - B: Fp751Element([0xea5077bd, 0x1e54ec26, 0xd8769f9a, 0x61380572, 0x84f59818, 0xc6151706, 0x3e84ef6e, 0x6309c3b9, 0x18c3fcd0, 0x33c74b13, 0x835afb14, 0xfe8d7956, 0x423c1ecc, 0x2d5a7b55, 0xdfafea68, 0x869db67e, 0x94f0a628, 0x12926323, 0x25bfd141, 0x10bba482, 0xb408daba, 0x6466c28, 0xcfdb7c43, 0x63ca]) }; - const AFFINE_XAP: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xd7f938bb, 0x2112f3c7, 0xa4df08f, 0x704a677f, 0x1fb4ef00, 0x825370e3, 0x7469f902, 0xddbf79b, 0x9ea739fd, 0x27640c89, 0xf244108e, 0xfb7b8b19, 0xdd3baebc, 0x546a6679, 0x98d5265f, 0xe9f0ecf3, 0xe75e461, 0x223d2b35, 0xb6aff016, 0x84b322a0, 0x539f8b39, 0xfabe426f, 0xa0604f50, 0x4507]), - B: Fp751Element([0x5618a5fe, 0xac77737e, 0xc436ca52, 0xf91c0e08, 0xc323533c, 0xd124037b, 0x52c58b63, 0xc9a772bf, 0x8ef6af4d, 0x3b30c8f3, 0xe134f36e, 0xb9eed160, 0x93b25017, 0x24e38363, 0x11baf1d9, 0xc828be1b, 0x5df50e93, 0x7b7dab58, 0x618bd8e0, 0x1ca3852c, 0xb359fa00, 0x4efa73bc, 0xa923c2d4, 0x50b6]) }; - - // m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 - const M_SCALAR_BYTES: [u8; 32] = [124, 123, 149, 250, 180, 117, 108, 72, 140, 23, 85, 180, 73, 245, 30, 163, 11, 49, 240, 164, 166, 129, 173, 148, 81, 17, 231, 245, 91, 125, 117, 213]; - - const EXTENSION_FIELD_ELEMENT_ONE: ExtensionFieldElement = ExtensionFieldElement{ - A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]), - B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), - }; - - const THREE_POINT_LADDER_INPUTS: [ProjectivePoint; 3] = [ - // x(P) - ProjectivePoint{ - X: ExtensionFieldElement{ A: Fp751Element([0xaac47247, 0xe8d05f30, 0x55441de7, 0x576ec00c, 0xfe558518, 0xbf1a8ec5, 0x77515881, 0xd77cb17f, 0x7ee73ec4, 0x8e985283, 0xd4f44a6b, 0x8159634a, 0x33a798c5, 0x2e4eb55, 0x4d5bc849, 0x9be8c435, 0x6496b84, 0xf47dc618, 0x295120e0, 0x25d0e130, 0x5f8139e3, 0xdbef5409, 0x4f20862c, 0x5a72]), - B: Fp751Element([0x23602e30, 0x3ca30d76, 0xf45f07b7, 0xfb281edd, 0x901a45bc, 0xd2bf62d5, 0x86306dd2, 0xc67c9baf, 0x93f538ca, 0x4e2bd930, 0xc25b9cbe, 0xcfd92075, 0x95bcbab, 0xceafe9a3, 0x80c85414, 0x7d928ad3, 0x2afdc095, 0x37c5f38b, 0xa7b779f4, 0x75325899, 0x49f20fdd, 0xf1305682, 0x264767d1, 0x178f]) }, - Z: EXTENSION_FIELD_ELEMENT_ONE, - }, - // x(Q) - ProjectivePoint{ - X: ExtensionFieldElement{ A: Fp751Element([0x3ad1e10e, 0x2b71a2a9, 0x92cfb333, 0xf0b9842a, 0x15a27f5c, 0xae173736, 0x428330c4, 0x3039239f, 0xed7dcf98, 0xa0c4b735, 0xddf6af6a, 0x6e359771, 0xc4584651, 0xe986e4ca, 0x622d5518, 0x8233a2b, 0xf06b818b, 0xbfd67bf5, 0xf5b966a6, 0xdffe38d0, 0x272ee00a, 0xa86b36a3, 0x2ea4f68f, 0x193e]), - B: Fp751Element([0x59d9d998, 0x5a0f3964, 0xb1b7dda, 0x479f4225, 0x2a15bf75, 0x4016b57e, 0x3fa3749, 0xc59f9152, 0x399cf8da, 0xd5f90257, 0xd86dcef4, 0x1fb2dadf, 0x429021dc, 0x600f20e6, 0x80c57581, 0x17e347d3, 0x8fe3e440, 0xc1b0d5fa, 0x30ac20e8, 0xbcf0353, 0x6a4f03e6, 0x50c2eb5f, 0x6b7c4571, 0x8]) }, - Z: EXTENSION_FIELD_ELEMENT_ONE, - }, - // x(P-Q) - ProjectivePoint{ - X: ExtensionFieldElement{ A: Fp751Element([0x78f7b5ff, 0x4aafa9f3, 0x3aa8eee0, 0x1172a68, 0xbec2c1de, 0xea518d8c, 0x63674557, 0xe191bcbb, 0x7b259011, 0x97bc1963, 0xf4a2e454, 0xdbeae5c9, 0x72a42f95, 0x78f64d1b, 0x7e181e54, 0xe71cb4ea, 0x48543994, 0xe4169d4c, 0x6a98730f, 0x6198c228, 0xbab1afa5, 0xd21d675b, 0x69fce391, 0x2e72]), - B: Fp751Element([0xce1d0450, 0x23355783, 0x4ce3d93f, 0x683164cf, 0x25970fd8, 0xae6d1c4d, 0xb80b48cf, 0x7807007f, 0xc2bbb8a2, 0xa005a62e, 0x16004cb, 0x6b5649bd, 0x1330176b, 0xbb1a13fa, 0x87660461, 0xbf38e510, 0x5dd7b930, 0xe577fddc, 0x56947cd3, 0x5f38116f, 0x98c36fde, 0x3124f30b, 0xb6e6db37, 0x4ca9]) }, - Z: EXTENSION_FIELD_ELEMENT_ONE, - }, - ]; - - #[test] - fn one() { - let tmp = &EXTENSION_FIELD_ELEMENT_ONE * &AFFINE_XP; - assert!(tmp.vartime_eq(&AFFINE_XP), "Not equal 1"); - } - - #[test] - fn jinvariant() { - let j = CURVE.j_invariant(); - // Computed using Sage: - // j = 3674553797500778604587777859668542828244523188705960771798425843588160903687122861541242595678107095655647237100722594066610650373491179241544334443939077738732728884873568393760629500307797547379838602108296735640313894560419*i + 3127495302417548295242630557836520229396092255080675419212556702820583041296798857582303163183558315662015469648040494128968509467224910895884358424271180055990446576645240058960358037224785786494172548090318531038910933793845 - let known_j = ExtensionFieldElement{ - A: Fp751Element([0x1fb23993, 0xc7a8921c, 0x1327620b, 0xa20aea32, 0xd9676fa8, 0xf1caa17e, 0xb1a04037, 0x61b780e6, 0xc24acc7a, 0x47784af4, 0x300b9adf, 0x83926e2e, 0x6fae5b66, 0xcd891d5, 0xbeb733bc, 0x49b66985, 0x473d518f, 0xd4bcd2a, 0x91abe224, 0xe2422399, 0xf98672f8, 0xa8af5b20, 0x4d4e4d98, 0x139e]), - B: Fp751Element([0x1f81f359, 0xb5b52a2, 0x5db6d920, 0x715e3a86, 0x8911978b, 0x9bac2f9d, 0xac4c1e3d, 0xef14acd8, 0xcfb09c8, 0xe81aacd9, 0xde4a09d9, 0xaf898288, 0x8c5c4601, 0xb85a7fb8, 0xdd303387, 0x2c37c3f1, 0xe332367c, 0x7ad3277f, 0x25a8e6f8, 0xd4cbee7f, 0x79eaeffa, 0x36eacbe9, 0x5a13ac33, 0x59eb]), - }; - - assert!(j.vartime_eq(&known_j), "Computed incorrect j-invariant: found\n{:?}\nexpected\n{:?}", j, known_j); - } - - #[test] - fn projective_point_vartime_eq() { - let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; - let mut xQ = xP; - // Scale xQ, which results in the same projective point. - xQ.X = &xQ.X * &CURVE_A; - xQ.Z = &xQ.Z * &CURVE_A; - - assert!(xQ.vartime_eq(&xP), "Expected the scaled point to be equal to the original"); - } - - #[test] - fn point_double_versus_sage() { - let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; - let xQ = xP.pow2k(&CURVE, 1); - let affine_xQ = xQ.to_affine(); - - assert!(affine_xQ.vartime_eq(&AFFINE_XP2), "\nExpected\n{:?}\nfound\n{:?}", AFFINE_XP2, affine_xQ); - } - - #[test] - fn point_triple_versus_sage() { - let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; - let xQ = xP.pow3k(&CURVE, 1); - let affine_xQ = xQ.to_affine(); - - assert!(affine_xQ.vartime_eq(&AFFINE_XP3), "\nExpected\n{:?}\nfound\n{:?}", AFFINE_XP3, affine_xQ); - } - - #[test] - fn point_pow2k_versus_scalar_mul() { - let byte = [32u8; 1]; - let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; - let xQ = xP.pow2k(&CURVE, 5); // = x([32]P) - let affine_xQ = xQ.to_affine(); - let xR = xP.scalar_mul(&CURVE, &byte[..]); // = x([32]P) - let affine_xR = xR.to_affine(); - - assert!(affine_xQ.vartime_eq(&affine_xR), "\nExpected\n{:?}\nfound\n{:?}", affine_xQ, affine_xR); - } - - #[test] - fn scalar_mul_versus_sage() { - let mut xP = ProjectivePoint{ X: AFFINE_XP, Z: ExtensionFieldElement::one() }; - xP = xP.scalar_mul(&CURVE, &M_SCALAR_BYTES[..]); // = x([m]P) - let affine_xQ = xP.to_affine(); - - assert!(AFFINE_XAP.vartime_eq(&affine_xQ), "\nExpected\n{:?}\nfound\n{:?}", AFFINE_XAP, affine_xQ); - } - - #[test] - fn recover_curve_params() { - // Created using old public key generation code that output the a value: - let a = ExtensionFieldElement{ A: Fp751Element([0xaaf59ea4, 0x9331d9c5, 0xe4046931, 0xb32b702b, 0x12ed4d34, 0xcebb3339, 0xcd29c7a2, 0x5628ce37, 0x48b7f58e, 0xbeac5ed, 0x81d65b07, 0x1fb9d3e2, 0x1e195662, 0x9c0cfacc, 0x6b70f7d9, 0xae4bce0f, 0x43fe71a0, 0x59e4e63d, 0x60cc8615, 0xef7ce575, 0x901e74e8, 0xe44a8fb7, 0x3c8366d1, 0x69d1]), - B: Fp751Element([0x279ab966, 0xf6da1070, 0x7268c762, 0xa78fb0ce, 0x4a57abfa, 0x19b40f04, 0x60c0c233, 0x7ac8ee61, 0x42947072, 0x93d49934, 0xa4e44860, 0x757d2b3f, 0x8c4d5257, 0x73a920f, 0x54734037, 0x2031f1b0, 0x406555cd, 0xdefaa1d2, 0x1496be3d, 0x26f9c70e, 0xa4d0976, 0x5b3f335a, 0x8b2e9c59, 0x1362]) }; - let affine_xP = ExtensionFieldElement{ A: Fp751Element([0x2aebb250, 0xea6b2d1e, 0xdc4f6386, 0x35d0b205, 0xb1830b8d, 0xb198e93c, 0x496ddcc6, 0x3b5b456b, 0x1132c260, 0x5be3f0d4, 0x7516a00, 0xce5f1888, 0x9ea8866d, 0x54f3e746, 0x47f36286, 0x33809ef, 0xeabe1edb, 0x6fa45f83, 0x5d19fd86, 0x1b3391ae, 0x8584af3f, 0x1e66daf4, 0xc14aaa87, 0xb430]), - B: Fp751Element([0x61dcb2ad, 0x97b41ebc, 0xb932f641, 0x80ead31c, 0x9948b642, 0x40a94009, 0xcdc7fe84, 0x2a22fd16, 0x7579667f, 0xaabf35b1, 0x9feb4032, 0x76c1d013, 0x7b1949be, 0x71467e1e, 0xdd0d6d81, 0x678ca8da, 0xa9064c66, 0x14445dae, 0xb4fa4691, 0x92d161ea, 0x6b238d36, 0x8dfbb01b, 0x18434e4e, 0x2e37]) }; - let affine_xQ = ExtensionFieldElement{ A: Fp751Element([0xa1943439, 0xb055cf0c, 0xfa6c69ed, 0xa9ff5de2, 0x34e5730a, 0x4f2761f9, 0x1f94aa4b, 0x61a1dcaa, 0xfd058543, 0xce3c8fad, 0xa6701b8e, 0xeac432aa, 0x93aea8b, 0x8491d523, 0xd92b9b7f, 0xba273f9b, 0x4439bb5a, 0xd8f59fd3, 0x1c1fe600, 0xdc035026, 0xeb151311, 0x99375ab1, 0x75bbdbc5, 0x14d1]), - B: Fp751Element([0x2111a107, 0xffb0ef8c, 0x25991829, 0x55ceca38, 0xc075d34b, 0xdbf8a1cc, 0xd85d8494, 0xb8e9187b, 0xc34a03b0, 0x670aa2d5, 0x2b064953, 0xef9fe2ed, 0x1d645aee, 0xc911f531, 0x9e410507, 0xf4411f40, 0x2d03e1a8, 0x934a0a85, 0xae1ad544, 0xe6274e67, 0xc69a87bc, 0x9f4bc563, 0x6019681e, 0x6f31]) }; - let affine_xQmP = ExtensionFieldElement{ A: Fp751Element([0x6a153779, 0x6ffb4430, 0xf2f918f3, 0xc0ffef21, 0x5d77f778, 0x196c46d3, 0x52edcfe6, 0x4a73f804, 0xce61c67f, 0x9b00836b, 0x8d84219e, 0x38787941, 0xfc1ec5d1, 0x20700cf9, 0xec64155e, 0x1dfe2356, 0x38256b1c, 0xf8b9e330, 0x4bada0f0, 0xd2aaf2e1, 0x79a4e313, 0xb33b226e, 0x76fad4e5, 0x6be5]), - B: Fp751Element([0x8e00de34, 0x7db5dbc8, 0xf8b6e11e, 0x75cc8cb9, 0x4ebc52ac, 0x8c8001c0, 0x1a0b5a94, 0x67ef6c98, 0x73230738, 0xc3654fbe, 0x2983ceca, 0xc6a46ee8, 0x27ef49f0, 0xed1aa61a, 0xb0858fe0, 0x17fe5a13, 0x5a4c6b3c, 0x9ae0ca94, 0x18ad8878, 0x234104a2, 0x66104394, 0xa6196271, 0x1ff2e7e, 0x556a]) }; - - let curve_params = ProjectiveCurveParameters::recover_curve_parameters(&affine_xP, &affine_xQ, &affine_xQmP); - let tmp = &curve_params.C.inv() * &curve_params.A; - - assert!(tmp.vartime_eq(&a), "\nExpected\n{:?}\nfound\n{:?}", a, tmp); - } - - #[test] - fn three_point_ladder_versus_sage() { - let xR = ProjectivePoint::three_point_ladder(&THREE_POINT_LADDER_INPUTS[0], &THREE_POINT_LADDER_INPUTS[1], &THREE_POINT_LADDER_INPUTS[2], &CURVE, &M_SCALAR_BYTES[..]); - let affine_xR = xR.to_affine(); - let sage_affine_xR = ExtensionFieldElement{ A: Fp751Element([0x800d4fd5, 0x729465ba, 0x59e514a1, 0x9398015b, 0xe76c748e, 0x1a59dd6b, 0xb28dd55c, 0x1a7db94e, 0x80b1b8ec, 0x444686e6, 0x2a2454ff, 0xcc3d4ace, 0xec95a419, 0x51d3dab4, 0x94acac6a, 0xc3b0f335, 0x7fd44f8a, 0x9598a74e, 0x8f1c2e37, 0x4fbf8c63, 0x33052f51, 0x844e3470, 0xde3eafcf, 0x6cd6]), - B: Fp751Element([0x12d73430, 0x85da1454, 0x66eb3232, 0xd83c0e3b, 0x53ec1369, 0xd08ff2d4, 0xdb395b13, 0xa64aaacf, 0xa20e806e, 0xe9cba211, 0x5d937cfc, 0xa4f80b17, 0x4b1f7937, 0x556ce5c6, 0x2b3fdf7a, 0xb59b39ea, 0x9a4196b3, 0xc2526b86, 0xa9371750, 0x8dad90bc, 0x9d9147a2, 0xdfb4a30c, 0x2130629b, 0x346d]) }; - - assert!(affine_xR.vartime_eq(&sage_affine_xR), "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR, affine_xR); - } - - #[test] - fn right_to_left_ladder_versus_sage() { - let xR = ProjectivePoint::right_to_left_ladder(&THREE_POINT_LADDER_INPUTS[0], &THREE_POINT_LADDER_INPUTS[1], &THREE_POINT_LADDER_INPUTS[2], &CURVE, &M_SCALAR_BYTES[..]); - let affine_xR = xR.to_affine(); - let sage_affine_xR = ExtensionFieldElement{ A: Fp751Element([0x800d4fd5, 0x729465ba, 0x59e514a1, 0x9398015b, 0xe76c748e, 0x1a59dd6b, 0xb28dd55c, 0x1a7db94e, 0x80b1b8ec, 0x444686e6, 0x2a2454ff, 0xcc3d4ace, 0xec95a419, 0x51d3dab4, 0x94acac6a, 0xc3b0f335, 0x7fd44f8a, 0x9598a74e, 0x8f1c2e37, 0x4fbf8c63, 0x33052f51, 0x844e3470, 0xde3eafcf, 0x6cd6]), - B: Fp751Element([0x12d73430, 0x85da1454, 0x66eb3232, 0xd83c0e3b, 0x53ec1369, 0xd08ff2d4, 0xdb395b13, 0xa64aaacf, 0xa20e806e, 0xe9cba211, 0x5d937cfc, 0xa4f80b17, 0x4b1f7937, 0x556ce5c6, 0x2b3fdf7a, 0xb59b39ea, 0x9a4196b3, 0xc2526b86, 0xa9371750, 0x8dad90bc, 0x9d9147a2, 0xdfb4a30c, 0x2130629b, 0x346d]) }; - - assert!(affine_xR.vartime_eq(&sage_affine_xR), "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR, affine_xR); - } - - #[test] - fn point_triple_versus_add_double() { - fn triple_equals_add_double(curve: ProjectiveCurveParameters, P: ProjectivePoint) -> bool { - let cached_params = curve.cached_params(); - let cached_triple_params = curve.cached_triple_params(); - let P2 = P.double(&cached_params); // = x([2]P) - let P3 = P.triple(&cached_triple_params); // = x([3]P) - let P2plusP = P2.add(&P, &P); // = x([2]P + P) - - P3.vartime_eq(&P2plusP) - } - QuickCheck::new().quickcheck(triple_equals_add_double as fn(ProjectiveCurveParameters, ProjectivePoint) -> bool); - } - - #[test] - fn scalar_mul_prime_field_and_coordinate_recovery_versus_sage_generated_torsion_points() { - // x((11,...)) = 11 - let x11 = ProjectivePrimeFieldPoint{ - X: PrimeFieldElement{ A: Fp751Element([0x192a73, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6f00000, 0x3916c5c3, 0x19024ab9, 0x68876318, 0x1dcd18cf, 0xc47ba23, 0x7d8c830e, 0x9388299a, 0x3588ea6a, 0xa8e3256c, 0x8259082a, 0x3f160446, 0x3353]) }, - Z: PrimeFieldElement::one(), - }; - // y((11,...)) = oddsqrt(11^3 + 11) - let y11 = PrimeFieldElement{ A: Fp751Element([0xf57f3c8a, 0xd38a264d, 0x5042dcdf, 0x9c0450d2, 0x7bbed0b6, 0xaf1ab7be, 0x42b29630, 0xa307981c, 0xe0fa2ecb, 0x845a7e79, 0x32108f55, 0x7ef77ef7, 0x51081f0d, 0x97b58367, 0xf5275ff4, 0x59e3d115, 0x82284916, 0x9a027362, 0x96540e99, 0xec39f711, 0x8dcc965a, 0xf8b521b2, 0xb9d7f54c, 0x6af0]) }; - - // x((6,...)) = 6 - let x6 = ProjectivePrimeFieldPoint{ - X: PrimeFieldElement{ A: Fp751Element([0xdba10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x35000000, 0xb8399915, 0x3714fe4e, 0x53eb43f4, 0xc3a25847, 0x5c520428, 0xa3151d60, 0x32c7c978, 0xc116cf52, 0x8efaf6aa, 0x49a84d4b, 0x31e97514, 0x3057]) }, - Z: PrimeFieldElement::one(), - }; - // y((6,...)) = oddsqrt(6^3 + 6) - let y6 = PrimeFieldElement{ A: Fp751Element([0xba55ff3c, 0xe4786c67, 0xc2a148e0, 0x6ffa02bc, 0xdf326e2a, 0xe1c5d019, 0xf712e87, 0x23214891, 0xee99c196, 0x6ade324b, 0x6bb821f3, 0x4372f82c, 0x5d391ec4, 0x91a374a1, 0x110b7c75, 0x6e98998b, 0xd4eeb574, 0x2e093f44, 0x68840958, 0x33cdd146, 0x9e353067, 0xb017cea8, 0x7085d4b7, 0x6f90]) }; - // Little-endian bytes of 3^239 - let three_239_bytes: [u8; 48] = [235, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109, 135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173, 123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6]; - // Little-endian bytes of 2^372 - let two_372_bytes: [u8; 47] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16]; - - // E_0 : y^2 = x^3 + x has a = 0, so (a+2)/4 = 1/2 - let aPlus2Over4 = PrimeFieldElement{ A: Fp751Element([0x124d6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8e00000, 0xc0aa7287, 0x9c8a2434, 0xa9a378a3, 0xa206996c, 0x41a41b52, 0x6876280d, 0x175ce04f, 0xe903b49f, 0x666d227, 0xf851186, 0x7cff6e7f, 0x4ea0]) }; - // Compute x(P_A) = x([3^239](11,...)) and x([3^239 + 1](11,...)) - let (xPA, xPAplus11) = x11.scalar_mul_prime_field(&aPlus2Over4, &three_239_bytes[..]); - // Compute x(P_B) = x([2^372](6,...)) and x([2^372 + 1](6,...)) - let (xPB, xPBplus6) = x6.scalar_mul_prime_field(&aPlus2Over4, &two_372_bytes[..]); - - // Check that the computed x-coordinates are correct: - let test_affine_xPA = xPA.to_affine(); - assert!(test_affine_xPA.vartime_eq(&AFFINE_X_PA), "Recomputed x(P_A) incorrectly: found\n{:?}\nexpected{:?}\n", AFFINE_X_PA, test_affine_xPA); - - let test_affine_xPB = xPB.to_affine(); - assert!(test_affine_xPB.vartime_eq(&AFFINE_X_PB), "Recomputed x(P_B) incorrectly: found\n{:?}\nexpected{:?}\n", AFFINE_X_PB, test_affine_xPB); - - // Recover y-coordinates and check that those are correct: - let (mut X_A, mut Y_A, Z_A) = ProjectivePoint::okeya_sakurai_coordinate_recovery(&x11.X, &y11, &xPA, &xPAplus11); - let invZ_A = Z_A.inv(); - Y_A = &Y_A * &invZ_A; // = Y_A / Z_A - X_A = &X_A * &invZ_A; // = X_A / Z_A - assert!(AFFINE_Y_PA.vartime_eq(&Y_A), "Recovered y(P_A) incorrectly: found\n{:?}\nexpected{:?}\n", Y_A, AFFINE_Y_PA); - assert!(AFFINE_X_PA.vartime_eq(&X_A), "Recovered x(P_A) incorrectly: found\n{:?}\nexpected{:?}\n", X_A, AFFINE_X_PA); - - let (mut X_B, mut Y_B, Z_B) = ProjectivePoint::okeya_sakurai_coordinate_recovery(&x6.X, &y6, &xPB, &xPBplus6); - let invZ_B = Z_B.inv(); - Y_B = &Y_B * &invZ_B; // = Y_B / Z_B - X_B = &X_B * &invZ_B; // = X_B / Z_B - assert!(AFFINE_Y_PB.vartime_eq(&Y_B), "Recovered y(P_B) incorrectly: found\n{:?}\nexpected{:?}\n", Y_B, AFFINE_Y_PB); - assert!(AFFINE_X_PB.vartime_eq(&X_B), "Recovered x(P_B) incorrectly: found\n{:?}\nexpected{:?}\n", X_B, AFFINE_X_PB); - } -} diff --git a/attic/sidhp751/field.rs b/attic/sidhp751/field.rs deleted file mode 100644 index 120237638..000000000 --- a/attic/sidhp751/field.rs +++ /dev/null @@ -1,830 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -//! This module contains finite field arithmetic functionality for SIDH, -//! which is not part of the public API. - -use crate::sidhp751::fp::*; - -use std::fmt::Debug; - -use std::cmp::{Eq, PartialEq}; -use std::ops::*; - -use subtle::ConditionallySelectable; -use subtle::ConstantTimeEq; -use subtle::Choice; - -#[cfg(test)] -use quickcheck::{Arbitrary, Gen}; - -//-----------------------------------------------------------------------------// -// Extension Field // -//-----------------------------------------------------------------------------// - -/// Represents an element of the extension field `F_{p^2}`. -#[allow(non_snake_case)] -#[derive(Copy, Clone, PartialEq)] -pub struct ExtensionFieldElement { - /// This field element is in Montgomery form, so that the value `A` is - /// represented by `aR mod p`. - pub A: Fp751Element, - /// This field element is in Montgomery form, so that the value `B` is - /// represented by `bR mod p`. - pub B: Fp751Element, -} - -impl<'b> AddAssign<&'b ExtensionFieldElement> for ExtensionFieldElement { - #[inline(always)] - fn add_assign(&mut self, _rhs: &'b ExtensionFieldElement) { - let result = (self as &ExtensionFieldElement) + _rhs; - self.A = result.A; - self.B = result.B; - } -} - -impl<'a, 'b> Add<&'b ExtensionFieldElement> for &'a ExtensionFieldElement { - type Output = ExtensionFieldElement; - #[inline(always)] - fn add(self, _rhs: &'b ExtensionFieldElement) -> ExtensionFieldElement { - let a = &self.A + &_rhs.A; - let b = &self.B + &_rhs.B; - - ExtensionFieldElement{ - A: a, - B: b - } - } -} - -impl <'b> SubAssign<&'b ExtensionFieldElement> for ExtensionFieldElement { - #[inline(always)] - fn sub_assign(&mut self, _rhs: &'b ExtensionFieldElement) { - let result = (self as &ExtensionFieldElement) - _rhs; - self.A = result.A; - self.B = result.B; - } -} - -impl<'a, 'b> Sub<&'b ExtensionFieldElement> for &'a ExtensionFieldElement { - type Output = ExtensionFieldElement; - #[inline(always)] - fn sub(self, _rhs: &'b ExtensionFieldElement) -> ExtensionFieldElement { - let a = &self.A - &_rhs.A; - let b = &self.B - &_rhs.B; - - ExtensionFieldElement{ - A: a, - B: b - } - } -} - -impl<'b> MulAssign<&'b ExtensionFieldElement> for ExtensionFieldElement { - #[inline(always)] - fn mul_assign(&mut self, _rhs: &'b ExtensionFieldElement) { - let result = (self as &ExtensionFieldElement) * _rhs; - self.A = result.A; - self.B = result.B; - } -} - -impl<'a, 'b> Mul<&'b ExtensionFieldElement> for &'a ExtensionFieldElement { - type Output = ExtensionFieldElement; - fn mul(self, _rhs: &'b ExtensionFieldElement) -> ExtensionFieldElement { - // Alias self, _rhs for more readable formulas. - let a = &self.A; - let b = &self.B; - let c = &_rhs.A; - let d = &_rhs.B; - - // We want to compute - // - // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i - // - // Use Karatsuba's trick: note that - // - // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d - // - // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. - // - let ac = a * c; // = a*c*R*R - let bd = b * d; // = b*d*R*R - let b_minus_a = b - a; // = (b-a)*R - let c_minus_d = c - d; // = (c-d)*R - - let mut ad_plus_bc = &b_minus_a * &c_minus_d; // = (b-a)*(c-d)*R*R - ad_plus_bc += ∾ // = ((b-a)*(c-d) + a*c)*R*R - ad_plus_bc += &bd; // = ((b-a)*(c-d) + a*c + b*d)*R*R - let _b = ad_plus_bc.reduce(); // = (a*d + b*c)*R mod p - - let ac_minus_bd = &ac - &bd; // = (a*c - b*d)*R*R - let _a = ac_minus_bd.reduce(); // = (a*c - b*d)*R mod p - - ExtensionFieldElement{ - A: _a, - B: _b - } - } -} - -impl <'a> Neg for &'a ExtensionFieldElement { - type Output = ExtensionFieldElement; - #[inline(always)] - fn neg(self) -> ExtensionFieldElement { - let zero = ExtensionFieldElement::zero(); - let result = &zero - (self as &ExtensionFieldElement); - result - } -} - -impl ConditionallySelectable for ExtensionFieldElement { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtensionFieldElement{ - A: Fp751Element::conditional_select(&a.A, &b.A, choice), - B: Fp751Element::conditional_select(&a.B, &b.B, choice) - } - } - - fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { - Fp751Element::conditional_swap(&mut a.A, &mut b.A, choice); - Fp751Element::conditional_swap(&mut a.B, &mut b.B, choice); - } -} - -impl Debug for ExtensionFieldElement { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "ExtensionFieldElement(A: {:?}\nB: {:?})", &self.A.0[..], &self.B.0[..]) - } -} - -#[cfg(test)] -pub struct ExtensionFieldElementDist; - -#[cfg(test)] -impl Arbitrary for ExtensionFieldElement { - fn arbitrary(g: &mut Gen) -> ExtensionFieldElement { - let a = Fp751Element::arbitrary(g); - let b = Fp751Element::arbitrary(g); - ExtensionFieldElement{ A: a, B: b } - } -} - -impl ExtensionFieldElement { - /// Construct a zero `ExtensionFieldElement`. - #[inline(always)] - pub fn zero() -> ExtensionFieldElement { - ExtensionFieldElement{ - A: Fp751Element::zero(), - B: Fp751Element::zero(), - } - } - - /// Construct a one `ExtensionFieldElement`. - #[inline(always)] - pub fn one() -> ExtensionFieldElement { - ExtensionFieldElement{ - A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]), - B: Fp751Element([0_u32; FP751_NUM_WORDS]), - } - } - - /// Set output to `1/x`. - pub fn inv(&self) -> ExtensionFieldElement { - let a = &self.A; - let b = &self.B; - - // We want to compute - // - // 1 1 (a - bi) (a - bi) - // -------- = -------- -------- = ----------- - // (a + bi) (a + bi) (a - bi) (a^2 + b^2) - // - // Letting c = 1/(a^2 + b^2), this is - // - // 1/(a+bi) = a*c - b*ci. - // - let mut asq = a * a; // = a*a*R*R - let bsq = b * b; // = b*b*R*R - asq = &asq + &bsq; // = (a^2 + b^2)*R*R - let mut asq_plus_bsq = PrimeFieldElement::zero(); - asq_plus_bsq.A = asq.reduce(); // = (a^2 + b^2)*R mod p - // Now asq_plus_bsq = a^2 + b^2 - - let asq_plus_bsq_inv = asq_plus_bsq.inv(); - let c = &asq_plus_bsq_inv.A; - - let ac = a * c; - let _a = ac.reduce(); - - let mut minus_b = Fp751Element::zero(); - minus_b = &minus_b - &b; - let minus_bc = &minus_b * &c; - let _b = minus_bc.reduce(); - - ExtensionFieldElement{ - A: _a, - B: _b - } - } - - // Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3). - // - // All xi, yi must be distinct. - pub fn batch3_inv(x1: &ExtensionFieldElement, x2: &ExtensionFieldElement, x3: &ExtensionFieldElement) -> - (ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) - { - let x1x2 = x1 * x2; // x1*x2 - let mut t = &x1x2 * x3; - t = t.inv(); // 1/(x1*x2*x3) - let y1 = &t * x2; - let _y1 = &y1 * x3; // 1/x1 - let y2 = &t * x1; - let _y2 = &y2 * x3; // 1/x2 - let _y3 = &t * &x1x2; // 1/x3 - - (_y1, _y2, _y3) - } - - /// Set the output to `x^2`. - pub fn square(&self) -> ExtensionFieldElement { - let a = &self.A; - let b = &self.B; - - // We want to compute - // - // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi - // - let a2 = a + a; // = a*R + a*R = 2*a*R - let a_plus_b = a + b; // = a*R + b*R = (a+b)*R - let a_minus_b = a - b; // = a*R - b*R = (a-b)*R - - let asq_minus_bsq = &a_plus_b * &a_minus_b; // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R - let ab2 = &a2 * b; // = 2*a*b*R*R - - let _a = asq_minus_bsq.reduce(); // = (a^2 - b^2)*R mod p - let _b = ab2.reduce(); // = 2*a*b*R mod p - - ExtensionFieldElement{ - A: _a, - B: _b - } - } - - /// Returns true if both sides are equal. Takes variable time. - #[inline(always)] - pub fn vartime_eq(&self, _rhs: &ExtensionFieldElement) -> bool { - (&self.A == &_rhs.A) && (&self.B == &_rhs.B) - } - - /// Convert the input to wire format. - #[inline(always)] - pub fn to_bytes(&self) -> [u8; 188] { - let mut bytes = [0u8; 188]; - bytes[0..94].clone_from_slice(&self.A.to_bytes()); - bytes[94..188].clone_from_slice(&self.B.to_bytes()); - bytes - } - - /// Read 188 bytes into the given `ExtensionFieldElement`. - #[inline(always)] - pub fn from_bytes(bytes: &[u8]) -> ExtensionFieldElement { - assert!(bytes.len() >= 188, "Too short input to ExtensionFieldElement from_bytes, expected 188 bytes"); - let a = Fp751Element::from_bytes(&bytes[0..94]); - let b = Fp751Element::from_bytes(&bytes[94..188]); - ExtensionFieldElement{ A: a, B: b } - } -} - -//-----------------------------------------------------------------------------// -// Prime Field // -//-----------------------------------------------------------------------------// - -/// Represents an element of the prime field `F_p`. -#[allow(non_snake_case)] -#[derive(Copy, Clone, PartialEq)] -pub struct PrimeFieldElement { - /// This field element is in Montgomery form, so that the value `A` is - /// represented by `aR mod p`. - pub A: Fp751Element -} - -impl<'b> AddAssign<&'b PrimeFieldElement> for PrimeFieldElement { - #[inline(always)] - fn add_assign(&mut self, _rhs: &'b PrimeFieldElement) { - let result = (self as &PrimeFieldElement) + _rhs; - self.A = result.A; - } -} - -impl<'a, 'b> Add<&'b PrimeFieldElement> for &'a PrimeFieldElement { - type Output = PrimeFieldElement; - #[inline(always)] - fn add(self, _rhs: &'b PrimeFieldElement) -> PrimeFieldElement { - let a = &self.A + &_rhs.A; - PrimeFieldElement{ A: a } - } -} - -impl <'b> SubAssign<&'b PrimeFieldElement> for PrimeFieldElement { - #[inline(always)] - fn sub_assign(&mut self, _rhs: &'b PrimeFieldElement) { - let result = (self as &PrimeFieldElement) - _rhs; - self.A = result.A; - } -} - -impl<'a, 'b> Sub<&'b PrimeFieldElement> for &'a PrimeFieldElement { - type Output = PrimeFieldElement; - #[inline(always)] - fn sub(self, _rhs: &'b PrimeFieldElement) -> PrimeFieldElement { - let a = &self.A - &_rhs.A; - PrimeFieldElement{ A: a } - } -} - -impl<'b> MulAssign<&'b PrimeFieldElement> for PrimeFieldElement { - #[inline(always)] - fn mul_assign(&mut self, _rhs: &'b PrimeFieldElement) { - let result = (self as &PrimeFieldElement) * _rhs; - self.A = result.A; - } -} - -impl<'a, 'b> Mul<&'b PrimeFieldElement> for &'a PrimeFieldElement { - type Output = PrimeFieldElement; - #[inline(always)] - fn mul(self, _rhs: &'b PrimeFieldElement) -> PrimeFieldElement { - // Alias self, _rhs for more readable formulas. - let a = &self.A; // = a*R - let b = &_rhs.A; // = b*R - let ab = a * b; // = a*b*R*R - let _a = ab.reduce(); // = a*b*R mod p - - PrimeFieldElement{ A: _a } - } -} - -impl <'a> Neg for &'a PrimeFieldElement { - type Output = PrimeFieldElement; - #[inline(always)] - fn neg(self) -> PrimeFieldElement { - let zero = PrimeFieldElement::zero(); - let result = &zero - (self as &PrimeFieldElement); - result - } -} - -impl ConditionallySelectable for PrimeFieldElement { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - PrimeFieldElement{ - A: Fp751Element::conditional_select(&a.A, &b.A, choice) - } - } - - fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { - Fp751Element::conditional_swap(&mut a.A, &mut b.A, choice); - } -} - -impl Debug for PrimeFieldElement { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "PrimeFieldElement(A: {:?})", &self.A.0[..]) - } -} - -#[cfg(test)] -impl Arbitrary for PrimeFieldElement { - fn arbitrary(g: &mut Gen) -> PrimeFieldElement { - let a = Fp751Element::arbitrary(g); - PrimeFieldElement{ A: a } - } -} - -impl PrimeFieldElement { - /// Construct a zero `PrimeFieldElement`. - #[inline(always)] - pub fn zero() -> PrimeFieldElement { - PrimeFieldElement{ - A: Fp751Element::zero(), - } - } - - /// Construct a one `PrimeFieldElement`. - pub fn one() -> PrimeFieldElement { - PrimeFieldElement{ - A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]), - } - } - - /// Set the output to `x^2`. - #[inline(always)] - pub fn square(&self) -> PrimeFieldElement { - let a = &self.A; // = a*R - let b = &self.A; // = b*R - let ab = a * b; // = a*b*R*R - let _a = ab.reduce(); // = a*b*R mod p - - PrimeFieldElement{ A: _a } - } - - /// Raise self to `2^(2^k)`-th power, for `k >= 1`, by repeated squarings. - fn pow2k(&self, k: u8) -> PrimeFieldElement { - let mut result = self.square(); - for _ in 1..k { result = result.square(); } - result - } - - /// Set output to `x^((p-3)/4)`. If `x` is square, this is `1/sqrt(x)`. - fn p34(&self) -> PrimeFieldElement { - // Sliding-window strategy computed with Sage, awk, sed, and tr. - // - // This performs sum(powStrategy) = 744 squarings and len(mulStrategy) - // = 137 multiplications, in addition to 1 squaring and 15 - // multiplications to build a lookup table. - // - // In total this is 745 squarings, 152 multiplications. Since squaring - // is not implemented for the prime field, this is 897 multiplications - // in total. - let pow_strategy: [u8; 137] = [5, 7, 6, 2, 10, 4, 6, 9, 8, 5, 9, 4, 7, 5, 5, 4, 8, 3, 9, 5, 5, 4, 10, 4, 6, 6, 6, 5, 8, 9, 3, 4, 9, 4, 5, 6, 6, 2, 9, 4, 5, 5, 5, 7, 7, 9, 4, 6, 4, 8, 5, 8, 6, 6, 2, 9, 7, 4, 8, 8, 8, 4, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2]; - let mul_strategy: [u8; 137] = [31, 23, 21, 1, 31, 7, 7, 7, 9, 9, 19, 15, 23, 23, 11, 7, 25, 5, 21, 17, 11, 5, 17, 7, 11, 9, 23, 9, 1, 19, 5, 3, 25, 15, 11, 29, 31, 1, 29, 11, 13, 9, 11, 27, 13, 19, 15, 31, 3, 29, 23, 31, 25, 11, 1, 21, 19, 15, 15, 21, 29, 13, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 3]; - let initial_mul: u8 = 27; - - // Build a lookup table of odd multiples of x. - let mut lookup = [PrimeFieldElement::zero(); 16]; - let xx: &PrimeFieldElement = &self.square(); // Set xx = x^2 - lookup[0] = *self; - - for i in 1..16 { - lookup[i as usize] = &lookup[(i-1) as usize] * xx; - } - // Now lookup = {x, x^3, x^5, ... } - // so that lookup[i] = x^{2*i + 1} - // so that lookup[k/2] = x^k, for odd k - let mut result = lookup[(initial_mul / 2) as usize]; - for i in 0..137 { - result = result.pow2k(pow_strategy[i]); - result = &result * &lookup[(mul_strategy[i] / 2) as usize]; - } - result - } - - /// Set output to `sqrt(x)`, if x is a square. If `x` is nonsquare output is undefined. - fn sqrt(&self) -> PrimeFieldElement { - let mut result = self.p34(); // result = (y^2)^((p-3)/4) = y^((p-3)/2) - result = &result * self; // result = y^2 * y^((p-3)/2) = y^((p+1)/2) - // Now result^2 = y^(p+1) = y^2 = x, so result = sqrt(x). - result - } - - /// Set output to `1/x`. - pub fn inv(&self) -> PrimeFieldElement { - let mut result = self.square(); // result = x^2 - result = result.p34(); // result = (x^2)^((p-3)/4) = x^((p-3)/2) - result = result.square(); // result = x^(p-3) - result = &result * self; // result = x^(p-2) - result - } - - /// Returns true if both sides are equal. Takes variable time. - #[inline(always)] - pub fn vartime_eq(&self, _rhs: &PrimeFieldElement) -> bool { - &self.A == &_rhs.A - } -} - -//-----------------------------------------------------------------------------// -// Internals // -//-----------------------------------------------------------------------------// - -impl<'b> AddAssign<&'b Fp751Element> for Fp751Element { - #[inline(always)] - fn add_assign(&mut self, _rhs: &'b Fp751Element) { - let result = (self as &Fp751Element) + _rhs; - self.0 = result.0 - } -} - -impl<'a, 'b> Add<&'b Fp751Element> for &'a Fp751Element { - type Output = Fp751Element; - #[inline(always)] - fn add(self, _rhs: &'b Fp751Element) -> Fp751Element { - let mut result = Fp751Element::zero(); - fpadd751(&self, _rhs, &mut result); - result - } -} - -impl <'b> SubAssign<&'b Fp751Element> for Fp751Element { - #[inline(always)] - fn sub_assign(&mut self, _rhs: &'b Fp751Element) { - let result = (self as &Fp751Element) - _rhs; - self.0 = result.0 - } -} - -impl<'a, 'b> Sub<&'b Fp751Element> for &'a Fp751Element { - type Output = Fp751Element; - #[inline(always)] - fn sub(self, _rhs: &'b Fp751Element) -> Fp751Element { - let mut result = Fp751Element::zero(); - fpsub751(&self, _rhs, &mut result); - result - } -} - -impl<'a, 'b> Mul<&'b Fp751Element> for &'a Fp751Element { - type Output = Fp751X2; - #[inline(always)] - fn mul(self, _rhs: &'b Fp751Element) -> Fp751X2 { - let mut result = Fp751X2::zero(); - mul751(&self, _rhs, &mut result); // = a*c*R*R - result - } -} - -impl <'a> Neg for &'a Fp751Element { - type Output = Fp751Element; - #[inline(always)] - fn neg(self) -> Fp751Element { - let zero = Fp751Element::zero(); - let result = &zero - (self as &Fp751Element); - result - } -} - -impl Eq for Fp751Element {} -impl PartialEq for Fp751Element { - /// Test equality between two `Fp751Element`s. - /// - /// # Warning - /// - /// This comparison is *not* constant time. - fn eq(&self, other: &Fp751Element) -> bool { - let mut _self = *self; - let mut _other = *other; - - _self = _self.strong_reduce(); - _other = _other.strong_reduce(); - - let mut eq: bool = true; - for i in 0..FP751_NUM_WORDS { - eq = (_self.0[i] == _other.0[i]) && eq; - } - eq - } -} - -impl ConstantTimeEq for Fp751Element { - /// Test equality between two `Fp751Element`s. - /// - /// # Returns - /// - /// `1u8` if the two `Fp751Element`s are equal, and `0u8` otherwise. - fn ct_eq(&self, other: &Fp751Element) -> Choice { - let self_bytes = self.to_bytes(); - let other_bytes = other.to_bytes(); - let len = self_bytes.len(); - - // Short-circuit on the *lengths* of the slices, not their - // contents. - if len != other_bytes.len() { - return Choice::from(0); - } - - // This loop shouldn't be shortcircuitable, since the compiler - // shouldn't be able to reason about the value of the `u8` - // unwrapped from the `ct_eq` result. - let mut x = 1u8; - for (selfi, otheri) in self_bytes.iter().zip(other_bytes.iter()) { - x &= selfi.ct_eq(otheri).unwrap_u8(); - } - - x.into() - } -} - -// #[cfg(test)] -// impl Arbitrary for Fp751Element { -// fn arbitrary(g: &mut Gen) -> Fp751Element { -// Fp751Element::arbitrary(g); -// } -// } - -impl Fp751Element { - /// Reduce a field element in `[0, 2*p)` to one in `[0,p)`. - #[inline(always)] - pub fn strong_reduce(&self) -> Fp751Element { - let mut _self = *self; - srdc751(&mut _self); - _self - } -} - -impl<'b> AddAssign<&'b Fp751X2> for Fp751X2 { - #[inline(always)] - fn add_assign(&mut self, _rhs: &'b Fp751X2) { - let result = (self as &Fp751X2) + _rhs; - self.0 = result.0 - } -} - -impl<'a, 'b> Add<&'b Fp751X2> for &'a Fp751X2 { - type Output = Fp751X2; - #[inline(always)] - fn add(self, _rhs: &'b Fp751X2) -> Fp751X2 { - let mut result = Fp751X2::zero(); - mp_add751x2(&self, _rhs, &mut result); - result - } -} - -impl <'b> SubAssign<&'b Fp751X2> for Fp751X2 { - #[inline(always)] - fn sub_assign(&mut self, _rhs: &'b Fp751X2) { - let result = (self as &Fp751X2) - _rhs; - self.0 = result.0 - } -} - -impl<'a, 'b> Sub<&'b Fp751X2> for &'a Fp751X2 { - type Output = Fp751X2; - #[inline(always)] - fn sub(self, _rhs: &'b Fp751X2) -> Fp751X2 { - let mut result = Fp751X2::zero(); - mp_sub751x2(&self, _rhs, &mut result); - result - } -} - -impl Fp751X2 { - /// Perform Montgomery reduction, `x R^{-1} (mod p)`. - #[inline(always)] - pub fn reduce(&self) -> Fp751Element { - let mut result = Fp751Element::zero(); - rdc751(self, &mut result); - result - } -} - -#[inline(always)] -pub fn checklt238(scalar: &[u8; 48], result: &mut u32) { - crate::sidhp751::fp::checklt238(scalar, result); -} - -#[inline(always)] -pub fn mulby3(scalar: &mut [u8; 48]) { - crate::sidhp751::fp::mulby3(scalar); -} - -#[cfg(test)] -mod test { - use super::*; - use quickcheck::QuickCheck; - - const SCALE_FACTOR: u8 = 3; - const MAX_TESTS: u64 = 1 << (10 + SCALE_FACTOR); - - #[test] - fn one_extension_field_to_byte() { - let one = &ExtensionFieldElement::one(); - let bytes = one.to_bytes(); - - assert_eq!(bytes[0], 1); - - for i in 1..188 { - assert_eq!(bytes[i], 0); - } - } - - #[test] - fn extension_field_element_to_bytes_round_trip() { - fn round_trips(x: ExtensionFieldElement) -> bool { - let bytes = x.to_bytes(); - let x_prime = ExtensionFieldElement::from_bytes(&bytes); - x.vartime_eq(&x_prime) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(round_trips as fn(ExtensionFieldElement) -> bool); - } - - #[test] - fn extension_field_element_mul_distributes_over_add() { - fn mul_distributes_over_add(x: ExtensionFieldElement, y: ExtensionFieldElement, z: ExtensionFieldElement) -> bool { - // Compute t1 = (x+y)*z - let t1 = &(&x + &y) * &z; - // Compute t2 = x*z + y*z - let t2 = &(&x * &z) + &(&y * &z); - - t1.vartime_eq(&t2) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(mul_distributes_over_add as fn(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) -> bool); - } - - #[test] - fn extension_field_element_mul_is_associative() { - fn is_associative(x: ExtensionFieldElement, y: ExtensionFieldElement, z: ExtensionFieldElement) -> bool { - // Compute t1 = (x*y)*z - let t1 = &(&x * &y) * &z; - // Compute t2 = (y*z)*x - let t2 = &(&y * &z) * &x; - - t1.vartime_eq(&t2) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(is_associative as fn(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) -> bool); - } - - #[test] - fn extension_field_element_square_matches_mul() { - fn square_matches_mul(x: ExtensionFieldElement) -> bool { - // Compute t1 = (x*x) - let t1 = &x * &x; - // Compute t2 = x^2 - let t2 = x.square(); - - t1.vartime_eq(&t2) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(square_matches_mul as fn(ExtensionFieldElement) -> bool); - } - - #[test] - fn extension_field_element_inv() { - fn inverse(x: ExtensionFieldElement) -> bool { - let mut z = x.inv(); - // Now z = (1/x), so (z * x) * x == x - z = &(&z * &x) * &x; - - z.vartime_eq(&x) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(inverse as fn(ExtensionFieldElement) -> bool); - } - - #[test] - fn extension_field_element_batch3_inv() { - fn batch_inverse(x1: ExtensionFieldElement, x2: ExtensionFieldElement, x3: ExtensionFieldElement) -> bool { - let x1_inv = x1.inv(); - let x2_inv = x2.inv(); - let x3_inv = x3.inv(); - - let (y1, y2, y3) = ExtensionFieldElement::batch3_inv(&x1, &x2, &x3); - - y1.vartime_eq(&x1_inv) && y2.vartime_eq(&x2_inv) && y3.vartime_eq(&x3_inv) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(batch_inverse as fn(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) -> bool); - } - - #[test] - fn prime_field_element_inv() { - fn inverse(x: PrimeFieldElement) -> bool { - let mut z = x.inv(); - // Now z = (1/x), so (z * x) * x == x - z = &(&z * &x) * &x; - - z.vartime_eq(&x) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(inverse as fn(PrimeFieldElement) -> bool); - } - - #[test] - fn prime_field_element_sqrt() { - fn square_root(x: PrimeFieldElement) -> bool { - // Construct y = x^2 so we're sure y is square. - let y = x.square(); - let mut z = y.sqrt(); - // Now z = sqrt(y), so z^2 == y - z = z.square(); - - z.vartime_eq(&y) - } - QuickCheck::new().max_tests(MAX_TESTS) - .quickcheck(square_root as fn(PrimeFieldElement) -> bool); - } - - #[test] - fn fp751_element_conditional_swap() { - let one = Fp751Element([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); - let two = Fp751Element([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]); - - let mut x = one; - let mut y = two; - - Fp751Element::conditional_swap(&mut x, &mut y, 0.into()); - assert_eq!(x, one); - assert_eq!(y, two); - - Fp751Element::conditional_swap(&mut x, &mut y, 1.into()); - assert_eq!(x, two); - assert_eq!(y, one); - } -} diff --git a/attic/sidhp751/fp.rs b/attic/sidhp751/fp.rs deleted file mode 100644 index a2134b785..000000000 --- a/attic/sidhp751/fp.rs +++ /dev/null @@ -1,408 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -use crate::random::SecureRandom; - -use std::mem::size_of; -use std::fmt::Debug; -use std::ops::Neg; - -#[cfg(test)] -use quickcheck::{Arbitrary, Gen}; - -use subtle::{ConditionallySelectable, Choice}; -use rand_core::RngCore; - -macro_rules! assign{ - {($v1:ident, $v2:expr) = $e:expr} => - { - { - let (v1, v2) = $e; - $v1 = v1; - $v2 = v2; - } - }; -} - -const RADIX: u32 = 32; -pub const FP751_NUM_WORDS: usize = 24; -const P751_ZERO_WORDS: usize = 11; - -const P751: [u32; FP751_NUM_WORDS] = [4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4004511743, 1241020584, 3823933061, 335006838, 3667237658, 3605784694, 139368551, 1555191624, 2237838596, 2545605734, 236097695, 3577870108, 28645]; -const P751P1: [u32; FP751_NUM_WORDS] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4004511744, 1241020584, 3823933061, 335006838, 3667237658, 3605784694, 139368551, 1555191624, 2237838596, 2545605734, 236097695, 3577870108, 28645]; -const P751X2: [u32; FP751_NUM_WORDS] = [4294967294, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 3714056191, 2482041169, 3352898826, 670013677, 3039508020, 2916602093, 278737103, 3110383248, 180709896, 796244173, 472195391, 2860772920, 57291]; - -#[inline(always)] -fn mul(multiplier: u32, multiplicant: u32, uv: &mut [u32; 2]) { - let p = (multiplier as u64) * (multiplicant as u64); - uv[0] = p as u32; - uv[1] = (p >> 32) as u32; -} - -#[inline(always)] -fn addc(carry_in: u32, addend1: u32, addend2: u32) -> (u32, u32) { - //let sum = (addend1 as u64) + (addend2 as u64) + (carry_in as u64); - //((sum > u32::MAX as u64) as u32, sum as u32) - let temp = addend1.wrapping_add(carry_in); - let sum = addend2.wrapping_add(temp); - ((temp < carry_in) as u32 | (sum < temp) as u32, sum) -} - -#[inline(always)] -fn subc(borrow_in: u32, minuend: u32, subtrahend: u32) -> (u32, u32) { - let temp = minuend.wrapping_sub(subtrahend); - let borrow = ((minuend < subtrahend) as u32) | (borrow_in & (temp == 0) as u32); - let difference = temp.wrapping_sub(borrow_in); - (borrow, difference) -} - -#[inline(always)] -pub fn fpadd751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { - let mut carry: u32 = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(carry, z.0[i]) = addc(carry, x.0[i], y.0[i])}; - } - carry = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(carry, z.0[i]) = subc(carry, z.0[i], P751X2[i])}; - } - let mask = (0 as u32).wrapping_sub(carry); - carry = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(carry, z.0[i]) = addc(carry, z.0[i], (P751X2[i] & mask))}; - } -} - -#[inline(always)] -pub fn fpsub751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { - let mut borrow: u32 = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(borrow, z.0[i]) = subc(borrow, x.0[i], y.0[i])}; - } - let mask = (0 as u32).wrapping_sub(borrow); - borrow = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(borrow, z.0[i]) = addc(borrow, z.0[i], (P751X2[i] & mask))}; - } -} - -pub fn mul751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751X2) { - let mut t: u32 = 0; - let mut u: u32 = 0; - let mut v: u32 = 0; - #[allow(non_snake_case)] - let mut UV = [0u32; 2]; - let mut carry: u32 = 0; - - for i in 0..FP751_NUM_WORDS { - for j in 0..(i+1) { - mul(x.0[j], y.0[i - j], &mut UV); - assign! {(carry, v) = addc(0, UV[0], v)}; - assign! {(carry, u) = addc(carry, UV[1], u)}; - t += carry; - } - z.0[i] = v; - v = u; - u = t; - t = 0; - } - - for i in FP751_NUM_WORDS..(2*FP751_NUM_WORDS-1) { - for j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS { - mul(x.0[j], y.0[i-j], &mut UV); - assign!{(carry, v) = addc(0, UV[0], v)}; - assign!{(carry, u) = addc(carry, UV[1], u)}; - t += carry; - } - z.0[i] = v; - v = u; - u = t; - t = 0; - } - z.0[2*FP751_NUM_WORDS-1] = v; -} - -pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) { - let mut t: u32 = 0; - let mut u: u32 = 0; - let mut v: u32 = 0; - #[allow(non_snake_case)] - let mut UV = [0u32; 2]; - let mut carry: u32 = 0; - let mut count = P751_ZERO_WORDS; - - for i in 0..FP751_NUM_WORDS { - z.0[i] = 0; - } - - for i in 0..FP751_NUM_WORDS { - for j in 0..i { - if j < (((i+1) as u32).wrapping_sub(P751_ZERO_WORDS as u32) as usize) { - mul(z.0[j], P751P1[i-j], &mut UV); - assign!{(carry, v) = addc(0, UV[0], v)}; - assign!{(carry, u) = addc(carry, UV[1], u)}; - t += carry; - } - } - assign!{(carry, v) = addc(0, v, x.0[i])}; - assign!{(carry, u) = addc(carry, u, 0)}; - - t += carry; - z.0[i] = v; - v = u; - u = t; - t = 0; - } - - for i in FP751_NUM_WORDS..(2*FP751_NUM_WORDS-1) { - if count > 0 { - count -= 1; - } - for j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS { - if j < (FP751_NUM_WORDS-count) { - mul(z.0[j], P751P1[i-j], &mut UV); - assign!{(carry, v) = addc(0, UV[0], v)}; - assign!{(carry, u) = addc(carry, UV[1], u)}; - t += carry; - } - } - assign!{(carry, v) = addc(0, v, x.0[i])}; - assign!{(carry, u) = addc(carry, u, 0)}; - - t += carry; - z.0[i-FP751_NUM_WORDS] = v; - v = u; - u = t; - t = 0; - } - assign!{(carry, v) = addc(0, v, x.0[2*FP751_NUM_WORDS-1])}; - z.0[FP751_NUM_WORDS-1] = v; -} - -#[inline(always)] -pub fn srdc751(x: &mut Fp751Element) { - let mut borrow: u32 = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(borrow, x.0[i]) = subc(borrow, x.0[i], P751[i])}; - } - let mask = (0 as u32).wrapping_sub(borrow); - borrow = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(borrow, x.0[i]) = addc(borrow, x.0[i], (P751[i] & mask))}; - } -} - -#[inline(always)] -pub fn mp_add751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { - let mut carry: u32 = 0; - for i in 0..FP751_NUM_WORDS { - assign!{(carry, z.0[i]) = addc(carry, x.0[i], y.0[i])}; - } -} - -#[inline(always)] -pub fn mp_add751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) { - let mut carry: u32 = 0; - for i in 0..(FP751_NUM_WORDS*2) { - assign!{(carry, z.0[i]) = addc(carry, x.0[i], y.0[i])}; - } -} - -#[inline(always)] -pub fn mp_sub751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) { - let mut borrow: u32 = 0; - for i in 0..(FP751_NUM_WORDS*2) { - assign!{(borrow, z.0[i]) = subc(borrow, x.0[i], y.0[i])}; - } - let mask = (0 as u32).wrapping_sub(borrow); - borrow = 0; - for i in FP751_NUM_WORDS..(FP751_NUM_WORDS*2) { - assign!{(borrow, z.0[i]) = addc(borrow, z.0[i], (P751[i-FP751_NUM_WORDS] & mask))}; - } -} - -#[inline(always)] -pub fn checklt238(scalar: &[u8; 48], result: &mut u32) { - let three238: [u32; 12] = [0x828384f8, 0xedcd718a, 0xd4427a14, 0x733b35bf, 0x94d7cf38, 0xf88229cf, 0xc7c2ad6, 0x63c56c99, 0x8f4222c7, 0xb858a87e, 0xb525eaf5, 0x254c9c6]; - let mut scalar_u32 = [0u32; 12]; - let mut ignored: u32 = 0; - - let mut j; - let mut k: u32; - for i in 0..48 { - j = i / 4; - k = (i % 4) as u32; - scalar_u32[j as usize] |= (scalar[i] as u32) << (8 * k); - } - - let mut borrow: u32 = 0; - - for i in 0..12 { - assign!{(borrow, ignored) = subc(borrow, three238[i], scalar_u32[i])}; - } - let mask = (0 as u32).wrapping_sub(borrow); - *result = mask; -} - -#[inline(always)] -pub fn mulby3(scalar: &mut [u8; 48]) { - let mut scalar_u32 = [0u32; 12]; - - let mut j; - let mut k: u32; - for i in 0..48 { - j = i / 4; - k = (i % 4) as u32; - scalar_u32[j as usize] |= (scalar[i] as u32) << (8 * k); - } - - let mut carry: u32 = 0; - let temp = scalar_u32; - for i in 0..12 { - assign!{(carry, scalar_u32[i]) = addc(carry, scalar_u32[i], temp[i])}; - } - for i in 0..12 { - assign!{(carry, scalar_u32[i]) = addc(carry, scalar_u32[i], temp[i])}; - } - - for i in 0..48 { - j = i / 4; - k = (i % 4) as u32; - scalar[i as usize] = (scalar_u32[j as usize] >> (8 * k)) as u8; - } -} - -#[derive(Copy, Clone)] -pub struct Fp751Element(pub (crate) [u32; FP751_NUM_WORDS]); - -#[cfg(test)] -pub struct Fp751ElementDist; - -impl ConditionallySelectable for Fp751Element { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - let mut bytes = Fp751Element::zero(); - for i in 0..FP751_NUM_WORDS { - bytes.0[i] = u32::conditional_select(&a.0[i], &b.0[i], choice); - } - bytes - } - - fn conditional_assign(&mut self, f: &Self, choice: Choice) { - let mask = ((choice.unwrap_u8() as i32).neg()) as u32; - for i in 0..FP751_NUM_WORDS { - self.0[i] ^= mask & (self.0[i] ^ f.0[i]); - } - } -} - -impl Debug for Fp751Element { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Fp751Element({:?})", &self.0[..]) - } -} - -#[cfg(test)] -impl Arbitrary for Fp751Element { - fn arbitrary(g: &mut Gen) -> Fp751Element { - // Generation strategy: low limbs taken from [0,2^64), high limb - // taken from smaller range. - // - // Field elements taken in range [0,2p). Emulate this by capping - // the high limb by the top digit of 2*p-1: - // - // sage: (2*p-1).digits(2^32)[-1] - // 57291 - // - // This still allows generating values >= 2p, but hopefully that - // excess is small. - let mut rng = SecureRandom::get(); - let high_limb = rng.next_u32() % 57291; - - Fp751Element([ - rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), - rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), - rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), - rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), - rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), - rng.next_u32(), rng.next_u32(), rng.next_u32(), high_limb - ]) - } -} - -impl Fp751Element { - /// Construct a new zero `Fp751Element`. - #[inline(always)] - pub fn zero() -> Fp751Element { - Fp751Element([0_u32; FP751_NUM_WORDS]) - } - - /// Given an `Fp751Element` in Montgomery form, convert to little-endian bytes. - pub fn to_bytes(&self) -> [u8; 94] { - let mut bytes = [0u8; 94]; - let mut a = Fp751Element::zero(); - #[allow(non_snake_case)] - let mut aR = Fp751X2::zero(); - - aR.0[..FP751_NUM_WORDS].clone_from_slice(&self.0); - a = aR.reduce(); // = a mod p in [0, 2p) - a = a.strong_reduce(); // = a mod p in [0, p) - - let mut j; - let mut k: u32; - // 4*24 = 96, but we drop the last two bytes since p is 751 < 752=94*8 bits. - for i in 0..94 { - j = i / 4; - k = (i % 4) as u32; - bytes[i as usize] = (a.0[j as usize] >> (8 * k)) as u8; - } - bytes - } - - /// Read an `Fp751Element` from little-endian bytes and convert to Montgomery form. - pub fn from_bytes(bytes: &[u8]) -> Fp751Element { - assert!(bytes.len() >= 94, "Too short input to Fp751Element from_bytes, expected 94 bytes"); - - let mut a = Fp751Element::zero(); - let mut j; - let mut k: u32; - for i in 0..94 { - j = i / 4; - k = (i % 4) as u32; - // Rust indexes are of type usize. - a.0[j as usize] |= (bytes[i as usize] as u32) << (8 * k); - } - - #[allow(non_snake_case)] - let aRR = &a * &MONTGOMERY_RSQ; // = a*R*R - let output = aRR.reduce(); // = a*R mod p - output - } -} - -#[derive(Copy, Clone)] -pub struct Fp751X2(pub (crate) [u32; 2*FP751_NUM_WORDS]); - -impl Debug for Fp751X2 { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Fp751X2({:?})", &self.0[..]) - } -} - -impl Fp751X2 { - // Construct a zero `Fp751X2`. - #[inline(always)] - pub fn zero() -> Fp751X2 { - Fp751X2([0_u32; { 2 * FP751_NUM_WORDS }]) - } -} - -/// `(2^768) mod p` -//pub const MONTGOMERY_R: Fp751Element = Fp751Element([149933, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2198863872, 928803942, 1428664804, 1062151376, 1769445311, 2891730478, 3365779378, 3523701078, 1285862457, 1964165097, 284660332, 616359394, 11611]); - -/// `(2^768)^2 mod p` -pub const MONTGOMERY_RSQ: Fp751Element = Fp751Element([2645377112, 590366276, 2794865962, 3674276193, 1927544206, 1580635156, 2191714054, 4094426656, 2421131089, 1228065960, 518519937, 527654687, 3238301208, 2723106176, 3451258821, 3043768380, 1935645840, 1142805627, 1785382954, 1450437932, 288500043, 113837350, 2198806325, 16813]); diff --git a/attic/sidhp751/isogeny.rs b/attic/sidhp751/isogeny.rs deleted file mode 100644 index 28b1b1f0b..000000000 --- a/attic/sidhp751/isogeny.rs +++ /dev/null @@ -1,394 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -//! This module contains internal isogeny representation and operations -//! for SIDH, which is not part of the public API. - -use crate::sidhp751::field::ExtensionFieldElement; -use crate::sidhp751::curve::{ProjectiveCurveParameters, ProjectivePoint}; - -/// Represents a 3-isogeny phi, holding the data necessary to evaluate phi. -#[allow(non_snake_case)] -#[derive(Copy, Clone)] -pub struct ThreeIsogeny { - pub X: ExtensionFieldElement, - pub Z: ExtensionFieldElement, -} - -#[allow(non_snake_case)] -impl ThreeIsogeny { - /// Given a three-torsion point `x3 = x(P_3)` on the curve `E_(A:C)`, construct the - /// three-isogeny `phi : E_(A:C) -> E_(A:C)/ = E_(A':C')`. - /// - /// Returns a tuple `(codomain, isogeny) = (E_(A':C'), phi)`. - pub fn compute_three_isogeny(x3: &ProjectivePoint) -> (ProjectiveCurveParameters, ThreeIsogeny) { - let isogeny = ThreeIsogeny{ X: x3.X, Z: x3.Z }; - // We want to compute - // (A':C') = (Z^4 + 18X^2Z^2 - 27X^4 : 4XZ^3) - // To do this, use the identity 18X^2Z^2 - 27X^4 = 9X^2(2Z^2 - 3X^2) - let mut v1 = x3.X.square(); // = X^2 - let mut v0 = &(&v1 + &v1) + &v1; // = 3X^2 - v1 = &(&v0 + &v0) + &v0; // = 9X^2 - let mut v2 = x3.Z.square(); // = Z^2 - let v3 = v2.square(); // = Z^4 - v2 = &v2 + &v2; // = 2Z^2 - v0 = &v2 - &v0; // = 2Z^2 - 3X^2 - v1 = &v1 * &v0; // = 9X^2(2Z^2 - 3X^2) - v0 = &x3.X * &x3.Z; // = XZ - v0 = &v0 + &v0; // = 2XZ - let a = &v3 + &v1; // = Z^4 + 9X^2(2Z^2 - 3X^2) - let c = &v0 * &v2; // = 4XZ^3 - let codomain = ProjectiveCurveParameters{ A: a, C: c }; - - (codomain, isogeny) - } - - /// Given a 3-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate - /// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`. - /// - /// The output `xQ = x(Q)` is then a point on the curve `E_(A':C')`; the curve - /// parameters are returned by the compute_three_isogeny function used to construct - /// phi. - pub fn eval(&self, xP: &ProjectivePoint) -> ProjectivePoint { - let phi = *self; - let mut t0 = &phi.X * &xP.X; // = X3*XP - let mut t1 = &phi.Z * &xP.Z; // = Z3*XP - let mut t2 = &t0 - &t1; // = X3*XP - Z3*ZP - t0 = &phi.Z * &xP.X; // = Z3*XP - t1 = &phi.X * &xP.Z; // = X3*ZP - t0 = &t0 - &t1; // = Z3*XP - X3*ZP - t2 = t2.square(); // = (X3*XP - Z3*ZP)^2 - t0 = t0.square(); // = (Z3*XP - X3*ZP)^2 - let x = &t2 * &xP.X; // = XP*(X3*XP - Z3*ZP)^2 - let z = &t0 * &xP.Z; // = ZP*(Z3*XP - X3*ZP)^2 - let xQ = ProjectivePoint{ X: x, Z: z }; - - xQ - } -} - -/// Represents a 4-isogeny phi, holding the data necessary to evaluate phi. -// -// See compute_four_isogeny for more details. -#[allow(non_snake_case)] -#[derive(Copy, Clone)] -pub struct FourIsogeny { - pub Xsq_plus_Zsq : ExtensionFieldElement, - pub Xsq_minus_Zsq: ExtensionFieldElement, - pub XZ2 : ExtensionFieldElement, - pub Xpow4 : ExtensionFieldElement, - pub Zpow4 : ExtensionFieldElement, -} - -#[allow(non_snake_case)] -impl FourIsogeny { - /// Given a four-torsion point `x4 = x(P_4)` on the curve `E_(A:C)`, compute the - /// coefficients of the codomain `E_(A':C')` of the four-isogeny `phi : E_(A:C) -> - /// E_(A:C)/`. - /// - /// Returns a tuple `(codomain, isogeny) = (E_(A':C') : phi)`. - // - // There are two sets of formulas in Costello-Longa-Naehrig for computing - // four-isogenies. One set is for the case where (1,...) lies in the kernel of - // the isogeny (this is the FirstFourIsogeny), and the other (this set) is for - // the case that (1,...) is *not* in the kernel. - pub fn compute_four_isogeny(x4: &ProjectivePoint) -> (ProjectiveCurveParameters, FourIsogeny) { - let mut v0 = x4.X.square(); // = X4^2 - let v1 = x4.Z.square(); // = Z4^2 - let Xsq_plus_Zsq = &v0 + &v1; // = X4^2 + Z4^2 - let Xsq_minus_Zsq = &v0 - &v1; // = X4^2 - Z4^2 - let mut XZ2 = &x4.X + &x4.Z; // = X4 + Z4 - XZ2 = XZ2.square(); // = X4^2 + Z4^2 + 2X4Z4 - XZ2 = &XZ2 - &Xsq_plus_Zsq; // = 2X4Z4 - let Xpow4 = v0.square(); // = X4^4 - let Zpow4 = v1.square(); // = Z4^4 - v0 = &Xpow4 + &Xpow4; // = 2X4^4 - v0 = &v0 - &Zpow4; // = 2X4^4 - Z4^4 - let a = &v0 + &v0; // = 2(2X4^4 - Z4^4) - let c = Zpow4; // = Z4^4 - - let codomain = ProjectiveCurveParameters{ A: a, C: c }; - let isogeny = FourIsogeny{ - Xsq_plus_Zsq, - Xsq_minus_Zsq, - XZ2, - Xpow4, - Zpow4 - }; - - (codomain, isogeny) - } - - /// Given a 4-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate - /// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`. - // - // The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve - // parameters are returned by the compute_four_isogeny function used to construct - // phi. - pub fn eval(&self, xP: &ProjectivePoint) -> ProjectivePoint { - let phi = *self; - // We want to compute formula (7) of Costello-Longa-Naehrig, namely - // - // Xprime = (2*X_4*Z*Z_4 - (X_4^2 + Z_4^2)*X)*(X*X_4 - Z*Z_4)^2*X - // Zprime = (2*X*X_4*Z_4 - (X_4^2 + Z_4^2)*Z)*(X_4*Z - X*Z_4)^2*Z - // - // To do this we adapt the method in the MSR implementation, which computes - // - // X_Q = Xprime*( 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 ) - // Z_Q = Zprime*( 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 ) - // - let mut t0 = &xP.X * &phi.XZ2; // = 2*X*X_4*Z_4 - let mut t1 = &xP.Z * &phi.Xsq_plus_Zsq; // = (X_4^2 + Z_4^2)*Z - t0 = &t0 - &t1; // = -X_4^2*Z + 2*X*X_4*Z_4 - Z*Z_4^2 - t1 = &xP.Z * &phi.Xsq_minus_Zsq; // = (X_4^2 - Z_4^2)*Z - let mut t2 = (&t0 - &t1).square(); // = 4*(X_4*Z - X*Z_4)^2*X_4^2 - t0 = &t0 * &t1; - t0 = &t0 + &t0; - t0 = &t0 + &t0; // = 4*(2*X*X_4*Z_4 - (X_4^2 + Z_4^2)*Z)*(X_4^2 - Z_4^2)*Z - t1 = &t0 + &t2; // = 4*(X*X_4 - Z*Z_4)^2*Z_4^2 - t0 = &t0 * &t2; // = Zprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2 - let z = &t0 * &phi.Zpow4; // = Zprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 - t2 = &t2 * &phi.Zpow4; // = 4*(X_4*Z - X*Z_4)^2*X_4^2*Z_4^4 - t0 = &t1 * &phi.Xpow4; // = 4*(X*X_4 - Z*Z_4)^2*X_4^4*Z_4^2 - t0 = &t2 - &t0; // = -4*(X*X_4^2 - 2*X_4*Z*Z_4 + X*Z_4^2)*X*(X_4^2 - Z_4^2)*X_4^2*Z_4^2 - let x = &t1 * &t0; // = Xprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 - let xQ = ProjectivePoint{ X: x, Z: z }; - - xQ - } -} - -/// Represents a 4-isogeny phi. -// -// See compute_four_isogeny for details. -#[allow(non_snake_case)] -#[derive(Copy, Clone)] -pub struct FirstFourIsogeny { - pub A: ExtensionFieldElement, - pub C: ExtensionFieldElement, -} - -#[allow(non_snake_case)] -impl FirstFourIsogeny { - /// Compute the "first" four-isogeny from the given curve. - // - // See also compute_four_isogeny and Costello-Longa-Naehrig for more details. - pub fn compute_first_four_isogeny(domain: &ProjectiveCurveParameters) -> (ProjectiveCurveParameters, FirstFourIsogeny) { - let mut t0 = &domain.C + &domain.C; // = 2*C - let c = &domain.A - &t0; // = A - 2*C - let mut t1 = &t0 + &t0; // = 4*C - t1 = &t1 + &t0; // = 6*C - t0 = &t1 + &domain.A; // = A + 6*C - let a = &t0 + &t0; // = 2*(A + 6*C) - - let codomain = ProjectiveCurveParameters{ A: a, C: c }; - let isogeny = FirstFourIsogeny{ A: domain.A, C: domain.C }; - - (codomain, isogeny) - } - - /// Given a 4-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate - /// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`. - // - // The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve - // parameters are returned by the compute_first_four_isogeny function used to - // construct phi. - pub fn eval(&self, xP: &ProjectivePoint) -> ProjectivePoint { - let phi = *self; - let mut t0 = (&xP.X + &xP.Z).square(); // = (X+Z)^2 - let t2 = &xP.X * &xP.Z; // = X*Z - let mut t1 = &t2 + &t2; // = 2*X*Z - t1 = &t0 - &t1; // = X^2 + Z^2 - let mut x = &phi.A * &t2; // = A*X*Z - let t3 = &phi.C * &t1; // = C*(X^2 + Z^2) - x = &x + &t3; // = A*X*Z + C*(X^2 + Z^2) - x = &x * &t0; // = (X+Z)^2 * (A*X*Z + C*(X^2 + Z^2)) - t0 = (&xP.X - &xP.Z).square(); // = (X-Z)^2 - t0 = &t0 * &t2; // = X*Z*(X-Z)^2 - t1 = &phi.C + &phi.C; // = 2*C - t1 = &t1 - &phi.A; // = 2*C - A - let z = &t1 * &t0; // = (2*C - A)*X*Z*(X-Z)^2 - let xQ = ProjectivePoint{ X: x, Z: z }; - - xQ - } -} - -#[allow(non_snake_case)] -#[cfg(test)] -mod test { - use super::*; - use crate::sidhp751::fp::Fp751Element; - - // Test the first four-isogeny from the base curve E_0(F_{p^2}). - #[test] - fn first_four_isogeny_versus_sage() { - // sage: p = 2^372 * 3^239 - 1; Fp = GF(p) - // sage: R. = Fp[] - // sage: Fp2 = Fp.extension(x^2 + 1, 'i') - // sage: i = Fp2.gen() - // sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) - // sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) - // sage: x_PA = 11 - // sage: y_PA = -Fp(11^3 + 11).sqrt() - // sage: x_PB = 6 - // sage: y_PB = -Fp(6^3 + 6).sqrt() - // sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) - // sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) - // sage: def tau(P): - // ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) - // ....: - // sage: m_B = 3*randint(0,3^238) - // sage: m_A = 2*randint(0,2^371) - // sage: R_A = E0Fp2(P_A) + m_A*tau(P_A) - // sage: def y_recover(x, a): - // ....: return (x**3 + a*x**2 + x).sqrt() - // ....: - // sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0)) - // sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point) - // sage: a = Fp2(0) - // sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0])) - // sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A)) - // - let xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x2a95fce9, 0xa179cb7e, 0xa0a892c0, 0xbfd6a0f3, 0x250ab3f3, 0x8b2f0aa4, 0x4118732d, 0x2e7aa4dd, 0x93acbc2a, 0x627969e4, 0xc7b8cc83, 0x21a5b852, 0x586324f2, 0x26084278, 0x5aa947c0, 0x383be1aa, 0xb5c0183e, 0xc6558ecb, 0x6a52b035, 0xf1f19208, 0x5b865c1b, 0x4c58b75, 0xceea2d2c, 0x67b4]), - B: Fp751Element([0x797fecbf, 0xfceb02a2, 0x21f95e99, 0x3fee9e1d, 0x6024e166, 0xa1c4ce89, 0x54517358, 0xc09c0242, 0xb17b94e7, 0xf0255994, 0xb41ee894, 0xa4834359, 0xb7ebefbe, 0x9487f7d, 0xa0bf1f24, 0x3bbeeb34, 0x514c6a05, 0xfa7e5533, 0x46450a9a, 0x92b03281, 0xfada4c06, 0xfde71ca3, 0xf995c2bd, 0x3610]) - }); - - let sage_isogenized_xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x78da1e05, 0xff99e76f, 0xbb8d97c4, 0xdaa36bd2, 0xa409daf, 0xb4328cee, 0x80c5da3f, 0xc28b0999, 0xcfebb852, 0xf2d7cd15, 0xded6cdef, 0x1935103d, 0xde1429c3, 0xade81528, 0x90a64319, 0x6775b0fa, 0xee52485d, 0x25f89817, 0x848e697, 0x706e2d0, 0x216d65c0, 0xc4958ec4, 0x9681417f, 0xc51]), - B: Fp751Element([0xe60e1fb9, 0x742fe7dd, 0x466a456b, 0x801a3c78, 0x86f48c35, 0xa9f945b7, 0xb144348f, 0x20ce89e1, 0x7776217e, 0xf633970b, 0xb38976e5, 0x4c6077a9, 0x766c7825, 0x34a513fc, 0x59b9cd65, 0xacccba3, 0xf0fd0125, 0xd0ca8383, 0x7196287a, 0x7735043, 0x6d4ea21, 0x9fe1ad77, 0x129ee42d, 0x4d26]) - }); - - let curve_params = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; - - let (_, phi) = FirstFourIsogeny::compute_first_four_isogeny(&curve_params); - let isogenized_xR = phi.eval(&xR); - - assert!(sage_isogenized_xR.vartime_eq(&isogenized_xR), - "\nExpected\n{:?}\nfound\n{:?}", sage_isogenized_xR.to_affine(), isogenized_xR.to_affine()); - } - - #[test] - fn four_isogeny_versus_sage() { - // sage: p = 2^372 * 3^239 - 1; Fp = GF(p) - // *** Warning: increasing stack size to 2000000. - // *** Warning: increasing stack size to 4000000. - // sage: R. = Fp[] - // sage: Fp2 = Fp.extension(x^2 + 1, 'i') - // sage: i = Fp2.gen() - // sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) - // sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) - // sage: x_PA = 11 - // sage: y_PA = -Fp(11^3 + 11).sqrt() - // sage: x_PB = 6 - // sage: y_PB = -Fp(6^3 + 6).sqrt() - // sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) - // sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) - // sage: def tau(P): - // ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) - // ....: - // sage: m_B = 3*randint(0,3^238) - // sage: m_A = 2*randint(0,2^371) - // sage: R_A = E0Fp2(P_A) + m_A*tau(P_A) - // sage: def y_recover(x, a): - // ....: return (x**3 + a*x**2 + x).sqrt() - // ....: - // sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0)) - // sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point) - // sage: a = Fp2(0) - // sage: E1A = EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0]) - // sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(E1A) - // sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A)) - // sage: P_4 = (2**(372-4))*isogenized_R_A - // sage: P_4._order = 4 #otherwise falls back to generic group methods for order - // sage: X4, Z4 = P_4.xy()[0], 1 - // sage: phi4 = EllipticCurveIsogeny(E1A, P_4, None, 4) - // sage: E2A_sage = phi4.codomain() # not in monty form - // sage: Aprime, Cprime = 2*(2*X4^4 - Z4^4), Z4^4 - // sage: E2A = EllipticCurve(Fp2, [0,Aprime/Cprime,0,1,0]) - // sage: sage_iso = E2A_sage.isomorphism_to(E2A) - // sage: isogenized2_R_A = sage_iso(phi4(isogenized_R_A)) - // - let xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x78da1e05, 0xff99e76f, 0xbb8d97c4, 0xdaa36bd2, 0xa409daf, 0xb4328cee, 0x80c5da3f, 0xc28b0999, 0xcfebb852, 0xf2d7cd15, 0xded6cdef, 0x1935103d, 0xde1429c3, 0xade81528, 0x90a64319, 0x6775b0fa, 0xee52485d, 0x25f89817, 0x848e697, 0x706e2d0, 0x216d65c0, 0xc4958ec4, 0x9681417f, 0xc51]), - B: Fp751Element([0xe60e1fb9, 0x742fe7dd, 0x466a456b, 0x801a3c78, 0x86f48c35, 0xa9f945b7, 0xb144348f, 0x20ce89e1, 0x7776217e, 0xf633970b, 0xb38976e5, 0x4c6077a9, 0x766c7825, 0x34a513fc, 0x59b9cd65, 0xacccba3, 0xf0fd0125, 0xd0ca8383, 0x7196287a, 0x7735043, 0x6d4ea21, 0x9fe1ad77, 0x129ee42d, 0x4d26]) - }); - - let xP4 = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x13f3d5e7, 0x2afd75a9, 0x6f88c9ab, 0x2918fba0, 0xcb526f05, 0xa4ac4dc7, 0x1a607300, 0x2d19e939, 0x34091b54, 0x7a79e2b, 0xb42f1792, 0x3ad809dc, 0x8bd6402a, 0xd4617932, 0x1e2c4f3f, 0x1afa7354, 0xce9bdbd8, 0xf602d73a, 0x6bab7004, 0xd77ac58f, 0x6793b3b3, 0x4689d97f, 0xb00e42b7, 0x4f26]), - B: Fp751Element([0xafdcb890, 0x6cdf918d, 0xc29cfae2, 0x666f273c, 0x1ba618e2, 0xad00fcd3, 0xef2f6a33, 0x5fbcf62b, 0x318e5098, 0xf408bb88, 0x9453d175, 0x84ab9784, 0xcfb8e1ac, 0x501bbfcd, 0xe6b5542c, 0xf2370098, 0xf0f6bd32, 0xc7dc73f5, 0x6729d1cf, 0xdd76dcd8, 0x29996e4, 0xca22c905, 0xa9373de3, 0x5cf4]) - }); - - let sage_isogenized_xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0xd0b7a01e, 0x111efd8b, 0x3789ca9b, 0x6ab75a4f, 0x8564cac4, 0x939dbe51, 0x601d0434, 0xf9eeaba1, 0x6edac998, 0x8d41f8ba, 0xfe9aa170, 0xfcd2557e, 0x98b7844, 0xb3c3549c, 0x6f81127c, 0x52874fef, 0xaa518bb3, 0xb2b9ac82, 0x30520a86, 0xee708202, 0x5efb184a, 0xd4012b7f, 0x4536329b, 0x573e]), - B: Fp751Element([0x1e932902, 0xa9995228, 0x71f2c7b1, 0x569a89a5, 0x46ba3f6b, 0x61501438, 0x41e91430, 0x11fd2044, 0x55c9b07b, 0x7f469bd, 0xde35b161, 0xb72db8b9, 0xa940512a, 0x455a9a37, 0xabaf906, 0xb0cff767, 0x583375fe, 0x18c785b7, 0x403c9148, 0x603ab9ca, 0x6e6c62c1, 0xab54ba3a, 0xd7d57c4f, 0x2726]) - }); - - let (_, phi) = FourIsogeny::compute_four_isogeny(&xP4); - let isogenized_xR = phi.eval(&xR); - - assert!(sage_isogenized_xR.vartime_eq(&isogenized_xR), - "\nExpected\n{:?}\nfound\n{:?}", sage_isogenized_xR.to_affine(), isogenized_xR.to_affine()); - } - - #[test] - fn three_isogeny_versus_sage() { - // sage: %colors Linux - // sage: p = 2^372 * 3^239 - 1; Fp = GF(p) - // *** Warning: increasing stack size to 2000000. - // *** Warning: increasing stack size to 4000000. - // sage: R. = Fp[] - // sage: Fp2 = Fp.extension(x^2 + 1, 'i') - // sage: i = Fp2.gen() - // sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) - // sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) - // sage: x_PA = 11 - // sage: y_PA = -Fp(11^3 + 11).sqrt() - // sage: x_PB = 6 - // sage: y_PB = -Fp(6^3 + 6).sqrt() - // sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) - // sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) - // sage: def tau(P): - // ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) - // ....: - // sage: m_B = 3*randint(0,3^238) - // sage: R_B = E0Fp2(P_B) + m_B*tau(P_B) - // sage: P_3 = (3^238)*R_B - // sage: def three_isog(P_3, P): - // ....: X3, Z3 = P_3.xy()[0], 1 - // ....: XP, ZP = P.xy()[0], 1 - // ....: x = (XP*(X3*XP - Z3*ZP)^2)/(ZP*(Z3*XP - X3*ZP)^2) - // ....: A3, C3 = (Z3^4 + 9*X3^2*(2*Z3^2 - 3*X3^2)), 4*X3*Z3^3 - // ....: cod = EllipticCurve(Fp2, [0,A3/C3,0,1,0]) - // ....: return cod.lift_x(x) - // ....: - // sage: isogenized_R_B = three_isog(P_3, R_B) - // - let xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x5cc9a3d7, 0xbd0737ed, 0x6517c101, 0x45ae6d47, 0x7364fdb2, 0x6f228e9e, 0x225b3dbd, 0xbba4871, 0xe5da1a07, 0x6299ccd2, 0xaf5f2d0e, 0x38488fe4, 0xa86e980c, 0xec23cae5, 0x3f1edffa, 0x26c804ba, 0x32df60e5, 0xfbbed819, 0x82ae9187, 0x7e00e9d1, 0x66d05f4b, 0xc7654abb, 0x567237b, 0x262d]), - B: Fp751Element([0xd0b2ac33, 0x3a3b5b6a, 0x179127d3, 0x246602b5, 0xad65077d, 0x502ae0e9, 0x37e1bf70, 0x10a3a372, 0x4dd05610, 0x4a1ab929, 0x30fe1fa6, 0xb0f3adac, 0x7faf70cb, 0x34199526, 0x39cf4ec1, 0xa14dd94d, 0xd1bf5568, 0xce4b7527, 0xed45c7e4, 0xe0410423, 0xb6425686, 0x38011809, 0x2472ebed, 0x28f5]) - }); - - let xP3 = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x7b0788dc, 0x7bb7a4a0, 0x607b21b0, 0xdc36a3f6, 0xe74cf2f0, 0x4750e18e, 0xb7ab806, 0x464e319d, 0x4f758ff, 0xc25aa44c, 0xa46e0a68, 0x392e8521, 0x3eff37df, 0xfc4e76b6, 0x92e67dd8, 0x1f3566d8, 0x73295e65, 0xf8d2eb0f, 0xc470bccb, 0x457b13eb, 0xfef5be33, 0xfda1cc9e, 0x3d92cc02, 0x5dbf]), - B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - }); - - let sage_isogenized_xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ - A: Fp751Element([0x5913c5b1, 0x286db7d7, 0x50189220, 0xcb2049ad, 0x765fa9f4, 0xccee90ef, 0x730e7d88, 0x65e52ce2, 0xbd0d06e7, 0xa6b6b553, 0x14591590, 0xb561ecec, 0x8c64d959, 0x17b7a66d, 0xcbe1461e, 0x77778ce, 0xc41a57ce, 0x9405c9c0, 0xe8ca7d3d, 0x8f6b4847, 0x7b366937, 0xf625eb98, 0x3590e345, 0x421b]), - B: Fp751Element([0x3e7d8d6, 0x566b8938, 0xd527e696, 0xe8c71a04, 0x7bf5eb51, 0x5a1d8f8, 0xe098724f, 0x42ae08a, 0xaf40ca2e, 0x4ee3d7c7, 0x67bb10a7, 0xd9f9ab90, 0xedd6328c, 0xecd53d69, 0x2dea107d, 0xa581e920, 0x8ecf9257, 0x8bcdfb6c, 0x5cbcf2af, 0xe7cbbc2e, 0x1f0e53e, 0x5f031a87, 0x2d93e3cb, 0x1831]) - }); - - let (_, phi) = ThreeIsogeny::compute_three_isogeny(&xP3); - let isogenized_xR = phi.eval(&xR); - - assert!(sage_isogenized_xR.vartime_eq(&isogenized_xR), - "\nExpected\n{:?}\nfound\n{:?}", sage_isogenized_xR.to_affine(), isogenized_xR.to_affine()); - } -} diff --git a/attic/sidhp751/mod.rs b/attic/sidhp751/mod.rs deleted file mode 100644 index 780dfd1d0..000000000 --- a/attic/sidhp751/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -#[allow(non_snake_case)] -#[allow(unused_variables)] -#[allow(unused)] -pub(crate) mod field; -#[allow(non_snake_case)] -#[allow(unused)] -pub(crate) mod curve; -#[allow(non_snake_case)] -pub(crate) mod isogeny; -#[allow(non_snake_case)] -#[allow(unused)] -#[macro_use] -pub(crate) mod fp; - -pub mod constants; -#[allow(unused_assignments)] -#[allow(non_snake_case)] -mod sidh; - -pub use sidh::*; diff --git a/attic/sidhp751/sidh.rs b/attic/sidhp751/sidh.rs deleted file mode 100644 index 9457ce950..000000000 --- a/attic/sidhp751/sidh.rs +++ /dev/null @@ -1,704 +0,0 @@ -// This file is part of sidh-rs. -// Copyright (c) 2017 Erkan Tairi -// See LICENSE for licensing information. -// -// Author: -// - Erkan Tairi -// - -use crate::sidhp751::field::ExtensionFieldElement; -use crate::sidhp751::curve::{ProjectiveCurveParameters, ProjectivePoint}; -use crate::sidhp751::isogeny::*; -use crate::sidhp751::constants::*; -use crate::sidhp751::fp::*; -#[allow(unused_imports)] -use crate::random::SecureRandom; - -use heapless::Vec; - -use std::fmt::Debug; -use std::ops::Neg; - -use rand_core::{RngCore, CryptoRng}; - -#[cfg(test)] -use quickcheck::{Arbitrary, Gen, QuickCheck}; - -/// The secret key size, in bytes. -pub const SIDH_P751_SECRET_KEY_SIZE: usize = 48; -/// The public key size, in bytes. -pub const SIDH_P751_PUBLIC_KEY_SIZE: usize = 564; -/// The shared secret size, in bytes. -pub const SIDH_P751_SHARED_SECRET_SIZE: usize = 188; - -const MAX_INT_POINTS_ALICE: usize = 8; -const MAX_INT_POINTS_BOB: usize = 10; - -const MAX_ALICE: usize = 185; -/// Alice's isogeny strategy. -pub const ALICE_ISOGENY_STRATEGY: [u8; MAX_ALICE] = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, - 6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11, 12, 12, 13, 14, 15, 16, 16, 16, 16, - 16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21, 20, 21, 21, 21, 21, 21, 22, 25, 25, - 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 35, 36, - 36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38, 38, 39, 40, 41, 42, 38, 39, 40, 41, - 42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48, 48, 48, 49, 49, 48, 53, 54, 51, 52, - 53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64, - 65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66, 69, 70, 66, 67, 66, 69, 70, 69, 70, - 70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72, 74, 74, 75, 72, 72, 74, 75, 75, 72, - 72, 74, 75, 75, 77, 77, 79, 80, 80, 82]; - - -const MAX_BOB: usize = 239; -/// Bob's isogeny strategy. -pub const BOB_ISOGENY_STRATEGY: [u8; MAX_BOB] = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, - 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, - 16, 16, 16, 16, 17, 16, 16, 17, 19, 19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 24, 24, 25, 27, 27, 28, 28, 29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, - 30, 33, 33, 33, 33, 34, 35, 37, 37, 37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, - 43, 38, 38, 38, 38, 43, 40, 41, 42, 43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, - 51, 50, 49, 49, 49, 49, 51, 49, 53, 50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, - 56, 56, 56, 56, 58, 58, 61, 61, 61, 63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, - 65, 66, 66, 66, 66, 66, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, - 73, 68, 68, 71, 71, 73, 73, 73, 75, 75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, - 84, 85, 86, 86, 86, 86, 86, 87, 86, 88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, - 88, 88, 86, 86, 86, 93, 90, 90, 92, 92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, - 97, 97]; - -/// Alice's public key. -#[allow(non_snake_case)] -#[derive(Copy, Clone)] -pub struct SIDHPublicKeyAlice { - pub affine_xP : ExtensionFieldElement, - pub affine_xQ : ExtensionFieldElement, - pub affine_xQmP: ExtensionFieldElement, -} - -impl SIDHPublicKeyAlice { - /// Read a public key from a byte slice. The input must be at least 564 bytes long. - #[allow(non_snake_case)] - pub fn from_bytes(bytes: &[u8]) -> SIDHPublicKeyAlice { - assert!(bytes.len() >= 564, "Too short input to SIDH public key from_bytes, expected 564 bytes"); - let affine_xP = ExtensionFieldElement::from_bytes(&bytes[0..188]); - let affine_xQ = ExtensionFieldElement::from_bytes(&bytes[188..376]); - let affine_xQmP = ExtensionFieldElement::from_bytes(&bytes[376..564]); - SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } - } - /// Write a public key to a byte slice. The output will be 564 bytes long. - pub fn to_bytes(&self) -> [u8; 564] { - let mut bytes = [0u8; 564]; - bytes[0..188].clone_from_slice(&self.affine_xP.to_bytes()); - bytes[188..376].clone_from_slice(&self.affine_xQ.to_bytes()); - bytes[376..564].clone_from_slice(&self.affine_xQmP.to_bytes()); - bytes - } -} - -/// Bob's public key. -#[allow(non_snake_case)] -#[derive(Copy, Clone)] -pub struct SIDHPublicKeyBob { - pub affine_xP : ExtensionFieldElement, - pub affine_xQ : ExtensionFieldElement, - pub affine_xQmP: ExtensionFieldElement, -} - -impl SIDHPublicKeyBob { - /// Read a public key from a byte slice. The input must be at least 564 bytes long. - #[allow(non_snake_case)] - pub fn from_bytes(bytes: &[u8]) -> SIDHPublicKeyBob { - assert!(bytes.len() >= 564, "Too short input to SIDH public key from_bytes, expected 564 bytes"); - let affine_xP = ExtensionFieldElement::from_bytes(&bytes[0..188]); - let affine_xQ = ExtensionFieldElement::from_bytes(&bytes[188..376]); - let affine_xQmP = ExtensionFieldElement::from_bytes(&bytes[376..564]); - SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } - } - /// Write a public key to a byte slice. The output will be 564 bytes long. - pub fn to_bytes(&self) -> [u8; 564] { - let mut bytes = [0u8; 564]; - bytes[0..188].clone_from_slice(&self.affine_xP.to_bytes()); - bytes[188..376].clone_from_slice(&self.affine_xQ.to_bytes()); - bytes[376..564].clone_from_slice(&self.affine_xQmP.to_bytes()); - bytes - } -} - -/// Alice's secret key. -#[derive(Copy, Clone)] -pub struct SIDHSecretKeyAlice { - pub scalar: [u8; SIDH_P751_SECRET_KEY_SIZE], -} - -impl Debug for SIDHSecretKeyAlice { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "SIDHSecretKeyAlice(scalar: {:?})", &self.scalar[..]) - } -} - -#[cfg(test)] -impl Arbitrary for SIDHSecretKeyAlice { - fn arbitrary(_g: &mut Gen) -> SIDHSecretKeyAlice { - let mut rng = SecureRandom::get(); - let (_, alice_secret_key) = generate_alice_keypair(&mut rng); - alice_secret_key - } -} - -impl SIDHSecretKeyAlice { - /// Compute the corresponding public key for the given secret key. - #[allow(non_snake_case)] - pub fn public_key(&self) -> SIDHPublicKeyAlice { - let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // = ( x_P : 1) = x(P_B) - let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // - xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_B) - let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PB); // = x(Q_B - P_B) - - let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PA, &AFFINE_Y_PA, &self.scalar[..]); - - // Starting curve has a = 0, so (A:C) = (0,1). - let current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; - let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); - - xP = firstPhi.eval(&xP); - xQ = firstPhi.eval(&xQ); - xQmP = firstPhi.eval(&xQmP); - xR = firstPhi.eval(&xR); - - let mut points: Vec = Vec::new(); - let mut indices: Vec = Vec::new(); - let mut i: usize = 0; - let mut phi: FourIsogeny; - for j in 1..MAX_ALICE { - while i < MAX_ALICE-j { - points.push(xR).unwrap(); - indices.push(i).unwrap(); - let k = ALICE_ISOGENY_STRATEGY[MAX_ALICE-i-j]; - xR = xR.pow2k(¤t_curve, (2*k) as u32); - i = i + k as usize; - } - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; - - for k in 0..points.len() { - points[k] = phi.eval(&points[k]); - } - - xP = phi.eval(&xP); - xQ = phi.eval(&xQ); - xQmP = phi.eval(&xQmP); - - // Pop xR from pointsm and i from indices. - xR = points.pop().unwrap(); - i = indices.pop().unwrap(); - } - - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; - - xP = phi.eval(&xP); - xQ = phi.eval(&xQ); - xQmP = phi.eval(&xQmP); - - let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); - let affine_xP = &xP.X * &invZP; - let affine_xQ = &xQ.X * &invZQ; - let affine_xQmP = &xQmP.X * &invZQmP; - - SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } - } - - /// Compute (Alice's view of) a shared secret using Alice's secret key and Bob's public key. - #[allow(non_snake_case)] - pub fn shared_secret(&self, bob_public: &SIDHPublicKeyBob) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] { - let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP); - let xP = ProjectivePoint::from_affine(&bob_public.affine_xP); - let xQ = ProjectivePoint::from_affine(&bob_public.affine_xQ); - let xQmP = ProjectivePoint::from_affine(&bob_public.affine_xQmP); - let mut xR = ProjectivePoint::right_to_left_ladder(&xP, &xQ, &xQmP, ¤t_curve, &self.scalar[..]); - - let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); - xR = firstPhi.eval(&xR); - - let mut points: Vec = Vec::new(); - let mut indices: Vec = Vec::new(); - let mut i: usize = 0; - let mut phi: FourIsogeny; - for j in 1..MAX_ALICE { - while i < MAX_ALICE-j { - points.push(xR).unwrap(); - indices.push(i).unwrap(); - let k = ALICE_ISOGENY_STRATEGY[MAX_ALICE-i-j]; - xR = xR.pow2k(¤t_curve, (2*k) as u32); - i = i + k as usize; - } - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; - - for k in 0..points.len() { - points[k] = phi.eval(&points[k]); - } - - // Pop xR from pointsm and i from indices. - xR = points.pop().unwrap(); - i = indices.pop().unwrap(); - } - - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; - - let j_inv = current_curve.j_invariant(); - let shared_secret = j_inv.to_bytes(); - shared_secret - } -} - -/// Bob's secret key. -#[derive(Copy, Clone)] -pub struct SIDHSecretKeyBob { - pub scalar: [u8; SIDH_P751_SECRET_KEY_SIZE], -} - -impl Debug for SIDHSecretKeyBob { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "SIDHSecretKeyBob(scalar: {:?})", &self.scalar[..]) - } -} - -#[cfg(test)] -impl Arbitrary for SIDHSecretKeyBob { - fn arbitrary(_g: &mut Gen) -> SIDHSecretKeyBob { - let mut rng = SecureRandom::get(); - let (_, bob_secret_key) = generate_bob_keypair(&mut rng); - bob_secret_key - } -} - -impl SIDHSecretKeyBob { - /// Compute the public key corresponding to the secret key. - #[allow(non_snake_case)] - pub fn public_key(&self) -> SIDHPublicKeyBob { - let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // = ( x_P : 1) = x(P_A) - let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // - xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_A) - let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PA); // = x(Q_B - P_B) - - let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PB, &AFFINE_Y_PB, &self.scalar[..]); - - // Starting curve has a = 0, so (A:C) = (0,1). - let mut current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; - - let mut points: Vec = Vec::new(); - let mut indices: Vec = Vec::new(); - let mut i: usize = 0; - let mut phi: ThreeIsogeny; - for j in 1..MAX_BOB { - while i < MAX_BOB-j { - points.push(xR).unwrap(); - indices.push(i).unwrap(); - let k = BOB_ISOGENY_STRATEGY[MAX_BOB-i-j]; - xR = xR.pow3k(¤t_curve, k as u32); - i = i + k as usize; - } - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; - - for k in 0..points.len() { - points[k] = phi.eval(&points[k]); - } - - xP = phi.eval(&xP); - xQ = phi.eval(&xQ); - xQmP = phi.eval(&xQmP); - - // Pop xR from points and i from indices. - xR = points.pop().unwrap(); - i = indices.pop().unwrap(); - } - - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; - - xP = phi.eval(&xP); - xQ = phi.eval(&xQ); - xQmP = phi.eval(&xQmP); - - let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); - let affine_xP = &xP.X * &invZP; - let affine_xQ = &xQ.X * &invZQ; - let affine_xQmP = &xQmP.X * &invZQmP; - - SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } - } - - /// Compute (Bob's view of) a shared secret using Bob's secret key and Alice's public key. - #[allow(non_snake_case)] - pub fn shared_secret(&self, alice_public: &SIDHPublicKeyAlice) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] { - let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP); - let xP = ProjectivePoint::from_affine(&alice_public.affine_xP); - let xQ = ProjectivePoint::from_affine(&alice_public.affine_xQ); - let xQmP = ProjectivePoint::from_affine(&alice_public.affine_xQmP); - let mut xR = ProjectivePoint::right_to_left_ladder(&xP, &xQ, &xQmP, ¤t_curve, &self.scalar[..]); - - let mut points: Vec = Vec::new(); - let mut indices: Vec = Vec::new(); - let mut i: usize = 0; - let mut phi: ThreeIsogeny; - for j in 1..MAX_BOB { - while i < MAX_BOB-j { - points.push(xR).unwrap(); - indices.push(i).unwrap(); - let k = BOB_ISOGENY_STRATEGY[MAX_BOB-i-j]; - xR = xR.pow3k(¤t_curve, k as u32); - i = i + k as usize; - } - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; - - for k in 0..points.len() { - points[k] = phi.eval(&points[k]); - } - - // Pop xR from points and i from indices. - xR = points.pop().unwrap(); - i = indices.pop().unwrap(); - } - - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; - - let j_inv = current_curve.j_invariant(); - let shared_secret = j_inv.to_bytes(); - shared_secret - } -} - -/// Generate a keypair for "Alice". Note that because this library does not -/// implement SIDH validation, each keypair should be used for at most one -/// shared secret computation. -pub fn generate_alice_keypair(rng: &mut R) -> (SIDHPublicKeyAlice, SIDHSecretKeyAlice) { - let mut scalar = [0u8; SIDH_P751_SECRET_KEY_SIZE]; - rng.fill_bytes(&mut scalar[..]); - - // Bit-twiddle to ensure scalar is in 2*[0,2^371): - scalar[47] = 0; - scalar[46] &= 15; // Clear high bits, so scalar < 2^372. - scalar[0] &= 254; // Clear low bit, so scalar is even. - - // We actually want scalar in 2*(0,2^371), but the above procedure - // generates 0 with probability 2^(-371), which isn't worth checking - // for. - let secret_key = SIDHSecretKeyAlice{ scalar }; - let public_key = secret_key.public_key(); - - (public_key, secret_key) -} - -/// Generate a keypair for "Bob". Note that because this library does not -/// implement SIDH validation, each keypair should be used for at most one -/// shared secret computation. -pub fn generate_bob_keypair(rng: &mut R) -> (SIDHPublicKeyBob, SIDHSecretKeyBob) { - let mut scalar = [0u8; SIDH_P751_SECRET_KEY_SIZE]; - // Perform rejection sampling to obtain a random value in [0,3^238]: - let mut ok: u32 = 1; - for _ in 0..102 { - rng.fill_bytes(&mut scalar[..]); - // Mask the high bits to obtain a uniform value in [0,2^378): - scalar[47] &= 3; - // Accept if scalar < 3^238 (this happens with probability ~0.5828). - checklt238(&scalar, &mut ok); - if ok == 0 { break; } - } - // ok is nonzero if all 102 trials failed. - // This happens with probability 0.41719...^102 < 2^(-128), i.e., never. - if ok != 0 { panic!("All 102 trials failed!"); } - - // Multiply by 3 to get a scalar in 3*[0,3^238): - mulby3(&mut scalar); - - // We actually want scalar in 2*(0,2^371), but the above procedure - // generates 0 with probability 3^(-238), which isn't worth checking - // for. - let secret_key = SIDHSecretKeyBob{ scalar }; - let public_key = secret_key.public_key(); - - (public_key, secret_key) -} - - -#[cfg(test)] -mod test { - use super::*; - #[allow(unused_imports)] - use std::time::SystemTime; - - // Perform Alice's (2-isogeny) key generation, using the slow but simple multiplication-based strategy. - // - // This function just exists to ensure that the fast isogeny-tree strategy works correctly. - #[allow(non_snake_case)] - pub fn alice_keygen_slow(secret_key: &SIDHSecretKeyAlice) -> SIDHPublicKeyAlice { - let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // = ( x_P : 1) = x(P_B) - let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // - xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_B) - let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PB); // = x(Q_B - P_B) - - let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PA, &AFFINE_Y_PA, &secret_key.scalar[..]); - // Starting curve has a = 0, so (A:C) = (0,1). - let current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; - - let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); - - xP = firstPhi.eval(&xP); - xQ = firstPhi.eval(&xQ); - xQmP = firstPhi.eval(&xQmP); - xR = firstPhi.eval(&xR); - - let mut phi: FourIsogeny; - // rev() makes the loop go from 368 down to 0. - for e in (0..(372 - 4 + 1)).rev().step_by(2) { - let xS = xR.pow2k(¤t_curve, e as u32); - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xS)}; - - xR = phi.eval(&xR); - xP = phi.eval(&xP); - xQ = phi.eval(&xQ); - xQmP = phi.eval(&xQmP); - } - - let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); - let affine_xP = &xP.X * &invZP; - let affine_xQ = &xQ.X * &invZQ; - let affine_xQmP = &xQmP.X * &invZQmP; - - SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } - } - - // Perform Bob's (3-isogeny) key generation, using the slow but simple multiplication-based strategy. - // - // This function just exists to ensure that the fast isogeny-tree strategy works correctly. - #[allow(non_snake_case)] - pub fn bob_keygen_slow(secret_key: &SIDHSecretKeyBob) -> SIDHPublicKeyBob { - let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // = ( x_P : 1) = x(P_A) - let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // - xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_A) - let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PA); // = x(Q_B - P_B) - - let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PB, &AFFINE_Y_PB, &secret_key.scalar[..]); - // Starting curve has a = 0, so (A:C) = (0,1). - let mut current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; - - let mut phi: ThreeIsogeny; - // rev() makes the loop go from 238 down to 0. - for e in (0..MAX_BOB).rev() { - let xS = xR.pow3k(¤t_curve, e as u32); - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xS)}; - - xR = phi.eval(&xR); - xP = phi.eval(&xP); - xQ = phi.eval(&xQ); - xQmP = phi.eval(&xQmP); - } - - let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); - let affine_xP = &xP.X * &invZP; - let affine_xQ = &xQ.X * &invZQ; - let affine_xQmP = &xQmP.X * &invZQmP; - - SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } - } - - // Perform Alice's key agreement, using the slow but simple multiplication-based strategy. - // - // This function just exists to ensure that the fast isogeny-tree strategy works correctly. - #[allow(non_snake_case)] - pub fn alice_shared_secret_slow(bob_public: &SIDHPublicKeyBob, alice_secret: &SIDHSecretKeyAlice) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] { - let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP); - let xP = ProjectivePoint::from_affine(&bob_public.affine_xP); - let xQ = ProjectivePoint::from_affine(&bob_public.affine_xQ); - let xQmP = ProjectivePoint::from_affine(&bob_public.affine_xQmP); - - let mut xR = ProjectivePoint::three_point_ladder(&xP, &xQ, &xQmP, ¤t_curve, &alice_secret.scalar[..]); - - let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); - xR = firstPhi.eval(&xR); - - let mut phi: FourIsogeny; - // rev() makes the loop go from 368 down to 2. - for e in (2..(372 - 4 + 1)).rev().step_by(2) { - let xS = xR.pow2k(¤t_curve, e as u32); - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xS)}; - - xR = phi.eval(&xR); - } - - assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; - - let j_inv = current_curve.j_invariant(); - let shared_secret = j_inv.to_bytes(); - shared_secret - } - - // Perform Bob's key agreement, using the slow but simple multiplication-based strategy. - // - // This function just exists to ensure that the fast isogeny-tree strategy works correctly. - #[allow(non_snake_case)] - pub fn bob_shared_secret_slow(alice_public: &SIDHPublicKeyAlice, bob_secret: &SIDHSecretKeyBob) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] { - let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP); - let xP = ProjectivePoint::from_affine(&alice_public.affine_xP); - let xQ = ProjectivePoint::from_affine(&alice_public.affine_xQ); - let xQmP = ProjectivePoint::from_affine(&alice_public.affine_xQmP); - - let mut xR = ProjectivePoint::three_point_ladder(&xP, &xQ, &xQmP, ¤t_curve, &bob_secret.scalar[..]); - - let mut phi: ThreeIsogeny; - // rev() makes the loop go from 239 down to 1. - for e in (1..MAX_BOB).rev() { - let xS = xR.pow3k(¤t_curve, e as u32); - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xS)}; - - xR = phi.eval(&xR); - } - - assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; - - let j_inv = current_curve.j_invariant(); - let shared_secret = j_inv.to_bytes(); - shared_secret - } - - #[test] - fn multiply_by_three() { - // sage: repr((3^238 -1).digits(256)) - let mut three238_minus1: [u8; 48] = [248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; - // sage: repr((3*(3^238 -1)).digits(256)) - let three_times_three238_minus1: [u8; 48] = [232, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109, 135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173, 123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6]; - - mulby3(&mut three238_minus1); - - assert!(three238_minus1.iter().zip(three_times_three238_minus1.iter()).all(|(a, b)| a == b), - "\nExpected\n{:?}\nfound\n{:?}", &three_times_three238_minus1[..], &three238_minus1[..]); - } - - #[test] - fn check_less_than_three238() { - let three238_minus1: [u8; 48] = [248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; - let three238: [u8; 48] = [249, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; - let three238_plus1: [u8; 48] = [250, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; - - let mut result: u32 = 57; - - checklt238(&three238_minus1, &mut result); - assert_eq!(result, 0, "\nExpected 0, got {}", result); - - checklt238(&three238, &mut result); - assert_ne!(result, 0, "\nExpected nonzero, got {}", result); - - checklt238(&three238_plus1, &mut result); - assert_ne!(result, 0, "\nExpected nonzero, got {}", result); - } - - #[test] - fn ephemeral_shared_secret() { - fn shared_secrets_match(alice_secret: SIDHSecretKeyAlice, bob_secret: SIDHSecretKeyBob) -> bool { - let start = SystemTime::now(); - let alice_public = alice_secret.public_key(); - println!("test and bench: generate alice_public from alice_secret: {}ms", SystemTime::now().duration_since(start).unwrap().as_secs_f64() * 1000.0); - let start = SystemTime::now(); - let bob_public = bob_secret.public_key(); - println!("test and bench: generate bob_public from bob_secret: {}ms", SystemTime::now().duration_since(start).unwrap().as_secs_f64() * 1000.0); - - let start = SystemTime::now(); - let alice_shared_secret = alice_secret.shared_secret(&bob_public); - println!("test and bench: generate alice_shared_secret: {}ms", SystemTime::now().duration_since(start).unwrap().as_secs_f64() * 1000.0); - let start = SystemTime::now(); - let bob_shared_secret = bob_secret.shared_secret(&alice_public); - println!("test and bench: generate bob_shared_secret: {}ms", SystemTime::now().duration_since(start).unwrap().as_secs_f64() * 1000.0); - - alice_shared_secret.iter().zip(bob_shared_secret.iter()).all(|(a, b)| a == b) - } - QuickCheck::new().max_tests(8).quickcheck(shared_secrets_match as fn(SIDHSecretKeyAlice, SIDHSecretKeyBob) -> bool); - } - - #[allow(non_snake_case)] - #[test] - fn alice_keygen_fast_vs_slow() { - // m_A = 2*randint(0,2^371) - let m_A: [u8; 48] = [248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0]; - - let alice_secret_key = SIDHSecretKeyAlice{ scalar: m_A }; - let fast_pubkey = alice_secret_key.public_key(); - let slow_pubkey = alice_keygen_slow(&alice_secret_key); - - assert!(fast_pubkey.affine_xP.vartime_eq(&slow_pubkey.affine_xP), - "\nExpected affine_xP = {:?}\nfound {:?}", fast_pubkey.affine_xP, slow_pubkey.affine_xP); - assert!(fast_pubkey.affine_xQ.vartime_eq(&slow_pubkey.affine_xQ), - "\nExpected affine_xQ = {:?}\nfound {:?}", fast_pubkey.affine_xQ, slow_pubkey.affine_xQ); - assert!(fast_pubkey.affine_xQmP.vartime_eq(&slow_pubkey.affine_xQmP), - "\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP); - } - - #[allow(non_snake_case)] - #[test] - fn bob_keygen_fast_vs_slow() { - // m_B = 3*randint(0,3^238) - let m_B: [u8; 48] = [246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0]; - - let bob_secret_key = SIDHSecretKeyBob{ scalar: m_B }; - let fast_pubkey = bob_secret_key.public_key(); - let slow_pubkey = bob_keygen_slow(&bob_secret_key); - - assert!(fast_pubkey.affine_xP.vartime_eq(&slow_pubkey.affine_xP), - "\nExpected affine_xP = {:?}\nfound {:?}", fast_pubkey.affine_xP, slow_pubkey.affine_xP); - assert!(fast_pubkey.affine_xQ.vartime_eq(&slow_pubkey.affine_xQ), - "\nExpected affine_xQ = {:?}\nfound {:?}", fast_pubkey.affine_xQ, slow_pubkey.affine_xQ); - assert!(fast_pubkey.affine_xQmP.vartime_eq(&slow_pubkey.affine_xQmP), - "\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP); - } - - #[allow(non_snake_case)] - #[test] - fn shared_secret() { - // m_A = 2*randint(0,2^371) - let m_A: [u8; 48] = [248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0]; - // m_B = 3*randint(0,3^238) - let m_B: [u8; 48] = [246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0]; - - let alice_secret = SIDHSecretKeyAlice{ scalar: m_A }; - let bob_secret = SIDHSecretKeyBob{ scalar: m_B }; - - let alice_public = alice_secret.public_key(); - let bob_public = bob_secret.public_key(); - - let alice_shared_secret_slow = alice_shared_secret_slow(&bob_public, &alice_secret); - let alice_shared_secret_fast = alice_secret.shared_secret(&bob_public); - let bob_shared_secret_slow = bob_shared_secret_slow(&alice_public, &bob_secret); - let bob_shared_secret_fast = bob_secret.shared_secret(&alice_public); - - assert!(alice_shared_secret_fast.iter().zip(bob_shared_secret_fast.iter()).all(|(a, b)| a == b), - "\nShared secret (fast) mismatch: Alice has {:?}\nBob has {:?}", &alice_shared_secret_fast[..], &bob_shared_secret_fast[..]); - assert!(alice_shared_secret_slow.iter().zip(bob_shared_secret_slow.iter()).all(|(a, b)| a == b), - "\nShared secret (slow) mismatch: Alice has {:?}\nBob has {:?}", &alice_shared_secret_slow[..], &bob_shared_secret_slow[..]); - assert!(alice_shared_secret_slow.iter().zip(bob_shared_secret_fast.iter()).all(|(a, b)| a == b), - "\nShared secret mismatch: Alice (slow) has {:?}\nBob (fast) has {:?}", &alice_shared_secret_slow[..], &bob_shared_secret_fast[..]); - } - - #[allow(non_snake_case)] - #[test] - fn secret_point() { - // m_A = 2*randint(0,2^371) - let m_A: [u8; 48] = [248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0]; - // m_B = 3*randint(0,3^238) - let m_B: [u8; 48] = [246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0]; - - let xR_A = ProjectivePoint::secret_point(&AFFINE_X_PA, &AFFINE_Y_PA, &m_A[..]); - let xR_B = ProjectivePoint::secret_point(&AFFINE_X_PB, &AFFINE_Y_PB, &m_B[..]); - - let sage_affine_xR_A = ExtensionFieldElement{ - A: Fp751Element([0x2103d089, 0x29f1dff1, 0x955e0d87, 0x7409b9bf, 0x1cca7288, 0xe812441c, 0xefba55f9, 0xc32b8b13, 0x696d83da, 0xc3b76a80, 0x3a3dc373, 0x185dd4f9, 0x115b6717, 0xfc07c1a9, 0x3b5c4254, 0x39bfcdd6, 0x1d41efd8, 0xc4d097d5, 0x389b21c7, 0x4f893494, 0x1d3d0446, 0x37343321, 0x5ccc3d22, 0x53c3]), - B: Fp751Element([0x33e40815, 0x722e718f, 0xdf715667, 0x8c5fc0f, 0xbbe8c74c, 0x850fd292, 0xfcbf5d3, 0x212938a6, 0xd58dc6e7, 0xfdb2a099, 0x63c9c205, 0x232f83ab, 0xa5543f5e, 0x23eda62f, 0x55d9d04f, 0x49b57588, 0x42ef25d1, 0x6b455e66, 0x37470202, 0x96511625, 0xf2e96ff0, 0xfeced582, 0xe0c0dea8, 0x33a9]) - }; - - let sage_affine_xR_B = ExtensionFieldElement{ - A: Fp751Element([0x6e8499f5, 0xdd4e6607, 0x907519da, 0xe7efddc6, 0xb337108c, 0xe31f9955, 0x79ffc5e1, 0x8e558c54, 0xd776bfc2, 0xfee963ea, 0x5846bf15, 0x33aa04c3, 0x23617a0d, 0xab77d91b, 0x746070e2, 0xbdd70948, 0xc277e942, 0x66f71291, 0x2f901fce, 0x187c39db, 0xd5d32aa2, 0x69262987, 0xb40057dc, 0xe1d]), - B: Fp751Element([0xcfd5c167, 0xd1b766ab, 0xc8a382fa, 0x4591059d, 0x736c223d, 0x1ddf9490, 0xbdf2b3dd, 0xc96db091, 0xc292f502, 0x7b8b9c3d, 0x5e4d3e33, 0xe5b18ad8, 0x6664b931, 0xc3f3479b, 0x299e21e6, 0xa4f17865, 0x32fa1c6e, 0x3f7ef5b3, 0xdab06119, 0x875bedb5, 0xa2e23b93, 0x9b5a06e, 0x8296fb26, 0x43d4]) - }; - - let affine_xR_A = xR_A.to_affine(); - assert!(sage_affine_xR_A.vartime_eq(&affine_xR_A), - "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR_A, affine_xR_A); - - let affine_xR_B = xR_B.to_affine(); - assert!(sage_affine_xR_B.vartime_eq(&affine_xR_B), - "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR_B, affine_xR_B); - } -} \ No newline at end of file