diff --git a/src/clause_types.rs b/src/clause_types.rs index 04509308..339c7d38 100644 --- a/src/clause_types.rs +++ b/src/clause_types.rs @@ -710,13 +710,13 @@ 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), - ("$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/builtins.pl b/src/lib/builtins.pl index ebfebfff..df312708 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -918,6 +918,7 @@ op(Priority, OpSpec, Op) :- halt :- halt(0). halt(N) :- + must_be_number(N, halt/1), ( -2^31 =< N, N =< 2^31 - 1 -> '$halt'(N) ; throw(error(domain_error(exit_code, N), halt/1)) diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 49aae46e..e88113c7 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) -> @@ -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,20 @@ 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). + algorithm_key_iv(Algorithm, Key, IV), + '$crypto_data_encrypt'(PlainText, AAD, Encoding, Key, IV, Tag, CipherText). + +algorithm_key_iv('chacha20-poly1305', Key, IV) :- + length(Key, 32), + length(IV, 12). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_decrypt(+CipherText, @@ -567,6 +580,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,16 +593,18 @@ 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), 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, octet, Key, IV, Encoding, PlainText). + algorithm_key_iv(Algorithm, Key, IV), + '$crypto_data_decrypt'(CipherText, AAD, Key, IV, Encoding, PlainText). encoding_chars(octet, Bs, Cs) :- @@ -637,19 +656,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 @@ -688,8 +707,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 df2187d1..2d890877 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1706,6 +1706,12 @@ impl MachineState { Ok(Number::Integer(n)) => { n.to_string() } + Ok(Number::Rational(r)) => { + // n has already been confirmed as an integer, and + // internally, Rational is assumed reduced, so its + // denominator must be 1. + r.numer().to_string() + } _ => { unreachable!() } @@ -3766,6 +3772,12 @@ impl MachineState { let code = match Number::try_from((code, &self.heap)) { Ok(Number::Fixnum(n)) => n as i32, Ok(Number::Integer(n)) => n.to_i32().unwrap(), + Ok(Number::Rational(r)) => { + // n has already been confirmed as an integer, and + // internally, Rational is assumed reduced, so its + // denominator must be 1. + r.numer().to_i32().unwrap() + } _ => { unreachable!() } }; @@ -5394,23 +5406,13 @@ 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) => { - if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] { - atom.as_str() - } else { - unreachable!() - } - } - _ => { - unreachable!() - } - }; + let algorithm = self.atom_argument_to_string(4); let ints_list = - match algorithm_str { + match algorithm.as_str() { "sha3_224" => { let mut context = Sha3_224::new(); context.input(&bytes); Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } @@ -5433,7 +5435,7 @@ impl MachineState { context.input(&bytes); Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } _ => { let ints = digest::digest( - match algorithm_str { + match algorithm.as_str() { "sha256" => { &digest::SHA256 } "sha384" => { &digest::SHA384 } "sha512" => { &digest::SHA512 } @@ -5448,27 +5450,19 @@ 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); let info = self.integers_to_bytevec(temp_v!(4), stub2); - 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() - } else { - unreachable!() - } - } - _ => { - unreachable!() - } - }; + let algorithm = self.atom_argument_to_string(5); + + let length = self.store(self.deref(self[temp_v!(6)])); let length = - match Number::try_from((self[temp_v!(6)], &self.heap)) { + match Number::try_from((length, &self.heap)) { Ok(Number::Fixnum(n)) => { usize::try_from(n).unwrap() } @@ -5483,7 +5477,7 @@ impl MachineState { let ints_list = { let digest_alg = - match algorithm { + match algorithm.as_str() { "sha256" => { hkdf::HKDF_SHA256 } "sha384" => { hkdf::HKDF_SHA384 } "sha512" => { hkdf::HKDF_SHA512 } @@ -5541,11 +5535,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(); @@ -5553,7 +5549,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(()); } }; @@ -5566,29 +5562,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); @@ -5597,12 +5582,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 } @@ -5618,26 +5603,17 @@ impl MachineState { self.unify(self[temp_v!(6)], complete_string); } &SystemClauseType::CryptoCurveScalarMult => { - let curve = match self.store(self.deref(self[temp_v!(1)])) { - 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 curve_id = match curve { + let curve = self.atom_argument_to_string(1); + let curve_id = match curve.as_str() { "secp112r1" => { Nid::SECP112R1 } "secp256k1" => { Nid::SECP256K1 } _ => { unreachable!() } }; + let scalar = self.store(self.deref(self[temp_v!(2)])); + let scalar = - match Number::try_from((self[temp_v!(2)], &self.heap)) { + match Number::try_from((scalar, &self.heap)) { Ok(Number::Fixnum(n)) => { Integer::from(n) } @@ -5676,7 +5652,7 @@ impl MachineState { self.unify(self[temp_v!(1)], complete_string); } &SystemClauseType::Ed25519KeyPairPublicKey => { - let bytes = self.string_encoding_bytes(1, 2); + let bytes = self.string_encoding_bytes(1, "octet"); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) { Ok(kp) => { kp } @@ -5688,11 +5664,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, 2); - let data = self.string_encoding_bytes(3, 4); + let key = self.string_encoding_bytes(1, "octet"); + 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 } @@ -5704,13 +5681,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, 2); - let data = self.string_encoding_bytes(3, 4); + let key = self.string_encoding_bytes(1, "octet"); + 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) { @@ -5729,10 +5707,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); } @@ -5777,32 +5752,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 @@ -5815,10 +5776,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); } @@ -5874,17 +5832,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!() } @@ -5892,9 +5847,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![];