Merge pull request #545 from triska/ed25519
ADDED: Public key signatures and signature verification with Ed25519
This commit is contained in:
@@ -380,6 +380,7 @@ The modules that ship with Scryer Prolog are also called
|
|||||||
* [`crypto`](src/prolog/lib/crypto.pl)
|
* [`crypto`](src/prolog/lib/crypto.pl)
|
||||||
Cryptographically secure random numbers and hashes, HMAC-based
|
Cryptographically secure random numbers and hashes, HMAC-based
|
||||||
key derivation (HKDF), password-based key derivation (PBKDF2),
|
key derivation (HKDF), password-based key derivation (PBKDF2),
|
||||||
|
public key signatures and signature verification with Ed25519,
|
||||||
authenticated encryption, and reasoning about elliptic curves.
|
authenticated encryption, and reasoning about elliptic curves.
|
||||||
|
|
||||||
To read contents of external files, use `phrase_from_file/2` from
|
To read contents of external files, use `phrase_from_file/2` from
|
||||||
|
|||||||
@@ -291,7 +291,11 @@ pub enum SystemClauseType {
|
|||||||
CryptoDataHKDF,
|
CryptoDataHKDF,
|
||||||
CryptoPasswordHash,
|
CryptoPasswordHash,
|
||||||
CryptoDataEncrypt,
|
CryptoDataEncrypt,
|
||||||
CryptoDataDecrypt
|
CryptoDataDecrypt,
|
||||||
|
Ed25519Sign,
|
||||||
|
Ed25519Verify,
|
||||||
|
Ed25519NewKeyPair,
|
||||||
|
Ed25519KeyPairPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemClauseType {
|
impl SystemClauseType {
|
||||||
@@ -480,6 +484,10 @@ impl SystemClauseType {
|
|||||||
&SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"),
|
&SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"),
|
||||||
&SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"),
|
&SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"),
|
||||||
&SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"),
|
&SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"),
|
||||||
|
&SystemClauseType::Ed25519Sign => clause_name!("$ed25519_sign"),
|
||||||
|
&SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"),
|
||||||
|
&SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
|
||||||
|
&SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,6 +656,10 @@ impl SystemClauseType {
|
|||||||
("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
|
("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
|
||||||
("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt),
|
("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt),
|
||||||
("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt),
|
("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt),
|
||||||
|
("$ed25519_sign", 3) => Some(SystemClauseType::Ed25519Sign),
|
||||||
|
("$ed25519_verify", 3) => Some(SystemClauseType::Ed25519Verify),
|
||||||
|
("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
|
||||||
|
("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,22 +9,30 @@
|
|||||||
and strings have the advantage that the atom table remains unmodified.
|
and strings have the advantage that the atom table remains unmodified.
|
||||||
|
|
||||||
Especially for cryptographic applications, it as an advantage that
|
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.
|
||||||
|
|
||||||
|
For predicates that accept an encoding/1 option to specify the encoding
|
||||||
|
of the input data, if encoding(octet) is used, then the input can also
|
||||||
|
be specified as a list of bytes, i.e., integers between 0 and 255.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
:- module(crypto,
|
:- module(crypto,
|
||||||
[hex_bytes/2, % ?Hex, ?Bytes
|
[hex_bytes/2, % ?Hex, ?Bytes
|
||||||
crypto_n_random_bytes/2, % +N, -Bytes
|
crypto_n_random_bytes/2, % +N, -Bytes
|
||||||
crypto_data_hash/3, % +Data, -Hash, +Options
|
crypto_data_hash/3, % +Data, -Hash, +Options
|
||||||
crypto_data_hkdf/4, % +Data, +Length, -Bytes, +Options
|
crypto_data_hkdf/4, % +Data, +Length, -Bytes, +Options
|
||||||
crypto_password_hash/2, % +Password, ?Hash
|
crypto_password_hash/2, % +Password, ?Hash
|
||||||
crypto_password_hash/3, % +Password, -Hash, +Options
|
crypto_password_hash/3, % +Password, -Hash, +Options
|
||||||
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
|
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
|
||||||
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
|
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
|
||||||
crypto_name_curve/2, % +Name, -Curve
|
ed25519_new_keypair/1, % -KeyPair
|
||||||
crypto_curve_order/2, % +Curve, -Order
|
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
|
||||||
crypto_curve_generator/2, % +Curve, -Generator
|
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
|
||||||
crypto_curve_scalar_mult/4 % +Curve, +Scalar, +Point, -Result
|
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
|
||||||
|
crypto_curve_scalar_mult/4 % +Curve, +Scalar, +Point, -Result
|
||||||
]).
|
]).
|
||||||
|
|
||||||
:- use_module(library(error)).
|
:- use_module(library(error)).
|
||||||
@@ -151,9 +159,8 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
|||||||
|
|
||||||
crypto_data_hash(+Data, -Hash, +Options)
|
crypto_data_hash(+Data, -Hash, +Options)
|
||||||
|
|
||||||
Where Data is a list of bytes (integers between 0 and 255) or
|
Where Data is a list of characters, and Hash is the computed hash
|
||||||
characters, and Hash is the computed hash as a list of hexadecimal
|
as a list of hexadecimal characters.
|
||||||
characters.
|
|
||||||
|
|
||||||
Options is a list of:
|
Options is a list of:
|
||||||
|
|
||||||
@@ -227,7 +234,7 @@ hash_algorithm(blake2b512).
|
|||||||
crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det.
|
crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det.
|
||||||
|
|
||||||
Concentrate possibly dispersed entropy of Data and then expand it
|
Concentrate possibly dispersed entropy of Data and then expand it
|
||||||
to the desired length. Data is a list of bytes or characters.
|
to the desired length. Data is a list of characters.
|
||||||
|
|
||||||
Bytes is unified with a list of bytes of length Length, and is
|
Bytes is unified with a list of bytes of length Length, and is
|
||||||
suitable as input keying material and initialization vectors to
|
suitable as input keying material and initialization vectors to
|
||||||
@@ -241,7 +248,7 @@ hash_algorithm(blake2b512).
|
|||||||
cryptographically secure algorithm by default.
|
cryptographically secure algorithm by default.
|
||||||
- info(+Info)
|
- info(+Info)
|
||||||
Optional context and application specific information,
|
Optional context and application specific information,
|
||||||
specified as a list of bytes or characters. The default is [].
|
specified as a list of characters. The default is [].
|
||||||
- salt(+List)
|
- salt(+List)
|
||||||
Optionally, a list of bytes that are used as salt. The
|
Optionally, a list of bytes that are used as salt. The
|
||||||
default is all zeroes.
|
default is all zeroes.
|
||||||
@@ -476,8 +483,8 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z],
|
|||||||
Algorithm, key Key, and initialization vector (or nonce) IV, to
|
Algorithm, key Key, and initialization vector (or nonce) IV, to
|
||||||
give CipherText.
|
give CipherText.
|
||||||
|
|
||||||
PlainText must be a list of codes or characters, Key and IV must be
|
PlainText must be a list of characters, Key and IV must be lists of
|
||||||
lists of bytes, and CipherText is created as a list of characters.
|
bytes, and CipherText is created as a list of characters.
|
||||||
|
|
||||||
Keys and IVs can be chosen at random (using for example
|
Keys and IVs can be chosen at random (using for example
|
||||||
crypto_n_random_bytes/2) or derived from input keying material (IKM)
|
crypto_n_random_bytes/2) or derived from input keying material (IKM)
|
||||||
@@ -575,9 +582,9 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
|
|||||||
|
|
||||||
Decrypt the given CipherText, using the symmetric algorithm
|
Decrypt the given CipherText, using the symmetric algorithm
|
||||||
Algorithm, key Key, and initialization vector IV, to give
|
Algorithm, key Key, and initialization vector IV, to give
|
||||||
PlainText. CipherText must be a list of bytes or characters, and
|
PlainText. CipherText must be a list of characters, and Key and IV
|
||||||
Key and IV must be lists of bytes. PlainText is created as a list
|
must be lists of bytes. PlainText is created as a list of
|
||||||
of characters.
|
characters.
|
||||||
|
|
||||||
Currently, the only supported algorithm is 'chacha20-poly1305',
|
Currently, the only supported algorithm is 'chacha20-poly1305',
|
||||||
a very secure, fast and versatile authenticated encryption method.
|
a very secure, fast and versatile authenticated encryption method.
|
||||||
@@ -624,6 +631,58 @@ encoding_bytes(utf8, Cs, Bs) :-
|
|||||||
; domain_error(encryption_encoding, Cs, crypto)
|
; domain_error(encryption_encoding, Cs, crypto)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Digital signatures with Ed25519
|
||||||
|
===============================
|
||||||
|
|
||||||
|
- ed25519_new_keypair(-Pair)
|
||||||
|
Yields a new Ed25519 key pair Pair, a list of characters. The
|
||||||
|
pair contains the private key and must be kept absolutely secret.
|
||||||
|
Pair can be used for signing. Its public key can be obtained
|
||||||
|
with ed25519_keypair_public_key/2.
|
||||||
|
|
||||||
|
- ed25519_keypair_public_key(+Pair, -PublicKey)
|
||||||
|
PublicKey is the public key of the given key pair. The public key
|
||||||
|
can be used for signature verification, and can be shared freely.
|
||||||
|
The public key is represented as a list of characters.
|
||||||
|
|
||||||
|
- ed25519_sign(+Key, +Data, -Signature, +Options)
|
||||||
|
Key and Data must be lists of characters. Key is a key pair in
|
||||||
|
PKCS#8 v2 format as generated by ed25519_new_keypair/1. 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.
|
||||||
|
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 signing and verifying is:
|
||||||
|
|
||||||
|
- encoding(+Encoding)
|
||||||
|
The default encoding of Data is utf8. The alternative is octet,
|
||||||
|
which treats Data as a list of raw bytes.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
ed25519_new_keypair(Pair) :-
|
||||||
|
'$ed25519_new_keypair'(Pair).
|
||||||
|
|
||||||
|
ed25519_keypair_public_key(Pair0, PublicKey) :-
|
||||||
|
encoding_bytes(octet, Pair0, Pair),
|
||||||
|
'$ed25519_keypair_public_key'(Pair, PublicKey).
|
||||||
|
|
||||||
|
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.
|
Modular multiplicative inverse.
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ use crate::crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
|
|||||||
use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode};
|
use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode};
|
||||||
|
|
||||||
use ring::rand::{SecureRandom, SystemRandom};
|
use ring::rand::{SecureRandom, SystemRandom};
|
||||||
use ring::{digest,hkdf,pbkdf2,aead,error};
|
use ring::{digest,hkdf,pbkdf2,aead,signature::{self,KeyPair}};
|
||||||
use ripemd160::{Ripemd160, Digest};
|
use ripemd160::{Ripemd160, Digest};
|
||||||
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512};
|
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512};
|
||||||
use blake2::{Blake2s, Blake2b};
|
use blake2::{Blake2s, Blake2b};
|
||||||
@@ -5378,12 +5378,12 @@ impl MachineState {
|
|||||||
let iv = self.integers_to_bytevec(temp_v!(3), stub3);
|
let iv = self.integers_to_bytevec(temp_v!(3), stub3);
|
||||||
|
|
||||||
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
||||||
let nonce_sequence = OneNonceSequence::new(aead::Nonce::try_assume_unique_for_key(&iv).unwrap());
|
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
|
||||||
let mut key: aead::SealingKey<OneNonceSequence> = aead::BoundKey::new(unbound_key, nonce_sequence);
|
let key = aead::LessSafeKey::new(unbound_key);
|
||||||
|
|
||||||
let mut in_out = data.clone();
|
let mut in_out = data.clone();
|
||||||
let tag =
|
let tag =
|
||||||
match key.seal_in_place_separate_tag(aead::Aad::empty(), &mut in_out) {
|
match key.seal_in_place_separate_tag(nonce, aead::Aad::empty(), &mut in_out) {
|
||||||
Ok(d) => { d }
|
Ok(d) => { d }
|
||||||
_ => { self.fail = true; return Ok(()); }
|
_ => { self.fail = true; return Ok(()); }
|
||||||
};
|
};
|
||||||
@@ -5421,14 +5421,14 @@ impl MachineState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
||||||
let nonce_sequence = OneNonceSequence::new(aead::Nonce::try_assume_unique_for_key(&iv).unwrap());
|
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
|
||||||
let mut key: aead::OpeningKey<OneNonceSequence> = aead::BoundKey::new(unbound_key, nonce_sequence);
|
let key = aead::LessSafeKey::new(unbound_key);
|
||||||
|
|
||||||
let mut in_out = data.clone();
|
let mut in_out = data.clone();
|
||||||
|
|
||||||
let complete_string = {
|
let complete_string = {
|
||||||
let decrypted_data =
|
let decrypted_data =
|
||||||
match key.open_in_place(aead::Aad::empty(), &mut in_out) {
|
match key.open_in_place(nonce, aead::Aad::empty(), &mut in_out) {
|
||||||
Ok(d) => { d }
|
Ok(d) => { d }
|
||||||
_ => { self.fail = true; return Ok(()); }
|
_ => { self.fail = true; return Ok(()); }
|
||||||
};
|
};
|
||||||
@@ -5448,6 +5448,63 @@ impl MachineState {
|
|||||||
|
|
||||||
self.unify(self[temp_v!(5)], complete_string);
|
self.unify(self[temp_v!(5)], complete_string);
|
||||||
}
|
}
|
||||||
|
&SystemClauseType::Ed25519NewKeyPair => {
|
||||||
|
let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(rng()).unwrap();
|
||||||
|
let complete_string = {
|
||||||
|
let buffer = String::from_iter(pkcs8_bytes.as_ref().iter().map(|b| *b as char));
|
||||||
|
self.heap.put_complete_string(&buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.unify(self[temp_v!(1)], complete_string);
|
||||||
|
}
|
||||||
|
&SystemClauseType::Ed25519KeyPairPublicKey => {
|
||||||
|
let stub1 = MachineError::functor_stub(clause_name!("ed25519_keypair_public_key"), 2);
|
||||||
|
let bytes = self.integers_to_bytevec(temp_v!(1), stub1);
|
||||||
|
|
||||||
|
let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) {
|
||||||
|
Ok(kp) => { kp }
|
||||||
|
_ => { self.fail = true; return Ok(()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
let complete_string = {
|
||||||
|
let buffer = String::from_iter(key_pair.public_key().as_ref().iter().map(|b| *b as char));
|
||||||
|
self.heap.put_complete_string(&buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.unify(self[temp_v!(2)], 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(&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)
|
return_from_clause!(self.last_call, self)
|
||||||
@@ -5472,17 +5529,3 @@ impl hkdf::KeyType for MyKey<usize> {
|
|||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OneNonceSequence(Option<aead::Nonce>);
|
|
||||||
|
|
||||||
impl OneNonceSequence {
|
|
||||||
fn new(nonce: aead::Nonce) -> Self {
|
|
||||||
Self(Some(nonce))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl aead::NonceSequence for OneNonceSequence {
|
|
||||||
fn advance(&mut self) -> Result<aead::Nonce, error::Unspecified> {
|
|
||||||
self.0.take().ok_or(error::Unspecified)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user