]> Repositorios git - scryer-prolog.git/commitdiff
ENHANCED: Faster Rust-based crypto_curve_scalar_mult/4 using OpenSSL.
authorMarkus Triska <[email protected]>
Sun, 7 Jun 2020 08:58:40 +0000 (10:58 +0200)
committerMarkus Triska <[email protected]>
Sun, 7 Jun 2020 09:04:24 +0000 (11:04 +0200)
Cargo.toml
src/prolog/clause_types.rs
src/prolog/lib/crypto.pl
src/prolog/machine/system_calls.rs

index 1d63c54ce43625d4e75efc0fb32a0c5a83658010..13acfe3447aba1a08505563329a2b5cb0db97f49 100644 (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"] }
index 14c353b28f71fbebfbbd51602b490b6f51515e4b..fe816cc0112522ba5c43aac0fe4c827fdda682ac 100644 (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),
index cd7f93c5d3cb44386739522e81c91f54fe8952c6..922020463835b5ad6da8783b00aed36d749fec04 100644 (file)
@@ -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)).
index 5a4523ce4cf5a2ded2a0eebf259cfa77f48960b9..bb470f0e99ad78b30b2fe588af3ab40a8233c774 100644 (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 = {