mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 13:33:44 +02:00
clean out attic
This commit is contained in:
parent
e7b82ead9c
commit
ed19fdeb6f
11 changed files with 0 additions and 4044 deletions
261
attic/flatkv.rs
261
attic/flatkv.rs
|
@ -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<Value> for FlatKV {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::KV(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for &'static str {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::S(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for String {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::S(Cow::Owned(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for i64 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::I(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for u64 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::UI(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for isize {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::I(self as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for usize {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::UI(self as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for i32 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::I(self as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for u32 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::UI(self as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for i16 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::I(self as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for u16 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::UI(self as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for i8 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::I(self as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for u8 {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::UI(self as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for bool {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::B(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for crate::vl1::Endpoint {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::Endpoint(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for crate::vl1::InetAddress {
|
||||
#[inline(always)]
|
||||
fn into(self) -> Value {
|
||||
Value::Endpoint(crate::vl1::Endpoint::IpUdp(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> 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<T: Into<Value>>(&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,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<u8> {
|
||||
let mut buf: Vec<u8> = 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<Secret<64>> {
|
||||
let mut k: Option<Secret<64>> = 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<P384PublicKey>,
|
||||
}
|
||||
|
||||
impl HybridPublicKey {
|
||||
pub fn from_bytes(mut b: &[u8]) -> Option<HybridPublicKey> {
|
||||
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 {}
|
|
@ -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
|
||||
<https://github.com/cloudflare/p751sidh>, 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, <https://github.com/Microsoft/PQCrypto-SIDH>, 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
|
|
@ -1,93 +0,0 @@
|
|||
# SIDH-RS [](https://crates.io/crates/sidh) [](https://docs.rs/sidh/) [](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).
|
|
@ -1,25 +0,0 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
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]) };
|
File diff suppressed because it is too large
Load diff
|
@ -1,830 +0,0 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
//! 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);
|
||||
}
|
||||
}
|
|
@ -1,408 +0,0 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
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]);
|
|
@ -1,394 +0,0 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
//! 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)/<P_3> = 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)/<P_4>`.
|
||||
///
|
||||
/// 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.<x> = 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.<x> = 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.<x> = 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());
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
#[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::*;
|
|
@ -1,704 +0,0 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
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<ProjectivePoint, MAX_INT_POINTS_ALICE> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_ALICE> = 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<ProjectivePoint, MAX_INT_POINTS_ALICE> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_ALICE> = 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<ProjectivePoint, MAX_INT_POINTS_BOB> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_BOB> = 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<ProjectivePoint, MAX_INT_POINTS_BOB> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_BOB> = 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<R: RngCore + CryptoRng>(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<R: RngCore + CryptoRng>(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);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue