:- module(crypto, [hex_bytes/2,
crypto_n_random_bytes/2,
- crypto_data_hash/3,
+ crypto_data_hash/3, % +Data, -Hash, +Options
+ crypto_data_hkdf/4, % +Data, +Length, -Bytes, +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(lists)).
true
; domain_error(hex_encoding, Hs, hex_bytes/2)
)
- ; must_be(list, Bytes),
- maplist(must_be(integer), Bytes),
- must_be_bytes(Bytes, hex_bytes/2),
+ ; must_be_bytes(Bytes, hex_bytes/2),
phrase(bytes_hex(Bytes), Hs)
).
must_be_bytes(Bytes, Context) :-
+ must_be(list, Bytes),
+ maplist(must_be(integer), Bytes),
( member(B, Bytes), \+ between(0, 255, B) ->
type_error(byte, B, Context)
; true
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Cryptographically secure random numbers
+ =======================================
+
crypto_n_random_bytes(+N, -Bytes) is det
Bytes is unified with a list of N cryptographically secure
crypto_random_byte(B) :- '$crypto_random_byte'(B).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Hashing
+ =======
+
crypto_data_hash(+Data, -Hash, +Options)
- Where Data is a list of bytes (integers between 0 and 255),
- and Hash is the computed hash as a list of hexadecimal characters.
+ Where Data is a list of bytes (integers between 0 and 255) or
+ characters, and Hash is the computed hash as a list of hexadecimal
+ characters.
The single supported option is:
Example:
- ?- crypto_data_hash([0'a,0'b,0'c], Hs, [algorithm(sha256)]).
+ ?- crypto_data_hash("abc", Hs, [algorithm(sha256)]).
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-crypto_data_hash(Data, Hash, Options) :-
- must_be(list, Data),
- must_be_bytes(Data, crypto_data_hash/3),
- must_be(list, Options),
- ( Options = [algorithm(A)] -> true
- ; true
- ),
- ( var(A) -> A = sha256
- ; true
- ),
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ SHA256 is the current default for several hash-related predicates.
+ It is deemed sufficiently secure for the foreseeable future. Yet,
+ application programmers must be aware that the default may change in
+ future versions. The hash predicates all yield the algorithm they
+ used if a Prolog variable is used for the pertaining option.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+crypto_data_hash(Data0, Hash, Options0) :-
+ chars_bytes_(Data0, Data, crypto_data_hash/3),
+ must_be(list, Options0),
+ functor_hash_options(algorithm, A, Options0, _),
( hash_algorithm(A) -> true
; domain_error(hash_algorithm, A, crypto_data_hash/3)
),
'$crypto_data_hash'(Data, HashBytes, A),
hex_bytes(Hash, HashBytes).
+
+default_hash(sha256).
+
+functor_hash_options(F, Hash, Options0, [Option|Options]) :-
+ Option =.. [F,Hash],
+ ( select(Option, Options0, Options) ->
+ ( var(Hash) ->
+ default_hash(Hash)
+ ; must_be(atom, Hash)
+ )
+ ; Options = Options0,
+ default_hash(Hash)
+ ).
+
hash_algorithm(ripemd160).
hash_algorithm(sha256).
hash_algorithm(sha512).
hash_algorithm(sha512_256).
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det.
+
+ Concentrate possibly dispersed entropy of Data and then expand it
+ to the desired length. Data is a list of bytes or characters.
+
+ Bytes is unified with a list of bytes of length Length, and is
+ suitable as input keying material and initialization vectors to
+ symmetric encryption algorithms.
+
+ Admissible options are:
+
+ - algorithm(+Algorithm)
+ A hashing algorithm as specified to crypto_data_hash/3. The
+ default is a cryptographically secure algorithm. If you
+ specify a variable, then it is unified with the algorithm
+ that was used, which is a cryptographically secure algorithm.
+ - info(+Info)
+ Optional context and application specific information,
+ specified as a list of bytes or characters. The default is [].
+ - salt(+List)
+ Optionally, a list of bytes that are used as salt. The
+ default is all zeroes.
+
+ The `info/1` option can be used to generate multiple keys from a
+ single master key, using for example values such as "key" and
+ "iv", or the name of a file that is to be encrypted.
+
+ See crypto_n_random_bytes/2 to obtain a suitable salt.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+crypto_data_hkdf(Data0, L, Bytes, Options0) :-
+ functor_hash_options(algorithm, Algorithm, Options0, Options),
+ chars_bytes_(Data0, Data, crypto_data_hkdf/4),
+ option(salt(SaltBytes), Options, []),
+ must_be_bytes(SaltBytes, crypto_data_hkdf/4),
+ option(info(Info0), Options, []),
+ chars_bytes_(Info0, Info, crypto_data_hkdf/4),
+ '$crypto_data_hkdf'(Data, SaltBytes, Info, Algorithm, L, Bytes).
+
+option(What, Options, Default) :-
+ ( member(What, Options) -> true
+ ; What =.. [_,Default]
+ ).
+
+chars_bytes_(Cs, Bytes, Context) :-
+ must_be(list, Cs),
+ ( maplist(integer, Cs) -> Bytes = Cs
+ ; % use chars_utf8bytes/2 here once it becomes available!
+ maplist(atom_codes, Cs, Css),
+ append(Css, Bytes)
+ ),
+ must_be_bytes(Bytes, Context).
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Modular multiplicative inverse.
use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode};
use ring::rand::{SecureRandom, SystemRandom};
-use ring::digest;
+use ring::{digest,hkdf};
use ripemd160::{Ripemd160, Digest};
pub fn get_key() -> KeyEvent {
self.unify(arg, byte);
}
&SystemClauseType::CryptoDataHash => {
- let mut bytes: Vec<u8> = Vec::new();
-
let stub = MachineError::functor_stub(clause_name!("crypto_data_hash"), 3);
-
- match self.try_from_list(temp_v!(1), stub) {
- Err(e) => return Err(e),
- Ok(addrs) => {
-
- for addr in addrs {
- let addr = self.store(self.deref(addr));
-
- match Number::try_from((addr, &self.heap)) {
- Ok(Number::Fixnum(n)) => {
- match u8::try_from(n) {
- Ok(b) => {
- bytes.push(b);
- }
- Err(_) => { }
- }
-
- continue;
- }
- Ok(Number::Integer(n)) => {
- if let Some(b) = n.to_u8() {
- bytes.push(b);
- }
-
- continue;
- }
- _ => {
- }
- }
- }
- }
- }
+ let bytes = self.integers_to_bytevec(temp_v!(1), stub);
let algorithm = self[temp_v!(3)];
let algorithm_str = match self.store(self.deref(algorithm)) {
self.unify(self[temp_v!(2)], ints_list);
}
+ &SystemClauseType::CryptoDataHKDF => {
+ let stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 6);
+ let data = self.integers_to_bytevec(temp_v!(1), stub1);
+ let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 6);
+ let salt = self.integers_to_bytevec(temp_v!(2), stub2);
+ let stub3 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 6);
+ let info = self.integers_to_bytevec(temp_v!(3), stub3);
+
+ let algorithm = match self.store(self.deref(self[temp_v!(4)])) {
+ Addr::Con(h) if self.heap.atom_at(h) => {
+ if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
+ atom.as_str()
+ } else {
+ unreachable!()
+ }
+ }
+ _ => {
+ unreachable!()
+ }
+ };
+
+ let length =
+ match Number::try_from((self[temp_v!(5)], &self.heap)) {
+ Ok(Number::Fixnum(n)) => {
+ usize::try_from(n).unwrap()
+ }
+ Ok(Number::Integer(n)) => {
+ n.to_usize().unwrap()
+ }
+ _ => {
+ unreachable!()
+ }
+ };
+
+ let ints_list =
+ { let digest_alg =
+ match algorithm {
+ "sha256" => { hkdf::HKDF_SHA256 }
+ "sha384" => { hkdf::HKDF_SHA384 }
+ "sha512" => { hkdf::HKDF_SHA512 }
+ _ => { unreachable!() }
+ };
+ let salt = hkdf::Salt::new(digest_alg, &salt);
+ let mut bytes : Vec<u8> = Vec::new();
+ bytes.resize(length, 0);
+ salt.extract(&data).expand(&[&info[..]], MyKey(length)).unwrap().fill(&mut bytes).unwrap();
+
+ Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b))))))
+ };
+
+ self.unify(self[temp_v!(6)], ints_list);
+ }
};
return_from_clause!(self.last_call, self)
RANDOM.deref()
}
+
+struct MyKey<T: core::fmt::Debug + PartialEq>(T);
+
+impl hkdf::KeyType for MyKey<usize> {
+ fn len(&self) -> usize {
+ self.0
+ }
+}