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:
@@ -710,7 +710,7 @@ impl SystemClauseType {
|
|||||||
("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash),
|
("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash),
|
||||||
("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF),
|
("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF),
|
||||||
("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
|
("$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_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt),
|
||||||
("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult),
|
("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult),
|
||||||
("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign),
|
("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign),
|
||||||
|
|||||||
@@ -492,6 +492,12 @@ bytes_base64(Bytes, Base64) :-
|
|||||||
list of _bytes_ holding the tag. This tag must be provided for
|
list of _bytes_ holding the tag. This tag must be provided for
|
||||||
decryption.
|
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
|
Here is an example encryption and decryption, using the ChaCha20
|
||||||
stream cipher with the Poly1305 authenticator. This cipher uses a
|
stream cipher with the Poly1305 authenticator. This cipher uses a
|
||||||
256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_,
|
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)
|
must_be_bytes(Tag, crypto_data_encrypt/6)
|
||||||
; true
|
; true
|
||||||
),
|
),
|
||||||
|
option(aad(AAD0), Options, []),
|
||||||
|
encoding_chars(Encoding, AAD0, AAD),
|
||||||
must_be_bytes(Key, crypto_data_encrypt/6),
|
must_be_bytes(Key, crypto_data_encrypt/6),
|
||||||
must_be_bytes(IV, crypto_data_encrypt/6),
|
must_be_bytes(IV, crypto_data_encrypt/6),
|
||||||
must_be(atom, Algorithm),
|
must_be(atom, Algorithm),
|
||||||
( Algorithm = 'chacha20-poly1305' -> true
|
( Algorithm = 'chacha20-poly1305' -> true
|
||||||
; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6)
|
; 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,
|
crypto_data_decrypt(+CipherText,
|
||||||
@@ -567,6 +575,10 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
|
|||||||
- tag(+Tag)
|
- tag(+Tag)
|
||||||
For authenticated encryption schemes, the tag must be specified as
|
For authenticated encryption schemes, the tag must be specified as
|
||||||
a list of bytes exactly as they were generated upon encryption.
|
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) :-
|
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_bytes(IV, crypto_data_decrypt/6),
|
||||||
must_be(atom, Algorithm),
|
must_be(atom, Algorithm),
|
||||||
option(encoding(Encoding), Options, utf8),
|
option(encoding(Encoding), Options, utf8),
|
||||||
|
option(aad(AAD0), Options, []),
|
||||||
|
encoding_chars(Encoding, AAD0, AAD),
|
||||||
must_be(atom, Encoding),
|
must_be(atom, Encoding),
|
||||||
member(Encoding, [utf8,octet]),
|
member(Encoding, [utf8,octet]),
|
||||||
must_be(list, CipherText0),
|
must_be(list, CipherText0),
|
||||||
@@ -585,7 +599,7 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
|
|||||||
( Algorithm = 'chacha20-poly1305' -> true
|
( Algorithm = 'chacha20-poly1305' -> true
|
||||||
; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6)
|
; 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) :-
|
encoding_chars(octet, Bs, Cs) :-
|
||||||
|
|||||||
@@ -5388,7 +5388,8 @@ impl MachineState {
|
|||||||
self.unify(arg, byte);
|
self.unify(arg, byte);
|
||||||
}
|
}
|
||||||
&SystemClauseType::CryptoDataHash => {
|
&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)])) {
|
let algorithm_str = match self.store(self.deref(self[temp_v!(4)])) {
|
||||||
Addr::Con(h) if self.heap.atom_at(h) => {
|
Addr::Con(h) if self.heap.atom_at(h) => {
|
||||||
@@ -5442,7 +5443,8 @@ impl MachineState {
|
|||||||
self.unify(self[temp_v!(3)], ints_list);
|
self.unify(self[temp_v!(3)], ints_list);
|
||||||
}
|
}
|
||||||
&SystemClauseType::CryptoDataHKDF => {
|
&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 stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
|
||||||
let salt = self.integers_to_bytevec(temp_v!(3), stub1);
|
let salt = self.integers_to_bytevec(temp_v!(3), stub1);
|
||||||
let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
|
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);
|
self.unify(self[temp_v!(4)], ints_list);
|
||||||
}
|
}
|
||||||
&SystemClauseType::CryptoDataEncrypt => {
|
&SystemClauseType::CryptoDataEncrypt => {
|
||||||
let data = self.string_encoding_bytes(1, 2);
|
let encoding = self.atom_argument_to_string(3);
|
||||||
let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6);
|
let data = self.string_encoding_bytes(1, &encoding);
|
||||||
let key = self.integers_to_bytevec(temp_v!(3), stub2);
|
let aad = self.string_encoding_bytes(2, &encoding);
|
||||||
let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 6);
|
let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 7);
|
||||||
let iv = self.integers_to_bytevec(temp_v!(4), stub3);
|
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 unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
||||||
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).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 mut in_out = data.clone();
|
||||||
let tag =
|
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 }
|
Ok(d) => { d }
|
||||||
_ => { self.fail = true; return Ok(()); }
|
_ => { self.fail = true; return Ok(()); }
|
||||||
};
|
};
|
||||||
@@ -5555,29 +5559,18 @@ impl MachineState {
|
|||||||
self.heap.put_complete_string(&buffer)
|
self.heap.put_complete_string(&buffer)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.unify(self[temp_v!(5)], tag_list);
|
self.unify(self[temp_v!(6)], tag_list);
|
||||||
self.unify(self[temp_v!(6)], complete_string);
|
self.unify(self[temp_v!(7)], complete_string);
|
||||||
}
|
}
|
||||||
&SystemClauseType::CryptoDataDecrypt => {
|
&SystemClauseType::CryptoDataDecrypt => {
|
||||||
let data = self.string_encoding_bytes(1, 2);
|
let data = self.string_encoding_bytes(1, "octet");
|
||||||
let stub1 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 6);
|
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 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 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 unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
||||||
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
|
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
|
||||||
let key = aead::LessSafeKey::new(unbound_key);
|
let key = aead::LessSafeKey::new(unbound_key);
|
||||||
@@ -5586,12 +5579,12 @@ impl MachineState {
|
|||||||
|
|
||||||
let complete_string = {
|
let complete_string = {
|
||||||
let decrypted_data =
|
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 }
|
Ok(d) => { d }
|
||||||
_ => { self.fail = true; return Ok(()); }
|
_ => { 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)) }
|
"octet" => { String::from_iter(decrypted_data.iter().map(|b| *b as char)) }
|
||||||
"utf8" => { match String::from_utf8(decrypted_data.to_vec()) {
|
"utf8" => { match String::from_utf8(decrypted_data.to_vec()) {
|
||||||
Ok(str) => { str }
|
Ok(str) => { str }
|
||||||
@@ -5665,7 +5658,8 @@ impl MachineState {
|
|||||||
self.unify(self[temp_v!(1)], complete_string);
|
self.unify(self[temp_v!(1)], complete_string);
|
||||||
}
|
}
|
||||||
&SystemClauseType::Ed25519KeyPairPublicKey => {
|
&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) {
|
let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) {
|
||||||
Ok(kp) => { kp }
|
Ok(kp) => { kp }
|
||||||
@@ -5680,8 +5674,9 @@ impl MachineState {
|
|||||||
self.unify(self[temp_v!(3)], complete_string);
|
self.unify(self[temp_v!(3)], complete_string);
|
||||||
}
|
}
|
||||||
&SystemClauseType::Ed25519Sign => {
|
&SystemClauseType::Ed25519Sign => {
|
||||||
let key = self.string_encoding_bytes(1, 2);
|
let key = self.string_encoding_bytes(1, "octet");
|
||||||
let data = self.string_encoding_bytes(3, 4);
|
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) {
|
let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) {
|
||||||
Ok(kp) => { kp }
|
Ok(kp) => { kp }
|
||||||
@@ -5696,8 +5691,9 @@ impl MachineState {
|
|||||||
self.unify(self[temp_v!(5)], sig_list);
|
self.unify(self[temp_v!(5)], sig_list);
|
||||||
}
|
}
|
||||||
&SystemClauseType::Ed25519Verify => {
|
&SystemClauseType::Ed25519Verify => {
|
||||||
let key = self.string_encoding_bytes(1, 2);
|
let key = self.string_encoding_bytes(1, "octet");
|
||||||
let data = self.string_encoding_bytes(3, 4);
|
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 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!(5), stub);
|
||||||
|
|
||||||
@@ -5863,17 +5859,14 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super)
|
pub(super)
|
||||||
fn string_encoding_bytes(
|
fn atom_argument_to_string(
|
||||||
&mut self,
|
&mut self,
|
||||||
data_arg: usize,
|
atom_arg: usize,
|
||||||
encoding_arg: usize,
|
) -> String {
|
||||||
) -> Vec<u8> {
|
match self.store(self.deref(self[temp_v!(atom_arg)])) {
|
||||||
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) => {
|
Addr::Con(h) if self.heap.atom_at(h) => {
|
||||||
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
|
if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
|
||||||
atom.as_str()
|
atom.as_str().to_string()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
@@ -5881,9 +5874,18 @@ impl MachineState {
|
|||||||
_ => {
|
_ => {
|
||||||
unreachable!()
|
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() }
|
"utf8" => { data.into_bytes() }
|
||||||
"octet" => {
|
"octet" => {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
|
|||||||
Reference in New Issue
Block a user