From: Markus Triska Date: Sun, 12 Jul 2020 08:07:56 +0000 (+0200) Subject: ENHANCED: library(crypto): Retain the compact representation of strings. X-Git-Tag: v0.8.127~13^2~1 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=88a2b82f7e13af5253fc1b33b2a782902c70739c;p=scryer-prolog.git ENHANCED: library(crypto): Retain the compact representation of strings. This avoids the costly (in terms of space requirements!) conversion of compact lists of characters to lists of integers, making hashing, HKDF, encryption, decryption, signing and signature verification an order of magnitude more efficient (primarily in terms of space, also in time). This makes library(crypto) suitable to process also very large files. --- diff --git a/src/clause_types.rs b/src/clause_types.rs index 64e3c2dd..09cab2bb 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -688,16 +688,16 @@ impl SystemClauseType { ("$write_term_to_chars", 7) => Some(SystemClauseType::WriteTermToChars), ("$scryer_prolog_version", 1) => Some(SystemClauseType::ScryerPrologVersion), ("$crypto_random_byte", 1) => Some(SystemClauseType::CryptoRandomByte), - ("$crypto_data_hash", 3) => Some(SystemClauseType::CryptoDataHash), - ("$crypto_data_hkdf", 6) => Some(SystemClauseType::CryptoDataHKDF), + ("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash), + ("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF), ("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash), - ("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt), - ("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt), + ("$crypto_data_encrypt", 6) => Some(SystemClauseType::CryptoDataEncrypt), + ("$crypto_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt), ("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult), - ("$ed25519_sign", 3) => Some(SystemClauseType::Ed25519Sign), - ("$ed25519_verify", 3) => Some(SystemClauseType::Ed25519Verify), + ("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign), + ("$ed25519_verify", 5) => Some(SystemClauseType::Ed25519Verify), ("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair), - ("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey), + ("$ed25519_keypair_public_key", 3) => Some(SystemClauseType::Ed25519KeyPairPublicKey), ("$load_html", 3) => Some(SystemClauseType::LoadHTML), ("$load_xml", 3) => Some(SystemClauseType::LoadXML), ("$getenv", 2) => Some(SystemClauseType::GetEnv), diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 91ebc6ef..b0ff40e6 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -76,13 +76,13 @@ hex_bytes([]) --> []. hex_bytes([H1,H2|Hs]) --> [Byte], { char_hexval(H1, High), char_hexval(H2, Low), - Byte is High*16 + Low }, + Byte #= High*16 + Low }, hex_bytes(Hs). bytes_hex([]) --> []. bytes_hex([B|Bs]) --> [C0,C1], - { High is B>>4, - Low is B /\ 0xf, + { High #= B>>4, + Low #= B /\ 0xf, char_hexval(C0, High), char_hexval(C1, Low) }, @@ -101,6 +101,16 @@ must_be_bytes(Bytes, Context) :- ). +must_be_byte_chars(Chars, Context) :- + must_be(list, Chars), + ( member(Char, Chars), + char_code(Char, Code), + \+ between(0, 255, Code) -> + domain_error(byte_char, Char, Context) + ; true + ). + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cryptographically secure random numbers ======================================= @@ -191,18 +201,18 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). crypto_data_hash(Data0, Hash, Options0) :- must_be(list, Options0), - options_data_bytes(Options0, Data0, Data), + options_data_chars(Options0, Data0, Data, Encoding), 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), + '$crypto_data_hash'(Data, Encoding, HashBytes, A), hex_bytes(Hash, HashBytes). -options_data_bytes(Options, Data, Bytes) :- +options_data_chars(Options, Data, Chars, Encoding) :- option(encoding(Encoding), Options, utf8), must_be(atom, Encoding), - encoding_bytes(Encoding, Data, Bytes). + encoding_chars(Encoding, Data, Chars). default_hash(sha256). @@ -270,12 +280,12 @@ crypto_data_hkdf(Data0, L, Bytes, Options0) :- ), must_be(integer, L), L >= 0, - options_data_bytes(Options, Data0, Data), + options_data_chars(Options, Data0, Data, Encoding), 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). + '$crypto_data_hkdf'(Data, Encoding, SaltBytes, Info, Algorithm, L, Bytes). hkdf_algorithm(sha256). hkdf_algorithm(sha384). @@ -558,7 +568,7 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z], - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- - options_data_bytes(Options, PlainText0, PlainText), + options_data_chars(Options, PlainText0, PlainText, Encoding), option(tag(Tag), Options, _), ( nonvar(Tag) -> must_be_bytes(Tag, crypto_data_encrypt/6) @@ -570,7 +580,7 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- ( Algorithm = 'chacha20-poly1305' -> true ; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6) ), - '$crypto_data_encrypt'(PlainText, Key, IV, Tag, CipherText). + '$crypto_data_encrypt'(PlainText, Encoding, Key, IV, Tag, CipherText). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_decrypt(+CipherText, @@ -610,26 +620,25 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- must_be(atom, Encoding), member(Encoding, [utf8,octet]), must_be(list, CipherText0), - encoding_bytes(octet, CipherText0, CipherText1), - append(CipherText1, Tag, CipherText), + encoding_chars(octet, CipherText0, CipherText1), + maplist(char_code, TagChars, Tag), + append(CipherText1, TagChars, CipherText), ( Algorithm = 'chacha20-poly1305' -> true ; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6) ), - '$crypto_data_decrypt'(CipherText, Key, IV, Encoding, PlainText). + '$crypto_data_decrypt'(CipherText, octet, Key, IV, Encoding, PlainText). + -encoding_bytes(octet, Bs0, Bs) :- - must_be(list, Bs0), - ( maplist(integer, Bs0) -> - Bs0 = Bs - ; maplist(char_code, Bs0, Bs) +encoding_chars(octet, Bs, Cs) :- + must_be(list, Bs), + ( maplist(integer, Bs) -> + maplist(char_code, Cs, Bs) + ; Bs = Cs ), - must_be_bytes(Bs, crypto_encoding). -encoding_bytes(utf8, Cs, Bs) :- + must_be_byte_chars(Cs, crypto_encoding). +encoding_chars(utf8, Cs, Cs) :- must_be(list, Cs), - ( maplist(atom, Cs) -> - chars_bytes_(Cs, Bs, crypto_encoding) - ; domain_error(encryption_encoding, Cs, crypto) - ). + maplist(must_be(character), Cs). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Digital signatures with Ed25519 @@ -667,21 +676,21 @@ encoding_bytes(utf8, Cs, Bs) :- 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_keypair_public_key(Pair, PublicKey) :- + must_be_byte_chars(Pair, ed25519_keypair_public_key), + '$ed25519_keypair_public_key'(Pair, octet, PublicKey). -ed25519_sign(Key0, Data0, Signature, Options) :- - options_data_bytes(Options, Data0, Data), - encoding_bytes(octet, Key0, Key), - '$ed25519_sign'(Key, Data, Signature0), +ed25519_sign(Key, Data0, Signature, Options) :- + must_be_byte_chars(Key, ed25519_sign), + options_data_chars(Options, Data0, Data, Encoding), + '$ed25519_sign'(Key, octet, Data, Encoding, Signature0), hex_bytes(Signature, Signature0). -ed25519_verify(Key0, Data0, Signature0, Options) :- - options_data_bytes(Options, Data0, Data), - encoding_bytes(octet, Key0, Key), +ed25519_verify(Key, Data0, Signature0, Options) :- + must_be_byte_chars(Key, ed25519_verify), + options_data_chars(Options, Data0, Data, Encoding), hex_bytes(Signature0, Signature), - '$ed25519_verify'(Key, Data, Signature). + '$ed25519_verify'(Key, octet, Data, Encoding, Signature). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Operations on Elliptic Curves diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index e8db40ce..20bf70e4 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5291,11 +5291,9 @@ impl MachineState { self.unify(arg, byte); } &SystemClauseType::CryptoDataHash => { - let stub = MachineError::functor_stub(clause_name!("crypto_data_hash"), 3); - let bytes = self.integers_to_bytevec(temp_v!(1), stub); + let bytes = self.string_encoding_bytes(1, 2); - let algorithm = self[temp_v!(3)]; - let algorithm_str = match self.store(self.deref(algorithm)) { + let algorithm_str = 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() @@ -5344,17 +5342,16 @@ impl MachineState { } }; - self.unify(self[temp_v!(2)], ints_list); + self.unify(self[temp_v!(3)], ints_list); } &SystemClauseType::CryptoDataHKDF => { + let data = self.string_encoding_bytes(1, 2); let stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); - let data = self.integers_to_bytevec(temp_v!(1), stub1); + let salt = self.integers_to_bytevec(temp_v!(3), stub1); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); - let salt = self.integers_to_bytevec(temp_v!(2), stub2); - let stub3 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); - let info = self.integers_to_bytevec(temp_v!(3), stub3); + let info = self.integers_to_bytevec(temp_v!(4), stub2); - let algorithm = match self.store(self.deref(self[temp_v!(4)])) { + let algorithm = match self.store(self.deref(self[temp_v!(5)])) { Addr::Con(h) if self.heap.atom_at(h) => { if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { atom.as_str() @@ -5368,7 +5365,7 @@ impl MachineState { }; let length = - match Number::try_from((self[temp_v!(5)], &self.heap)) { + match Number::try_from((self[temp_v!(6)], &self.heap)) { Ok(Number::Fixnum(n)) => { usize::try_from(n).unwrap() } @@ -5400,7 +5397,7 @@ impl MachineState { 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); + self.unify(self[temp_v!(7)], ints_list); } &SystemClauseType::CryptoPasswordHash => { let stub1 = MachineError::functor_stub(clause_name!("crypto_password_hash"), 3); @@ -5436,12 +5433,11 @@ impl MachineState { self.unify(self[temp_v!(4)], ints_list); } &SystemClauseType::CryptoDataEncrypt => { - let stub1 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); - let data = self.integers_to_bytevec(temp_v!(1), stub1); + let data = self.string_encoding_bytes(1, 2); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); - let key = self.integers_to_bytevec(temp_v!(2), stub2); + let key = self.integers_to_bytevec(temp_v!(3), stub2); let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); - let iv = self.integers_to_bytevec(temp_v!(3), stub3); + let iv = self.integers_to_bytevec(temp_v!(4), stub3); let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap(); @@ -5462,18 +5458,17 @@ impl MachineState { self.heap.put_complete_string(&buffer) }; - self.unify(self[temp_v!(4)], tag_list); - self.unify(self[temp_v!(5)], complete_string); + self.unify(self[temp_v!(5)], tag_list); + self.unify(self[temp_v!(6)], complete_string); } &SystemClauseType::CryptoDataDecrypt => { + let data = self.string_encoding_bytes(1, 2); let stub1 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6); - let data = self.integers_to_bytevec(temp_v!(1), stub1); + let key = self.integers_to_bytevec(temp_v!(3), stub1); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6); - let key = self.integers_to_bytevec(temp_v!(2), stub2); - let stub3 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6); - let iv = self.integers_to_bytevec(temp_v!(3), stub3); + let iv = self.integers_to_bytevec(temp_v!(4), stub2); - let encoding = match self.store(self.deref(self[temp_v!(4)])) { + let encoding = match self.store(self.deref(self[temp_v!(5)])) { Addr::Con(h) if self.heap.atom_at(h) => { if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { atom.as_str() @@ -5512,7 +5507,7 @@ impl MachineState { self.heap.put_complete_string(&buffer) }; - self.unify(self[temp_v!(5)], complete_string); + self.unify(self[temp_v!(6)], complete_string); } &SystemClauseType::CryptoCurveScalarMult => { let curve = match self.store(self.deref(self[temp_v!(1)])) { @@ -5573,8 +5568,7 @@ impl MachineState { 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 bytes = self.string_encoding_bytes(1, 2); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) { Ok(kp) => { kp } @@ -5586,13 +5580,11 @@ impl MachineState { self.heap.put_complete_string(&buffer) }; - self.unify(self[temp_v!(2)], complete_string); + self.unify(self[temp_v!(3)], 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 = self.string_encoding_bytes(1, 2); + let data = self.string_encoding_bytes(3, 4); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { Ok(kp) => { kp } @@ -5604,15 +5596,13 @@ impl MachineState { 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); + self.unify(self[temp_v!(5)], 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 key = self.string_encoding_bytes(1, 2); + let data = self.string_encoding_bytes(3, 4); + let stub = MachineError::functor_stub(clause_name!("ed25519_verify"), 5); + let signature = self.integers_to_bytevec(temp_v!(5), stub); let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key); match peer_public_key.verify(&data, &signature) { @@ -5665,6 +5655,40 @@ impl MachineState { return_from_clause!(self.last_call, self) } + pub(super) + fn string_encoding_bytes( + &mut self, + data_arg: usize, + encoding_arg: usize, + ) -> Vec { + let data = self.heap_pstr_iter(self[temp_v!(data_arg)]).to_string(); + + let encoding_str = match self.store(self.deref(self[temp_v!(encoding_arg)])) { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { + atom.as_str() + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + + match encoding_str { + "utf8" => { data.into_bytes() } + "octet" => { + let mut buf = vec![]; + for c in data.chars() { + buf.push(c as u8); + } + buf + } + _ => { unreachable!() } + } + } + pub(super) fn xml_node_to_term( &mut self,