From: Markus Triska Date: Sun, 7 Jun 2020 08:58:40 +0000 (+0200) Subject: ENHANCED: Faster Rust-based crypto_curve_scalar_mult/4 using OpenSSL. X-Git-Tag: v0.8.127~53^2~2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=b586618411068b557ba7fbb310a9169f838f564a;p=scryer-prolog.git ENHANCED: Faster Rust-based crypto_curve_scalar_mult/4 using OpenSSL. --- diff --git a/Cargo.toml b/Cargo.toml index 1d63c54c..13acfe34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 14c353b2..fe816cc0 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -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), diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index cd7f93c5..92202046 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -683,45 +683,6 @@ 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 ============================= @@ -742,90 +703,37 @@ 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). - -crypto_curve_order(curve(_,_,_,_,Order,_), Order). -crypto_curve_generator(curve(_,_,_,G,_,_), G). - -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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_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). +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_generator(curve(_,_,_,_,G,_,_,_), G). +crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order). + +crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :- + 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). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Doubling a point: R = A + A. +?- crypto_name_curve(secp256k1, Curve), + crypto_curve_generator(Curve, G), + crypto_curve_scalar_mult(Curve, 2, G, R). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -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 +746,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 +763,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)). diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 5a4523ce..bb470f0e 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -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 = {