Merge pull request #576 from triska/crypto_curve_scalar_mult

ENHANCED: Faster Rust-based crypto_curve_scalar_mult/4 using OpenSSL.
This commit is contained in:
Mark Thom
2020-06-07 19:34:12 -03:00
committed by GitHub
4 changed files with 101 additions and 118 deletions

View File

@@ -41,3 +41,4 @@ ring = "0.16.13"
ripemd160 = "0.8.0"
sha3 = "0.8.2"
blake2 = "0.8.1"
openssl = { version = "0.10.29", features = ["vendored"] }

View File

@@ -293,6 +293,7 @@ pub enum SystemClauseType {
CryptoPasswordHash,
CryptoDataEncrypt,
CryptoDataDecrypt,
CryptoCurveScalarMult,
Ed25519Sign,
Ed25519Verify,
Ed25519NewKeyPair,
@@ -488,6 +489,7 @@ impl SystemClauseType {
&SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"),
&SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"),
&SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"),
&SystemClauseType::CryptoCurveScalarMult => clause_name!("$crypto_curve_scalar_mult"),
&SystemClauseType::Ed25519Sign => clause_name!("$ed25519_sign"),
&SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"),
&SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
@@ -663,6 +665,7 @@ impl SystemClauseType {
("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt),
("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt),
("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult),
("$ed25519_sign", 3) => Some(SystemClauseType::Ed25519Sign),
("$ed25519_verify", 3) => Some(SystemClauseType::Ed25519Verify),
("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),

View File

@@ -683,52 +683,13 @@ ed25519_verify(Key0, Data0, Signature0, Options) :-
hex_bytes(Signature0, Signature),
'$ed25519_verify'(Key, Data, Signature).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Modular multiplicative inverse.
Compute Y = X^(-1) mod p, using the extended Euclidean algorithm.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
multiplicative_inverse_modulo_p(X, P, Y) :-
eea(X, P, _, _, Y),
R #= X*Y mod P,
zcompare(C, 1, R),
must_be_one(C, X, P, Y).
must_be_one(=, _, _, _).
must_be_one(>, X, P, Y) :- throw(multiplicative_inverse_modulo_p(X,P,Y)).
must_be_one(<, X, P, Y) :- throw(multiplicative_inverse_modulo_p(X,P,Y)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Extended Euclidean algorithm.
Computes the GCD and the Bézout coefficients S and T.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
eea(I, J, G, S, T) :-
State0 = state(1,0,0,1),
eea_loop(I, J, State0, G, S, T).
eea_loop(I, J, State0, G, S, T) :-
zcompare(C, 0, J),
eea_(C, I, J, State0, G, S, T).
eea_(=, I, _, state(_,_,U,V), I, U, V).
eea_(<, I0, J0, state(S0,T0,U0,V0), I, U, V) :-
Q #= I0 // J0,
R #= I0 mod J0,
S1 #= U0 - (Q*S0),
T1 #= V0 - (Q*T0),
eea_loop(J0, R, state(S1,T1,S0,T0), I, U, V).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Operations on Elliptic Curves
=============================
Sample use: Establishing a shared secret S, using ECDH key exchange.
?- crypto_name_curve(Name, C),
?- crypto_name_curve(secp256k1, C),
crypto_curve_generator(C, Generator),
PrivateKey = 10,
crypto_curve_scalar_mult(C, PrivateKey, Generator, PublicKey),
@@ -742,90 +703,38 @@ eea_(<, I0, J0, state(S0,T0,U0,V0), I, U, V) :-
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
An elliptic curve over a prime field F_p is represented as:
curve(P,A,B,point(X,Y),Order,Cofactor).
curve(Name,P,A,B,point(X,Y),Order,FieldLength,Cofactor).
First, we define suitable accessors.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
curve_p(curve(P,_,_,_,_,_), P).
curve_a(curve(_,A,_,_,_,_), A).
curve_b(curve(_,_,B,_,_,_), B).
curve_name(curve(Name,_,_,_,_,_,_,_), Name).
curve_p(curve(_,P,_,_,_,_,_,_), P).
curve_a(curve(_,_,A,_,_,_,_,_), A).
curve_b(curve(_,_,_,B,_,_,_,_), B).
curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength).
crypto_curve_order(curve(_,_,_,_,Order,_), Order).
crypto_curve_generator(curve(_,_,_,G,_,_), G).
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
must_be(integer, Scalar),
must_be_on_curve(Curve, point(X,Y)),
curve_name(Curve, Name),
curve_field_length(Curve, L0),
L #= 2*L0, % for hex encoding
phrase(format_("04~|~`0t~16r~*+~`0t~16r~*+", [X,L,Y,L]), Hex),
hex_bytes(Hex, Bytes),
'$crypto_curve_scalar_mult'(Name, Scalar, Bytes, SX, SY),
number_chars(RX, SX),
number_chars(RY, SY).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Scalar point multiplication.
R = k*Q.
The Montgomery ladder method is used to mitigate side-channel
attacks such as timing attacks, since the number of multiplications
and additions is independent of the private key K. This method does
not even reveal the key's Hamming weight (number of 1s).
?- crypto_name_curve(secp256k1, Curve),
crypto_curve_generator(Curve, G),
crypto_curve_scalar_mult(Curve, 2, G, R).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_curve_scalar_mult(Curve, K, Q, R) :-
msb(K, Upper),
scalar_multiplication(Curve, K, Upper, ml(null,Q)-R),
must_be_on_curve(Curve, R).
scalar_multiplication(Curve, K, I, R0-R) :-
zcompare(C, -1, I),
scalar_mult_(C, Curve, K, I, R0-R).
scalar_mult_(=, _, _, _, ml(R,_)-R).
scalar_mult_(<, Curve, K, I0, ML0-R) :-
BitSet #= K /\ (1 << I0),
zcompare(C, 0, BitSet),
montgomery_step(C, Curve, ML0, ML1),
I1 #= I0 - 1,
scalar_multiplication(Curve, K, I1, ML1-R).
montgomery_step(=, Curve, ml(R0,S0), ml(R,S)) :-
curve_points_addition(Curve, R0, S0, S),
curve_point_double(Curve, R0, R).
montgomery_step(<, Curve, ml(R0,S0), ml(R,S)) :-
curve_points_addition(Curve, R0, S0, R),
curve_point_double(Curve, S0, S).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Doubling a point: R = A + A.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
curve_point_double(_, null, null).
curve_point_double(Curve, point(AX,AY), R) :-
curve_p(Curve, P),
curve_a(Curve, A),
Numerator #= (3*AX^2 + A) mod P,
Denom0 #= 2*AY mod P,
multiplicative_inverse_modulo_p(Denom0, P, Denom),
S #= (Numerator*Denom) mod P,
R = point(RX,RY),
RX #= (S^2 - 2*AX) mod P,
RY #= (S*(AX - RX) - AY) mod P,
must_be_on_curve(Curve, R).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Adding two points.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
curve_points_addition(Curve, P, Q, R) :-
curve_points_addition_(P, Curve, Q, R).
curve_points_addition_(null, _, P, P).
curve_points_addition_(P, _, null, P).
curve_points_addition_(point(AX,AY), Curve, point(BX,BY), R) :-
curve_p(Curve, P),
Numerator #= (AY - BY) mod P,
Denom0 #= (AX - BX) mod P,
multiplicative_inverse_modulo_p(Denom0, P, Denom),
S #= (Numerator * Denom) mod P,
R = point(RX,RY),
RX #= (S^2 - AX - BX) mod P,
RY #= (S*(AX - RX) - AY) mod P,
must_be_on_curve(Curve, R).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Validation.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -838,7 +747,7 @@ curve_contains_point(Curve, point(QX,QY)) :-
must_be_on_curve(Curve, P) :-
\+ curve_contains_point(Curve, P),
throw(not_on_curve(P)).
domain_error(point_on_curve, P, crypto_elliptic_curves).
must_be_on_curve(Curve, P) :- curve_contains_point(Curve, P).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -855,21 +764,38 @@ must_be_on_curve(Curve, P) :- curve_contains_point(Curve, P).
-text -no_seed -name secp256k1
You must remove the leading "04:" from the generator.
The field length depends on the order of the curve and can be computed
with order_field_length/2.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
order_field_length(Order, L) :-
fitting_exponent(Order, 0, E),
L #= (E + 7) // 8.
fitting_exponent(N, E0, E) :-
( 2^E0 #>= N -> E #= E0
; E1 #= E0 + 1,
fitting_exponent(N, E1, E)
).
crypto_name_curve(secp112r1,
curve(0x00db7c2abf62e35e668076bead208b,
curve(secp112r1,
0x00db7c2abf62e35e668076bead208b,
0x00db7c2abf62e35e668076bead2088,
0x659ef8ba043916eede8911702b22,
point(0x09487239995a5ee76b55f9c2f098,
0xa89ce5af8724c0a23e0e0ff77500),
0x00db7c2abf62e35e7628dfac6561c5,
14,
1)).
crypto_name_curve(secp256k1,
curve(0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
curve(secp256k1,
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
0x0,
0x7,
point(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8),
0x00fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
32,
1)).

View File

@@ -45,6 +45,10 @@ use ripemd160::{Ripemd160, Digest};
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512};
use blake2::{Blake2s, Blake2b};
use openssl::ec::{EcGroup, EcPoint};
use openssl::bn::{BigNum, BigNumContext};
use openssl::nid::Nid;
pub fn get_key() -> KeyEvent {
let key;
enable_raw_mode().expect("failed to enable raw mode");
@@ -5441,6 +5445,55 @@ impl MachineState {
self.unify(self[temp_v!(5)], complete_string);
}
&SystemClauseType::CryptoCurveScalarMult => {
let curve = match self.store(self.deref(self[temp_v!(1)])) {
Addr::Con(h) if self.heap.atom_at(h) => {
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
atom.as_str()
} else {
unreachable!()
}
}
_ => {
unreachable!()
}
};
let curve_id = match curve {
"secp112r1" => { Nid::SECP112R1 }
"secp256k1" => { Nid::SECP256K1 }
_ => { unreachable!() }
};
let scalar =
match Number::try_from((self[temp_v!(2)], &self.heap)) {
Ok(Number::Fixnum(n)) => {
Integer::from(n)
}
Ok(Number::Integer(n)) => {
Integer::from(&*n.clone())
}
_ => { unreachable!() }
};
let stub = MachineError::functor_stub(clause_name!("crypto_curve_scalar_mult"), 5);
let qbytes = self.integers_to_bytevec(temp_v!(3), stub);
let mut bnctx = BigNumContext::new().unwrap();
let group = EcGroup::from_curve_name(curve_id).unwrap();
let mut point = EcPoint::from_bytes(&group, &qbytes, &mut bnctx).unwrap();
let scalar_bn = BigNum::from_dec_str(&scalar.to_string()).unwrap();
let mut result = EcPoint::new(&group).unwrap();
result.mul(&group, &mut point, &scalar_bn, &mut bnctx).ok();
let mut rx = BigNum::new().unwrap();
let mut ry = BigNum::new().unwrap();
result.affine_coordinates_gfp(&group, &mut rx, &mut ry, &mut bnctx).ok();
let sx = self.heap.put_complete_string(&rx.to_dec_str().unwrap().to_string());
let sy = self.heap.put_complete_string(&ry.to_dec_str().unwrap().to_string());
self.unify(self[temp_v!(4)], sx);
self.unify(self[temp_v!(5)], sy);
}
&SystemClauseType::Ed25519NewKeyPair => {
let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(rng()).unwrap();
let complete_string = {