From: Markus Triska Date: Wed, 5 Aug 2020 18:09:07 +0000 (+0200) Subject: ADDED: library(crypto): Support for additional authenticated data (AAD). X-Git-Tag: v0.9.0~174^2~15^2~2^2~2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=a622ffddfe529a172d2c9a7eb3cf7166fbeccd00;p=scryer-prolog.git 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. --- 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!() } - }; + } + } + + 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_str { + match encoding { "utf8" => { data.into_bytes() } "octet" => { let mut buf = vec![];