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.
This commit is contained in:
Markus Triska
2020-08-05 20:09:07 +02:00
parent 79cf0c63c4
commit a622ffddfe
3 changed files with 62 additions and 46 deletions

View File

@@ -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),

View File

@@ -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) :-

View File

@@ -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<u8> {
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<u8> {
let data = self.heap_pstr_iter(self[temp_v!(data_arg)]).to_string();
match encoding {
"utf8" => { data.into_bytes() }
"octet" => {
let mut buf = vec![];