From a622ffddfe529a172d2c9a7eb3cf7166fbeccd00 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 5 Aug 2020 20:09:07 +0200 Subject: [PATCH 1/3] ADDED: library(crypto): Support for additional authenticated data (AAD). Additional authenticated data can now be specified with the new aad(Chars) option for encryption and decryption. It is authenticated, but not encrypted. --- src/clause_types.rs | 2 +- src/lib/crypto.pl | 18 +++++++- src/machine/system_calls.rs | 88 +++++++++++++++++++------------------ 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/clause_types.rs b/src/clause_types.rs index 04509308..3f1fee68 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -710,7 +710,7 @@ impl SystemClauseType { ("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash), ("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF), ("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash), - ("$crypto_data_encrypt", 6) => Some(SystemClauseType::CryptoDataEncrypt), + ("$crypto_data_encrypt", 7) => Some(SystemClauseType::CryptoDataEncrypt), ("$crypto_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt), ("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult), ("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign), diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 49aae46e..7daab599 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -492,6 +492,12 @@ bytes_base64(Bytes, Base64) :- list of _bytes_ holding the tag. This tag must be provided for decryption. + - aad(+Data) + Data is additional authenticated data (AAD), a list of + characters. It is authenticated in that it influences the tag, + but it is not encrypted. The encoding/1 option also specifies + the encoding of Data. + Here is an example encryption and decryption, using the ChaCha20 stream cipher with the Poly1305 authenticator. This cipher uses a 256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_, @@ -533,13 +539,15 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- must_be_bytes(Tag, crypto_data_encrypt/6) ; true ), + option(aad(AAD0), Options, []), + encoding_chars(Encoding, AAD0, AAD), must_be_bytes(Key, crypto_data_encrypt/6), must_be_bytes(IV, crypto_data_encrypt/6), must_be(atom, Algorithm), ( Algorithm = 'chacha20-poly1305' -> true ; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6) ), - '$crypto_data_encrypt'(PlainText, Encoding, Key, IV, Tag, CipherText). + '$crypto_data_encrypt'(PlainText, AAD, Encoding, Key, IV, Tag, CipherText). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_decrypt(+CipherText, @@ -567,6 +575,10 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- - tag(+Tag) For authenticated encryption schemes, the tag must be specified as a list of bytes exactly as they were generated upon encryption. + + - aad(+Data) + Any additional authenticated data (AAD) must be specified. The + encoding/1 option also specifies the encoding of Data. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- @@ -576,6 +588,8 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- must_be_bytes(IV, crypto_data_decrypt/6), must_be(atom, Algorithm), option(encoding(Encoding), Options, utf8), + option(aad(AAD0), Options, []), + encoding_chars(Encoding, AAD0, AAD), must_be(atom, Encoding), member(Encoding, [utf8,octet]), must_be(list, CipherText0), @@ -585,7 +599,7 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- ( Algorithm = 'chacha20-poly1305' -> true ; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6) ), - '$crypto_data_decrypt'(CipherText, octet, Key, IV, Encoding, PlainText). + '$crypto_data_decrypt'(CipherText, AAD, Key, IV, Encoding, PlainText). encoding_chars(octet, Bs, Cs) :- diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index ada49a0f..f938140a 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5388,7 +5388,8 @@ impl MachineState { self.unify(arg, byte); } &SystemClauseType::CryptoDataHash => { - let bytes = self.string_encoding_bytes(1, 2); + let encoding = self.atom_argument_to_string(2); + let bytes = self.string_encoding_bytes(1, &encoding); let algorithm_str = match self.store(self.deref(self[temp_v!(4)])) { Addr::Con(h) if self.heap.atom_at(h) => { @@ -5442,7 +5443,8 @@ impl MachineState { self.unify(self[temp_v!(3)], ints_list); } &SystemClauseType::CryptoDataHKDF => { - let data = self.string_encoding_bytes(1, 2); + let encoding = self.atom_argument_to_string(2); + let data = self.string_encoding_bytes(1, &encoding); let stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); let salt = self.integers_to_bytevec(temp_v!(3), stub1); let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4); @@ -5530,11 +5532,13 @@ impl MachineState { self.unify(self[temp_v!(4)], ints_list); } &SystemClauseType::CryptoDataEncrypt => { - 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!(3), stub2); - let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6); - let iv = self.integers_to_bytevec(temp_v!(4), stub3); + let encoding = self.atom_argument_to_string(3); + let data = self.string_encoding_bytes(1, &encoding); + let aad = self.string_encoding_bytes(2, &encoding); + let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 7); + let key = self.integers_to_bytevec(temp_v!(4), stub2); + let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 7); + let iv = self.integers_to_bytevec(temp_v!(5), stub3); let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap(); @@ -5542,7 +5546,7 @@ impl MachineState { let mut in_out = data.clone(); let tag = - match key.seal_in_place_separate_tag(nonce, aead::Aad::empty(), &mut in_out) { + match key.seal_in_place_separate_tag(nonce, aead::Aad::from(aad), &mut in_out) { Ok(d) => { d } _ => { self.fail = true; return Ok(()); } }; @@ -5555,29 +5559,18 @@ impl MachineState { self.heap.put_complete_string(&buffer) }; - self.unify(self[temp_v!(5)], tag_list); - self.unify(self[temp_v!(6)], complete_string); + self.unify(self[temp_v!(6)], tag_list); + self.unify(self[temp_v!(7)], 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.string_encoding_bytes(1, "octet"); + let encoding = self.atom_argument_to_string(5); + let aad = self.string_encoding_bytes(2, &encoding); + let stub1 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 7); let key = self.integers_to_bytevec(temp_v!(3), stub1); - let stub2 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6); + let stub2 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 7); let iv = self.integers_to_bytevec(temp_v!(4), stub2); - 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() - } else { - unreachable!() - } - } - _ => { - unreachable!() - } - }; - let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap(); let key = aead::LessSafeKey::new(unbound_key); @@ -5586,12 +5579,12 @@ impl MachineState { let complete_string = { let decrypted_data = - match key.open_in_place(nonce, aead::Aad::empty(), &mut in_out) { + match key.open_in_place(nonce, aead::Aad::from(aad), &mut in_out) { Ok(d) => { d } _ => { self.fail = true; return Ok(()); } }; - let buffer = match encoding { + let buffer = match encoding.as_str() { "octet" => { String::from_iter(decrypted_data.iter().map(|b| *b as char)) } "utf8" => { match String::from_utf8(decrypted_data.to_vec()) { Ok(str) => { str } @@ -5665,7 +5658,8 @@ impl MachineState { self.unify(self[temp_v!(1)], complete_string); } &SystemClauseType::Ed25519KeyPairPublicKey => { - let bytes = self.string_encoding_bytes(1, 2); + let encoding = self.atom_argument_to_string(2); + let bytes = self.string_encoding_bytes(1, &encoding); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) { Ok(kp) => { kp } @@ -5680,8 +5674,9 @@ impl MachineState { self.unify(self[temp_v!(3)], complete_string); } &SystemClauseType::Ed25519Sign => { - let key = self.string_encoding_bytes(1, 2); - let data = self.string_encoding_bytes(3, 4); + let key = self.string_encoding_bytes(1, "octet"); + let encoding = self.atom_argument_to_string(4); + let data = self.string_encoding_bytes(3, &encoding); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { Ok(kp) => { kp } @@ -5696,8 +5691,9 @@ impl MachineState { self.unify(self[temp_v!(5)], sig_list); } &SystemClauseType::Ed25519Verify => { - let key = self.string_encoding_bytes(1, 2); - let data = self.string_encoding_bytes(3, 4); + let key = self.string_encoding_bytes(1, "octet"); + let encoding = self.atom_argument_to_string(4); + let data = self.string_encoding_bytes(3, &encoding); let stub = MachineError::functor_stub(clause_name!("ed25519_verify"), 5); let signature = self.integers_to_bytevec(temp_v!(5), stub); @@ -5863,17 +5859,14 @@ impl MachineState { } pub(super) - fn string_encoding_bytes( + fn atom_argument_to_string( &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)])) { + atom_arg: usize, + ) -> String { + match self.store(self.deref(self[temp_v!(atom_arg)])) { Addr::Con(h) if self.heap.atom_at(h) => { if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { - atom.as_str() + atom.as_str().to_string() } else { unreachable!() } @@ -5881,9 +5874,18 @@ impl MachineState { _ => { unreachable!() } - }; + } + } - match encoding_str { + pub(super) + fn string_encoding_bytes( + &mut self, + data_arg: usize, + encoding: &str, + ) -> Vec { + let data = self.heap_pstr_iter(self[temp_v!(data_arg)]).to_string(); + + match encoding { "utf8" => { data.into_bytes() } "octet" => { let mut buf = vec![]; From 1c23336cffbcbf09dd0dfba8c7d399189317a02c Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 5 Aug 2020 20:10:58 +0200 Subject: [PATCH 2/3] use "octet" as a string literal, reducing the number of arguments --- src/clause_types.rs | 6 +++--- src/lib/crypto.pl | 6 +++--- src/machine/system_calls.rs | 17 ++++++++--------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/clause_types.rs b/src/clause_types.rs index 3f1fee68..339c7d38 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -713,10 +713,10 @@ impl SystemClauseType { ("$crypto_data_encrypt", 7) => Some(SystemClauseType::CryptoDataEncrypt), ("$crypto_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt), ("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult), - ("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign), - ("$ed25519_verify", 5) => Some(SystemClauseType::Ed25519Verify), + ("$ed25519_sign", 4) => Some(SystemClauseType::Ed25519Sign), + ("$ed25519_verify", 4) => Some(SystemClauseType::Ed25519Verify), ("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair), - ("$ed25519_keypair_public_key", 3) => Some(SystemClauseType::Ed25519KeyPairPublicKey), + ("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey), ("$curve25519_scalar_mult", 3) => Some(SystemClauseType::Curve25519ScalarMult), ("$load_html", 3) => Some(SystemClauseType::LoadHTML), ("$load_xml", 3) => Some(SystemClauseType::LoadXML), diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 7daab599..330368ed 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -651,19 +651,19 @@ ed25519_new_keypair(Pair) :- ed25519_keypair_public_key(Pair, PublicKey) :- must_be_byte_chars(Pair, ed25519_keypair_public_key), - '$ed25519_keypair_public_key'(Pair, octet, PublicKey). + '$ed25519_keypair_public_key'(Pair, PublicKey). 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), + '$ed25519_sign'(Key, Data, Encoding, Signature0), hex_bytes(Signature, Signature0). 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, octet, Data, Encoding, Signature). + '$ed25519_verify'(Key, Data, Encoding, Signature). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - X25519: ECDH key exchange over Curve25519 diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f938140a..2dd584a0 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5658,8 +5658,7 @@ impl MachineState { self.unify(self[temp_v!(1)], complete_string); } &SystemClauseType::Ed25519KeyPairPublicKey => { - let encoding = self.atom_argument_to_string(2); - let bytes = self.string_encoding_bytes(1, &encoding); + let bytes = self.string_encoding_bytes(1, "octet"); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) { Ok(kp) => { kp } @@ -5671,12 +5670,12 @@ impl MachineState { self.heap.put_complete_string(&buffer) }; - self.unify(self[temp_v!(3)], complete_string); + self.unify(self[temp_v!(2)], complete_string); } &SystemClauseType::Ed25519Sign => { let key = self.string_encoding_bytes(1, "octet"); - let encoding = self.atom_argument_to_string(4); - let data = self.string_encoding_bytes(3, &encoding); + let encoding = self.atom_argument_to_string(3); + let data = self.string_encoding_bytes(2, &encoding); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { Ok(kp) => { kp } @@ -5688,14 +5687,14 @@ 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!(5)], sig_list); + self.unify(self[temp_v!(4)], sig_list); } &SystemClauseType::Ed25519Verify => { let key = self.string_encoding_bytes(1, "octet"); - let encoding = self.atom_argument_to_string(4); - let data = self.string_encoding_bytes(3, &encoding); + let encoding = self.atom_argument_to_string(3); + let data = self.string_encoding_bytes(2, &encoding); let stub = MachineError::functor_stub(clause_name!("ed25519_verify"), 5); - let signature = self.integers_to_bytevec(temp_v!(5), stub); + let signature = self.integers_to_bytevec(temp_v!(4), stub); let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key); match peer_public_key.verify(&data, &signature) { From 2d3f1e51ec5e707a72b423719a6f6aaa85ae4beb Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 5 Aug 2020 20:31:56 +0200 Subject: [PATCH 3/3] shorten, and increase readability --- src/lib/crypto.pl | 6 ++---- src/machine/system_calls.rs | 34 +++++++--------------------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 330368ed..af03e7d6 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -282,7 +282,7 @@ crypto_data_hkdf(Data0, L, Bytes, Options0) :- ; domain_error(hkdf_algorithm, Algorithm, crypto_data_hkdf/4) ), must_be(integer, L), - L >= 0, + L #>= 0, options_data_chars(Options, Data0, Data, Encoding), option(salt(SaltBytes), Options, []), must_be_bytes(SaltBytes, crypto_data_hkdf/4), @@ -415,7 +415,7 @@ crypto_password_hash(Password0, Hash, Options) :- chars_bytes_(Password0, Password, crypto_password_hash/3), must_be(list, Options), option(cost(C), Options, 17), - Iterations is 2^C, + Iterations #= 2^C, Algorithm = 'pbkdf2-sha512', % current default and only option option(algorithm(Algorithm), Options, Algorithm), ( member(salt(SaltBytes), Options) -> @@ -702,8 +702,6 @@ curve25519_generator(Gs) :- curve25519_scalar_mult(Scalar, Point, Result) :- ( integer_si(Scalar) -> - Scalar #>= 0, - Scalar #< 2^256, length(ScalarBytes, 32), bytes_integer(ScalarBytes, Scalar) ; ScalarBytes = Scalar, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 2dd584a0..d8275877 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5713,10 +5713,7 @@ impl MachineState { let result = scalarmult(&scalar, &point).unwrap(); - let mut string = String::new(); - for c in result[..].iter() { - string.push(*c as char); - } + let string = String::from_iter(result[..].iter().map(|b| *b as char)); let cstr = self.heap.put_complete_string(&string); self.unify(self[temp_v!(3)], cstr); } @@ -5761,32 +5758,18 @@ impl MachineState { env::remove_var(key); } &SystemClauseType::CharsBase64 => { - let mut options = vec![]; - - for i in 3..5 { - match self.store(self.deref(self[temp_v!(i)])) { - Addr::Con(h) if self.heap.atom_at(h) => { - if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { - options.push(atom.as_str()); - } else { - unreachable!() - } - } - _ => { - unreachable!() - } - }; - } + let padding = self.atom_argument_to_string(3); + let charset = self.atom_argument_to_string(4); let config = - if options[0] == "true" { - if options[1] == "standard" { + if padding == "true" { + if charset == "standard" { base64::STANDARD } else { base64::URL_SAFE } } else { - if options[1] == "standard" { + if charset == "standard" { base64::STANDARD_NO_PAD } else { base64::URL_SAFE_NO_PAD @@ -5799,10 +5782,7 @@ impl MachineState { match bytes { Ok(bs) => { - let mut string = String::new(); - for c in bs { - string.push(c as char); - } + let string = String::from_iter(bs.iter().map(|b| *b as char)); let cstr = self.heap.put_complete_string(&string); self.unify(self[temp_v!(1)], cstr); }