Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 blackshirt
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,6 @@
|
||||
## Description
|
||||
|
||||
`crypto.ed25519` implements the `ed25519` public key digital signature algorithm for the V Language.
|
||||
The module is ported from the `Go` version of `crypto.ed25519`.
|
||||
|
||||
See [Ed25519](http://ed25519.cr.yp.to/) for more detail about `ed25519`.
|
||||
@@ -0,0 +1,181 @@
|
||||
module ed25519
|
||||
|
||||
import crypto.rand
|
||||
import crypto.sha512
|
||||
import crypto.internal.subtle
|
||||
import crypto.ed25519.internal.edwards25519
|
||||
|
||||
// public_key_size is the sizeof public keys in bytes
|
||||
pub const public_key_size = 32
|
||||
|
||||
// private_key_size is the sizeof private keys in bytes
|
||||
pub const private_key_size = 64
|
||||
|
||||
// signature_size is the size of signatures generated and verified by this modules, in bytes.
|
||||
pub const signature_size = 64
|
||||
|
||||
// seed_size is the size of private key seeds in bytes
|
||||
pub const seed_size = 32
|
||||
|
||||
// `PublicKey` is Ed25519 public keys.
|
||||
pub type PublicKey = []u8
|
||||
|
||||
// equal reports whether p and x have the same value.
|
||||
pub fn (p PublicKey) equal(x []u8) bool {
|
||||
return subtle.constant_time_compare(p, PublicKey(x)) == 1
|
||||
}
|
||||
|
||||
// PrivateKey is Ed25519 private keys
|
||||
pub type PrivateKey = []u8
|
||||
|
||||
// seed returns the private key seed corresponding to priv.
|
||||
// RFC 8032's private keys correspond to seeds in this module.
|
||||
pub fn (priv PrivateKey) seed() []u8 {
|
||||
mut seed := []u8{len: seed_size}
|
||||
copy(mut seed, priv[..32])
|
||||
return seed
|
||||
}
|
||||
|
||||
// public_key returns the []u8 corresponding to priv.
|
||||
pub fn (priv PrivateKey) public_key() PublicKey {
|
||||
assert priv.len == private_key_size
|
||||
mut publickey := []u8{len: public_key_size}
|
||||
copy(mut publickey, priv[32..])
|
||||
return PublicKey(publickey)
|
||||
}
|
||||
|
||||
// currentyly x not `crypto.PrivateKey`
|
||||
pub fn (priv PrivateKey) equal(x []u8) bool {
|
||||
return subtle.constant_time_compare(priv, PrivateKey(x)) == 1
|
||||
}
|
||||
|
||||
// sign signs the given message with priv.
|
||||
pub fn (priv PrivateKey) sign(message []u8) ![]u8 {
|
||||
/*
|
||||
if opts.HashFunc() != crypto.Hash(0) {
|
||||
return nil, errors.New("ed25519: cannot sign hashed message")
|
||||
}*/
|
||||
|
||||
return sign(priv, message)
|
||||
}
|
||||
|
||||
// sign`signs the message with privatekey and returns a signature
|
||||
pub fn sign(privatekey PrivateKey, message []u8) ![]u8 {
|
||||
mut signature := []u8{len: signature_size}
|
||||
sign_generic(mut signature, privatekey, message)!
|
||||
return signature
|
||||
}
|
||||
|
||||
fn sign_generic(mut signature []u8, privatekey []u8, message []u8) ! {
|
||||
if privatekey.len != private_key_size {
|
||||
panic('ed25519: bad private key length: ${privatekey.len}')
|
||||
}
|
||||
seed, publickey := privatekey[..seed_size], privatekey[seed_size..]
|
||||
|
||||
mut h := sha512.sum512(seed)
|
||||
mut s := edwards25519.new_scalar()
|
||||
s.set_bytes_with_clamping(h[..32])!
|
||||
mut prefix := unsafe { h[32..] }
|
||||
|
||||
mut mh := sha512.new()
|
||||
mh.write(prefix)!
|
||||
mh.write(message)!
|
||||
|
||||
mut msg_digest := []u8{cap: sha512.size}
|
||||
msg_digest = mh.sum(msg_digest)
|
||||
|
||||
mut r := edwards25519.new_scalar()
|
||||
r.set_uniform_bytes(msg_digest)!
|
||||
|
||||
mut rr := edwards25519.Point{}
|
||||
rr.scalar_base_mult(mut r)
|
||||
|
||||
mut kh := sha512.new()
|
||||
kh.write(rr.bytes())!
|
||||
kh.write(publickey)!
|
||||
kh.write(message)!
|
||||
|
||||
mut hram_digest := []u8{cap: sha512.size}
|
||||
hram_digest = kh.sum(hram_digest)
|
||||
mut k := edwards25519.new_scalar()
|
||||
k.set_uniform_bytes(hram_digest)!
|
||||
|
||||
mut ss := edwards25519.new_scalar()
|
||||
ss.multiply_add(k, s, r)
|
||||
|
||||
copy(mut signature[..32], rr.bytes())
|
||||
copy(mut signature[32..], ss.bytes())
|
||||
}
|
||||
|
||||
// verify reports whether sig is a valid signature of message by publickey.
|
||||
pub fn verify(publickey PublicKey, message []u8, sig []u8) !bool {
|
||||
if publickey.len != public_key_size {
|
||||
return error('ed25519: bad public key length: ${publickey.len}')
|
||||
}
|
||||
|
||||
if sig.len != signature_size || sig[63] & 224 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
mut aa := edwards25519.Point{}
|
||||
aa.set_bytes(publickey)!
|
||||
|
||||
mut kh := sha512.new()
|
||||
kh.write(sig[..32])!
|
||||
kh.write(publickey)!
|
||||
kh.write(message)!
|
||||
|
||||
mut hram_digest := []u8{cap: sha512.size}
|
||||
hram_digest = kh.sum(hram_digest)
|
||||
|
||||
mut k := edwards25519.new_scalar()
|
||||
k.set_uniform_bytes(hram_digest)!
|
||||
|
||||
mut ss := edwards25519.new_scalar()
|
||||
ss.set_canonical_bytes(sig[32..])!
|
||||
|
||||
// [S]B = R + [k]A --> [k](-A) + [S]B = R
|
||||
mut minus_a := edwards25519.Point{}
|
||||
minus_a.negate(aa)
|
||||
mut rr := edwards25519.Point{}
|
||||
rr.vartime_double_scalar_base_mult(k, minus_a, ss)
|
||||
|
||||
return subtle.constant_time_compare(sig[..32], rr.bytes()) == 1
|
||||
}
|
||||
|
||||
// generate_key generates a public/private key pair entropy using `crypto.rand`.
|
||||
pub fn generate_key() !(PublicKey, PrivateKey) {
|
||||
mut seed := rand.bytes(seed_size)!
|
||||
|
||||
privatekey := new_key_from_seed(seed)
|
||||
mut publickey := []u8{len: public_key_size}
|
||||
copy(mut publickey, privatekey[32..])
|
||||
|
||||
return publickey, privatekey
|
||||
}
|
||||
|
||||
// new_key_from_seed calculates a private key from a seed. private keys of RFC 8032
|
||||
// correspond to seeds in this module
|
||||
pub fn new_key_from_seed(seed []u8) PrivateKey {
|
||||
// Outline the function body so that the returned key can be stack-allocated.
|
||||
mut privatekey := []u8{len: private_key_size}
|
||||
new_key_from_seed_generic(mut privatekey, seed)
|
||||
return PrivateKey(privatekey)
|
||||
}
|
||||
|
||||
fn new_key_from_seed_generic(mut privatekey []u8, seed []u8) {
|
||||
if seed.len != seed_size {
|
||||
panic('ed25519: bad seed length: ${seed.len}')
|
||||
}
|
||||
|
||||
mut h := sha512.sum512(seed)
|
||||
mut s := edwards25519.new_scalar()
|
||||
s.set_bytes_with_clamping(h[..32]) or { panic(err) }
|
||||
mut aa := edwards25519.Point{}
|
||||
aa.scalar_base_mult(mut s)
|
||||
|
||||
mut publickey := aa.bytes()
|
||||
|
||||
copy(mut privatekey, seed)
|
||||
copy(mut privatekey[32..], publickey)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
module main
|
||||
|
||||
import encoding.hex
|
||||
import encoding.base64
|
||||
import crypto.ed25519
|
||||
|
||||
// adapted from https://asecuritysite.com/signatures/ed25519
|
||||
fn main() {
|
||||
msg := 'Hello Girl'
|
||||
|
||||
publ, priv := ed25519.generate_key()!
|
||||
|
||||
m := msg.bytes()
|
||||
|
||||
sig := ed25519.sign(priv, m)!
|
||||
|
||||
println('=== Message ===')
|
||||
println('Msg: ${msg} \nHash: ${m}')
|
||||
|
||||
println('=== Public key ===')
|
||||
println('Public key (Hex): ${hex.encode(publ)}')
|
||||
println(' Public key (Base64): ${base64.encode(publ)}')
|
||||
|
||||
println('=== Private key ===')
|
||||
println('Private key: ${priv.seed().hex()}') // priv[0:32]
|
||||
println(' Private key (Base64): ${base64.encode(priv.seed())}') // priv[0:32]
|
||||
println(' Private key (Base64) Full key: ${base64.encode(priv)}')
|
||||
println(' Private key (Full key in Hex): ${hex.encode(priv)}')
|
||||
|
||||
println('=== signature (R,s) ===')
|
||||
println('signature: R=${sig[0..32].hex()} s=${sig[32..64].hex()}')
|
||||
println(' signature (Base64)=${base64.encode(sig)}')
|
||||
|
||||
rtn := ed25519.verify(publ, m, sig)!
|
||||
|
||||
if rtn {
|
||||
println('Signature verified :${rtn}')
|
||||
} else {
|
||||
println('signature does not verify :${!rtn}')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
module main
|
||||
|
||||
// Note: this should be in vlib/crypto/ed25519/ed25519_test.v
|
||||
// but is currently one folder below, because of a V parser/symbol registration bug.
|
||||
// TODO: move this test back to vlib/crypto/ed25519/ed25519_test.v
|
||||
import crypto.ed25519
|
||||
|
||||
fn test_sign_verify() {
|
||||
// mut zero := ZeroReader{}
|
||||
public, private := ed25519.generate_key()!
|
||||
|
||||
message := 'test message'.bytes()
|
||||
sig := ed25519.sign(private, message)!
|
||||
res := ed25519.verify(public, message, sig) or { false }
|
||||
assert res == true
|
||||
|
||||
wrongmessage := 'wrong message'.bytes()
|
||||
res2 := ed25519.verify(public, wrongmessage, sig)!
|
||||
assert res2 == false
|
||||
}
|
||||
|
||||
fn test_equal() {
|
||||
public, private := ed25519.generate_key()!
|
||||
|
||||
assert public.equal(public) == true
|
||||
|
||||
// This is not AVAILABLE
|
||||
/*
|
||||
if !public.Equal(crypto.Signer(private).Public()) {
|
||||
t.Errorf("private.Public() is not Equal to public: %q", public)
|
||||
}*/
|
||||
assert private.equal(private) == true
|
||||
|
||||
otherpub, otherpriv := ed25519.generate_key()!
|
||||
assert public.equal(otherpub) == false
|
||||
|
||||
assert private.equal(otherpriv) == false
|
||||
}
|
||||
|
||||
fn test_malleability() {
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
|
||||
// that s be in [0, order). This prevents someone from adding a multiple of
|
||||
// order to s and obtaining a second valid signature for the same message.
|
||||
msg := [u8(0x54), 0x65, 0x73, 0x74]
|
||||
sig := [u8(0x7c), 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a, 0x0f, 0x2d, 0xb8,
|
||||
0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b, 0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27,
|
||||
0x77, 0x4a, 0xb0, 0x67, 0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
|
||||
0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33, 0x36, 0xa5, 0xc5, 0x1e,
|
||||
0xb6, 0xf9, 0x46, 0xb3, 0x1d]
|
||||
publickey := [u8(0x7d), 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5, 0x22, 0xab,
|
||||
0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34, 0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36,
|
||||
0x9e, 0xf5, 0x49, 0xfa]
|
||||
// verify should fail on provided bytes
|
||||
res := ed25519.verify(publickey, msg, sig) or { false }
|
||||
assert res == false
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
## Description
|
||||
|
||||
`crypto.ed25519.internal.edwards25519` provides arithmetic primitives operations that are useful to
|
||||
implement cryptographic schemes over the edwards25519 elliptic curve, including:
|
||||
|
||||
1. Arithmetic functions for point addition, doubling, negation, scalar multiplication
|
||||
with an arbitrary point, with the base point, etc.
|
||||
2. Arithmetic functions dealing with scalars modulo the prime order L of the base point.
|
||||
|
||||
This modules was port of Golang `edwards25519` library from [edwards25519](https://github.com/FiloSottile/edwards25519) to the V language.
|
||||
|
||||
## About Edwards25519
|
||||
|
||||
Twisted Edwards curves are a family of elliptic curves allowing complete addition
|
||||
formulas without any special case and no point at infinity.
|
||||
Curve edwards25519 is based on prime 2^255 - 19 for efficient implementation.
|
||||
Equation and parameters are given in RFC 7748.
|
||||
@@ -0,0 +1 @@
|
||||
module edwards25519
|
||||
@@ -0,0 +1,731 @@
|
||||
module edwards25519
|
||||
|
||||
import math.bits
|
||||
import math.unsigned
|
||||
import encoding.binary
|
||||
import crypto.internal.subtle
|
||||
|
||||
// embedded unsigned.Uint128
|
||||
struct Uint128 {
|
||||
unsigned.Uint128
|
||||
}
|
||||
|
||||
// Element represents an element of the edwards25519 GF(2^255-19). Note that this
|
||||
// is not a cryptographically secure group, and should only be used to interact
|
||||
// with edwards25519.Point coordinates.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is a valid zero element.
|
||||
pub struct Element {
|
||||
mut:
|
||||
// An element t represents the integer
|
||||
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
||||
//
|
||||
// Between operations, all limbs are expected to be lower than 2^52.
|
||||
l0 u64
|
||||
l1 u64
|
||||
l2 u64
|
||||
l3 u64
|
||||
l4 u64
|
||||
}
|
||||
|
||||
const mask_low_51_bits = u64((1 << 51) - 1)
|
||||
const fe_zero = Element{
|
||||
l0: 0
|
||||
l1: 0
|
||||
l2: 0
|
||||
l3: 0
|
||||
l4: 0
|
||||
}
|
||||
const fe_one = Element{
|
||||
l0: 1
|
||||
l1: 0
|
||||
l2: 0
|
||||
l3: 0
|
||||
l4: 0
|
||||
}
|
||||
// sqrt_m1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
||||
const sqrt_m1 = Element{
|
||||
l0: 1718705420411056
|
||||
l1: 234908883556509
|
||||
l2: 2233514472574048
|
||||
l3: 2117202627021982
|
||||
l4: 765476049583133
|
||||
}
|
||||
|
||||
// mul_64 returns a * b.
|
||||
fn mul_64(a u64, b u64) Uint128 {
|
||||
hi, lo := bits.mul_64(a, b)
|
||||
return Uint128{
|
||||
lo: lo
|
||||
hi: hi
|
||||
}
|
||||
}
|
||||
|
||||
// add_mul_64 returns v + a * b.
|
||||
fn add_mul_64(v Uint128, a u64, b u64) Uint128 {
|
||||
mut hi, lo := bits.mul_64(a, b)
|
||||
low, carry := bits.add_64(lo, v.lo, 0)
|
||||
hi, _ = bits.add_64(hi, v.hi, carry)
|
||||
return Uint128{
|
||||
lo: low
|
||||
hi: hi
|
||||
}
|
||||
}
|
||||
|
||||
// shift_right_by_51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||
fn shift_right_by_51(a Uint128) u64 {
|
||||
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||
}
|
||||
|
||||
fn fe_mul_generic(a Element, b Element) Element {
|
||||
a0 := a.l0
|
||||
a1 := a.l1
|
||||
a2 := a.l2
|
||||
a3 := a.l3
|
||||
a4 := a.l4
|
||||
|
||||
b0 := b.l0
|
||||
b1 := b.l1
|
||||
b2 := b.l2
|
||||
b3 := b.l3
|
||||
b4 := b.l4
|
||||
|
||||
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
||||
// with 51-bit limbs instead of digits.
|
||||
//
|
||||
// a4 a3 a2 a1 a0 x
|
||||
// b4 b3 b2 b1 b0 =
|
||||
// ------------------------
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
||||
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
||||
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
||||
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
||||
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
||||
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
||||
//
|
||||
// Reduction can be carried out simultaneously to multiplication. For
|
||||
// example, we do not compute r5: whenever the result of a multiplication
|
||||
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
||||
//
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
||||
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
||||
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
||||
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// Finally we add up the columns into wide, overlapping limbs.
|
||||
|
||||
a1_19 := a1 * 19
|
||||
a2_19 := a2 * 19
|
||||
a3_19 := a3 * 19
|
||||
a4_19 := a4 * 19
|
||||
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
mut r0 := mul_64(a0, b0)
|
||||
r0 = add_mul_64(r0, a1_19, b4)
|
||||
r0 = add_mul_64(r0, a2_19, b3)
|
||||
r0 = add_mul_64(r0, a3_19, b2)
|
||||
r0 = add_mul_64(r0, a4_19, b1)
|
||||
|
||||
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||
mut r1 := mul_64(a0, b1)
|
||||
r1 = add_mul_64(r1, a1, b0)
|
||||
r1 = add_mul_64(r1, a2_19, b4)
|
||||
r1 = add_mul_64(r1, a3_19, b3)
|
||||
r1 = add_mul_64(r1, a4_19, b2)
|
||||
|
||||
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||
mut r2 := mul_64(a0, b2)
|
||||
r2 = add_mul_64(r2, a1, b1)
|
||||
r2 = add_mul_64(r2, a2, b0)
|
||||
r2 = add_mul_64(r2, a3_19, b4)
|
||||
r2 = add_mul_64(r2, a4_19, b3)
|
||||
|
||||
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||
mut r3 := mul_64(a0, b3)
|
||||
r3 = add_mul_64(r3, a1, b2)
|
||||
r3 = add_mul_64(r3, a2, b1)
|
||||
r3 = add_mul_64(r3, a3, b0)
|
||||
r3 = add_mul_64(r3, a4_19, b4)
|
||||
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
mut r4 := mul_64(a0, b4)
|
||||
r4 = add_mul_64(r4, a1, b3)
|
||||
r4 = add_mul_64(r4, a2, b2)
|
||||
r4 = add_mul_64(r4, a3, b1)
|
||||
r4 = add_mul_64(r4, a4, b0)
|
||||
|
||||
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||
// to respect the Element invariant.
|
||||
//
|
||||
// Overall, the reduction works the same as carryPropagate, except with
|
||||
// wider inputs: we take the carry for each coefficient by shifting it right
|
||||
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
||||
// according to the reduction identity and added to the lowest limb.
|
||||
//
|
||||
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
||||
// that all carries are at most 111 - 51 = 60 bits, which fits in a u64.
|
||||
//
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
||||
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
||||
// r0 < 2⁷ × 2⁵² × 2⁵²
|
||||
// r0 < 2¹¹¹
|
||||
//
|
||||
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
||||
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a u64 and
|
||||
// allows us to easily apply the reduction identity.
|
||||
//
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
// r4 < 5 × 2⁵² × 2⁵²
|
||||
// r4 < 2¹⁰⁷
|
||||
//
|
||||
|
||||
c0 := shift_right_by_51(r0)
|
||||
c1 := shift_right_by_51(r1)
|
||||
c2 := shift_right_by_51(r2)
|
||||
c3 := shift_right_by_51(r3)
|
||||
c4 := shift_right_by_51(r4)
|
||||
|
||||
rr0 := r0.lo & mask_low_51_bits + c4 * 19
|
||||
rr1 := r1.lo & mask_low_51_bits + c0
|
||||
rr2 := r2.lo & mask_low_51_bits + c1
|
||||
rr3 := r3.lo & mask_low_51_bits + c2
|
||||
rr4 := r4.lo & mask_low_51_bits + c3
|
||||
|
||||
// Now all coefficients fit into 64-bit registers but are still too large to
|
||||
// be passed around as a Element. We therefore do one last carry chain,
|
||||
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||
mut v := Element{
|
||||
l0: rr0
|
||||
l1: rr1
|
||||
l2: rr2
|
||||
l3: rr3
|
||||
l4: rr4
|
||||
}
|
||||
// v.carryPropagate()
|
||||
// using `carry_propagate_generic()` instead
|
||||
v = v.carry_propagate_generic()
|
||||
return v
|
||||
}
|
||||
|
||||
// carry_propagate_generic brings the limbs below 52 bits by applying the reduction
|
||||
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
|
||||
fn (mut v Element) carry_propagate_generic() Element {
|
||||
c0 := v.l0 >> 51
|
||||
c1 := v.l1 >> 51
|
||||
c2 := v.l2 >> 51
|
||||
c3 := v.l3 >> 51
|
||||
c4 := v.l4 >> 51
|
||||
|
||||
v.l0 = v.l0 & mask_low_51_bits + c4 * 19
|
||||
v.l1 = v.l1 & mask_low_51_bits + c0
|
||||
v.l2 = v.l2 & mask_low_51_bits + c1
|
||||
v.l3 = v.l3 & mask_low_51_bits + c2
|
||||
v.l4 = v.l4 & mask_low_51_bits + c3
|
||||
return v
|
||||
}
|
||||
|
||||
fn fe_square_generic(a Element) Element {
|
||||
l0 := a.l0
|
||||
l1 := a.l1
|
||||
l2 := a.l2
|
||||
l3 := a.l3
|
||||
l4 := a.l4
|
||||
|
||||
// Squaring works precisely like multiplication above, but thanks to its
|
||||
// symmetry we get to group a few terms together.
|
||||
//
|
||||
// l4 l3 l2 l1 l0 x
|
||||
// l4 l3 l2 l1 l0 =
|
||||
// ------------------------
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
||||
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
||||
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
||||
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
||||
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
||||
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
||||
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
||||
// only three mul_64 and four Add64, instead of five and eight.
|
||||
|
||||
l0_2 := l0 * 2
|
||||
l1_2 := l1 * 2
|
||||
|
||||
l1_38 := l1 * 38
|
||||
l2_38 := l2 * 38
|
||||
l3_38 := l3 * 38
|
||||
|
||||
l3_19 := l3 * 19
|
||||
l4_19 := l4 * 19
|
||||
|
||||
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||
mut r0 := mul_64(l0, l0)
|
||||
r0 = add_mul_64(r0, l1_38, l4)
|
||||
r0 = add_mul_64(r0, l2_38, l3)
|
||||
|
||||
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||
mut r1 := mul_64(l0_2, l1)
|
||||
r1 = add_mul_64(r1, l2_38, l4)
|
||||
r1 = add_mul_64(r1, l3_19, l3)
|
||||
|
||||
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||
mut r2 := mul_64(l0_2, l2)
|
||||
r2 = add_mul_64(r2, l1, l1)
|
||||
r2 = add_mul_64(r2, l3_38, l4)
|
||||
|
||||
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||
mut r3 := mul_64(l0_2, l3)
|
||||
r3 = add_mul_64(r3, l1_2, l2)
|
||||
r3 = add_mul_64(r3, l4_19, l4)
|
||||
|
||||
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||
mut r4 := mul_64(l0_2, l4)
|
||||
r4 = add_mul_64(r4, l1_2, l3)
|
||||
r4 = add_mul_64(r4, l2, l2)
|
||||
|
||||
c0 := shift_right_by_51(r0)
|
||||
c1 := shift_right_by_51(r1)
|
||||
c2 := shift_right_by_51(r2)
|
||||
c3 := shift_right_by_51(r3)
|
||||
c4 := shift_right_by_51(r4)
|
||||
|
||||
rr0 := r0.lo & mask_low_51_bits + c4 * 19
|
||||
rr1 := r1.lo & mask_low_51_bits + c0
|
||||
rr2 := r2.lo & mask_low_51_bits + c1
|
||||
rr3 := r3.lo & mask_low_51_bits + c2
|
||||
rr4 := r4.lo & mask_low_51_bits + c3
|
||||
|
||||
mut v := Element{
|
||||
l0: rr0
|
||||
l1: rr1
|
||||
l2: rr2
|
||||
l3: rr3
|
||||
l4: rr4
|
||||
}
|
||||
v = v.carry_propagate_generic()
|
||||
return v
|
||||
}
|
||||
|
||||
// zero sets v = 0, and returns v.
|
||||
pub fn (mut v Element) zero() Element {
|
||||
v = fe_zero
|
||||
return v
|
||||
}
|
||||
|
||||
// one sets v = 1, and returns v.
|
||||
pub fn (mut v Element) one() Element {
|
||||
v = fe_one
|
||||
return v
|
||||
}
|
||||
|
||||
// reduce reduces v modulo 2^255 - 19 and returns it.
|
||||
pub fn (mut v Element) reduce() Element {
|
||||
v = v.carry_propagate_generic()
|
||||
|
||||
// After the light reduction we now have a edwards25519 element representation
|
||||
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
||||
|
||||
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
||||
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
||||
mut c := (v.l0 + 19) >> 51
|
||||
c = (v.l1 + c) >> 51
|
||||
c = (v.l2 + c) >> 51
|
||||
c = (v.l3 + c) >> 51
|
||||
c = (v.l4 + c) >> 51
|
||||
|
||||
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
||||
// effectively applying the reduction identity to the carry.
|
||||
v.l0 += 19 * c
|
||||
|
||||
v.l1 += v.l0 >> 51
|
||||
v.l0 = v.l0 & mask_low_51_bits
|
||||
v.l2 += v.l1 >> 51
|
||||
v.l1 = v.l1 & mask_low_51_bits
|
||||
v.l3 += v.l2 >> 51
|
||||
v.l2 = v.l2 & mask_low_51_bits
|
||||
v.l4 += v.l3 >> 51
|
||||
v.l3 = v.l3 & mask_low_51_bits
|
||||
// no additional carry
|
||||
v.l4 = v.l4 & mask_low_51_bits
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// add sets v = a + b, and returns v.
|
||||
pub fn (mut v Element) add(a Element, b Element) Element {
|
||||
v.l0 = a.l0 + b.l0
|
||||
v.l1 = a.l1 + b.l1
|
||||
v.l2 = a.l2 + b.l2
|
||||
v.l3 = a.l3 + b.l3
|
||||
v.l4 = a.l4 + b.l4
|
||||
// Using the generic implementation here is actually faster than the
|
||||
// assembly. Probably because the body of this function is so simple that
|
||||
// the compiler can figure out better optimizations by inlining the carry
|
||||
// propagation.
|
||||
return v.carry_propagate_generic()
|
||||
}
|
||||
|
||||
// subtract sets v = a - b, and returns v.
|
||||
pub fn (mut v Element) subtract(a Element, b Element) Element {
|
||||
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
||||
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
||||
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
||||
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
||||
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
||||
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
||||
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
||||
return v.carry_propagate_generic()
|
||||
}
|
||||
|
||||
// negate sets v = -a, and returns v.
|
||||
pub fn (mut v Element) negate(a Element) Element {
|
||||
return v.subtract(fe_zero, a)
|
||||
}
|
||||
|
||||
// invert sets v = 1/z mod p, and returns v.
|
||||
//
|
||||
// If z == 0, invert returns v = 0.
|
||||
pub fn (mut v Element) invert(z Element) Element {
|
||||
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
||||
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
||||
mut z2 := Element{}
|
||||
mut z9 := Element{}
|
||||
mut z11 := Element{}
|
||||
mut z2_5_0 := Element{}
|
||||
mut z2_10_0 := Element{}
|
||||
mut z2_20_0 := Element{}
|
||||
mut z2_50_0 := Element{}
|
||||
mut z2_100_0 := Element{}
|
||||
mut t := Element{}
|
||||
|
||||
z2.square(z) // 2
|
||||
t.square(z2) // 4
|
||||
t.square(t) // 8
|
||||
z9.multiply(t, z) // 9
|
||||
z11.multiply(z9, z2) // 11
|
||||
t.square(z11) // 22
|
||||
z2_5_0.multiply(t, z9) // 31 = 2^5 - 2^0
|
||||
|
||||
t.square(z2_5_0) // 2^6 - 2^1
|
||||
for i := 0; i < 4; i++ {
|
||||
t.square(t) // 2^10 - 2^5
|
||||
}
|
||||
z2_10_0.multiply(t, z2_5_0) // 2^10 - 2^0
|
||||
|
||||
t.square(z2_10_0) // 2^11 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.square(t) // 2^20 - 2^10
|
||||
}
|
||||
z2_20_0.multiply(t, z2_10_0) // 2^20 - 2^0
|
||||
|
||||
t.square(z2_20_0) // 2^21 - 2^1
|
||||
for i := 0; i < 19; i++ {
|
||||
t.square(t) // 2^40 - 2^20
|
||||
}
|
||||
t.multiply(t, z2_20_0) // 2^40 - 2^0
|
||||
|
||||
t.square(t) // 2^41 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.square(t) // 2^50 - 2^10
|
||||
}
|
||||
z2_50_0.multiply(t, z2_10_0) // 2^50 - 2^0
|
||||
|
||||
t.square(z2_50_0) // 2^51 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.square(t) // 2^100 - 2^50
|
||||
}
|
||||
z2_100_0.multiply(t, z2_50_0) // 2^100 - 2^0
|
||||
|
||||
t.square(z2_100_0) // 2^101 - 2^1
|
||||
for i := 0; i < 99; i++ {
|
||||
t.square(t) // 2^200 - 2^100
|
||||
}
|
||||
t.multiply(t, z2_100_0) // 2^200 - 2^0
|
||||
|
||||
t.square(t) // 2^201 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.square(t) // 2^250 - 2^50
|
||||
}
|
||||
t.multiply(t, z2_50_0) // 2^250 - 2^0
|
||||
|
||||
t.square(t) // 2^251 - 2^1
|
||||
t.square(t) // 2^252 - 2^2
|
||||
t.square(t) // 2^253 - 2^3
|
||||
t.square(t) // 2^254 - 2^4
|
||||
t.square(t) // 2^255 - 2^5
|
||||
|
||||
return v.multiply(t, z11) // 2^255 - 21
|
||||
}
|
||||
|
||||
// square sets v = x * x, and returns v.
|
||||
pub fn (mut v Element) square(x Element) Element {
|
||||
v = fe_square_generic(x)
|
||||
return v
|
||||
}
|
||||
|
||||
// multiply sets v = x * y, and returns v.
|
||||
pub fn (mut v Element) multiply(x Element, y Element) Element {
|
||||
v = fe_mul_generic(x, y)
|
||||
return v
|
||||
}
|
||||
|
||||
// mul_51 returns lo + hi * 2⁵¹ = a * b.
|
||||
fn mul_51(a u64, b u32) (u64, u64) {
|
||||
mh, ml := bits.mul_64(a, u64(b))
|
||||
lo := ml & mask_low_51_bits
|
||||
hi := (mh << 13) | (ml >> 51)
|
||||
return lo, hi
|
||||
}
|
||||
|
||||
// pow_22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
||||
pub fn (mut v Element) pow_22523(x Element) Element {
|
||||
mut t0, mut t1, mut t2 := Element{}, Element{}, Element{}
|
||||
|
||||
t0.square(x) // x^2
|
||||
t1.square(t0) // x^4
|
||||
t1.square(t1) // x^8
|
||||
t1.multiply(x, t1) // x^9
|
||||
t0.multiply(t0, t1) // x^11
|
||||
t0.square(t0) // x^22
|
||||
t0.multiply(t1, t0) // x^31
|
||||
t1.square(t0) // x^62
|
||||
for i := 1; i < 5; i++ { // x^992
|
||||
t1.square(t1)
|
||||
}
|
||||
t0.multiply(t1, t0) // x^1023 -> 1023 = 2^10 - 1
|
||||
t1.square(t0) // 2^11 - 2
|
||||
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
||||
t1.square(t1)
|
||||
}
|
||||
t1.multiply(t1, t0) // 2^20 - 1
|
||||
t2.square(t1) // 2^21 - 2
|
||||
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
||||
t2.square(t2)
|
||||
}
|
||||
t1.multiply(t2, t1) // 2^40 - 1
|
||||
t1.square(t1) // 2^41 - 2
|
||||
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
||||
t1.square(t1)
|
||||
}
|
||||
t0.multiply(t1, t0) // 2^50 - 1
|
||||
t1.square(t0) // 2^51 - 2
|
||||
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
||||
t1.square(t1)
|
||||
}
|
||||
t1.multiply(t1, t0) // 2^100 - 1
|
||||
t2.square(t1) // 2^101 - 2
|
||||
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
||||
t2.square(t2)
|
||||
}
|
||||
t1.multiply(t2, t1) // 2^200 - 1
|
||||
t1.square(t1) // 2^201 - 2
|
||||
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
||||
t1.square(t1)
|
||||
}
|
||||
t0.multiply(t1, t0) // 2^250 - 1
|
||||
t0.square(t0) // 2^251 - 2
|
||||
t0.square(t0) // 2^252 - 4
|
||||
return v.multiply(t0, x) // 2^252 - 3 -> x^(2^252-3)
|
||||
}
|
||||
|
||||
// sqrt_ratio sets r to the non-negative square root of the ratio of u and v.
|
||||
//
|
||||
// If u/v is square, sqrt_ratio returns r and 1. If u/v is not square, sqrt_ratio
|
||||
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
||||
// and returns r and 0.
|
||||
pub fn (mut r Element) sqrt_ratio(u Element, v Element) (Element, int) {
|
||||
mut a, mut b := Element{}, Element{}
|
||||
|
||||
// r = (u * v3) * (u * v7)^((p-5)/8)
|
||||
v2 := a.square(v)
|
||||
uv3 := b.multiply(u, b.multiply(v2, v))
|
||||
uv7 := a.multiply(uv3, a.square(v2))
|
||||
r.multiply(uv3, r.pow_22523(uv7))
|
||||
|
||||
mut check := a.multiply(v, a.square(r)) // check = v * r^2
|
||||
|
||||
mut uneg := b.negate(u)
|
||||
correct_sign_sqrt := check.equal(u)
|
||||
flipped_sign_sqrt := check.equal(uneg)
|
||||
flipped_sign_sqrt_i := check.equal(uneg.multiply(uneg, sqrt_m1))
|
||||
|
||||
rprime := b.multiply(r, sqrt_m1) // r_prime = SQRT_M1 * r
|
||||
// r = CT_selected(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
||||
r.selected(rprime, r, flipped_sign_sqrt | flipped_sign_sqrt_i)
|
||||
|
||||
r.absolute(r) // Choose the nonnegative square root.
|
||||
return r, correct_sign_sqrt | flipped_sign_sqrt
|
||||
}
|
||||
|
||||
// mask_64_bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
||||
fn mask_64_bits(cond int) u64 {
|
||||
// in go, `^` operates on bit mean NOT, flip bit
|
||||
// in v, its a ~ bitwise NOT
|
||||
return ~(u64(cond) - 1)
|
||||
}
|
||||
|
||||
// selected sets v to a if cond == 1, and to b if cond == 0.
|
||||
pub fn (mut v Element) selected(a Element, b Element, cond int) Element {
|
||||
// see above notes
|
||||
m := mask_64_bits(cond)
|
||||
v.l0 = (m & a.l0) | (~m & b.l0)
|
||||
v.l1 = (m & a.l1) | (~m & b.l1)
|
||||
v.l2 = (m & a.l2) | (~m & b.l2)
|
||||
v.l3 = (m & a.l3) | (~m & b.l3)
|
||||
v.l4 = (m & a.l4) | (~m & b.l4)
|
||||
return v
|
||||
}
|
||||
|
||||
// is_negative returns 1 if v is negative, and 0 otherwise.
|
||||
pub fn (mut v Element) is_negative() int {
|
||||
return int(v.bytes()[0] & 1)
|
||||
}
|
||||
|
||||
// absolute sets v to |u|, and returns v.
|
||||
pub fn (mut v Element) absolute(u Element) Element {
|
||||
mut e := Element{}
|
||||
mut uk := u
|
||||
return v.selected(e.negate(uk), uk, uk.is_negative())
|
||||
}
|
||||
|
||||
// set sets v = a, and returns v.
|
||||
pub fn (mut v Element) set(a Element) Element {
|
||||
v = a
|
||||
return v
|
||||
}
|
||||
|
||||
// set_bytes sets v to x, where x is a 32-byte little-endian encoding. If x is
|
||||
// not of the right length, SetUniformBytes returns an error, and the
|
||||
// receiver is unchanged.
|
||||
//
|
||||
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
||||
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
||||
// are accepted. Note that this is laxer than specified by RFC 8032.
|
||||
pub fn (mut v Element) set_bytes(x []u8) !Element {
|
||||
if x.len != 32 {
|
||||
return error('edwards25519: invalid edwards25519 element input size')
|
||||
}
|
||||
|
||||
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
||||
v.l0 = binary.little_endian_u64(x[0..8])
|
||||
v.l0 &= mask_low_51_bits
|
||||
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
||||
v.l1 = binary.little_endian_u64(x[6..14]) >> 3
|
||||
v.l1 &= mask_low_51_bits
|
||||
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
||||
v.l2 = binary.little_endian_u64(x[12..20]) >> 6
|
||||
v.l2 &= mask_low_51_bits
|
||||
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
||||
v.l3 = binary.little_endian_u64(x[19..27]) >> 1
|
||||
v.l3 &= mask_low_51_bits
|
||||
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
||||
// Note: not bytes 25:33, shift 4, to avoid overread.
|
||||
v.l4 = binary.little_endian_u64(x[24..32]) >> 12
|
||||
v.l4 &= mask_low_51_bits
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// bytes returns the canonical 32-byte little-endian encoding of v.
|
||||
pub fn (mut v Element) bytes() []u8 {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
// out := v.bytes_generic()
|
||||
return v.bytes_generic()
|
||||
}
|
||||
|
||||
fn (mut v Element) bytes_generic() []u8 {
|
||||
mut out := []u8{len: 32}
|
||||
|
||||
v = v.reduce()
|
||||
|
||||
mut buf := []u8{len: 8}
|
||||
idxs := [v.l0, v.l1, v.l2, v.l3, v.l4]
|
||||
for i, l in idxs {
|
||||
bits_offset := i * 51
|
||||
binary.little_endian_put_u64(mut buf, l << u32(bits_offset % 8))
|
||||
for j, bb in buf {
|
||||
off := bits_offset / 8 + j
|
||||
if off >= out.len {
|
||||
break
|
||||
}
|
||||
out[off] |= bb
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// equal returns 1 if v and u are equal, and 0 otherwise.
|
||||
pub fn (mut v Element) equal(ue Element) int {
|
||||
mut u := ue
|
||||
sa := u.bytes()
|
||||
sv := v.bytes()
|
||||
return subtle.constant_time_compare(sa, sv)
|
||||
}
|
||||
|
||||
// swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
||||
pub fn (mut v Element) swap(mut u Element, cond int) {
|
||||
// mut u := ue
|
||||
m := mask_64_bits(cond)
|
||||
mut t := m & (v.l0 ^ u.l0)
|
||||
v.l0 ^= t
|
||||
u.l0 ^= t
|
||||
t = m & (v.l1 ^ u.l1)
|
||||
v.l1 ^= t
|
||||
u.l1 ^= t
|
||||
t = m & (v.l2 ^ u.l2)
|
||||
v.l2 ^= t
|
||||
u.l2 ^= t
|
||||
t = m & (v.l3 ^ u.l3)
|
||||
v.l3 ^= t
|
||||
u.l3 ^= t
|
||||
t = m & (v.l4 ^ u.l4)
|
||||
v.l4 ^= t
|
||||
u.l4 ^= t
|
||||
}
|
||||
|
||||
// mult_32 sets v = x * y, and returns v.
|
||||
pub fn (mut v Element) mult_32(x Element, y u32) Element {
|
||||
x0lo, x0hi := mul_51(x.l0, y)
|
||||
x1lo, x1hi := mul_51(x.l1, y)
|
||||
x2lo, x2hi := mul_51(x.l2, y)
|
||||
x3lo, x3hi := mul_51(x.l3, y)
|
||||
x4lo, x4hi := mul_51(x.l4, y)
|
||||
v.l0 = x0lo + 19 * x4hi // carried over per the reduction identity
|
||||
v.l1 = x1lo + x0hi
|
||||
v.l2 = x2lo + x1hi
|
||||
v.l3 = x3lo + x2hi
|
||||
v.l4 = x4lo + x3hi
|
||||
// The hi portions are going to be only 32 bits, plus any previous excess,
|
||||
// so we can skip the carry propagation.
|
||||
return v
|
||||
}
|
||||
|
||||
fn swap_endianness(mut buf []u8) []u8 {
|
||||
for i := 0; i < buf.len / 2; i++ {
|
||||
buf[i], buf[buf.len - i - 1] = buf[buf.len - i - 1], buf[i]
|
||||
}
|
||||
return buf
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
module edwards25519
|
||||
|
||||
import os
|
||||
import rand
|
||||
import math.bits
|
||||
import math.big
|
||||
import encoding.hex
|
||||
|
||||
const github_job = os.getenv('GITHUB_JOB')
|
||||
|
||||
fn testsuite_begin() {
|
||||
if github_job != '' {
|
||||
// ensure that the CI does not run flaky tests:
|
||||
rand.seed([u32(0xffff24), 0xabcd])
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut v Element) str() string {
|
||||
return hex.encode(v.bytes())
|
||||
}
|
||||
|
||||
const mask_low_52_bits = (u64(1) << 52) - 1
|
||||
|
||||
fn generate_field_element() Element {
|
||||
return Element{
|
||||
l0: rand.u64() & mask_low_52_bits
|
||||
l1: rand.u64() & mask_low_52_bits
|
||||
l2: rand.u64() & mask_low_52_bits
|
||||
l3: rand.u64() & mask_low_52_bits
|
||||
l4: rand.u64() & mask_low_52_bits
|
||||
}
|
||||
}
|
||||
|
||||
// weirdLimbs can be combined to generate a range of edge-case edwards25519 elements.
|
||||
// 0 and -1 are intentionally more weighted, as they combine well.
|
||||
const two_to_51 = u64(1) << 51
|
||||
const two_to_52 = u64(1) << 52
|
||||
const weird_limbs_51 = [
|
||||
u64(0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
19 - 1,
|
||||
19,
|
||||
0x2aaaaaaaaaaaa,
|
||||
0x5555555555555,
|
||||
two_to_51 - 20,
|
||||
two_to_51 - 19,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
]
|
||||
const weird_limbs_52 = [
|
||||
u64(0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
19 - 1,
|
||||
19,
|
||||
0x2aaaaaaaaaaaa,
|
||||
0x5555555555555,
|
||||
two_to_51 - 20,
|
||||
two_to_51 - 19,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51 - 1,
|
||||
two_to_51,
|
||||
two_to_51 + 1,
|
||||
two_to_52 - 19,
|
||||
two_to_52 - 1,
|
||||
]
|
||||
|
||||
fn generate_weird_field_element() Element {
|
||||
return Element{
|
||||
l0: weird_limbs_52[rand.intn(weird_limbs_52.len) or { 0 }]
|
||||
l1: weird_limbs_51[rand.intn(weird_limbs_51.len) or { 0 }]
|
||||
l2: weird_limbs_51[rand.intn(weird_limbs_51.len) or { 0 }]
|
||||
l3: weird_limbs_51[rand.intn(weird_limbs_51.len) or { 0 }]
|
||||
l4: weird_limbs_51[rand.intn(weird_limbs_51.len) or { 0 }]
|
||||
}
|
||||
}
|
||||
|
||||
fn (e Element) generate_element() Element {
|
||||
if rand.intn(2) or { 0 } == 0 {
|
||||
return generate_weird_field_element()
|
||||
}
|
||||
return generate_field_element()
|
||||
}
|
||||
|
||||
fn is_in_bounds(x Element) bool {
|
||||
return bits.len_64(x.l0) <= 52 && bits.len_64(x.l1) <= 52 && bits.len_64(x.l2) <= 52
|
||||
&& bits.len_64(x.l3) <= 52 && bits.len_64(x.l4) <= 52
|
||||
}
|
||||
|
||||
fn carry_gen(a [5]u64) bool {
|
||||
mut t1 := Element{a[0], a[1], a[2], a[3], a[4]}
|
||||
mut t2 := Element{a[0], a[1], a[2], a[3], a[4]}
|
||||
|
||||
t1.carry_propagate_generic()
|
||||
t2.carry_propagate_generic()
|
||||
|
||||
return t1 == t2 && is_in_bounds(t2)
|
||||
}
|
||||
|
||||
fn test_carry_propagate_generic() {
|
||||
// closures not supported on windows
|
||||
for i := 0; i <= 10; i++ {
|
||||
els := [rand.u64(), rand.u64(), rand.u64(), rand.u64(),
|
||||
rand.u64()]!
|
||||
p := carry_gen(els)
|
||||
assert p == true
|
||||
}
|
||||
res := carry_gen([u64(0xffffffffffffffff), 0xffffffffffffffff, 0xffffffffffffffff,
|
||||
0xffffffffffffffff, 0xffffffffffffffff]!)
|
||||
assert res == true
|
||||
}
|
||||
|
||||
fn test_fe_mul_generic() {
|
||||
for i in 0 .. 20 {
|
||||
el := Element{}
|
||||
a := el.generate_element()
|
||||
b := el.generate_element()
|
||||
a1 := a
|
||||
a2 := a
|
||||
|
||||
b1 := b
|
||||
b2 := b
|
||||
|
||||
a1b1 := fe_mul_generic(a1, b1)
|
||||
a2b2 := fe_mul_generic(a2, b2)
|
||||
assert a1b1 == a2b2 && is_in_bounds(a1b1) && is_in_bounds(a2b2)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_fe_square_generic() {
|
||||
for i in 0 .. 20 {
|
||||
a := generate_field_element()
|
||||
|
||||
a1 := a
|
||||
a2 := a
|
||||
|
||||
a11 := fe_square_generic(a1)
|
||||
a22 := fe_square_generic(a2)
|
||||
assert a11 == a22 && is_in_bounds(a11) && is_in_bounds(a22)
|
||||
}
|
||||
}
|
||||
|
||||
struct SqrtRatioTest {
|
||||
u string
|
||||
v string
|
||||
was_square int
|
||||
r string
|
||||
}
|
||||
|
||||
fn test_sqrt_ratio() {
|
||||
// From draft-irtf-cfrg-ristretto255-decaf448-00, Appendix A.4.
|
||||
|
||||
tests := [
|
||||
// If u is 0, the function is defined to return (0, TRUE), even if v
|
||||
// is zero. Note that where used in this package, the denominator v
|
||||
// is never zero.
|
||||
SqrtRatioTest{'0000000000000000000000000000000000000000000000000000000000000000', '0000000000000000000000000000000000000000000000000000000000000000', 1, '0000000000000000000000000000000000000000000000000000000000000000'},
|
||||
// 0/1 == 0²
|
||||
SqrtRatioTest{'0000000000000000000000000000000000000000000000000000000000000000', '0100000000000000000000000000000000000000000000000000000000000000', 1, '0000000000000000000000000000000000000000000000000000000000000000'},
|
||||
// If u is non-zero and v is zero, defined to return (0, FALSE).
|
||||
SqrtRatioTest{'0100000000000000000000000000000000000000000000000000000000000000', '0000000000000000000000000000000000000000000000000000000000000000', 0, '0000000000000000000000000000000000000000000000000000000000000000'},
|
||||
// 2/1 is not square in this edwards25519.
|
||||
SqrtRatioTest{'0200000000000000000000000000000000000000000000000000000000000000', '0100000000000000000000000000000000000000000000000000000000000000', 0, '3c5ff1b5d8e4113b871bd052f9e7bcd0582804c266ffb2d4f4203eb07fdb7c54'},
|
||||
// 4/1 == 2²
|
||||
SqrtRatioTest{'0400000000000000000000000000000000000000000000000000000000000000', '0100000000000000000000000000000000000000000000000000000000000000', 1, '0200000000000000000000000000000000000000000000000000000000000000'},
|
||||
// 1/4 == (2⁻¹)² == (2^(p-2))² per Euler's theorem
|
||||
SqrtRatioTest{'0100000000000000000000000000000000000000000000000000000000000000', '0400000000000000000000000000000000000000000000000000000000000000', 1, 'f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f'},
|
||||
]
|
||||
|
||||
for i, tt in tests {
|
||||
mut elu := Element{}
|
||||
mut elv := Element{}
|
||||
mut elw := Element{}
|
||||
mut elg := Element{}
|
||||
|
||||
u := elu.set_bytes(hex.decode(tt.u)!)!
|
||||
v := elv.set_bytes(hex.decode(tt.v)!)!
|
||||
want := elw.set_bytes(hex.decode(tt.r)!)!
|
||||
mut got, was_square := elg.sqrt_ratio(u, v)
|
||||
|
||||
assert got.equal(want) != 0
|
||||
assert was_square == tt.was_square
|
||||
// if got.Equal(want) == 0 || wasSquare != tt.wasSquare {
|
||||
// t.Errorf("%d: got (%v, %v), want (%v, %v)", i, got, wasSquare, want, tt.wasSquare)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
fn test_set_bytes_normal() {
|
||||
for i in 0 .. 15 {
|
||||
mut el := Element{}
|
||||
mut random_inp := rand.bytes(32)!
|
||||
|
||||
el = el.set_bytes(random_inp.clone())!
|
||||
random_inp[random_inp.len - 1] &= (1 << 7) - 1
|
||||
// assert f1(random_inp, el) == true
|
||||
|
||||
assert random_inp == el.bytes()
|
||||
assert is_in_bounds(el) == true
|
||||
}
|
||||
}
|
||||
|
||||
fn test_set_bytes_reduced() {
|
||||
mut fe := Element{}
|
||||
mut r := Element{}
|
||||
mut random_inp := rand.bytes(32) or { return }
|
||||
|
||||
fe.set_bytes(random_inp) or { return }
|
||||
r.set_bytes(fe.bytes()) or { return }
|
||||
|
||||
assert fe == r
|
||||
}
|
||||
|
||||
// Check some fixed vectors from dalek
|
||||
struct FeRTTest {
|
||||
mut:
|
||||
fe Element
|
||||
b []u8
|
||||
}
|
||||
|
||||
fn test_set_bytes_from_dalek_test_vectors() {
|
||||
mut tests := [
|
||||
FeRTTest{
|
||||
fe: Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676}
|
||||
b: [u8(74), 209, 69, 197, 70, 70, 161, 222, 56, 226, 229, 19, 112, 60, 25, 92, 187,
|
||||
74, 222, 56, 50, 153, 51, 233, 40, 74, 57, 6, 160, 185, 213, 31]
|
||||
},
|
||||
FeRTTest{
|
||||
fe: Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972}
|
||||
b: [u8(199), 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42,
|
||||
32, 83, 250, 44, 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122]
|
||||
},
|
||||
]
|
||||
for _, mut tt in tests {
|
||||
b := tt.fe.bytes()
|
||||
mut el := Element{}
|
||||
mut fe := el.set_bytes(tt.b)!
|
||||
|
||||
assert b == tt.b
|
||||
assert fe.equal(tt.fe) == 1
|
||||
}
|
||||
}
|
||||
|
||||
fn test_equal() {
|
||||
mut x := Element{1, 1, 1, 1, 1}
|
||||
y := Element{5, 4, 3, 2, 1}
|
||||
|
||||
mut eq1 := x.equal(x)
|
||||
assert eq1 == 1
|
||||
|
||||
eq1 = x.equal(y)
|
||||
assert eq1 == 0
|
||||
}
|
||||
|
||||
fn test_invert() {
|
||||
mut x := Element{1, 1, 1, 1, 1}
|
||||
mut one := Element{1, 0, 0, 0, 0}
|
||||
mut xinv := Element{}
|
||||
mut r := Element{}
|
||||
|
||||
xinv.invert(x)
|
||||
r.multiply(x, xinv)
|
||||
r.reduce()
|
||||
|
||||
assert one == r
|
||||
bytes := rand.bytes(32)!
|
||||
|
||||
x.set_bytes(bytes)!
|
||||
|
||||
xinv.invert(x)
|
||||
r.multiply(x, xinv)
|
||||
r.reduce()
|
||||
|
||||
assert one == r
|
||||
|
||||
zero := Element{}
|
||||
x.set(zero)
|
||||
|
||||
xx := xinv.invert(x)
|
||||
assert xx == xinv
|
||||
assert xinv.equal(zero) == 1
|
||||
// s := if num % 2 == 0 { 'even' } else { 'odd' }
|
||||
}
|
||||
|
||||
fn test_mult_32() {
|
||||
for j in 0 .. 10 {
|
||||
mut x := Element{}
|
||||
mut t1 := Element{}
|
||||
y := u32(0)
|
||||
for i := 0; i < 100; i++ {
|
||||
t1.mult_32(x, y)
|
||||
}
|
||||
mut ty := Element{}
|
||||
ty.l0 = u64(y)
|
||||
mut t2 := Element{}
|
||||
for i := 0; i < 100; i++ {
|
||||
t2.multiply(x, ty)
|
||||
}
|
||||
assert t1.equal(t2) == 1 && is_in_bounds(t1) && is_in_bounds(t2)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_selected_and_swap() {
|
||||
a :=
|
||||
Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676}
|
||||
b :=
|
||||
Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972}
|
||||
|
||||
mut c := Element{}
|
||||
mut d := Element{}
|
||||
|
||||
c.selected(a, b, 1)
|
||||
d.selected(a, b, 0)
|
||||
|
||||
assert c.equal(a) == 1
|
||||
assert d.equal(b) == 1
|
||||
|
||||
c.swap(mut d, 0)
|
||||
assert c.equal(a) == 1
|
||||
assert d.equal(b) == 1
|
||||
|
||||
c.swap(mut d, 1)
|
||||
assert c.equal(b) == 1
|
||||
assert d.equal(a) == 1
|
||||
}
|
||||
|
||||
// Tests self-consistency between multiply and Square.
|
||||
fn test_consistency_between_mult_and_square() {
|
||||
mut x := Element{1, 1, 1, 1, 1}
|
||||
mut x2 := Element{}
|
||||
mut x2sq := Element{}
|
||||
|
||||
x2.multiply(x, x)
|
||||
x2sq.square(x)
|
||||
|
||||
assert x2 == x2sq
|
||||
|
||||
bytes := rand.bytes(32) or { return }
|
||||
x.set_bytes(bytes) or { return }
|
||||
x2.multiply(x, x)
|
||||
x2sq.square(x)
|
||||
|
||||
assert x2 == x2sq
|
||||
}
|
||||
|
||||
// to_big_integer returns v as a big.Integer.
|
||||
fn (mut v Element) to_big_integer() big.Integer {
|
||||
mut buf := v.bytes()
|
||||
return big.integer_from_bytes(swap_endianness(mut buf))
|
||||
}
|
||||
|
||||
// from_big_integer sets v = n, and returns v. The bit length of n must not exceed 256.
|
||||
fn (mut v Element) from_big_integer(n big.Integer) !Element {
|
||||
if n.bin_str().len > 32 * 8 {
|
||||
return error('invalid edwards25519 element input size')
|
||||
}
|
||||
mut bytes, _ := n.bytes()
|
||||
mut padded := []u8{len: 32}
|
||||
copy(mut padded[32 - bytes.len..], bytes)
|
||||
swap_endianness(mut padded) // convert big-endian integer bytes to little-endian element bytes
|
||||
v.set_bytes(padded)!
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v Element) from_decimal_string(s string) !Element {
|
||||
num := big.integer_from_string(s)!
|
||||
|
||||
v = v.from_big_integer(num)!
|
||||
return v
|
||||
}
|
||||
|
||||
fn test_bytes_big_equivalence() {
|
||||
mut inp := rand.bytes(32)!
|
||||
el := Element{}
|
||||
mut fe := el.generate_element()
|
||||
mut fe1 := el.generate_element()
|
||||
|
||||
fe.set_bytes(inp) or { panic(err) }
|
||||
inp[inp.len - 1] &= (1 << 7) - 1 // mask the most significant bit
|
||||
|
||||
mut b := big.integer_from_bytes(swap_endianness(mut inp)) // need swap_endianness
|
||||
fe1.from_big_integer(b) or { panic(err) } // do swap_endianness internally
|
||||
|
||||
assert fe == fe1
|
||||
|
||||
mut buf := []u8{len: 32} // pad with zeroes
|
||||
fedtobig := fe1.to_big_integer()
|
||||
mut fedbig_bytes, _ := fedtobig.bytes()
|
||||
copy(mut buf[32 - fedbig_bytes.len..], fedbig_bytes)
|
||||
swap_endianness(mut buf)
|
||||
|
||||
assert fe.bytes() == buf && is_in_bounds(fe) && is_in_bounds(fe1)
|
||||
// assert big_equivalence(inp, fe, fe1) == true
|
||||
}
|
||||
|
||||
fn test_decimal_constants() {
|
||||
sqrtm1string := '19681161376707505956807079304988542015446066515923890162744021073123829784752'
|
||||
mut el := Element{}
|
||||
mut exp := el.from_decimal_string(sqrtm1string)!
|
||||
|
||||
assert sqrt_m1.equal(exp) == 1
|
||||
|
||||
dstring := '37095705934669439343138083508754565189542113879843219016388785533085940283555'
|
||||
exp = el.from_decimal_string(dstring)!
|
||||
mut d := d_const
|
||||
|
||||
assert d.equal(exp) == 1
|
||||
}
|
||||
|
||||
fn test_mul_64_to_128() {
|
||||
mut a := u64(5)
|
||||
mut b := u64(5)
|
||||
mut r := mul_64(a, b)
|
||||
|
||||
assert r.lo == 0x19
|
||||
assert r.hi == 0
|
||||
|
||||
a = u64(18014398509481983) // 2^54 - 1
|
||||
b = u64(18014398509481983) // 2^54 - 1
|
||||
r = mul_64(a, b)
|
||||
|
||||
assert r.lo == 0xff80000000000001 && r.hi == 0xfffffffffff
|
||||
|
||||
a = u64(1125899906842661)
|
||||
b = u64(2097155)
|
||||
r = mul_64(a, b)
|
||||
r = add_mul_64(r, a, b)
|
||||
r = add_mul_64(r, a, b)
|
||||
r = add_mul_64(r, a, b)
|
||||
r = add_mul_64(r, a, b)
|
||||
|
||||
assert r.lo == 16888498990613035 && r.hi == 640
|
||||
}
|
||||
|
||||
fn test_multiply_distributes_over_add() {
|
||||
for i in 0 .. 10 {
|
||||
el := Element{}
|
||||
x := el.generate_element()
|
||||
y := el.generate_element()
|
||||
z := el.generate_element()
|
||||
mut t1 := Element{}
|
||||
t1.add(x, y)
|
||||
t1.multiply(t1, z)
|
||||
|
||||
// Compute t2 = x*z + y*z
|
||||
mut t2 := Element{}
|
||||
mut t3 := Element{}
|
||||
t2.multiply(x, z)
|
||||
t3.multiply(y, z)
|
||||
t2.add(t2, t3)
|
||||
assert t1.equal(t2) == 1 && is_in_bounds(t1) && is_in_bounds(t2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
module edwards25519
|
||||
|
||||
// extended_coordinates returns v in extended coordinates (X:Y:Z:T) where
|
||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||
fn (mut v Point) extended_coordinates() (Element, Element, Element, Element) {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap. Don't change the style without making
|
||||
// sure it doesn't increase the inliner cost.
|
||||
mut e := []Element{len: 4}
|
||||
x, y, z, t := v.extended_coordinates_generic(mut e)
|
||||
return x, y, z, t
|
||||
}
|
||||
|
||||
fn (mut v Point) extended_coordinates_generic(mut e []Element) (Element, Element, Element, Element) {
|
||||
check_initialized(v)
|
||||
x := e[0].set(v.x)
|
||||
y := e[1].set(v.y)
|
||||
z := e[2].set(v.z)
|
||||
t := e[3].set(v.t)
|
||||
return x, y, z, t
|
||||
}
|
||||
|
||||
// Given k > 0, set s = s**(2*i).
|
||||
fn (mut s Scalar) pow2k(k int) {
|
||||
for i := 0; i < k; i++ {
|
||||
s.multiply(s, s)
|
||||
}
|
||||
}
|
||||
|
||||
// set_extended_coordinates sets v = (X:Y:Z:T) in extended coordinates where
|
||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||
//
|
||||
// If the coordinates are invalid or don't represent a valid point on the curve,
|
||||
// set_extended_coordinates returns an error and the receiver is
|
||||
// unchanged. Otherwise, set_extended_coordinates returns v.
|
||||
fn (mut v Point) set_extended_coordinates(x Element, y Element, z Element, t Element) !Point {
|
||||
if !is_on_curve(x, y, z, t) {
|
||||
return error('edwards25519: invalid point coordinates')
|
||||
}
|
||||
v.x.set(x)
|
||||
v.y.set(y)
|
||||
v.z.set(z)
|
||||
v.t.set(t)
|
||||
return v
|
||||
}
|
||||
|
||||
fn is_on_curve(x Element, y Element, z Element, t Element) bool {
|
||||
mut lhs := Element{}
|
||||
mut rhs := Element{}
|
||||
|
||||
mut xx := Element{}
|
||||
xx.square(x)
|
||||
|
||||
mut yy := Element{}
|
||||
yy.square(y)
|
||||
|
||||
mut zz := Element{}
|
||||
zz.square(z)
|
||||
// zz := new(Element).Square(Z)
|
||||
|
||||
mut tt := Element{}
|
||||
tt.square(t)
|
||||
// tt := new(Element).Square(T)
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// -(X/Z)² + (Y/Z)² = 1 + d(T/Z)²
|
||||
// -X² + Y² = Z² + dT²
|
||||
lhs.subtract(yy, xx)
|
||||
mut d := d_const
|
||||
rhs.multiply(d, tt)
|
||||
rhs.add(rhs, zz)
|
||||
if lhs.equal(rhs) != 1 {
|
||||
return false
|
||||
}
|
||||
// xy = T/Z
|
||||
// XY/Z² = T/Z
|
||||
// XY = TZ
|
||||
lhs.multiply(x, y)
|
||||
rhs.multiply(t, z)
|
||||
return lhs.equal(rhs) == 1
|
||||
}
|
||||
|
||||
// bytes_montgomery converts v to a point on the birationally-equivalent
|
||||
// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding
|
||||
// according to RFC 7748.
|
||||
//
|
||||
// Note that bytes_montgomery only encodes the u-coordinate, so v and -v encode
|
||||
// to the same value. If v is the identity point, bytes_montgomery returns 32
|
||||
// zero bytes, analogously to the X25519 function.
|
||||
pub fn (mut v Point) bytes_montgomery() []u8 {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
mut buf := [32]u8{}
|
||||
return v.bytes_montgomery_generic(mut buf)
|
||||
}
|
||||
|
||||
fn (mut v Point) bytes_montgomery_generic(mut buf [32]u8) []u8 {
|
||||
check_initialized(v)
|
||||
|
||||
// RFC 7748, Section 4.1 provides the bilinear map to calculate the
|
||||
// Montgomery u-coordinate
|
||||
//
|
||||
// u = (1 + y) / (1 - y)
|
||||
//
|
||||
// where y = Y / Z.
|
||||
|
||||
mut y := Element{}
|
||||
mut recip := Element{}
|
||||
mut u := Element{}
|
||||
|
||||
y.multiply(v.y, y.invert(v.z)) // y = Y / Z
|
||||
recip.invert(recip.subtract(fe_one, y)) // r = 1/(1 - y)
|
||||
u.multiply(u.add(fe_one, y), recip) // u = (1 + y)*r
|
||||
|
||||
return copy_field_element(mut buf, mut u)
|
||||
}
|
||||
|
||||
// mult_by_cofactor sets v = 8 * p, and returns v.
|
||||
pub fn (mut v Point) mult_by_cofactor(p Point) Point {
|
||||
check_initialized(p)
|
||||
mut result := ProjectiveP1{}
|
||||
mut pp := ProjectiveP2{}
|
||||
pp.from_p3(p)
|
||||
result.double(pp)
|
||||
pp.from_p1(result)
|
||||
result.double(pp)
|
||||
pp.from_p1(result)
|
||||
result.double(pp)
|
||||
return v.from_p1(result)
|
||||
}
|
||||
|
||||
// invert sets s to the inverse of a nonzero scalar v, and returns s.
|
||||
//
|
||||
// If t is zero, invert returns zero.
|
||||
pub fn (mut s Scalar) invert(t Scalar) Scalar {
|
||||
// Uses a hardcoded sliding window of width 4.
|
||||
mut table := [8]Scalar{}
|
||||
mut tt := Scalar{}
|
||||
tt.multiply(t, t)
|
||||
table[0] = t
|
||||
for i := 0; i < 7; i++ {
|
||||
table[i + 1].multiply(table[i], tt)
|
||||
}
|
||||
// Now table = [t**1, t**3, t**7, t**11, t**13, t**15]
|
||||
// so t**k = t[k/2] for odd k
|
||||
|
||||
// To compute the sliding window digits, use the following Sage script:
|
||||
|
||||
// sage: import itertools
|
||||
// sage: def sliding_window(w,k):
|
||||
// ....: digits = []
|
||||
// ....: while k > 0:
|
||||
// ....: if k % 2 == 1:
|
||||
// ....: kmod = k % (2**w)
|
||||
// ....: digits.append(kmod)
|
||||
// ....: k = k - kmod
|
||||
// ....: else:
|
||||
// ....: digits.append(0)
|
||||
// ....: k = k // 2
|
||||
// ....: return digits
|
||||
|
||||
// Now we can compute s roughly as follows:
|
||||
|
||||
// sage: s = 1
|
||||
// sage: for coeff in reversed(sliding_window(4,l-2)):
|
||||
// ....: s = s*s
|
||||
// ....: if coeff > 0 :
|
||||
// ....: s = s*t**coeff
|
||||
|
||||
// This works on one bit at a time, with many runs of zeros.
|
||||
// The digits can be collapsed into [(count, coeff)] as follows:
|
||||
|
||||
// sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))]
|
||||
|
||||
// Entries of the form (k, 0) turn into pow2k(k)
|
||||
// Entries of the form (1, coeff) turn into a squaring and then a table lookup.
|
||||
// We can fold the squaring into the previous pow2k(k) as pow2k(k+1).
|
||||
|
||||
s = table[1 / 2]
|
||||
s.pow2k(127 + 1)
|
||||
s.multiply(s, table[1 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[9 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[11 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[13 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[15 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[7 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[15 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[5 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[1 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[15 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[15 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[7 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[3 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[11 / 2])
|
||||
s.pow2k(5 + 1)
|
||||
s.multiply(s, table[11 / 2])
|
||||
s.pow2k(9 + 1)
|
||||
s.multiply(s, table[9 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[3 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[3 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[3 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[9 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[7 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[3 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[13 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[7 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[9 / 2])
|
||||
s.pow2k(3 + 1)
|
||||
s.multiply(s, table[15 / 2])
|
||||
s.pow2k(4 + 1)
|
||||
s.multiply(s, table[11 / 2])
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// multi_scalar_mult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||
//
|
||||
// Execution time depends only on the lengths of the two slices, which must match.
|
||||
pub fn (mut v Point) multi_scalar_mult(scalars []Scalar, points []Point) Point {
|
||||
if scalars.len != points.len {
|
||||
panic('edwards25519: called multi_scalar_mult with different size inputs')
|
||||
}
|
||||
check_initialized(...points)
|
||||
|
||||
mut sc := scalars.clone()
|
||||
// Proceed as in the single-base case, but share doublings
|
||||
// between each point in the multiscalar equation.
|
||||
|
||||
// Build lookup tables for each point
|
||||
mut tables := []ProjLookupTable{len: points.len}
|
||||
for i, _ in tables {
|
||||
tables[i].from_p3(points[i])
|
||||
}
|
||||
// Compute signed radix-16 digits for each scalar
|
||||
// digits := make([][64]int8, len(scalars))
|
||||
mut digits := [][]i8{len: sc.len, init: []i8{len: 64, cap: 64}}
|
||||
|
||||
for j, _ in digits {
|
||||
digits[j] = sc[j].signed_radix16()
|
||||
}
|
||||
|
||||
// Unwrap first loop iteration to save computing 16*identity
|
||||
mut multiple := ProjectiveCached{}
|
||||
mut tmp1 := ProjectiveP1{}
|
||||
mut tmp2 := ProjectiveP2{}
|
||||
// Lookup-and-add the appropriate multiple of each input point
|
||||
for k, _ in tables {
|
||||
tables[k].select_into(mut multiple, digits[k][63])
|
||||
tmp1.add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords
|
||||
v.from_p1(tmp1) // update v
|
||||
}
|
||||
tmp2.from_p3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||
for r := 62; r >= 0; r-- {
|
||||
tmp1.double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||
v.from_p1(tmp1) // v = 16*(prev) in P3 coords
|
||||
// Lookup-and-add the appropriate multiple of each input point
|
||||
for s, _ in tables {
|
||||
tables[s].select_into(mut multiple, digits[s][r])
|
||||
tmp1.add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords
|
||||
v.from_p1(tmp1) // update v
|
||||
}
|
||||
tmp2.from_p3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// vartime_multiscalar_mult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||
//
|
||||
// Execution time depends on the inputs.
|
||||
pub fn (mut v Point) vartime_multiscalar_mult(scalars []Scalar, points []Point) Point {
|
||||
if scalars.len != points.len {
|
||||
panic('edwards25519: called VarTimeMultiScalarMult with different size inputs')
|
||||
}
|
||||
check_initialized(...points)
|
||||
|
||||
// Generalize double-base NAF computation to arbitrary sizes.
|
||||
// Here all the points are dynamic, so we only use the smaller
|
||||
// tables.
|
||||
|
||||
// Build lookup tables for each point
|
||||
mut tables := []NafLookupTable5{len: points.len}
|
||||
for i, _ in tables {
|
||||
tables[i].from_p3(points[i])
|
||||
}
|
||||
mut sc := scalars.clone()
|
||||
// Compute a NAF for each scalar
|
||||
// mut nafs := make([][256]int8, len(scalars))
|
||||
mut nafs := [][]i8{len: sc.len, init: []i8{len: 256, cap: 256}}
|
||||
for j, _ in nafs {
|
||||
nafs[j] = sc[j].non_adjacent_form(5)
|
||||
}
|
||||
|
||||
mut multiple := ProjectiveCached{}
|
||||
mut tmp1 := ProjectiveP1{}
|
||||
mut tmp2 := ProjectiveP2{}
|
||||
tmp2.zero()
|
||||
|
||||
// Move from high to low bits, doubling the accumulator
|
||||
// at each iteration and checking whether there is a nonzero
|
||||
// coefficient to look up a multiple of.
|
||||
//
|
||||
// Skip trying to find the first nonzero coefficient, because
|
||||
// searching might be more work than a few extra doublings.
|
||||
// k == i, l == j
|
||||
for k := 255; k >= 0; k-- {
|
||||
tmp1.double(tmp2)
|
||||
|
||||
for l, _ in nafs {
|
||||
if nafs[l][k] > 0 {
|
||||
v.from_p1(tmp1)
|
||||
tables[l].select_into(mut multiple, nafs[l][k])
|
||||
tmp1.add(v, multiple)
|
||||
} else if nafs[l][k] < 0 {
|
||||
v.from_p1(tmp1)
|
||||
tables[l].select_into(mut multiple, -nafs[l][k])
|
||||
tmp1.sub(v, multiple)
|
||||
}
|
||||
}
|
||||
|
||||
tmp2.from_p1(tmp1)
|
||||
}
|
||||
|
||||
v.from_p2(tmp2)
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
module edwards25519
|
||||
|
||||
import os
|
||||
import rand
|
||||
import encoding.hex
|
||||
|
||||
const github_job = os.getenv('GITHUB_JOB')
|
||||
|
||||
fn testsuite_begin() {
|
||||
if github_job != '' {
|
||||
// ensure that the CI does not run flaky tests:
|
||||
rand.seed([u32(0xffff24), 0xabcd])
|
||||
}
|
||||
}
|
||||
|
||||
// test_bytes_montgomery tests the set_bytes_with_clamping+bytes_montgomery path
|
||||
// equivalence to curve25519.X25519 for basepoint scalar multiplications.
|
||||
//
|
||||
// Note that you can't actually implement X25519 with this package because
|
||||
// there is no SetBytesMontgomery, and it would not be possible to implement
|
||||
// it properly: points on the twist would get rejected, and the Scalar returned
|
||||
// by set_bytes_with_clamping does not preserve its cofactor-clearing properties.
|
||||
//
|
||||
// Disabled curve25519 not available yet, but maybe can use own curve25519
|
||||
/*
|
||||
fn fn_mon(scalar [32]u8) bool {
|
||||
mut s := new_scalar().set_bytes_with_clamping(scalar[..])
|
||||
p := (&Point{}).scalar_base_mult(s)
|
||||
got := p.bytes_montgomery()
|
||||
want, _ := curve25519.X25519(scalar[..], curve25519.Basepoint)
|
||||
return bytes.equal(got, want)
|
||||
}
|
||||
|
||||
fn test_bytes_montgomery() {
|
||||
/* f := fn(scalar [32]u8) bool {
|
||||
s := new_scalar().set_bytes_with_clamping(scalar[..])
|
||||
p := (&Point{}).scalar_base_mult(s)
|
||||
got := p.bytes_montgomery()
|
||||
want, _ := curve25519.X25519(scalar[..], curve25519.Basepoint)
|
||||
return bytes.equal(got, want)
|
||||
} */
|
||||
if err := quick.Check(f, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}*/
|
||||
|
||||
fn test_bytes_montgomery_sodium() {
|
||||
// Generated with libsodium.js 1.0.18
|
||||
// crypto_sign_keypair().pubkey
|
||||
pubkey := '3bf918ffc2c955dc895bf145f566fb96623c1cadbe040091175764b5fde322c0'
|
||||
mut p := Point{}
|
||||
p.set_bytes(hex.decode(pubkey)!)!
|
||||
|
||||
// crypto_sign_ed25519_pk_to_curve25519(pubkey)
|
||||
want := 'efc6c9d0738e9ea18d738ad4a2653631558931b0f1fde4dd58c436d19686dc28'
|
||||
got := hex.encode(p.bytes_montgomery())
|
||||
assert got == want
|
||||
}
|
||||
|
||||
fn test_bytes_montgomery_infinity() {
|
||||
mut p := new_identity_point()
|
||||
want := '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
got := hex.encode(p.bytes_montgomery())
|
||||
|
||||
assert got == want
|
||||
}
|
||||
|
||||
const loworder_string = '26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85'
|
||||
const loworder_bytes = hex.decode(loworder_string) or { []u8{} }
|
||||
|
||||
fn fn_cofactor(mut data []u8) bool {
|
||||
if data.len != 64 {
|
||||
panic('data.len should be 64')
|
||||
}
|
||||
mut loworder := Point{}
|
||||
loworder.set_bytes(loworder_bytes) or { return false }
|
||||
|
||||
mut s := new_scalar()
|
||||
mut p := Point{}
|
||||
mut p8 := Point{}
|
||||
s.set_uniform_bytes(data) or { panic(err) }
|
||||
p.scalar_base_mult(mut s)
|
||||
p8.mult_by_cofactor(p)
|
||||
|
||||
assert check_on_curve(p8) == true
|
||||
|
||||
// 8 * p == (8 * s) * B
|
||||
mut sc := Scalar{
|
||||
s: [32]u8{}
|
||||
}
|
||||
sc.s[0] = u8(0x08)
|
||||
s.multiply(s, sc)
|
||||
mut pp := Point{}
|
||||
pp.scalar_base_mult(mut s)
|
||||
if p8.equal(pp) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 8 * p == 8 * (loworder + p)
|
||||
pp.add(p, loworder)
|
||||
pp.mult_by_cofactor(pp)
|
||||
if p8.equal(pp) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 8 * p == p + p + p + p + p + p + p + p
|
||||
pp.set(new_identity_point())
|
||||
for i := 0; i < 8; i++ {
|
||||
pp.add(pp, p)
|
||||
}
|
||||
return p8.equal(pp) == 1
|
||||
}
|
||||
|
||||
fn test_mult_by_cofactor() {
|
||||
mut loworder := Point{}
|
||||
mut data := rand.bytes(64)!
|
||||
|
||||
assert fn_cofactor(mut data) == true
|
||||
}
|
||||
|
||||
fn invert_works(mut xinv Scalar, x NotZeroScalar) bool {
|
||||
xinv.invert(x)
|
||||
mut check := Scalar{}
|
||||
check.multiply(x, xinv)
|
||||
return check == sc_one && is_reduced(xinv)
|
||||
}
|
||||
|
||||
fn test_scalar_invert() {
|
||||
nsc := generate_notzero_scalar(5) or { panic(err) }
|
||||
mut xsc := generate_scalar(5) or { panic(err) }
|
||||
assert invert_works(mut xsc, nsc) == true
|
||||
|
||||
mut zero := new_scalar()
|
||||
mut xx := new_scalar()
|
||||
xx.invert(zero)
|
||||
assert xx.equal(zero) == 1
|
||||
}
|
||||
|
||||
fn test_multiscalarmultmatchesbasemult() {
|
||||
for i in 0 .. 6 {
|
||||
x := generate_scalar(100) or { panic(err) }
|
||||
y := generate_scalar(5) or { panic(err) }
|
||||
z := generate_scalar(2) or { panic(err) }
|
||||
assert multiscalarmultmatchesbasemult(x, y, z) == true
|
||||
}
|
||||
}
|
||||
|
||||
fn multiscalarmultmatchesbasemult(xx Scalar, yy Scalar, zz Scalar) bool {
|
||||
mut x := xx
|
||||
mut y := yy
|
||||
mut z := zz
|
||||
|
||||
mut p := Point{}
|
||||
mut q1 := Point{}
|
||||
mut q2 := Point{}
|
||||
mut q3 := Point{}
|
||||
mut check := Point{}
|
||||
mut b := new_generator_point()
|
||||
|
||||
p.multi_scalar_mult([x, y, z], [b, b, b])
|
||||
|
||||
q1.scalar_base_mult(mut x)
|
||||
q2.scalar_base_mult(mut y)
|
||||
q3.scalar_base_mult(mut z)
|
||||
check.add(q1, q2)
|
||||
check.add(check, q3)
|
||||
|
||||
check_on_curve(p, check, q1, q2, q3)
|
||||
return p.equal(check) == 1
|
||||
}
|
||||
|
||||
fn vartime_multiscala_rmultmatches_basemult(xx Scalar, yy Scalar, zz Scalar) bool {
|
||||
mut x := xx
|
||||
mut y := yy
|
||||
mut z := zz
|
||||
mut p := Point{}
|
||||
mut q1 := Point{}
|
||||
mut q2 := Point{}
|
||||
mut q3 := Point{}
|
||||
mut check := Point{}
|
||||
mut b := new_generator_point()
|
||||
|
||||
p.vartime_multiscalar_mult([x, y, z], [b, b, b])
|
||||
|
||||
q1.scalar_base_mult(mut x)
|
||||
q2.scalar_base_mult(mut y)
|
||||
q3.scalar_base_mult(mut z)
|
||||
check.add(q1, q2)
|
||||
check.add(check, q3)
|
||||
|
||||
check_on_curve(p, check, q1, q2, q3)
|
||||
return p.equal(check) == 1
|
||||
}
|
||||
|
||||
fn test_vartimemultiscalarmultmatchesbasemult() {
|
||||
for i in 0 .. 5 {
|
||||
x := generate_scalar(100) or { panic(err) }
|
||||
y := generate_scalar(5) or { panic(err) }
|
||||
z := generate_scalar(2) or { panic(err) }
|
||||
assert vartime_multiscala_rmultmatches_basemult(x, y, z) == true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
module edwards25519
|
||||
|
||||
// d is a constant in the curve equation.
|
||||
const d_bytes = [u8(0xa3), 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, 0xab, 0xd8, 0x41, 0x41, 0x4d,
|
||||
0x0a, 0x70, 0x00, 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, 0x73, 0xfe, 0x6f, 0x2b, 0xee,
|
||||
0x6c, 0x03, 0x52]
|
||||
const id_bytes = [u8(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0]
|
||||
const gen_bytes = [u8(0x58), 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66]
|
||||
const d_const = d_const_generate() or { panic(err) }
|
||||
const d2_const = d2_const_generate() or { panic(err) }
|
||||
// id_point is the point at infinity.
|
||||
const id_point = id_point_generate() or { panic(err) }
|
||||
// generator point
|
||||
const gen_point = generator() or { panic(err) }
|
||||
|
||||
fn d_const_generate() !Element {
|
||||
mut v := Element{}
|
||||
v.set_bytes(d_bytes)!
|
||||
return v
|
||||
}
|
||||
|
||||
fn d2_const_generate() !Element {
|
||||
mut v := Element{}
|
||||
v.add(d_const, d_const)
|
||||
return v
|
||||
}
|
||||
|
||||
// id_point_generate is the point at infinity.
|
||||
fn id_point_generate() !Point {
|
||||
mut p := Point{}
|
||||
p.set_bytes(id_bytes)!
|
||||
return p
|
||||
}
|
||||
|
||||
// generator is the canonical curve basepoint. See TestGenerator for the
|
||||
// correspondence of this encoding with the values in RFC 8032.
|
||||
fn generator() !Point {
|
||||
mut p := Point{}
|
||||
p.set_bytes(gen_bytes)!
|
||||
return p
|
||||
}
|
||||
|
||||
// Point types.
|
||||
struct ProjectiveP1 {
|
||||
mut:
|
||||
x Element
|
||||
y Element
|
||||
z Element
|
||||
t Element
|
||||
}
|
||||
|
||||
struct ProjectiveP2 {
|
||||
mut:
|
||||
x Element
|
||||
y Element
|
||||
z Element
|
||||
}
|
||||
|
||||
// Point represents a point on the edwards25519 curve.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is NOT valid, and it may be used only as a receiver.
|
||||
pub struct Point {
|
||||
mut:
|
||||
// The point is internally represented in extended coordinates (x, y, z, T)
|
||||
// where x = x/z, y = y/z, and xy = T/z per https://eprint.iacr.org/2008/522.
|
||||
x Element
|
||||
y Element
|
||||
z Element
|
||||
t Element
|
||||
// Make the type not comparable (i.e. used with == or as a map key), as
|
||||
// equivalent points can be represented by different values.
|
||||
// _ incomparable
|
||||
}
|
||||
|
||||
fn check_initialized(points ...Point) {
|
||||
for _, p in points {
|
||||
if p.x == fe_zero && p.y == fe_zero {
|
||||
panic('edwards25519: use of uninitialized Point')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProjectiveCached {
|
||||
mut:
|
||||
ypx Element // y + x
|
||||
ymx Element // y - x
|
||||
z Element
|
||||
t2d Element
|
||||
}
|
||||
|
||||
struct AffineCached {
|
||||
mut:
|
||||
ypx Element // y + x
|
||||
ymx Element // y - x
|
||||
t2d Element
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveP2) zero() ProjectiveP2 {
|
||||
v.x.zero()
|
||||
v.y.one()
|
||||
v.z.one()
|
||||
return v
|
||||
}
|
||||
|
||||
// set_bytes sets v = x, where x is a 32-byte encoding of v. If x does not
|
||||
// represent a valid point on the curve, set_bytes returns an error and
|
||||
// the receiver is unchanged. Otherwise, set_bytes returns v.
|
||||
//
|
||||
// Note that set_bytes accepts all non-canonical encodings of valid points.
|
||||
// That is, it follows decoding rules that match most implementations in
|
||||
// the ecosystem rather than RFC 8032.
|
||||
pub fn (mut v Point) set_bytes(x []u8) !Point {
|
||||
// Specifically, the non-canonical encodings that are accepted are
|
||||
// 1) the ones where the edwards25519 element is not reduced (see the
|
||||
// (*edwards25519.Element).set_bytes docs) and
|
||||
// 2) the ones where the x-coordinate is zero and the sign bit is set.
|
||||
//
|
||||
// This is consistent with crypto/ed25519/internal/edwards25519. Read more
|
||||
// at https://hdevalence.ca/blog/2020-10-04-its-25519am, specifically the
|
||||
// "Canonical A, R" section.
|
||||
mut el0 := Element{}
|
||||
y := el0.set_bytes(x) or { return error('edwards25519: invalid point encoding length') }
|
||||
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// x² + dx²y² = x²(dy² + 1) = y² - 1
|
||||
// x² = (y² - 1) / (dy² + 1)
|
||||
|
||||
// u = y² - 1
|
||||
mut el1 := Element{}
|
||||
y2 := el1.square(y)
|
||||
mut el2 := Element{}
|
||||
u := el2.subtract(y2, fe_one)
|
||||
|
||||
// v = dy² + 1
|
||||
mut el3 := Element{}
|
||||
mut vv := el3.multiply(y2, d_const)
|
||||
vv = vv.add(vv, fe_one)
|
||||
|
||||
// x = +√(u/v)
|
||||
mut el4 := Element{}
|
||||
mut xx, was_square := el4.sqrt_ratio(u, vv)
|
||||
if was_square == 0 {
|
||||
return error('edwards25519: invalid point encoding')
|
||||
}
|
||||
|
||||
// selected the negative square root if the sign bit is set.
|
||||
mut el5 := Element{}
|
||||
xx_neg := el5.negate(xx)
|
||||
xx.selected(xx_neg, xx, int(x[31] >> 7))
|
||||
|
||||
v.x.set(xx)
|
||||
v.y.set(y)
|
||||
v.z.one()
|
||||
v.t.multiply(xx, y) // xy = T / z
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// set sets v = u, and returns v.
|
||||
pub fn (mut v Point) set(u Point) Point {
|
||||
v = u
|
||||
return v
|
||||
}
|
||||
|
||||
// new_identity_point returns a new Point set to the identity.
|
||||
pub fn new_identity_point() Point {
|
||||
mut p := Point{}
|
||||
return p.set(id_point)
|
||||
}
|
||||
|
||||
// new_generator_point returns a new Point set to the canonical generator.
|
||||
pub fn new_generator_point() Point {
|
||||
mut p := Point{}
|
||||
return p.set(gen_point)
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveCached) zero() ProjectiveCached {
|
||||
v.ypx.one()
|
||||
v.ymx.one()
|
||||
v.z.one()
|
||||
v.t2d.zero()
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v AffineCached) zero() AffineCached {
|
||||
v.ypx.one()
|
||||
v.ymx.one()
|
||||
v.t2d.zero()
|
||||
return v
|
||||
}
|
||||
|
||||
// Encoding.
|
||||
|
||||
// bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
|
||||
// Section 5.1.2.
|
||||
pub fn (mut v Point) bytes() []u8 {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
mut buf := [32]u8{}
|
||||
return v.bytes_generic(mut buf)
|
||||
}
|
||||
|
||||
fn (mut v Point) bytes_generic(mut buf [32]u8) []u8 {
|
||||
check_initialized(v)
|
||||
|
||||
mut zinv := Element{}
|
||||
mut x := Element{}
|
||||
mut y := Element{}
|
||||
zinv.invert(v.z) // zinv = 1 / z
|
||||
x.multiply(v.x, zinv) // x = x / z
|
||||
y.multiply(v.y, zinv) // y = y / z
|
||||
|
||||
mut out := copy_field_element(mut buf, mut y)
|
||||
unsafe {
|
||||
// out[31] |= u8(x.is_negative() << 7) //original one
|
||||
out[31] |= u8(x.is_negative() * 128) // x << 7 == x * 2^7
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fn copy_field_element(mut buf [32]u8, mut v Element) []u8 {
|
||||
// this fail in test
|
||||
/*
|
||||
copy(mut buf[..], v.bytes())
|
||||
return buf[..]
|
||||
*/
|
||||
|
||||
// this pass the test
|
||||
mut out := []u8{len: 32}
|
||||
for i := 0; i <= buf.len - 1; i++ {
|
||||
out[i] = v.bytes()[i]
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Conversions.
|
||||
|
||||
fn (mut v ProjectiveP2) from_p1(p ProjectiveP1) ProjectiveP2 {
|
||||
v.x.multiply(p.x, p.t)
|
||||
v.y.multiply(p.y, p.z)
|
||||
v.z.multiply(p.z, p.t)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveP2) from_p3(p Point) ProjectiveP2 {
|
||||
v.x.set(p.x)
|
||||
v.y.set(p.y)
|
||||
v.z.set(p.z)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v Point) from_p1(p ProjectiveP1) Point {
|
||||
v.x.multiply(p.x, p.t)
|
||||
v.y.multiply(p.y, p.z)
|
||||
v.z.multiply(p.z, p.t)
|
||||
v.t.multiply(p.x, p.y)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v Point) from_p2(p ProjectiveP2) Point {
|
||||
v.x.multiply(p.x, p.z)
|
||||
v.y.multiply(p.y, p.z)
|
||||
v.z.square(p.z)
|
||||
v.t.multiply(p.x, p.y)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveCached) from_p3(p Point) ProjectiveCached {
|
||||
v.ypx.add(p.y, p.x)
|
||||
v.ymx.subtract(p.y, p.x)
|
||||
v.z.set(p.z)
|
||||
v.t2d.multiply(p.t, d2_const)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v AffineCached) from_p3(p Point) AffineCached {
|
||||
v.ypx.add(p.y, p.x)
|
||||
v.ymx.subtract(p.y, p.x)
|
||||
v.t2d.multiply(p.t, d2_const)
|
||||
|
||||
mut invz := Element{}
|
||||
invz.invert(p.z)
|
||||
v.ypx.multiply(v.ypx, invz)
|
||||
v.ymx.multiply(v.ymx, invz)
|
||||
v.t2d.multiply(v.t2d, invz)
|
||||
return v
|
||||
}
|
||||
|
||||
// (Re)addition and subtraction.
|
||||
|
||||
// add sets v = p + q, and returns v.
|
||||
pub fn (mut v Point) add(p Point, q Point) Point {
|
||||
check_initialized(p, q)
|
||||
mut pc := ProjectiveCached{}
|
||||
mut p1 := ProjectiveP1{}
|
||||
qcached := pc.from_p3(q)
|
||||
|
||||
result := p1.add(p, qcached)
|
||||
return v.from_p1(result)
|
||||
}
|
||||
|
||||
// subtract sets v = p - q, and returns v.
|
||||
pub fn (mut v Point) subtract(p Point, q Point) Point {
|
||||
check_initialized(p, q)
|
||||
mut pc := ProjectiveCached{}
|
||||
mut p1 := ProjectiveP1{}
|
||||
qcached := pc.from_p3(q)
|
||||
result := p1.sub(p, qcached)
|
||||
return v.from_p1(result)
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveP1) add(p Point, q ProjectiveCached) ProjectiveP1 {
|
||||
// var ypx, ymx, pp, mm, tt2d, zz2 edwards25519.Element
|
||||
mut ypx := Element{}
|
||||
mut ymx := Element{}
|
||||
mut pp := Element{}
|
||||
mut mm := Element{}
|
||||
mut tt2d := Element{}
|
||||
mut zz2 := Element{}
|
||||
|
||||
ypx.add(p.y, p.x)
|
||||
ymx.subtract(p.y, p.x)
|
||||
|
||||
pp.multiply(ypx, q.ypx)
|
||||
mm.multiply(ymx, q.ymx)
|
||||
tt2d.multiply(p.t, q.t2d)
|
||||
zz2.multiply(p.z, q.z)
|
||||
|
||||
zz2.add(zz2, zz2)
|
||||
|
||||
v.x.subtract(pp, mm)
|
||||
v.y.add(pp, mm)
|
||||
v.z.add(zz2, tt2d)
|
||||
v.t.subtract(zz2, tt2d)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveP1) sub(p Point, q ProjectiveCached) ProjectiveP1 {
|
||||
mut ypx := Element{}
|
||||
mut ymx := Element{}
|
||||
mut pp := Element{}
|
||||
mut mm := Element{}
|
||||
mut tt2d := Element{}
|
||||
mut zz2 := Element{}
|
||||
|
||||
ypx.add(p.y, p.x)
|
||||
ymx.subtract(p.y, p.x)
|
||||
|
||||
pp.multiply(ypx, q.ymx) // flipped sign
|
||||
mm.multiply(ymx, q.ypx) // flipped sign
|
||||
tt2d.multiply(p.t, q.t2d)
|
||||
zz2.multiply(p.z, q.z)
|
||||
|
||||
zz2.add(zz2, zz2)
|
||||
|
||||
v.x.subtract(pp, mm)
|
||||
v.y.add(pp, mm)
|
||||
v.z.subtract(zz2, tt2d) // flipped sign
|
||||
v.t.add(zz2, tt2d) // flipped sign
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveP1) add_affine(p Point, q AffineCached) ProjectiveP1 {
|
||||
mut ypx := Element{}
|
||||
mut ymx := Element{}
|
||||
mut pp := Element{}
|
||||
mut mm := Element{}
|
||||
mut tt2d := Element{}
|
||||
mut z2 := Element{}
|
||||
|
||||
ypx.add(p.y, p.x)
|
||||
ymx.subtract(p.y, p.x)
|
||||
|
||||
pp.multiply(ypx, q.ypx)
|
||||
mm.multiply(ymx, q.ymx)
|
||||
tt2d.multiply(p.t, q.t2d)
|
||||
|
||||
z2.add(p.z, p.z)
|
||||
|
||||
v.x.subtract(pp, mm)
|
||||
v.y.add(pp, mm)
|
||||
v.z.add(z2, tt2d)
|
||||
v.t.subtract(z2, tt2d)
|
||||
return v
|
||||
}
|
||||
|
||||
fn (mut v ProjectiveP1) sub_affine(p Point, q AffineCached) ProjectiveP1 {
|
||||
mut ypx := Element{}
|
||||
mut ymx := Element{}
|
||||
mut pp := Element{}
|
||||
mut mm := Element{}
|
||||
mut tt2d := Element{}
|
||||
mut z2 := Element{}
|
||||
|
||||
ypx.add(p.y, p.x)
|
||||
ymx.subtract(p.y, p.x)
|
||||
|
||||
pp.multiply(ypx, q.ymx) // flipped sign
|
||||
mm.multiply(ymx, q.ypx) // flipped sign
|
||||
tt2d.multiply(p.t, q.t2d)
|
||||
|
||||
z2.add(p.z, p.z)
|
||||
|
||||
v.x.subtract(pp, mm)
|
||||
v.y.add(pp, mm)
|
||||
v.z.subtract(z2, tt2d) // flipped sign
|
||||
v.t.add(z2, tt2d) // flipped sign
|
||||
return v
|
||||
}
|
||||
|
||||
// Doubling.
|
||||
|
||||
fn (mut v ProjectiveP1) double(p ProjectiveP2) ProjectiveP1 {
|
||||
// var xx, yy, zz2, xplusysq edwards25519.Element
|
||||
mut xx := Element{}
|
||||
mut yy := Element{}
|
||||
mut zz2 := Element{}
|
||||
mut xplusysq := Element{}
|
||||
|
||||
xx.square(p.x)
|
||||
yy.square(p.y)
|
||||
zz2.square(p.z)
|
||||
zz2.add(zz2, zz2)
|
||||
xplusysq.add(p.x, p.y)
|
||||
xplusysq.square(xplusysq)
|
||||
|
||||
v.y.add(yy, xx)
|
||||
v.z.subtract(yy, xx)
|
||||
|
||||
v.x.subtract(xplusysq, v.y)
|
||||
v.t.subtract(zz2, v.z)
|
||||
return v
|
||||
}
|
||||
|
||||
// Negation.
|
||||
|
||||
// negate sets v = -p, and returns v.
|
||||
pub fn (mut v Point) negate(p Point) Point {
|
||||
check_initialized(p)
|
||||
v.x.negate(p.x)
|
||||
v.y.set(p.y)
|
||||
v.z.set(p.z)
|
||||
v.t.negate(p.t)
|
||||
return v
|
||||
}
|
||||
|
||||
// equal returns 1 if v is equivalent to u, and 0 otherwise.
|
||||
pub fn (mut v Point) equal(u Point) int {
|
||||
check_initialized(v, u)
|
||||
|
||||
mut t1 := Element{}
|
||||
mut t2 := Element{}
|
||||
mut t3 := Element{}
|
||||
mut t4 := Element{}
|
||||
|
||||
t1.multiply(v.x, u.z)
|
||||
t2.multiply(u.x, v.z)
|
||||
t3.multiply(v.y, u.z)
|
||||
t4.multiply(u.y, v.z)
|
||||
|
||||
return t1.equal(t2) & t3.equal(t4)
|
||||
}
|
||||
|
||||
// Constant-time operations
|
||||
|
||||
// selected sets v to a if cond == 1 and to b if cond == 0.
|
||||
fn (mut v ProjectiveCached) selected(a ProjectiveCached, b ProjectiveCached, cond int) ProjectiveCached {
|
||||
v.ypx.selected(a.ypx, b.ypx, cond)
|
||||
v.ymx.selected(a.ymx, b.ymx, cond)
|
||||
v.z.selected(a.z, b.z, cond)
|
||||
v.t2d.selected(a.t2d, b.t2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// selected sets v to a if cond == 1 and to b if cond == 0.
|
||||
fn (mut v AffineCached) selected(a AffineCached, b AffineCached, cond int) AffineCached {
|
||||
v.ypx.selected(a.ypx, b.ypx, cond)
|
||||
v.ymx.selected(a.ymx, b.ymx, cond)
|
||||
v.t2d.selected(a.t2d, b.t2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// cond_neg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||
fn (mut v ProjectiveCached) cond_neg(cond int) ProjectiveCached {
|
||||
mut el := Element{}
|
||||
v.ypx.swap(mut v.ymx, cond)
|
||||
v.t2d.selected(el.negate(v.t2d), v.t2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// cond_neg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||
fn (mut v AffineCached) cond_neg(cond int) AffineCached {
|
||||
mut el := Element{}
|
||||
v.ypx.swap(mut v.ymx, cond)
|
||||
v.t2d.selected(el.negate(v.t2d), v.t2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
fn check_on_curve(points ...Point) bool {
|
||||
for p in points {
|
||||
mut xx := Element{}
|
||||
mut yy := Element{}
|
||||
mut zz := Element{}
|
||||
mut zzzz := Element{}
|
||||
xx.square(p.x)
|
||||
yy.square(p.y)
|
||||
zz.square(p.z)
|
||||
zzzz.square(zz)
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// -(X/Z)² + (Y/Z)² = 1 + d(X/Z)²(Y/Z)²
|
||||
// (-X² + Y²)/Z² = 1 + (dX²Y²)/Z⁴
|
||||
// (-X² + Y²)*Z² = Z⁴ + dX²Y²
|
||||
mut lhs := Element{}
|
||||
mut rhs := Element{}
|
||||
lhs.subtract(yy, xx)
|
||||
lhs.multiply(lhs, zz)
|
||||
rhs.multiply(d_const, xx)
|
||||
rhs.multiply(rhs, yy)
|
||||
rhs.add(rhs, zzzz)
|
||||
|
||||
if lhs.equal(rhs) != 1 {
|
||||
return false
|
||||
}
|
||||
/*
|
||||
if lhs.equal(rhs) != 1 {
|
||||
lg.error('X, Y, and Z do not specify a point on the curve\nX = ${p.x} \nY = ${p.y}\nZ = ${p.z}')
|
||||
}*/
|
||||
|
||||
// xy = T/Z
|
||||
lhs.multiply(p.x, p.y)
|
||||
rhs.multiply(p.z, p.t)
|
||||
/*
|
||||
if lhs.equal(rhs) != 1 {
|
||||
lg.error('point ${i} is not valid\nX = ${p.x}\nY = ${p.y}\nZ = ${p.z}')
|
||||
}*/
|
||||
if lhs.equal(rhs) != 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
module edwards25519
|
||||
|
||||
import encoding.hex
|
||||
|
||||
const zero_point = Point{fe_zero, fe_zero, fe_zero, fe_zero}
|
||||
|
||||
fn test_invalid_encodings() {
|
||||
// An invalid point, that also happens to have y > p.
|
||||
invalid := 'efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f'
|
||||
inv_bytes := hex.decode(invalid) or { []u8{} }
|
||||
mut p := new_generator_point()
|
||||
|
||||
out := p.set_bytes(inv_bytes) or { zero_point }
|
||||
assert out == zero_point
|
||||
// assert p.equal(bgp) == 1 //not makes sense when error
|
||||
|
||||
assert check_on_curve(p) == true
|
||||
}
|
||||
|
||||
fn test_add_sub_neg_on_basepoint() {
|
||||
bgp := new_generator_point()
|
||||
mut idp := new_identity_point()
|
||||
mut checklhs := Point{}
|
||||
mut checkrhs := Point{}
|
||||
|
||||
checklhs.add(bgp, bgp)
|
||||
|
||||
mut proj_p1 := ProjectiveP1{}
|
||||
mut proj_p2 := ProjectiveP2{}
|
||||
|
||||
tmp_p2 := proj_p2.from_p3(bgp)
|
||||
tmp_p1 := proj_p1.double(tmp_p2)
|
||||
checkrhs.from_p1(tmp_p1)
|
||||
|
||||
assert checklhs.equal(checkrhs) == 1
|
||||
assert check_on_curve(checklhs, checkrhs) == true
|
||||
|
||||
checklhs.subtract(bgp, bgp)
|
||||
mut p0 := Point{}
|
||||
bneg := p0.negate(bgp)
|
||||
checkrhs.add(bgp, bneg)
|
||||
|
||||
assert checklhs.equal(checkrhs) == 1
|
||||
assert idp.equal(checklhs) == 1
|
||||
assert idp.equal(checkrhs) == 1
|
||||
assert check_on_curve(checklhs, checkrhs, bneg) == true
|
||||
}
|
||||
|
||||
struct NonCanonicalTest {
|
||||
name string
|
||||
encoding string
|
||||
canonical string
|
||||
}
|
||||
|
||||
fn test_non_canonical_points() {
|
||||
tests := [
|
||||
// Points with x = 0 and the sign bit set. With x = 0 the curve equation
|
||||
// gives y² = 1, so y = ±1. 1 has two valid encodings.
|
||||
NonCanonicalTest{'y=1,sign-', '0100000000000000000000000000000000000000000000000000000000000080', '0100000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+1,sign-', 'eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0100000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p-1,sign-', 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f'},
|
||||
// Non-canonical y encodings with values 2²⁵⁵-19 (p) to 2²⁵⁵-1 (p+18).
|
||||
NonCanonicalTest{'y=p,sign+', 'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0000000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p,sign-', 'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0000000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+1,sign+', 'eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0100000000000000000000000000000000000000000000000000000000000000'},
|
||||
// "y=p+1,sign-" is already tested above.
|
||||
// p+2 is not a valid y-coordinate.
|
||||
NonCanonicalTest{'y=p+3,sign+', 'f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0300000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+3,sign-', 'f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0300000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+4,sign+', 'f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0400000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+4,sign-', 'f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0400000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+5,sign+', 'f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0500000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+5,sign-', 'f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0500000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+6,sign+', 'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0600000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+6,sign-', 'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0600000000000000000000000000000000000000000000000000000000000080'},
|
||||
// p+7 is not a valid y-coordinate.
|
||||
// p+8 is not a valid y-coordinate.
|
||||
NonCanonicalTest{'y=p+9,sign+', 'f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0900000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+9,sign-', 'f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0900000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+10,sign+', 'f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0a00000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+10,sign-', 'f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0a00000000000000000000000000000000000000000000000000000000000080'},
|
||||
// p+11 is not a valid y-coordinate.
|
||||
// p+12 is not a valid y-coordinate.
|
||||
// p+13 is not a valid y-coordinate.
|
||||
NonCanonicalTest{'y=p+14,sign+', 'fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0e00000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+14,sign-', 'fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0e00000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+15,sign+', 'fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '0f00000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+15,sign-', 'fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '0f00000000000000000000000000000000000000000000000000000000000080'},
|
||||
NonCanonicalTest{'y=p+16,sign+', 'fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '1000000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+16,sign-', 'fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '1000000000000000000000000000000000000000000000000000000000000080'},
|
||||
// p+17 is not a valid y-coordinate.
|
||||
NonCanonicalTest{'y=p+18,sign+', 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', '1200000000000000000000000000000000000000000000000000000000000000'},
|
||||
NonCanonicalTest{'y=p+18,sign-', 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', '1200000000000000000000000000000000000000000000000000000000000080'},
|
||||
]
|
||||
for tt in tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// p1, err := new(Point).SetBytes(decodeHex(tt.encoding))
|
||||
mut p1 := Point{}
|
||||
p1.set_bytes(hex.decode(tt.encoding) or { []u8{} })!
|
||||
|
||||
// p2, err := new(Point).SetBytes(decodeHex(tt.canonical))
|
||||
mut p2 := Point{}
|
||||
p2.set_bytes(hex.decode(tt.canonical) or { []u8{} })!
|
||||
|
||||
assert p1.equal(p2) == 1
|
||||
assert p1.bytes() == p2.bytes()
|
||||
assert hex.encode(p1.bytes()) == tt.canonical // NEED FIX!
|
||||
|
||||
assert check_on_curve(p1, p2) == true
|
||||
}
|
||||
}
|
||||
|
||||
fn test_generator() {
|
||||
// These are the coordinates of B from RFC 8032, Section 5.1, converted to
|
||||
// little endian hex.
|
||||
x := '1ad5258f602d56c9b2a7259560c72c695cdcd6fd31e2a4c0fe536ecdd3366921'
|
||||
y := '5866666666666666666666666666666666666666666666666666666666666666'
|
||||
mut b := new_generator_point()
|
||||
|
||||
assert hex.encode(b.x.bytes()) == x
|
||||
assert hex.encode(b.y.bytes()) == y
|
||||
assert b.z.equal(fe_one) == 1
|
||||
// Check that t is correct.
|
||||
assert check_on_curve(b) == true
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,106 @@
|
||||
module edwards25519
|
||||
|
||||
fn check_aliasing_onearg(f fn (mut v Scalar, x Scalar) Scalar, mut v Scalar, x Scalar) bool {
|
||||
x1, mut v1 := x, x
|
||||
|
||||
// Calculate a reference f(x) without aliasing.
|
||||
mut out := f(mut v, x)
|
||||
if out != v || !is_reduced(out) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the argument and the receiver.
|
||||
out2 := f(mut v1, v1)
|
||||
if out2 != v1 || v1 != v || !is_reduced(out2) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the arguments was not modified.
|
||||
return x == x1
|
||||
}
|
||||
|
||||
fn negate_aliasing(mut v Scalar, x Scalar) Scalar {
|
||||
// mut t := v
|
||||
return v.negate(x)
|
||||
}
|
||||
|
||||
fn test_check_aliasing_oneargs() {
|
||||
x := generate_notzero_scalar(10)!
|
||||
mut v := generate_notzero_scalar(10)!
|
||||
out := check_aliasing_onearg(negate_aliasing, mut v, x)
|
||||
assert out == true
|
||||
}
|
||||
|
||||
fn multiply_aliasing(mut v Scalar, x Scalar, y Scalar) Scalar {
|
||||
return v.multiply(x, y)
|
||||
}
|
||||
|
||||
fn add_aliasing(mut v Scalar, x Scalar, y Scalar) Scalar {
|
||||
return v.add(x, y)
|
||||
}
|
||||
|
||||
fn subtract_aliasing(mut v Scalar, x Scalar, y Scalar) Scalar {
|
||||
return v.subtract(x, y)
|
||||
}
|
||||
|
||||
fn test_check_aliasing_twoargs() {
|
||||
fn_with_twoargs := [add_aliasing, multiply_aliasing, subtract_aliasing]
|
||||
for f in fn_with_twoargs {
|
||||
mut v := generate_notzero_scalar(10)!
|
||||
x := generate_notzero_scalar(10)!
|
||||
y := generate_notzero_scalar(10)!
|
||||
out := check_aliasing_twoargs(f, mut v, x, y)
|
||||
assert out == true
|
||||
}
|
||||
}
|
||||
|
||||
fn check_aliasing_twoargs(f fn (mut v Scalar, x Scalar, y Scalar) Scalar, mut v Scalar, x Scalar, y Scalar) bool {
|
||||
x1, y1, mut v1 := x, y, Scalar{}
|
||||
|
||||
// Calculate a reference f(x, y) without aliasing.
|
||||
mut out := f(mut v, x, y)
|
||||
if out != v || !is_reduced(out) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the first argument and the receiver.
|
||||
v1 = x
|
||||
out2 := f(mut v1, v1, y)
|
||||
if out2 != v1 || v1 != v || !is_reduced(out2) {
|
||||
return false
|
||||
}
|
||||
// Test aliasing the second argument and the receiver.
|
||||
v1 = y
|
||||
out3 := f(mut v1, x, v1)
|
||||
if out3 != v1 || v1 != v || !is_reduced(out3) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Calculate a reference f(x, x) without aliasing.
|
||||
out4 := f(mut v, x, x)
|
||||
if out4 != v || !is_reduced(out4) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the first argument and the receiver.
|
||||
v1 = x
|
||||
out5 := f(mut v1, v1, x)
|
||||
if out5 != v1 || v1 != v || !is_reduced(out5) {
|
||||
return false
|
||||
}
|
||||
// Test aliasing the second argument and the receiver.
|
||||
v1 = x
|
||||
out6 := f(mut v1, x, v1)
|
||||
if out6 != v1 || v1 != v || !is_reduced(out6) {
|
||||
return false
|
||||
}
|
||||
// Test aliasing both arguments and the receiver.
|
||||
v1 = x
|
||||
out7 := f(mut v1, v1, v1)
|
||||
if out7 != v1 || v1 != v || !is_reduced(out7) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the arguments were not modified.
|
||||
return x == x1 && y == y1
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
module edwards25519
|
||||
|
||||
import os
|
||||
import rand
|
||||
import encoding.hex
|
||||
import math.big
|
||||
|
||||
const github_job = os.getenv('GITHUB_JOB')
|
||||
|
||||
fn testsuite_begin() {
|
||||
if github_job != '' {
|
||||
// ensure that the CI does not run flaky tests:
|
||||
rand.seed([u32(0xffff24), 0xabcd])
|
||||
}
|
||||
}
|
||||
|
||||
fn test_scalar_equal() {
|
||||
assert sc_one.equal(sc_minus_one) != 1
|
||||
|
||||
assert sc_minus_one.equal(sc_minus_one) != 0
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn test_scalar_non_adjacent_form() {
|
||||
mut s := Scalar{
|
||||
s: [u8(0x1a), 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8,
|
||||
0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f,
|
||||
0x23, 0x76, 0xef, 0x09]!
|
||||
}
|
||||
expected_naf := [i8(0), 13, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, -11,
|
||||
0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 3, 0, 0,
|
||||
0, 0, 11, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, -15, 0, 0, 0, 0, -7, 0, 0,
|
||||
0, 0, -9, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, -11, 0, 0, 0,
|
||||
0, -7, 0, 0, 0, 0, -13, 0, 0, 0, 0, 11, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
|
||||
-15, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 13, 0, 0,
|
||||
0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0,
|
||||
0, 0, 0, 0, 7, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 0]!
|
||||
|
||||
snaf := s.non_adjacent_form(5)
|
||||
for i := 0; i < 256; i++ {
|
||||
assert expected_naf[i] == snaf[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn addlike_subneg(x Scalar, y Scalar) bool {
|
||||
// Compute t1 = x - y
|
||||
mut t1 := Scalar{}
|
||||
t1.subtract(x, y)
|
||||
|
||||
// Compute t2 = -y + x
|
||||
mut t2 := Scalar{}
|
||||
t2.negate(y)
|
||||
t2.add(t2, x)
|
||||
|
||||
return t1 == t2 && is_reduced(t1)
|
||||
}
|
||||
|
||||
fn test_scalar_add_like_subneg() {
|
||||
for i in 0 .. 15 {
|
||||
x := generate_scalar(1000) or { panic(err) }
|
||||
y := generate_scalar(1000) or { panic(err) }
|
||||
assert addlike_subneg(x, y) == true
|
||||
}
|
||||
}
|
||||
|
||||
fn fg(sc Scalar) bool {
|
||||
return is_reduced(sc)
|
||||
}
|
||||
|
||||
fn test_scalar_generate() {
|
||||
for i in 0 .. 15 {
|
||||
sc := generate_scalar(1000) or { panic(err) }
|
||||
|
||||
assert fg(sc) == true
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
fn test_scalar_set_canonical_bytes() {
|
||||
for i in 0 .. 10 {
|
||||
mut buf := rand.bytes(32) or { panic(err) }
|
||||
mut sc := generate_scalar(1000) or { panic(err) }
|
||||
buf[buf.len - 1] &= (1 << 4) - 1
|
||||
sc = sc.set_canonical_bytes(buf) or { panic(err) }
|
||||
|
||||
assert buf[..] == sc.bytes()
|
||||
assert is_reduced(sc)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_scalar_set_canonical_bytes_round_trip() {
|
||||
for i in 0 .. 10 {
|
||||
mut sc1 := generate_scalar(2)!
|
||||
mut sc2 := generate_scalar(6)!
|
||||
sc2.set_canonical_bytes(sc1.bytes()) or { panic(err) }
|
||||
|
||||
assert sc1 == sc2
|
||||
}
|
||||
}
|
||||
|
||||
const sc_error = Scalar{
|
||||
s: [32]u8{init: (u8(-1))}
|
||||
}
|
||||
|
||||
fn test_scalar_set_canonical_bytes_on_noncanonical_value() {
|
||||
mut b := sc_minus_one.s
|
||||
b[31] += 1
|
||||
|
||||
mut s := sc_one
|
||||
out :=
|
||||
s.set_canonical_bytes(b[..]) or { sc_error } // set_canonical_bytes shouldn't worked on a non-canonical value"
|
||||
assert out == sc_error
|
||||
assert s == sc_one
|
||||
}
|
||||
|
||||
fn test_scalar_set_uniform_bytes() {
|
||||
// mod, _ := new(big.Integer).SetString("27742317777372353535851937790883648493", 10)
|
||||
mut mod := big.integer_from_string('27742317777372353535851937790883648493')!
|
||||
// mod.Add(mod, new(big.Integer).Lsh(big.NewInt(1), 252))
|
||||
mod = mod + big.integer_from_i64(1).left_shift(252)
|
||||
|
||||
mut sc := generate_scalar(100)!
|
||||
inp := rand.bytes(64)!
|
||||
|
||||
sc.set_uniform_bytes(inp[..])!
|
||||
assert is_reduced(sc) == true
|
||||
|
||||
scbig := bigint_from_le_bytes(sc.s[..])
|
||||
inbig := bigint_from_le_bytes(inp)
|
||||
// return inbig.Mod(inbig, mod).Cmp(scbig) == 0
|
||||
_, m := inbig.div_mod(mod)
|
||||
assert m.abs_cmp(scbig) == 0 // NEED FIX
|
||||
}
|
||||
|
||||
fn bigint_from_le_bytes(b []u8) big.Integer {
|
||||
mut bc := b.clone()
|
||||
buf := swap_endianness(mut bc) // WITHOUT THIS, some test would fail
|
||||
bg := big.integer_from_bytes(buf)
|
||||
return bg
|
||||
}
|
||||
|
||||
fn test_scalar_set_bytes_with_clamping() {
|
||||
// Generated with libsodium.js 1.0.18 crypto_scalarmult_ed25519_base.
|
||||
/*
|
||||
random := "633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e"
|
||||
s, _ := new(Scalar).SetBytesWithClamping(decodeHex(random))
|
||||
p := new(Point).ScalarBaseMult(s)
|
||||
want := "1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94"
|
||||
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||
t.Errorf("random: got %q, want %q", got, want)
|
||||
}*/
|
||||
random := '633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e'
|
||||
random_bytes := hex.decode(random) or { []u8{} }
|
||||
|
||||
mut s0 := Scalar{}
|
||||
s0.set_bytes_with_clamping(random_bytes) or { Scalar{} }
|
||||
|
||||
mut p0 := Point{}
|
||||
p0.scalar_base_mult(mut s0)
|
||||
|
||||
want0 := '1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94'
|
||||
got0 := hex.encode(p0.bytes())
|
||||
|
||||
assert got0 == want0
|
||||
|
||||
zero := '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
mut s1 := Scalar{}
|
||||
zero_bytes := hex.decode(zero) or { []u8{} }
|
||||
s1.set_bytes_with_clamping(zero_bytes) or { Scalar{} }
|
||||
mut p1 := Point{}
|
||||
p1.scalar_base_mult(mut s1)
|
||||
|
||||
want1 := '693e47972caf527c7883ad1b39822f026f47db2ab0e1919955b8993aa04411d1'
|
||||
got1 := hex.encode(p1.bytes())
|
||||
assert want1 == got1
|
||||
|
||||
one := 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
||||
mut s2 := Scalar{}
|
||||
mut one_bytes := hex.decode(one) or { []u8{} }
|
||||
s2.set_bytes_with_clamping(one_bytes) or { Scalar{} }
|
||||
mut p2 := Point{}
|
||||
p2.scalar_base_mult(mut s2)
|
||||
|
||||
want2 := '12e9a68b73fd5aacdbcaf3e88c46fea6ebedb1aa84eed1842f07f8edab65e3a7'
|
||||
got2 := hex.encode(p2.bytes())
|
||||
|
||||
assert want2 == got2
|
||||
}
|
||||
|
||||
fn test_scalar_multiply_distributes_over_add() {
|
||||
x := generate_scalar(100)!
|
||||
y := generate_scalar(100)!
|
||||
z := generate_scalar(100)!
|
||||
|
||||
// Compute t1 = (x+y)*z
|
||||
mut t1 := Scalar{}
|
||||
t1.add(x, y)
|
||||
t1.multiply(t1, z)
|
||||
|
||||
// Compute t2 = x*z + y*z
|
||||
mut t2 := Scalar{}
|
||||
mut t3 := Scalar{}
|
||||
t2.multiply(x, z)
|
||||
t3.multiply(y, z)
|
||||
t2.add(t2, t3)
|
||||
|
||||
assert t1 == t2 && is_reduced(t1) && is_reduced(t3)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
module edwards25519
|
||||
|
||||
import sync
|
||||
|
||||
struct BasepointTablePrecomp {
|
||||
mut:
|
||||
table []AffineLookupTable
|
||||
initonce sync.Once
|
||||
}
|
||||
|
||||
// basepoint_table is a set of 32 affineLookupTables, where table i is generated
|
||||
// from 256i * basepoint. It is precomputed the first time it's used.
|
||||
fn basepoint_table() []AffineLookupTable {
|
||||
mut bpt := &BasepointTablePrecomp{
|
||||
table: []AffineLookupTable{len: 32}
|
||||
initonce: sync.new_once()
|
||||
}
|
||||
|
||||
// replaced to use do_with_param on newest sync lib
|
||||
/*
|
||||
bpt.initonce.do(fn [mut bpt] () {
|
||||
mut p := new_generator_point()
|
||||
for i := 0; i < 32; i++ {
|
||||
bpt.table[i].from_p3(p)
|
||||
for j := 0; j < 8; j++ {
|
||||
p.add(p, p)
|
||||
}
|
||||
}
|
||||
})*/
|
||||
bpt.initonce.do_with_param(fn (mut o BasepointTablePrecomp) {
|
||||
mut p := new_generator_point()
|
||||
for i := 0; i < 32; i++ {
|
||||
o.table[i].from_p3(p)
|
||||
for j := 0; j < 8; j++ {
|
||||
p.add(p, p)
|
||||
}
|
||||
}
|
||||
}, bpt)
|
||||
return bpt.table
|
||||
}
|
||||
|
||||
// scalar_base_mult sets v = x * B, where B is the canonical generator, and
|
||||
// returns v.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
pub fn (mut v Point) scalar_base_mult(mut x Scalar) Point {
|
||||
mut bpt_table := basepoint_table()
|
||||
|
||||
// Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i )
|
||||
// as described in the Ed25519 paper
|
||||
//
|
||||
// Group even and odd coefficients
|
||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||
// + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B
|
||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||
// + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B)
|
||||
//
|
||||
// We use a lookup table for each i to get x_i*16^(2*i)*B
|
||||
// and do four doublings to multiply by 16.
|
||||
digits := x.signed_radix16()
|
||||
|
||||
mut multiple := AffineCached{}
|
||||
mut tmp1 := ProjectiveP1{}
|
||||
mut tmp2 := ProjectiveP2{}
|
||||
|
||||
// Accumulate the odd components first
|
||||
v.set(new_identity_point())
|
||||
for i := 1; i < 64; i += 2 {
|
||||
bpt_table[i / 2].select_into(mut multiple, digits[i])
|
||||
tmp1.add_affine(v, multiple)
|
||||
v.from_p1(tmp1)
|
||||
}
|
||||
|
||||
// Multiply by 16
|
||||
tmp2.from_p3(v) // tmp2 = v in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 2*v in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 2*v in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 4*v in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 4*v in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 8*v in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 8*v in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 16*v in P1xP1 coords
|
||||
v.from_p1(tmp1) // now v = 16*(odd components)
|
||||
|
||||
// Accumulate the even components
|
||||
for j := 0; j < 64; j += 2 {
|
||||
bpt_table[j / 2].select_into(mut multiple, digits[j])
|
||||
tmp1.add_affine(v, multiple)
|
||||
v.from_p1(tmp1)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// scalar_mult sets v = x * q, and returns v.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
pub fn (mut v Point) scalar_mult(mut x Scalar, q Point) Point {
|
||||
check_initialized(q)
|
||||
|
||||
mut table := ProjLookupTable{}
|
||||
table.from_p3(q)
|
||||
|
||||
// Write x = sum(x_i * 16^i)
|
||||
// so x*Q = sum( Q*x_i*16^i )
|
||||
// = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... )
|
||||
// <------compute inside out---------
|
||||
//
|
||||
// We use the lookup table to get the x_i*Q values
|
||||
// and do four doublings to compute 16*Q
|
||||
digits := x.signed_radix16()
|
||||
|
||||
// Unwrap first loop iteration to save computing 16*identity
|
||||
mut multiple := ProjectiveCached{}
|
||||
mut tmp1 := ProjectiveP1{}
|
||||
mut tmp2 := ProjectiveP2{}
|
||||
table.select_into(mut multiple, digits[63])
|
||||
|
||||
v.set(new_identity_point())
|
||||
tmp1.add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords
|
||||
for i := 62; i >= 0; i-- {
|
||||
tmp2.from_p1(tmp1) // tmp2 = (prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2.from_p1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1.double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||
v.from_p1(tmp1) // v = 16*(prev) in P3 coords
|
||||
table.select_into(mut multiple, digits[i])
|
||||
tmp1.add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords
|
||||
}
|
||||
v.from_p1(tmp1)
|
||||
return v
|
||||
}
|
||||
|
||||
struct BasepointNaftablePrecomp {
|
||||
mut:
|
||||
table NafLookupTable8
|
||||
initonce sync.Once
|
||||
}
|
||||
|
||||
fn basepoint_naf_table() NafLookupTable8 {
|
||||
mut bnft := &BasepointNaftablePrecomp{}
|
||||
bnft.initonce.do_with_param(fn (mut o BasepointNaftablePrecomp) {
|
||||
o.table.from_p3(new_generator_point())
|
||||
}, bnft)
|
||||
return bnft.table
|
||||
}
|
||||
|
||||
// vartime_double_scalar_base_mult sets v = a * A + b * B, where B is the canonical
|
||||
// generator, and returns v.
|
||||
//
|
||||
// Execution time depends on the inputs.
|
||||
pub fn (mut v Point) vartime_double_scalar_base_mult(xa Scalar, aa Point, xb Scalar) Point {
|
||||
check_initialized(aa)
|
||||
|
||||
// Similarly to the single variable-base approach, we compute
|
||||
// digits and use them with a lookup table. However, because
|
||||
// we are allowed to do variable-time operations, we don't
|
||||
// need constant-time lookups or constant-time digit
|
||||
// computations.
|
||||
//
|
||||
// So we use a non-adjacent form of some width w instead of
|
||||
// radix 16. This is like a binary representation (one digit
|
||||
// for each binary place) but we allow the digits to grow in
|
||||
// magnitude up to 2^{w-1} so that the nonzero digits are as
|
||||
// sparse as possible. Intuitively, this "condenses" the
|
||||
// "mass" of the scalar onto sparse coefficients (meaning
|
||||
// fewer additions).
|
||||
|
||||
mut bp_naftable := basepoint_naf_table()
|
||||
mut atable := NafLookupTable5{}
|
||||
atable.from_p3(aa)
|
||||
// Because the basepoint is fixed, we can use a wider NAF
|
||||
// corresponding to a bigger table.
|
||||
mut a := xa
|
||||
mut b := xb
|
||||
anaf := a.non_adjacent_form(5)
|
||||
bnaf := b.non_adjacent_form(8)
|
||||
|
||||
// Find the first nonzero coefficient.
|
||||
mut i := 255
|
||||
for j := i; j >= 0; j-- {
|
||||
if anaf[j] != 0 || bnaf[j] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
mut multa := ProjectiveCached{}
|
||||
mut multb := AffineCached{}
|
||||
mut tmp1 := ProjectiveP1{}
|
||||
mut tmp2 := ProjectiveP2{}
|
||||
tmp2.zero()
|
||||
|
||||
// Move from high to low bits, doubling the accumulator
|
||||
// at each iteration and checking whether there is a nonzero
|
||||
// coefficient to look up a multiple of.
|
||||
for ; i >= 0; i-- {
|
||||
tmp1.double(tmp2)
|
||||
|
||||
// Only update v if we have a nonzero coeff to add in.
|
||||
if anaf[i] > 0 {
|
||||
v.from_p1(tmp1)
|
||||
atable.select_into(mut multa, anaf[i])
|
||||
tmp1.add(v, multa)
|
||||
} else if anaf[i] < 0 {
|
||||
v.from_p1(tmp1)
|
||||
atable.select_into(mut multa, -anaf[i])
|
||||
tmp1.sub(v, multa)
|
||||
}
|
||||
|
||||
if bnaf[i] > 0 {
|
||||
v.from_p1(tmp1)
|
||||
bp_naftable.select_into(mut multb, bnaf[i])
|
||||
tmp1.add_affine(v, multb)
|
||||
} else if bnaf[i] < 0 {
|
||||
v.from_p1(tmp1)
|
||||
bp_naftable.select_into(mut multb, -bnaf[i])
|
||||
tmp1.sub_affine(v, multb)
|
||||
}
|
||||
|
||||
tmp2.from_p1(tmp1)
|
||||
}
|
||||
|
||||
v.from_p2(tmp2)
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
module edwards25519
|
||||
|
||||
const dalek_scalar = Scalar{[u8(219), 106, 114, 9, 174, 249, 155, 89, 69, 203, 201, 93, 92, 116,
|
||||
234, 187, 78, 115, 103, 172, 182, 98, 62, 103, 187, 136, 13, 100, 248, 110, 12, 4]!}
|
||||
const dsc_basepoint = [u8(0xf4), 0xef, 0x7c, 0xa, 0x34, 0x55, 0x7b, 0x9f, 0x72, 0x3b, 0xb6, 0x1e,
|
||||
0xf9, 0x46, 0x9, 0x91, 0x1c, 0xb9, 0xc0, 0x6c, 0x17, 0x28, 0x2d, 0x8b, 0x43, 0x2b, 0x5, 0x18,
|
||||
0x6a, 0x54, 0x3e, 0x48]
|
||||
|
||||
fn dalek_scalar_basepoint() Point {
|
||||
mut p := Point{}
|
||||
p.set_bytes(dsc_basepoint) or { panic(err) }
|
||||
return p
|
||||
}
|
||||
|
||||
fn test_scalar_mult_small_scalars() {
|
||||
mut z := Scalar{}
|
||||
mut p := Point{}
|
||||
mut b := new_generator_point()
|
||||
mut i := new_identity_point()
|
||||
p.scalar_mult(mut z, b)
|
||||
|
||||
assert i.equal(p) == 1
|
||||
assert check_on_curve(p) == true
|
||||
|
||||
z = Scalar{[u8(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0]!}
|
||||
p.scalar_mult(mut z, b)
|
||||
|
||||
assert b.equal(p) == 1
|
||||
assert check_on_curve(p) == true
|
||||
}
|
||||
|
||||
fn test_scalar_mult_vs_dalek() {
|
||||
mut p := Point{}
|
||||
mut b := new_generator_point()
|
||||
mut dsc := dalek_scalar
|
||||
p.scalar_mult(mut dsc, b)
|
||||
mut ds := dalek_scalar_basepoint()
|
||||
assert ds.equal(p) == 1
|
||||
|
||||
assert check_on_curve(p) == true
|
||||
}
|
||||
|
||||
fn test_scalar_base_mult_vs_dalek() {
|
||||
mut p := Point{}
|
||||
mut dsc := dalek_scalar
|
||||
p.scalar_base_mult(mut dsc)
|
||||
mut ds := dalek_scalar_basepoint()
|
||||
assert ds.equal(p) == 1
|
||||
|
||||
assert check_on_curve(p)
|
||||
}
|
||||
|
||||
fn test_vartime_double_basemult_vs_dalek() {
|
||||
mut p := Point{}
|
||||
mut z := Scalar{}
|
||||
b := new_generator_point()
|
||||
p.vartime_double_scalar_base_mult(dalek_scalar, b, z)
|
||||
|
||||
mut ds := dalek_scalar_basepoint()
|
||||
assert ds.equal(p) == 1
|
||||
assert check_on_curve(p)
|
||||
|
||||
p.vartime_double_scalar_base_mult(z, b, dalek_scalar)
|
||||
|
||||
assert ds.equal(p) == 1
|
||||
assert check_on_curve(p)
|
||||
}
|
||||
|
||||
fn test_scalar_mult_distributes_over_add() {
|
||||
mut x := generate_scalar(100) or { panic(err) }
|
||||
mut y := generate_scalar(100) or { panic(err) }
|
||||
mut z := Scalar{}
|
||||
|
||||
z.add(x, y)
|
||||
|
||||
mut p := Point{}
|
||||
mut q := Point{}
|
||||
mut r := Point{}
|
||||
mut check := Point{}
|
||||
mut b := new_generator_point()
|
||||
|
||||
p.scalar_mult(mut x, b)
|
||||
q.scalar_mult(mut y, b)
|
||||
r.scalar_mult(mut z, b)
|
||||
check.add(p, q)
|
||||
|
||||
assert check_on_curve(p, q, r, check) == true
|
||||
assert check.equal(r) == 1
|
||||
}
|
||||
|
||||
fn test_scalarmult_non_identity_point() {
|
||||
// Check whether p.ScalarMult and q.ScalaBaseMult give the same,
|
||||
// when p and q are originally set to the base point.
|
||||
|
||||
mut x := generate_scalar(5000)!
|
||||
|
||||
mut p := Point{}
|
||||
mut q := Point{}
|
||||
mut b := new_generator_point()
|
||||
p.set(b)
|
||||
q.set(b)
|
||||
|
||||
p.scalar_mult(mut x, b)
|
||||
q.scalar_base_mult(mut x)
|
||||
|
||||
assert check_on_curve(p, q) == true
|
||||
|
||||
assert p.equal(q) == 1
|
||||
}
|
||||
|
||||
fn test_basepoint_table_generation() {
|
||||
// The basepoint table is 32 affineLookupTables,
|
||||
// corresponding to (16^2i)*B for table i.
|
||||
bptable := basepoint_table()
|
||||
b := new_generator_point()
|
||||
mut tmp1 := ProjectiveP1{}
|
||||
mut tmp2 := ProjectiveP2{}
|
||||
mut tmp3 := Point{}
|
||||
tmp3.set(b)
|
||||
mut table := []AffineLookupTable{len: 32}
|
||||
for i := 0; i < 32; i++ {
|
||||
// Build the table
|
||||
table[i].from_p3(tmp3)
|
||||
|
||||
// Assert equality with the hardcoded one
|
||||
assert table[i] == bptable[i]
|
||||
|
||||
// Set p = (16^2)*p = 256*p = 2^8*p
|
||||
tmp2.from_p3(tmp3)
|
||||
for j := 0; j < 7; j++ {
|
||||
tmp1.double(tmp2)
|
||||
tmp2.from_p1(tmp1)
|
||||
}
|
||||
tmp1.double(tmp2)
|
||||
tmp3.from_p1(tmp1)
|
||||
|
||||
assert check_on_curve(tmp3) == true
|
||||
}
|
||||
}
|
||||
|
||||
fn test_scalar_mult_matches_base_mult() {
|
||||
mut x := generate_scalar(100) or { panic(err) }
|
||||
b := new_generator_point()
|
||||
mut p := Point{}
|
||||
mut q := Point{}
|
||||
|
||||
p.scalar_mult(mut x, b)
|
||||
q.scalar_base_mult(mut x)
|
||||
|
||||
assert check_on_curve(p, q) == true
|
||||
assert p.equal(q) == 1
|
||||
}
|
||||
|
||||
fn test_basepoint_naf_table_generation() {
|
||||
mut table := NafLookupTable8{}
|
||||
b := new_generator_point()
|
||||
|
||||
table.from_p3(b)
|
||||
|
||||
bnt := basepoint_naf_table()
|
||||
assert table == bnt
|
||||
}
|
||||
|
||||
fn test_vartime_double_scalar_base_mult() {
|
||||
mut x := generate_scalar(100) or { panic(err) }
|
||||
mut y := generate_scalar(100) or { panic(err) }
|
||||
b := new_generator_point()
|
||||
|
||||
mut p := Point{}
|
||||
mut q1 := Point{}
|
||||
mut q2 := Point{}
|
||||
mut check := Point{}
|
||||
|
||||
p.vartime_double_scalar_base_mult(x, b, y)
|
||||
|
||||
q1.scalar_base_mult(mut x)
|
||||
q2.scalar_base_mult(mut y)
|
||||
check.add(q1, q2)
|
||||
|
||||
assert check_on_curve(p, check, q1, q2) == true
|
||||
assert p.equal(check) == 1
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
module edwards25519
|
||||
|
||||
import crypto.internal.subtle
|
||||
|
||||
// A precomputed lookup table for fixed-base, constant-time scalar muls.
|
||||
struct AffineLookupTable {
|
||||
mut:
|
||||
points [8]AffineCached
|
||||
}
|
||||
|
||||
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
||||
struct ProjLookupTable {
|
||||
mut:
|
||||
points [8]ProjectiveCached
|
||||
}
|
||||
|
||||
// A dynamic lookup table for variable-base, variable-time scalar muls.
|
||||
struct NafLookupTable5 {
|
||||
mut:
|
||||
points [8]ProjectiveCached
|
||||
}
|
||||
|
||||
// A precomputed lookup table for fixed-base, variable-time scalar muls.
|
||||
struct NafLookupTable8 {
|
||||
mut:
|
||||
points [64]AffineCached
|
||||
}
|
||||
|
||||
// Constructors.
|
||||
|
||||
// Builds a lookup table at runtime. Fast.
|
||||
fn (mut v ProjLookupTable) from_p3(q Point) {
|
||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||
v.points[0].from_p3(q)
|
||||
mut tmp_p3 := Point{}
|
||||
mut tmp_p1 := ProjectiveP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
|
||||
// This is needlessly complicated because the API has explicit
|
||||
// receivers instead of creating stack objects and relying on RVO
|
||||
v.points[i + 1].from_p3(tmp_p3.from_p1(tmp_p1.add(q, v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||
fn (mut v AffineLookupTable) from_p3(q Point) {
|
||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||
v.points[0].from_p3(q)
|
||||
mut tmp_p3 := Point{}
|
||||
mut tmp_p1 := ProjectiveP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
|
||||
v.points[i + 1].from_p3(tmp_p3.from_p1(tmp_p1.add_affine(q, v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// Builds a lookup table at runtime. Fast.
|
||||
fn (mut v NafLookupTable5) from_p3(q Point) {
|
||||
// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
|
||||
// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
|
||||
v.points[0].from_p3(q)
|
||||
mut q2 := Point{}
|
||||
q2.add(q, q)
|
||||
mut tmp_p3 := Point{}
|
||||
mut tmp_p1 := ProjectiveP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
v.points[i + 1].from_p3(tmp_p3.from_p1(tmp_p1.add(q2, v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||
fn (mut v NafLookupTable8) from_p3(q Point) {
|
||||
v.points[0].from_p3(q)
|
||||
mut q2 := Point{}
|
||||
q2.add(q, q)
|
||||
mut tmp_p3 := Point{}
|
||||
mut tmp_p1 := ProjectiveP1{}
|
||||
for i := 0; i < 63; i++ {
|
||||
v.points[i + 1].from_p3(tmp_p3.from_p1(tmp_p1.add_affine(q2, v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// Selectors.
|
||||
|
||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||
fn (mut v ProjLookupTable) select_into(mut dest ProjectiveCached, x i8) {
|
||||
// Compute xabs = |x|
|
||||
xmask := x >> 7
|
||||
xabs := u8((x + xmask) ^ xmask)
|
||||
|
||||
dest.zero()
|
||||
for j := 1; j <= 8; j++ {
|
||||
// Set dest = j*Q if |x| = j
|
||||
cond := subtle.constant_time_byte_eq(xabs, u8(j))
|
||||
dest.selected(v.points[j - 1], dest, cond)
|
||||
}
|
||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||
dest.cond_neg(int(xmask & 1))
|
||||
}
|
||||
|
||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||
fn (mut v AffineLookupTable) select_into(mut dest AffineCached, x i8) {
|
||||
// Compute xabs = |x|
|
||||
xmask := x >> 7
|
||||
xabs := u8((x + xmask) ^ xmask)
|
||||
|
||||
dest.zero()
|
||||
for j := 1; j <= 8; j++ {
|
||||
// Set dest = j*Q if |x| = j
|
||||
cond := subtle.constant_time_byte_eq(xabs, u8(j))
|
||||
dest.selected(v.points[j - 1], dest, cond)
|
||||
}
|
||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||
dest.cond_neg(int(xmask & 1))
|
||||
}
|
||||
|
||||
// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
|
||||
fn (mut v NafLookupTable5) select_into(mut dest ProjectiveCached, x i8) {
|
||||
dest = v.points[x / 2]
|
||||
}
|
||||
|
||||
// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
|
||||
fn (mut v NafLookupTable8) select_into(mut dest AffineCached, x i8) {
|
||||
dest = v.points[x / 2]
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
module edwards25519
|
||||
|
||||
fn test_proj_lookup_table() {
|
||||
mut table := ProjLookupTable{}
|
||||
b := new_generator_point()
|
||||
table.from_p3(b)
|
||||
|
||||
mut tmp1 := ProjectiveCached{}
|
||||
mut tmp2 := ProjectiveCached{}
|
||||
mut tmp3 := ProjectiveCached{}
|
||||
|
||||
table.select_into(mut tmp1, 6)
|
||||
table.select_into(mut tmp2, -2)
|
||||
table.select_into(mut tmp3, -4)
|
||||
|
||||
// Expect T1 + T2 + T3 = identity
|
||||
mut acc_p1 := ProjectiveP1{}
|
||||
mut acc_p3 := new_identity_point()
|
||||
|
||||
acc_p1.add(acc_p3, tmp1)
|
||||
acc_p3.from_p1(acc_p1)
|
||||
acc_p1.add(acc_p3, tmp2)
|
||||
acc_p3.from_p1(acc_p1)
|
||||
acc_p1.add(acc_p3, tmp3)
|
||||
acc_p3.from_p1(acc_p1)
|
||||
|
||||
assert acc_p3.equal(id_point) == 1
|
||||
}
|
||||
|
||||
fn test_affine_lookup_table() {
|
||||
mut table := AffineLookupTable{}
|
||||
b := new_generator_point()
|
||||
table.from_p3(b)
|
||||
|
||||
mut tmp1 := AffineCached{}
|
||||
mut tmp2 := AffineCached{}
|
||||
mut tmp3 := AffineCached{}
|
||||
|
||||
table.select_into(mut tmp1, 3)
|
||||
table.select_into(mut tmp2, -7)
|
||||
table.select_into(mut tmp3, 4)
|
||||
// Expect T1 + T2 + T3 = identity
|
||||
|
||||
mut acc_p1 := ProjectiveP1{}
|
||||
mut acc_p3 := new_identity_point()
|
||||
|
||||
acc_p1.add_affine(acc_p3, tmp1)
|
||||
acc_p3.from_p1(acc_p1)
|
||||
acc_p1.add_affine(acc_p3, tmp2)
|
||||
acc_p3.from_p1(acc_p1)
|
||||
acc_p1.add_affine(acc_p3, tmp3)
|
||||
acc_p3.from_p1(acc_p1)
|
||||
|
||||
assert acc_p3.equal(id_point) == 1
|
||||
}
|
||||
|
||||
fn test_naf_lookup_table5() {
|
||||
mut table := NafLookupTable5{}
|
||||
b := new_generator_point()
|
||||
table.from_p3(b)
|
||||
|
||||
mut tmp1 := ProjectiveCached{}
|
||||
mut tmp2 := ProjectiveCached{}
|
||||
mut tmp3 := ProjectiveCached{}
|
||||
mut tmp4 := ProjectiveCached{}
|
||||
|
||||
table.select_into(mut tmp1, 9)
|
||||
table.select_into(mut tmp2, 11)
|
||||
table.select_into(mut tmp3, 7)
|
||||
table.select_into(mut tmp4, 13)
|
||||
// Expect T1 + T2 = T3 + T4
|
||||
|
||||
mut acc_p1 := ProjectiveP1{}
|
||||
mut lhs := new_identity_point()
|
||||
mut rhs := new_identity_point()
|
||||
|
||||
acc_p1.add(lhs, tmp1)
|
||||
lhs.from_p1(acc_p1)
|
||||
acc_p1.add(lhs, tmp2)
|
||||
lhs.from_p1(acc_p1)
|
||||
|
||||
acc_p1.add(rhs, tmp3)
|
||||
rhs.from_p1(acc_p1)
|
||||
acc_p1.add(rhs, tmp4)
|
||||
rhs.from_p1(acc_p1)
|
||||
|
||||
assert lhs.equal(rhs) == 1
|
||||
}
|
||||
|
||||
fn test_naf_lookup_table8() {
|
||||
mut table := NafLookupTable8{}
|
||||
b := new_generator_point()
|
||||
table.from_p3(b)
|
||||
|
||||
mut tmp1 := AffineCached{}
|
||||
mut tmp2 := AffineCached{}
|
||||
mut tmp3 := AffineCached{}
|
||||
mut tmp4 := AffineCached{}
|
||||
|
||||
table.select_into(mut tmp1, 49)
|
||||
table.select_into(mut tmp2, 11)
|
||||
table.select_into(mut tmp3, 35)
|
||||
table.select_into(mut tmp4, 25)
|
||||
// Expect T1 + T2 = T3 + T4
|
||||
|
||||
mut acc_p1 := ProjectiveP1{}
|
||||
mut lhs := new_identity_point()
|
||||
mut rhs := new_identity_point()
|
||||
|
||||
acc_p1.add_affine(lhs, tmp1)
|
||||
lhs.from_p1(acc_p1)
|
||||
acc_p1.add_affine(lhs, tmp2)
|
||||
lhs.from_p1(acc_p1)
|
||||
|
||||
acc_p1.add_affine(rhs, tmp3)
|
||||
rhs.from_p1(acc_p1)
|
||||
acc_p1.add_affine(rhs, tmp4)
|
||||
rhs.from_p1(acc_p1)
|
||||
|
||||
assert lhs.equal(rhs) == 1
|
||||
}
|
||||
Reference in new issue
Block a user