From f5c2f6f9e9d98870eaf8b6c0dc0ae06e8fed8376 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 10:50:33 +0200 Subject: [PATCH 1/8] ADDED: Support for SHA-3 algorithms in crypto_data_hash/3 --- Cargo.toml | 1 + src/prolog/lib/crypto.pl | 19 +++++++++++-------- src/prolog/machine/system_calls.rs | 21 +++++++++++++++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f48283c..6e5c657e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,4 @@ rustyline = "6.0.0" unicode_reader = "1.0.0" ring = "0.16.13" ripemd160 = "0.8.0" +sha3 = "0.8.2" diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index 909127d4..9881afba 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -159,10 +159,10 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). - algorithm(+A) where A is one of ripemd160, 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. + sha512_256, sha3_224, sha3_256, sha3_384, sha3_512, 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. - encoding(+Encoding) The default encoding is utf8. The alternative is octet, to treat the input as a list of raw bytes. @@ -215,6 +215,10 @@ hash_algorithm(sha256). hash_algorithm(sha512). hash_algorithm(sha384). hash_algorithm(sha512_256). +hash_algorithm(sha3_224). +hash_algorithm(sha3_256). +hash_algorithm(sha3_384). +hash_algorithm(sha3_512). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -230,10 +234,9 @@ hash_algorithm(sha512_256). 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. + One of sha256, sha384 or sha512. If you specify a variable, + then it is unified with the algorithm that was used, which is a + cryptographically secure algorithm by default. - info(+Info) Optional context and application specific information, specified as a list of bytes or characters. The default is []. diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 6160e7dd..9643aac5 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -42,6 +42,7 @@ use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode}; use ring::rand::{SecureRandom, SystemRandom}; use ring::{digest,hkdf,pbkdf2,aead,error}; use ripemd160::{Ripemd160, Digest}; +use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512}; pub fn get_key() -> KeyEvent { let key; @@ -5219,7 +5220,23 @@ impl MachineState { }; let ints_list = - if algorithm_str == "ripemd160" { + if algorithm_str == "sha3_224" { + let mut context = Sha3_224::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } else if algorithm_str == "sha3_256" { + let mut context = Sha3_256::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } else if algorithm_str == "sha3_384" { + let mut context = Sha3_384::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } else if algorithm_str == "sha3_512" { + let mut context = Sha3_512::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } else if algorithm_str == "ripemd160" { let mut context = Ripemd160::new(); context.input(&bytes); Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) @@ -5278,7 +5295,7 @@ impl MachineState { "sha256" => { hkdf::HKDF_SHA256 } "sha384" => { hkdf::HKDF_SHA384 } "sha512" => { hkdf::HKDF_SHA512 } - _ => { unreachable!() } + _ => { self.fail = true; return Ok(()); } }; let salt = hkdf::Salt::new(digest_alg, &salt); let mut bytes : Vec = Vec::new(); From 9f5322d309a395fd38922e736b47335aa2f8b72b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 12:22:49 +0200 Subject: [PATCH 2/8] ADDED: Support for BLAKE2 algorithms in crypto_data_hash/3. --- Cargo.toml | 1 + src/prolog/lib/crypto.pl | 8 +++++--- src/prolog/machine/system_calls.rs | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e5c657e..b869277c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,4 @@ unicode_reader = "1.0.0" ring = "0.16.13" ripemd160 = "0.8.0" sha3 = "0.8.2" +blake2 = "0.8.1" diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index 9881afba..0df0dbd7 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -158,9 +158,9 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). Options is a list of: - algorithm(+A) - where A is one of ripemd160, sha256, sha384, sha512, - sha512_256, sha3_224, sha3_256, sha3_384, sha3_512, or a - variable. If A is a variable, then it is unified with the + where A is one of ripemd160, sha256, sha384, sha512, sha512_256, + sha3_224, sha3_256, sha3_384, sha3_512, blake2s256, blake2b512, + 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. - encoding(+Encoding) @@ -219,6 +219,8 @@ hash_algorithm(sha3_224). hash_algorithm(sha3_256). hash_algorithm(sha3_384). hash_algorithm(sha3_512). +hash_algorithm(blake2s256). +hash_algorithm(blake2b512). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 9643aac5..b3df3c78 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -43,6 +43,7 @@ use ring::rand::{SecureRandom, SystemRandom}; use ring::{digest,hkdf,pbkdf2,aead,error}; use ripemd160::{Ripemd160, Digest}; use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512}; +use blake2::{Blake2s, Blake2b}; pub fn get_key() -> KeyEvent { let key; @@ -5236,6 +5237,14 @@ impl MachineState { let mut context = Sha3_512::new(); context.input(&bytes); Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } else if algorithm_str == "blake2s256" { + let mut context = Blake2s::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } else if algorithm_str == "blake2b512" { + let mut context = Blake2b::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } else if algorithm_str == "ripemd160" { let mut context = Ripemd160::new(); context.input(&bytes); From dd32e690618862e87d529f1a6876d5299bba4992 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 16:36:06 +0200 Subject: [PATCH 3/8] use matching to select the hashing algorithm Suggested by @notoria in #533. Many thanks! --- src/prolog/machine/system_calls.rs | 72 ++++++++++++++---------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index b3df3c78..847d4e6d 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -5221,45 +5221,39 @@ impl MachineState { }; let ints_list = - if algorithm_str == "sha3_224" { - let mut context = Sha3_224::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else if algorithm_str == "sha3_256" { - let mut context = Sha3_256::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else if algorithm_str == "sha3_384" { - let mut context = Sha3_384::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else if algorithm_str == "sha3_512" { - let mut context = Sha3_512::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else if algorithm_str == "blake2s256" { - let mut context = Blake2s::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else if algorithm_str == "blake2b512" { - let mut context = Blake2b::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else if algorithm_str == "ripemd160" { - let mut context = Ripemd160::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else { - let ints = digest::digest( - match algorithm_str { - "sha256" => { &digest::SHA256 } - "sha384" => { &digest::SHA384 } - "sha512" => { &digest::SHA512 } - "sha512_256" => { &digest::SHA512_256 } - _ => { unreachable!() } - }, - &bytes); - Addr::HeapCell(self.heap.to_list(ints.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + match algorithm_str { + "sha3_224" => { let mut context = Sha3_224::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + "sha3_256" => { let mut context = Sha3_256::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + "sha3_384" => { let mut context = Sha3_384::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + "sha3_512" => { let mut context = Sha3_512::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + "blake2s256" => { let mut context = Blake2s::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + "blake2b512" => { let mut context = Blake2b::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + "ripemd160" => { let mut context = Ripemd160::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + _ => { let ints = digest::digest( + match algorithm_str { + "sha256" => { &digest::SHA256 } + "sha384" => { &digest::SHA384 } + "sha512" => { &digest::SHA512 } + "sha512_256" => { &digest::SHA512_256 } + _ => { unreachable!() } + }, + &bytes); + Addr::HeapCell(self.heap.to_list(ints.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + } }; self.unify(self[temp_v!(2)], ints_list); From 85155439be7378105af65481c7e49fa1461cb1fb Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 16:43:44 +0200 Subject: [PATCH 4/8] use Fixnums for bytes in hashing. Suggested by @notoria in #533. Many thanks! --- src/prolog/machine/system_calls.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 847d4e6d..fe90950d 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -5224,25 +5224,25 @@ impl MachineState { match algorithm_str { "sha3_224" => { let mut context = Sha3_224::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } "sha3_256" => { let mut context = Sha3_256::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } "sha3_384" => { let mut context = Sha3_384::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } "sha3_512" => { let mut context = Sha3_512::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } "blake2s256" => { let mut context = Blake2s::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } "blake2b512" => { let mut context = Blake2b::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } "ripemd160" => { let mut context = Ripemd160::new(); context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) } + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } _ => { let ints = digest::digest( match algorithm_str { "sha256" => { &digest::SHA256 } From da4d061067d9f51e3a48c488bb5772d9fe926e24 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 16:56:34 +0200 Subject: [PATCH 5/8] correct option processing in crypto_data_decrypt/6 --- src/prolog/lib/crypto.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index 0df0dbd7..bf613603 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -592,7 +592,9 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- must_be_bytes(Key, crypto_data_decrypt/6), must_be_bytes(IV, crypto_data_decrypt/6), must_be(atom, Algorithm), - encoding_options(Encoding, Options), + option(encoding(Encoding), Options, utf8), + must_be(atom, Encoding), + member(Encoding, [utf8,octet]), must_be(list, CipherText0), encoding_bytes(octet, CipherText0, CipherText1), append(CipherText1, Tag, CipherText), From 34f7752c0f864218e0530e85563a3a3bfe80dc76 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 17:02:15 +0200 Subject: [PATCH 6/8] use more fixnums in cryptographic routines --- src/prolog/machine/system_calls.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index fe90950d..1fd341c0 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -5252,7 +5252,7 @@ impl MachineState { _ => { unreachable!() } }, &bytes); - Addr::HeapCell(self.heap.to_list(ints.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + Addr::HeapCell(self.heap.to_list(ints.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } }; @@ -5308,7 +5308,7 @@ impl MachineState { _ => { self.fail = true; return Ok(()); } } - Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) }; self.unify(self[temp_v!(6)], ints_list); @@ -5341,7 +5341,7 @@ impl MachineState { NonZeroU32::new(iterations as u32).unwrap(), &salt, &data, &mut bytes); - Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) }; self.unify(self[temp_v!(4)], ints_list); @@ -5366,7 +5366,7 @@ impl MachineState { }; let tag_list = - Addr::HeapCell(self.heap.to_list(tag.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))); + Addr::HeapCell(self.heap.to_list(tag.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))); let complete_string = { let buffer = String::from_iter(in_out.iter().map(|b| *b as char)); From d19d8ea77067319fbc1cbfb4238549107ef9585e Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 17:31:36 +0200 Subject: [PATCH 7/8] crypto_data_hkdf/4: do not crash for length > usize::max_value() For now, we fail silently in such cases. Noted by @notoria in #533. Many thanks! --- src/prolog/machine/system_calls.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 1fd341c0..ad3973ac 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -5285,11 +5285,12 @@ impl MachineState { usize::try_from(n).unwrap() } Ok(Number::Integer(n)) => { - n.to_usize().unwrap() - } - _ => { - unreachable!() + match n.to_usize() { + Some(u) => { u } + _ => { self.fail = true; return Ok(()); } + } } + _ => { unreachable!() } }; let ints_list = From dec2ef70c74faf05262edc27f78f511a1fb43644 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 19 May 2020 18:21:07 +0200 Subject: [PATCH 8/8] better error handling in crypto_data_hkdf/4 Noted by @notoria in #533. Many thanks! --- src/prolog/lib/crypto.pl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index bf613603..e07b4b1d 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -258,6 +258,9 @@ hash_algorithm(blake2b512). crypto_data_hkdf(Data0, L, Bytes, Options0) :- functor_hash_options(algorithm, Algorithm, Options0, Options), + ( hkdf_algorithm(Algorithm) -> true + ; domain_error(hkdf_algorithm, Algorithm, crypto_data_hkdf/4) + ), must_be(integer, L), L >= 0, options_data_bytes(Options, Data0, Data), @@ -267,6 +270,10 @@ crypto_data_hkdf(Data0, L, Bytes, Options0) :- chars_bytes_(Info0, Info, crypto_data_hkdf/4), '$crypto_data_hkdf'(Data, SaltBytes, Info, Algorithm, L, Bytes). +hkdf_algorithm(sha256). +hkdf_algorithm(sha384). +hkdf_algorithm(sha512). + option(What, Options, Default) :- ( member(V, Options), var(V) -> instantiation_error(option/3)