From: Markus Triska Date: Wed, 20 May 2020 16:30:34 +0000 (+0200) Subject: ADDED: Public key signatures and signature verification with Ed25519 X-Git-Tag: v0.8.123~7^2~10 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=ad3be5c848dec53814d13a646c636bcf50976460;p=scryer-prolog.git ADDED: Public key signatures and signature verification with Ed25519 --- diff --git a/README.md b/README.md index ed9161f5..9ad29239 100644 --- a/README.md +++ b/README.md @@ -380,6 +380,7 @@ The modules that ship with Scryer 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 diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 78ca09de..067e4437 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -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, } } diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index e07b4b1d..1562b4c1 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -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. diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 4eacea2a..4ebbcf29 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -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)