From: Markus Triska Date: Thu, 22 Feb 2024 19:17:20 +0000 (+0100) Subject: ADDED: Hash-based message authentication code (HMAC), using hmac(Key). X-Git-Tag: v0.9.4~5^2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=27852eafd7c5ee0a4b2344693f77dcac08a024a8;p=scryer-prolog.git ADDED: Hash-based message authentication code (HMAC), using hmac(Key). --- diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 1840341b..e5544239 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -497,6 +497,8 @@ enum SystemClauseType { CryptoRandomByte, #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_data_hash")))] CryptoDataHash, + #[strum_discriminants(strum(props(Arity = "5", Name = "$crypto_hmac")))] + CryptoHMAC, #[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_hkdf")))] CryptoDataHKDF, #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_password_hash")))] @@ -1846,6 +1848,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallScryerPrologVersion | &Instruction::CallCryptoRandomByte | &Instruction::CallCryptoDataHash | + &Instruction::CallCryptoHMAC | &Instruction::CallCryptoDataHKDF | &Instruction::CallCryptoPasswordHash | &Instruction::CallCryptoCurveScalarMult | @@ -2082,6 +2085,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteScryerPrologVersion | &Instruction::ExecuteCryptoRandomByte | &Instruction::ExecuteCryptoDataHash | + &Instruction::ExecuteCryptoHMAC | &Instruction::ExecuteCryptoDataHKDF | &Instruction::ExecuteCryptoPasswordHash | &Instruction::ExecuteCryptoCurveScalarMult | diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index bcd2bb9d..bd5e622d 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -1,5 +1,5 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020-2023 by Markus Triska (triska@metalevel.at) + Written 2020-2024 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. /** Predicates for cryptographic applications. @@ -192,6 +192,11 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). % The default encoding is `utf8`. The alternative is `octet`, to % treat the input as a list of raw bytes. % +% - `hmac(+Key)` +% Compute a hash-based message authentication code (HMAC) using +% Key, a list of bytes. This option is currently supported for +% algorithms `sha256`, `sha384` and `sha512`. +% % Example: % % ``` @@ -214,9 +219,18 @@ crypto_data_hash(Data0, Hash, Options0) :- ( hash_algorithm(A) -> true ; domain_error(hash_algorithm, A, crypto_data_hash/3) ), - '$crypto_data_hash'(Data, Encoding, HashBytes, A), + ( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) -> + must_be_bytes(Ks, crypto_data_hash/3), + hmac_algorithm(A), + '$crypto_hmac'(Data, Encoding, Ks, HashBytes, A) + ; '$crypto_data_hash'(Data, Encoding, HashBytes, A) + ), hex_bytes(Hash, HashBytes). +hmac_algorithm(sha256). +hmac_algorithm(sha384). +hmac_algorithm(sha512). + options_data_chars(Options, Data, Chars, Encoding) :- option(encoding(Encoding), Options, utf8), must_be(atom, Encoding), diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index f3a73e6c..ddd077a5 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4464,6 +4464,14 @@ impl Machine { self.crypto_data_hash(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallCryptoHMAC => { + self.crypto_hmac(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteCryptoHMAC => { + self.crypto_hmac(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallCryptoDataHKDF => { self.crypto_data_hkdf(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 73bf62f2..fb3ecc1d 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -78,7 +78,7 @@ use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use blake2::{Blake2b, Blake2s}; use ring::rand::{SecureRandom, SystemRandom}; -use ring::{digest, hkdf, pbkdf2}; +use ring::{digest, hkdf, hmac, pbkdf2}; #[cfg(feature = "crypto-full")] use ring::aead; @@ -7393,6 +7393,40 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[3], ints_list); } + #[inline(always)] + pub(crate) fn crypto_hmac(&mut self) { + let encoding = cell_as_atom!(self.deref_register(2)); + let data = self.string_encoding_bytes(self.machine_st.registers[1], encoding); + + let stub_gen = || functor_stub(atom!("crypto_data_hash"), 3); + + let key = self + .machine_st + .integers_to_bytevec(self.machine_st.registers[3], stub_gen); + + let algorithm = cell_as_atom!(self.deref_register(5)); + let ralg = match algorithm { + atom!("sha256") => hmac::HMAC_SHA256, + atom!("sha384") => hmac::HMAC_SHA384, + atom!("sha512") => hmac::HMAC_SHA512, + _ => { + unreachable!() + } + }; + + let rkey = hmac::Key::new(ralg, key.as_ref()); + let tag = hmac::sign(&rkey, &data); + + let ints_list = heap_loc_as_cell!(iter_to_heap_list( + &mut self.machine_st.heap, + tag.as_ref() + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), + )); + + unify!(self.machine_st, self.machine_st.registers[4], ints_list); + } + #[inline(always)] pub(crate) fn crypto_data_hkdf(&mut self) { let encoding = cell_as_atom!(self.deref_register(2));