]> Repositorios git - scryer-prolog.git/commitdiff
ADDED: Public key signatures and signature verification with Ed25519
authorMarkus Triska <[email protected]>
Wed, 20 May 2020 16:30:34 +0000 (18:30 +0200)
committerMarkus Triska <[email protected]>
Wed, 20 May 2020 20:32:39 +0000 (22:32 +0200)
README.md
src/prolog/clause_types.rs
src/prolog/lib/crypto.pl
src/prolog/machine/system_calls.rs

index ed9161f5157a216446d7df94db3f78e6c4be671a..9ad292398e80079533f8f782ce4c10cbf5ca8728 100644 (file)
--- a/README.md
+++ b/README.md
@@ -380,6 +380,7 @@ The modules that ship with Scryer&nbsp;Prolog are also called
 * [`crypto`](src/prolog/lib/crypto.pl)
   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 encryption, and reasoning about elliptic curves.
 
 To read contents of external files, use `phrase_from_file/2` from
index 78ca09de92f250b18deb955cb95c65ca169c1da7..067e44375d0463fd609de28e2b0404746b251fa6 100644 (file)
@@ -291,7 +291,9 @@ pub enum SystemClauseType {
     CryptoDataHKDF,
     CryptoPasswordHash,
     CryptoDataEncrypt,
-    CryptoDataDecrypt
+    CryptoDataDecrypt,
+    Ed25519Sign,
+    Ed25519Verify
 }
 
 impl SystemClauseType {
@@ -480,6 +482,8 @@ impl SystemClauseType {
             &SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"),
             &SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"),
             &SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"),
+            &SystemClauseType::Ed25519Sign => clause_name!("$ed25519_sign"),
+            &SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"),
         }
     }
 
@@ -648,6 +652,8 @@ impl SystemClauseType {
             ("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
             ("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt),
             ("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt),
+            ("$ed25519_sign", 3) => Some(SystemClauseType::Ed25519Sign),
+            ("$ed25519_verify", 3) => Some(SystemClauseType::Ed25519Verify),
             _ => None,
         }
     }
index e07b4b1db3b3a41c71f513d6f2a38e3087c9c19b..1562b4c1c07a3b4bb97297665c3dd3cf0569e5e2 100644 (file)
@@ -9,7 +9,7 @@
    and strings have the advantage that the atom table remains unmodified.
 
    Especially for cryptographic applications, it as an advantage that
-   using strings leaves little trace of what was processed in the system,
+   using strings leaves little trace of what was processed in the system.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
 :- module(crypto,
@@ -21,6 +21,8 @@
            crypto_password_hash/3,     % +Password, -Hash, +Options
            crypto_data_encrypt/6,      % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
            crypto_data_decrypt/6,      % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
+           ed25519_sign/4,             % +PrivateKey, +Data, -Signature, +Options
+           ed25519_verify/4,           % +PublicKey, +Data, -Signature, +Options
            crypto_name_curve/2,        % +Name, -Curve
            crypto_curve_order/2,       % +Curve, -Order
            crypto_curve_generator/2,   % +Curve, -Generator
@@ -624,6 +626,44 @@ encoding_bytes(utf8, Cs, Bs) :-
         ;   domain_error(encryption_encoding, Cs, crypto)
         ).
 
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+   Digital signatures with Ed25519
+   ===============================
+
+   ed25519_sign(+Key, +Data, -Signature, +Options)
+
+   Key and Data must be lists of characters. Key is a private key in
+   PKCS#8 (v1 or v2) DER format. Sign Data with Key, yielding
+   Signature as a list of hexadecimal characters.
+
+
+   ed25519_verify(+Key, +Data, +Signature, +Options)
+
+   Key and Data must be lists of characters. Key is a public key in
+   PKCS#8 DER format. Succeeds if Data was signed with the private key
+   corresponding to Key, where Signature is a list of hexadecimal
+   characters as generated by ed25519_sign/4. Fails otherwise.
+
+
+   Currently, the only option for both predicates is:
+
+     - encoding(+Encoding)
+       The default encoding of Data is utf8. The alternative is octet,
+       which treats Data as a list of raw bytes.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ed25519_sign(Key0, Data0, Signature, Options) :-
+        options_data_bytes(Options, Data0, Data),
+        encoding_bytes(octet, Key0, Key),
+        '$ed25519_sign'(Key, Data, Signature0),
+        hex_bytes(Signature, Signature0).
+
+ed25519_verify(Key0, Data0, Signature0, Options) :-
+        options_data_bytes(Options, Data0, Data),
+        encoding_bytes(octet, Key0, Key),
+        hex_bytes(Signature0, Signature),
+        '$ed25519_verify'(Key, Data, Signature).
+
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Modular multiplicative inverse.
 
index 4eacea2a77fdc423b16105bcd98a189f80f51abb..4ebbcf299382ee3c47dc08bd0d9fe8f4c3468bbb 100644 (file)
@@ -40,7 +40,7 @@ use crate::crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
 use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode};
 
 use ring::rand::{SecureRandom, SystemRandom};
-use ring::{digest,hkdf,pbkdf2,aead,error};
+use ring::{digest,hkdf,pbkdf2,aead,error,signature};
 use ripemd160::{Ripemd160, Digest};
 use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512};
 use blake2::{Blake2s, Blake2b};
@@ -5448,6 +5448,38 @@ impl MachineState {
 
                 self.unify(self[temp_v!(5)], complete_string);
             }
+            &SystemClauseType::Ed25519Sign => {
+                let stub1 = MachineError::functor_stub(clause_name!("ed25519_sign"), 4);
+                let key = self.integers_to_bytevec(temp_v!(1), stub1);
+                let stub2 = MachineError::functor_stub(clause_name!("ed25519_sign"), 4);
+                let data = self.integers_to_bytevec(temp_v!(2), stub2);
+
+                let key_pair = match signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(&key) {
+                                  Ok(kp) => { kp }
+                                  _ => { self.fail = true; return Ok(()); }
+                               };
+
+                let sig = key_pair.sign(&data);
+
+                let sig_list =
+                      Addr::HeapCell(self.heap.to_list(sig.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize)))));
+
+                self.unify(self[temp_v!(3)], sig_list);
+            }
+            &SystemClauseType::Ed25519Verify => {
+                let stub1 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4);
+                let key = self.integers_to_bytevec(temp_v!(1), stub1);
+                let stub2 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4);
+                let data = self.integers_to_bytevec(temp_v!(2), stub2);
+                let stub3 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4);
+                let signature = self.integers_to_bytevec(temp_v!(3), stub3);
+
+                let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key);
+                match peer_public_key.verify(&data, &signature) {
+                    Ok(_) => { }
+                    _ => { self.fail = true; return Ok(()); }
+                }
+            }
         };
 
         return_from_clause!(self.last_call, self)