]> Repositorios git - scryer-prolog.git/commitdiff
ADDED: library(crypto): ECDH key exchange over Curve25519 (X25519)
authorMarkus Triska <[email protected]>
Wed, 29 Jul 2020 21:14:16 +0000 (23:14 +0200)
committerMarkus Triska <[email protected]>
Wed, 29 Jul 2020 21:51:11 +0000 (23:51 +0200)
Cargo.toml
README.md
src/clause_types.rs
src/lib/crypto.pl
src/machine/system_calls.rs

index 6ac8ebe48391f6bf6ba716e7f606a5927d0d58a2..6eaf21285e65f73b440002ef9421318885a0ef61 100644 (file)
@@ -47,3 +47,4 @@ chrono = "0.4.11"
 select = "0.4.3"
 roxmltree = "0.11.0"
 base64 = "0.12.3"
+sodiumoxide = "0.2.6"
index 0cb1a268ca22d07743fdbf4990e1539ac57890db..dab6a379d02a59f280da0ed648d9418b3f85ba2a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -456,8 +456,8 @@ The modules that ship with Scryer&nbsp;Prolog are also called
   Cryptographically secure random numbers and hashes, HMAC-based key
   derivation&nbsp;(HKDF), password-based key derivation&nbsp;(PBKDF2),
   public key signatures and signature verification with&nbsp;Ed25519,
-  authenticated symmetric encryption with ChaCha20-Poly1305, and
-  reasoning about elliptic curves.
+  ECDH key&nbsp;exchange over Curve25519 (X25519), authenticated symmetric
+  encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
 
 To use predicates provided by the `lists` library, write:
 
index ee6ad24ce718c103665ea9e17dee4020c0a34de4..045093087db807a7a05b2414896c2b5a9b5255ac 100644 (file)
@@ -309,6 +309,7 @@ pub enum SystemClauseType {
     Ed25519Verify,
     Ed25519NewKeyPair,
     Ed25519KeyPairPublicKey,
+    Curve25519ScalarMult,
     LoadHTML,
     LoadXML,
     GetEnv,
@@ -522,6 +523,7 @@ impl SystemClauseType {
             &SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"),
             &SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
             &SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key"),
+            &SystemClauseType::Curve25519ScalarMult => clause_name!("$curve25519_scalar_mult"),
             &SystemClauseType::LoadHTML => clause_name!("$load_html"),
             &SystemClauseType::LoadXML => clause_name!("$load_xml"),
             &SystemClauseType::GetEnv => clause_name!("$getenv"),
@@ -715,6 +717,7 @@ impl SystemClauseType {
             ("$ed25519_verify", 5) => Some(SystemClauseType::Ed25519Verify),
             ("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
             ("$ed25519_keypair_public_key", 3) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
+            ("$curve25519_scalar_mult", 3) => Some(SystemClauseType::Curve25519ScalarMult),
             ("$load_html", 3) => Some(SystemClauseType::LoadHTML),
             ("$load_xml", 3) => Some(SystemClauseType::LoadXML),
             ("$getenv", 2) => Some(SystemClauseType::GetEnv),
index 5058658d313ef5d5808a41ab96669e14c5da151c..49aae46e07911db3eee3f7e1458e605a56e2a2b2 100644 (file)
@@ -29,6 +29,8 @@
            ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
            ed25519_sign/4,               % +KeyPair, +Data, -Signature, +Options
            ed25519_verify/4,             % +PublicKey, +Data, +Signature, +Options
+           curve25519_generator/1,       % -Generator
+           curve25519_scalar_mult/3,     % +Scalar, +Point, -Result
            crypto_name_curve/2,          % +Name, -Curve
            crypto_curve_order/2,         % +Curve, -Order
            crypto_curve_generator/2,     % +Curve, -Generator
@@ -43,6 +45,7 @@
 :- use_module(library(arithmetic)).
 :- use_module(library(format)).
 :- use_module(library(charsio)).
+:- use_module(library(si)).
 
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    hex_bytes(?Hex, ?Bytes) is det.
@@ -648,6 +651,62 @@ ed25519_verify(Key, Data0, Signature0, Options) :-
         hex_bytes(Signature0, Signature),
         '$ed25519_verify'(Key, octet, Data, Encoding, Signature).
 
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+   X25519: ECDH key exchange over Curve25519
+   =========================================
+
+   Points on Curve25519 are represented as lists of characters that denote
+   the u-coordinate of the Montgomery curve.
+
+   - curve25519_generator(-Gs)
+     Gs is the generator point of Curve25519.
+
+   - curve25519_scalar_mult(+Scalar, +Ps, -Rs)
+     Scalar must be an integer between 0 and 2^256-1,
+     or a list of 32 bytes, and Ps must be a point on the curve.
+     Computes the point Rs = Scalar*Ps as mandated by X25519.
+
+   Alice and Bob can use this to establish a shared secret as follows,
+   where Gs is the generator point of Curve25519:
+
+     1. Alice creates a random integer a and sends As = a*Gs to Bob.
+     2. Bob creates a random integer b and sends Bs = b*Gs to Alice.
+     3. Alice computes Rs = a*Bs.
+     4. Bob computes Rs = b*As.
+     5. Alice and Bob use crypto_data_hkdf/4 on Rs with suitable
+        (same) parameters to obtain lists of bytes that can be used as
+        keys and initialization vectors for symmetric encryption.
+
+   If a and b are kept secret, this method is considered very secure.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+curve25519_generator(Gs) :-
+        length(Gs0, 32),
+        Gs0 = [9|Zs],
+        maplist(=(0), Zs),
+        maplist(char_code, Gs, Gs0).
+
+curve25519_scalar_mult(Scalar, Point, Result) :-
+        (   integer_si(Scalar) ->
+            Scalar #>= 0,
+            Scalar #< 2^256,
+            length(ScalarBytes, 32),
+            bytes_integer(ScalarBytes, Scalar)
+        ;   ScalarBytes = Scalar,
+            must_be_bytes(ScalarBytes, curve25519_scalar_mult/3),
+            length(ScalarBytes, 32)
+        ),
+        maplist(char_code, Point, PointBytes),
+        '$curve25519_scalar_mult'(ScalarBytes, PointBytes, Result).
+
+bytes_integer(Bs, N) :-
+        foldl(pow, Bs, 0-0, N-_).
+
+pow(B, N0-I0, N-I) :-
+        B in 0..255,
+        N #= N0 + B*256^I0,
+        I #= I0 + 1.
+
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Operations on Elliptic Curves
    =============================
@@ -663,6 +722,7 @@ ed25519_verify(Key, Data0, Signature0, Options) :-
        crypto_curve_scalar_mult(C, Random, PublicKey, S),
        crypto_curve_scalar_mult(C, PrivateKey, R, S).
 
+   For better security, new code should use Curve25519 instead.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
index 5a1f9d53d98ccbed9cfdbcb214f1811d2dfa0eee..10ae3d7f40885a80e0ec3a83c43f2341c205992b 100644 (file)
@@ -51,6 +51,8 @@ use crate::openssl::ec::{EcGroup, EcPoint};
 use crate::openssl::bn::{BigNum, BigNumContext};
 use crate::openssl::nid::Nid;
 
+use sodiumoxide::crypto::scalarmult::curve25519::*;
+
 use crate::native_tls::TlsConnector;
 
 extern crate select;
@@ -5706,6 +5708,24 @@ impl MachineState {
                     _ => { self.fail = true; return Ok(()); }
                 }
             }
+            &SystemClauseType::Curve25519ScalarMult => {
+                let stub1 = MachineError::functor_stub(clause_name!("curve25519_scalar_mult"), 3);
+                let scalar_bytes = self.integers_to_bytevec(temp_v!(1), stub1);
+                let scalar = Scalar(<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap());
+
+                let stub2 = MachineError::functor_stub(clause_name!("curve25519_scalar_mult"), 3);
+                let point_bytes = self.integers_to_bytevec(temp_v!(2), stub2);
+                let point = GroupElement(<[u8; 32]>::try_from(&point_bytes[..]).unwrap());
+
+                let result = scalarmult(&scalar, &point).unwrap();
+
+                let mut string = String::new();
+                for c in result[..].iter() {
+                    string.push(*c as char);
+                }
+                let cstr = self.heap.put_complete_string(&string);
+                self.unify(self[temp_v!(3)], cstr);
+            }
             &SystemClauseType::LoadHTML => {
                 let string = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
                 let doc = select::document::Document::from_read(string.as_bytes()).unwrap();