From: Markus Triska Date: Wed, 29 Jul 2020 21:14:16 +0000 (+0200) Subject: ADDED: library(crypto): ECDH key exchange over Curve25519 (X25519) X-Git-Tag: v0.8.127~2^2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=32c612b7472a40fc196ed2098c28df1a081dd471;p=scryer-prolog.git ADDED: library(crypto): ECDH key exchange over Curve25519 (X25519) --- diff --git a/Cargo.toml b/Cargo.toml index 6ac8ebe4..6eaf2128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,3 +47,4 @@ chrono = "0.4.11" select = "0.4.3" roxmltree = "0.11.0" base64 = "0.12.3" +sodiumoxide = "0.2.6" diff --git a/README.md b/README.md index 0cb1a268..dab6a379 100644 --- a/README.md +++ b/README.md @@ -456,8 +456,8 @@ The modules that ship with Scryer Prolog are also called Cryptographically secure random numbers and hashes, HMAC-based key derivation (HKDF), password-based key derivation (PBKDF2), public key signatures and signature verification with Ed25519, - authenticated symmetric encryption with ChaCha20-Poly1305, and - reasoning about elliptic curves. + ECDH key exchange over Curve25519 (X25519), authenticated symmetric + encryption with ChaCha20-Poly1305, and reasoning about elliptic curves. To use predicates provided by the `lists` library, write: diff --git a/src/clause_types.rs b/src/clause_types.rs index ee6ad24c..04509308 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -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), diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 5058658d..49aae46e 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -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. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 5a1f9d53..10ae3d7f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -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();