- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(crypto, [hex_bytes/2,
- crypto_n_random_bytes/2]).
+ crypto_n_random_bytes/2,
+ crypto_data_hash/3
+ ]).
:- use_module(library(error)).
:- use_module(library(lists)).
:- use_module(library(between)).
:- use_module(library(dcgs)).
-% hex_bytes(?Hex, ?Bytes) is det.
-%
-% Relation between a hexadecimal sequence and a list of bytes. Hex
-% is a string of hexadecimal numbers. Bytes is a list of *integers*
-% between 0 and 255 that represent the sequence as a list of bytes.
-% At least one of the arguments must be instantiated.
-%
-% Example:
-%
-% ==
-% ?- hex_bytes("501ACE", Bs).
-% Bs = [80,26,206]
-% ; false.
-% ==
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ hex_bytes(?Hex, ?Bytes) is det.
+
+ Relation between a hexadecimal sequence and a list of bytes. Hex
+ is a string of hexadecimal numbers. Bytes is a list of *integers*
+ between 0 and 255 that represent the sequence as a list of bytes.
+ At least one of the arguments must be instantiated.
+
+ Example:
+
+ ?- hex_bytes("501ACE", Bs).
+ Bs = [80,26,206]
+ ; false.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
hex_bytes(Hs, Bytes) :-
( ground(Hs) ->
)
; must_be(list, Bytes),
maplist(must_be(integer), Bytes),
- ( member(B, Bytes), \+ between(0, 255, B) ->
- type_error(byte, B, hex_bytes/2)
- ; true
- ),
+ must_be_bytes(Bytes, hex_bytes/2),
phrase(bytes_hex(Bytes), Hs)
).
char_hexval(C, H) :- nth0(H, "0123456789ABCDEF", C), !.
+must_be_bytes(Bytes, Context) :-
+ ( member(B, Bytes), \+ between(0, 255, B) ->
+ type_error(byte, B, Context)
+ ; true
+ ).
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ crypto_n_random_bytes(+N, -Bytes) is det
+
+ Bytes is unified with a list of N cryptographically secure
+ pseudo-random bytes. Each byte is an integer between 0 and 255. If
+ the internal pseudo-random number generator (PRNG) has not been
+ seeded with enough entropy to ensure an unpredictable byte
+ sequence, an exception is thrown.
+
+ One way to relate such a list of bytes to an _integer_ is to use
+ CLP(ℤ) constraints as follows:
+
+ :- use_module(library(clpz)).
+ :- use_module(library(lists)).
+
+ 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.
+
+ With this definition, we can generate a random 256-bit integer
+ _from_ a list of 32 random _bytes_:
+
+ ?- crypto_n_random_bytes(32, Bs),
+ bytes_integer(Bs, I).
+ Bs = [146,166,162,210,242,7,25,132,64,94|...],
+ I = 337420085690608915485...(56 digits omitted)
+
+ The above relation also works in the other direction, letting you
+ translate an integer _to_ a list of bytes. In addition, you can
+ use hex_bytes/2 to convert bytes to _tokens_ that can be easily
+ exchanged in your applications.
+
+ ?- crypto_n_random_bytes(12, Bs),
+ hex_bytes(Hex, Bs).
+ Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ..."
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+
crypto_n_random_bytes(N, Bs) :-
must_be(integer, N),
length(Bs, N),
maplist(crypto_random_byte, Bs).
crypto_random_byte(B) :- '$crypto_random_byte'(B).
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ 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.
+
+ The single supported option is:
+
+ algorithm(A)
+
+ where A is one of sha256, sha384, sha512, sha512_256, or a variable.
+
+ If A is a variable, then it is unified with the default algorithm,
+ which is an algorithm that is considered cryptographically secure
+ at the time of this writing.
+
+ Example:
+
+ ?- crypto_data_hash([0'a,0'b,0'c], 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
+ ),
+ ( hash_algorithm(A) -> true
+ ; domain_error(hash_algorithm, A, crypto_data_hash/3)
+ ),
+ '$crypto_data_hash'(Data, HashBytes, A),
+ hex_bytes(Hash, HashBytes).
+
+hash_algorithm(sha256).
+hash_algorithm(sha512).
+hash_algorithm(sha384).
+hash_algorithm(sha512_256).
use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode};
use ring::rand::{SecureRandom, SystemRandom};
+use ring::digest;
pub fn get_key() -> KeyEvent {
let key;
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 algorithm = self[temp_v!(3)];
+ let algorithm_str = match self.store(self.deref(algorithm)) {
+ 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 hash = digest::digest(
+ match algorithm_str {
+ "sha256" => { &digest::SHA256 }
+ "sha384" => { &digest::SHA384 }
+ "sha512" => { &digest::SHA512 }
+ "sha512_256" => { &digest::SHA512_256 }
+ _ => { unreachable!() }
+ },
+ &bytes);
+
+ let ints = hash.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b))));
+ let ints_list = Addr::HeapCell(self.heap.to_list(ints));
+
+ self.unify(self[temp_v!(2)], ints_list);
+ }
};
return_from_clause!(self.last_call, self)