diff --git a/.dockerignore b/.dockerignore deleted file mode 100755 index 050103c6..00000000 --- a/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -target -Dockerfile -README.md -.git -.gitignore -.gitmodules diff --git a/Cargo.lock b/Cargo.lock index be47e095..0f0b8616 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" +dependencies = [ + "byte-tools", + "crypto-mac", + "digest", + "opaque-debug", +] + [[package]] name = "blake2b_simd" version = "0.5.10" @@ -174,6 +186,16 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "digest" version = "0.8.1" @@ -323,6 +345,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -606,9 +634,9 @@ dependencies = [ [[package]] name = "prolog_parser" -version = "0.8.58" +version = "0.8.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90d34a4268bf5256d4e55f8ec920220aa96dc7ee779441b32f97635cba72aa9" +checksum = "029a4682cf40923b8eb05c4b943d1d6be73775e7bf80bcb0866b5ef29abb0802" dependencies = [ "lexical", "num-rug-adapter", @@ -740,8 +768,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scryer-prolog" -version = "0.8.122" +version = "0.8.123" dependencies = [ + "blake2", "cpu-time", "crossterm", "dirs", @@ -761,6 +790,7 @@ dependencies = [ "ripemd160", "rug", "rustyline", + "sha3", "unicode_reader", ] @@ -779,6 +809,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +dependencies = [ + "block-buffer", + "byte-tools", + "digest", + "keccak", + "opaque-debug", +] + [[package]] name = "signal-hook" version = "0.1.13" @@ -833,6 +876,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + [[package]] name = "syn" version = "1.0.18" diff --git a/Cargo.toml b/Cargo.toml index e8f52cd7..b869277c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scryer-prolog" -version = "0.8.122" +version = "0.8.123" authors = ["Mark Thom "] build = "build.rs" repository = "https://github.com/mthom/scryer-prolog" @@ -29,10 +29,12 @@ libc = "0.2.62" nix = "0.15.0" num-rug-adapter = { optional = true, version = "0.1.3" } ordered-float = "0.5.0" -prolog_parser = { version = "0.8.58", default-features = false } +prolog_parser = { version = "0.8.59", default-features = false } ref_thread_local = "0.0.0" rug = { version = "1.4.0", optional = true } rustyline = "6.0.0" unicode_reader = "1.0.0" ring = "0.16.13" ripemd160 = "0.8.0" +sha3 = "0.8.2" +blake2 = "0.8.1" diff --git a/Dockerfile b/Dockerfile deleted file mode 100755 index 3b2c3a05..00000000 --- a/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -# Based on https://hub.docker.com/_/rust?tab=description and https://hub.docker.com/_/rust?tab=description - -# The first container is for build purposes only. -FROM rust as builder - -WORKDIR /usr/src/scryer-prolog - -# Using a dummy build.rs and src/main.rs with your Cargo.toml lets Docker cache your Rust dependencies and not rebuild -# them every time. -COPY Cargo.toml . -COPY Cargo.lock . -RUN mkdir -p src -RUN echo "fn main() {}" > src/main.rs -RUN echo "fn main() {}" > build.rs -RUN cargo build --release - -# We need to touch our real main.rs and build.rs files or else -# docker will use the cached ones. -COPY . . -RUN touch src/main.rs -RUN touch build.rs - -RUN cargo build --release - -RUN ls ./target/release - -# Finally, copy the scryer-prolog executable to a slimmer container. -FROM debian:buster-slim -COPY --from=builder /usr/src/scryer-prolog/target/release/scryer-prolog /usr/local/bin/scryer-prolog -CMD ["scryer-prolog"] diff --git a/README.md b/README.md index fbb8bb9a..2a477a28 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,6 @@ strings. ## Installing Scryer Prolog -### Native Install (Unix Only) - First, install the latest stable version of [Rust](https://www.rust-lang.org/en-US/install.html) using your preferred method. Scryer tends to use features from newer Rust @@ -128,26 +126,6 @@ $> cargo run [--release] The optional `--release` flag will perform various optimizations, producing a faster executable. -### Docker Install (All Platforms) - -To automatically download, install, and run Scryer Prolog via Docker, -simply run: -``` -$> docker run -it mthom/scryer-prolog -``` - -To be able to load your program files, bind mount your programs folder -as a Docker volume: - -``` -$> docker run -v /home/user/prolog:/mnt -it mthom/scryer-prolog -?- consult('mnt/program.pl'). -true. -``` - -[Docker](https://hub.docker.com/editions/community/docker-ce-desktop-windows) -is currently the only way to run scryer-prolog on Windows. - ## Tutorial Prolog files are loaded by specifying them as arguments on the command @@ -375,7 +353,7 @@ The modules that ship with Scryer Prolog are also called * [`format`](src/prolog/lib/format.pl) The nonterminal `format_//2` is used to describe formatted output, arranging arguments according to a given format string. - The predicates `format/2`, `portray_clause/1` and `listing/1` + The predicates `format/[2,3]`, `portray_clause/1` and `listing/1` provide formatted *impure* output. * [`assoc`](src/prolog/lib/assoc.pl) providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc. @@ -402,6 +380,7 @@ The modules that ship with Scryer Prolog are also called * [`crypto`](src/prolog/lib/crypto.pl) Cryptographically secure random numbers and hashes, HMAC-based key derivation (HKDF), password-based key derivation (PBKDF2), + public key signatures and signature verification with Ed25519, authenticated encryption, and reasoning about elliptic curves. To read contents of external files, use `phrase_from_file/2` from diff --git a/src/prolog/arithmetic.rs b/src/prolog/arithmetic.rs index 87df3dbd..e889b019 100644 --- a/src/prolog/arithmetic.rs +++ b/src/prolog/arithmetic.rs @@ -474,7 +474,12 @@ impl Neg for Number { fn neg(self) -> Self::Output { match self { - Number::Fixnum(n) => Number::Fixnum(-n), + Number::Fixnum(n) => + if let Some(n) = n.checked_neg() { + Number::Fixnum(n) + } else { + Number::from(-Integer::from(n)) + } Number::Integer(n) => Number::Integer(Rc::new(-Integer::from(&*n))), Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)), Number::Rational(r) => Number::Rational(Rc::new(-Rational::from(&*r))), diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index cf4e1aa6..e685c65f 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -229,6 +229,7 @@ pub enum SystemClauseType { PeekCode, PointsToContinuationResetMarker, PutByte, + PutBytes, PutChar, PutCode, REPL(REPLCodePtr), @@ -291,7 +292,11 @@ pub enum SystemClauseType { CryptoDataHKDF, CryptoPasswordHash, CryptoDataEncrypt, - CryptoDataDecrypt + CryptoDataDecrypt, + Ed25519Sign, + Ed25519Verify, + Ed25519NewKeyPair, + Ed25519KeyPairPublicKey } impl SystemClauseType { @@ -411,6 +416,9 @@ impl SystemClauseType { &SystemClauseType::PutByte => { clause_name!("$put_byte") } + &SystemClauseType::PutBytes => { + clause_name!("$put_bytes") + } &SystemClauseType::PutChar => { clause_name!("$put_char") } @@ -480,6 +488,10 @@ impl SystemClauseType { &SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"), &SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"), &SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"), + &SystemClauseType::Ed25519Sign => clause_name!("$ed25519_sign"), + &SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"), + &SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"), + &SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key") } } @@ -533,7 +545,7 @@ impl SystemClauseType { ("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal), ("$fetch_global_var", 2) => Some(SystemClauseType::FetchGlobalVar), ("$fetch_global_var_with_offset", 3) => Some(SystemClauseType::FetchGlobalVarWithOffset), - ("$file_to_chars", 2) => Some(SystemClauseType::FileToChars), + ("$file_to_chars", 3) => Some(SystemClauseType::FileToChars), ("$get_byte", 2) => Some(SystemClauseType::GetByte), ("$get_char", 2) => Some(SystemClauseType::GetChar), ("$get_code", 2) => Some(SystemClauseType::GetCode), @@ -544,6 +556,9 @@ impl SystemClauseType { ("$put_byte", 2) => { Some(SystemClauseType::PutByte) } + ("$put_bytes", 2) => { + Some(SystemClauseType::PutBytes) + } ("$put_char", 2) => { Some(SystemClauseType::PutChar) } @@ -648,6 +663,10 @@ impl SystemClauseType { ("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash), ("$crypto_data_encrypt", 5) => Some(SystemClauseType::CryptoDataEncrypt), ("$crypto_data_decrypt", 5) => Some(SystemClauseType::CryptoDataDecrypt), + ("$ed25519_sign", 3) => Some(SystemClauseType::Ed25519Sign), + ("$ed25519_verify", 3) => Some(SystemClauseType::Ed25519Verify), + ("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair), + ("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey), _ => None, } } diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index 28da88e4..55087f54 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -92,6 +92,10 @@ fn is_partial_string<'a>( Term::Constant(_, Constant::EmptyList) => { return Some((string, None)); } + Term::Constant(_, Constant::String(tail)) => { + string += &tail; + return Some((string, None)); + } _ => { return None; } diff --git a/src/prolog/lib/crypto.pl b/src/prolog/lib/crypto.pl index b93a9cff..cd7f93c5 100644 --- a/src/prolog/lib/crypto.pl +++ b/src/prolog/lib/crypto.pl @@ -9,22 +9,30 @@ and strings have the advantage that the atom table remains unmodified. Especially for cryptographic applications, it as an advantage that - using strings leaves little trace of what was processed in the system, + using strings leaves little trace of what was processed in the system. + + For predicates that accept an encoding/1 option to specify the encoding + of the input data, if encoding(octet) is used, then the input can also + be specified as a list of bytes, i.e., integers between 0 and 255. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- module(crypto, - [hex_bytes/2, % ?Hex, ?Bytes - crypto_n_random_bytes/2, % +N, -Bytes - crypto_data_hash/3, % +Data, -Hash, +Options - crypto_data_hkdf/4, % +Data, +Length, -Bytes, +Options - crypto_password_hash/2, % +Password, ?Hash - crypto_password_hash/3, % +Password, -Hash, +Options - crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options - crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options - crypto_name_curve/2, % +Name, -Curve - crypto_curve_order/2, % +Curve, -Order - crypto_curve_generator/2, % +Curve, -Generator - crypto_curve_scalar_mult/4 % +Curve, +Scalar, +Point, -Result + [hex_bytes/2, % ?Hex, ?Bytes + crypto_n_random_bytes/2, % +N, -Bytes + crypto_data_hash/3, % +Data, -Hash, +Options + crypto_data_hkdf/4, % +Data, +Length, -Bytes, +Options + crypto_password_hash/2, % +Password, ?Hash + crypto_password_hash/3, % +Password, -Hash, +Options + crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options + crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options + ed25519_new_keypair/1, % -KeyPair + ed25519_keypair_public_key/2, % +KeyPair, +PublicKey + ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options + ed25519_verify/4, % +PublicKey, +Data, +Signature, +Options + crypto_name_curve/2, % +Name, -Curve + crypto_curve_order/2, % +Curve, -Order + crypto_curve_generator/2, % +Curve, -Generator + crypto_curve_scalar_mult/4 % +Curve, +Scalar, +Point, -Result ]). :- use_module(library(error)). @@ -151,18 +159,17 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). crypto_data_hash(+Data, -Hash, +Options) - Where Data is a list of bytes (integers between 0 and 255) or - characters, and Hash is the computed hash as a list of hexadecimal - characters. + Where Data is a list of characters, and Hash is the computed hash + as a list of hexadecimal characters. Options is a list of: - algorithm(+A) - where A is one of ripemd160, sha256, sha384, sha512, - sha512_256, or a variable. If A is a variable, then it is - unified with the default algorithm, which is an algorithm that - is considered cryptographically secure at the time of this - writing. + where A is one of ripemd160, sha256, sha384, sha512, sha512_256, + sha3_224, sha3_256, sha3_384, sha3_512, blake2s256, blake2b512, + or a variable. If A is a variable, then it is unified with the + default algorithm, which is an algorithm that is considered + cryptographically secure at the time of this writing. - encoding(+Encoding) The default encoding is utf8. The alternative is octet, to treat the input as a list of raw bytes. @@ -184,8 +191,7 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). crypto_data_hash(Data0, Hash, Options0) :- must_be(list, Options0), - option(encoding(Encoding), Options0, utf8), - encoding_bytes(Encoding, Data0, Data), + options_data_bytes(Options0, Data0, Data), functor_hash_options(algorithm, A, Options0, _), ( hash_algorithm(A) -> true ; domain_error(hash_algorithm, A, crypto_data_hash/3) @@ -193,6 +199,10 @@ crypto_data_hash(Data0, Hash, Options0) :- '$crypto_data_hash'(Data, HashBytes, A), hex_bytes(Hash, HashBytes). +options_data_bytes(Options, Data, Bytes) :- + option(encoding(Encoding), Options, utf8), + must_be(atom, Encoding), + encoding_bytes(Encoding, Data, Bytes). default_hash(sha256). @@ -212,13 +222,19 @@ hash_algorithm(sha256). hash_algorithm(sha512). hash_algorithm(sha384). hash_algorithm(sha512_256). +hash_algorithm(sha3_224). +hash_algorithm(sha3_256). +hash_algorithm(sha3_384). +hash_algorithm(sha3_512). +hash_algorithm(blake2s256). +hash_algorithm(blake2b512). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det. Concentrate possibly dispersed entropy of Data and then expand it - to the desired length. Data is a list of bytes or characters. + to the desired length. Data is a list of characters. Bytes is unified with a list of bytes of length Length, and is suitable as input keying material and initialization vectors to @@ -227,13 +243,12 @@ hash_algorithm(sha512_256). Admissible options are: - algorithm(+Algorithm) - A hashing algorithm as specified to crypto_data_hash/3. The - default is a cryptographically secure algorithm. If you - specify a variable, then it is unified with the algorithm - that was used, which is a cryptographically secure algorithm. + One of sha256, sha384 or sha512. If you specify a variable, + then it is unified with the algorithm that was used, which is a + cryptographically secure algorithm by default. - info(+Info) Optional context and application specific information, - specified as a list of bytes or characters. The default is []. + specified as a list of characters. The default is []. - salt(+List) Optionally, a list of bytes that are used as salt. The default is all zeroes. @@ -250,15 +265,27 @@ hash_algorithm(sha512_256). crypto_data_hkdf(Data0, L, Bytes, Options0) :- functor_hash_options(algorithm, Algorithm, Options0, Options), - option(encoding(Encoding), Options, utf8), - encoding_bytes(Encoding, Data0, Data), + ( hkdf_algorithm(Algorithm) -> true + ; domain_error(hkdf_algorithm, Algorithm, crypto_data_hkdf/4) + ), + must_be(integer, L), + L >= 0, + options_data_bytes(Options, Data0, Data), option(salt(SaltBytes), Options, []), must_be_bytes(SaltBytes, crypto_data_hkdf/4), option(info(Info0), Options, []), chars_bytes_(Info0, Info, crypto_data_hkdf/4), '$crypto_data_hkdf'(Data, SaltBytes, Info, Algorithm, L, Bytes). +hkdf_algorithm(sha256). +hkdf_algorithm(sha384). +hkdf_algorithm(sha512). + option(What, Options, Default) :- + ( member(V, Options), var(V) -> + instantiation_error(option/3) + ; true + ), ( member(What, Options) -> true ; What =.. [_,Default] ). @@ -379,7 +406,7 @@ crypto_password_hash(Password0, Hash, Options) :- Algorithm = 'pbkdf2-sha512', % current default and only option option(algorithm(Algorithm), Options, Algorithm), ( member(salt(SaltBytes), Options) -> - true + must_be_bytes(SaltBytes, crypto_password_hash/2) ; crypto_n_random_bytes(16, SaltBytes) ), '$crypto_password_hash'(Password, SaltBytes, Iterations, HashBytes), @@ -456,8 +483,8 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z], Algorithm, key Key, and initialization vector (or nonce) IV, to give CipherText. - PlainText must be a list of codes or characters, Key and IV must be - lists of bytes, and CipherText is created as a list of characters. + PlainText must be a list of characters, Key and IV must be lists of + bytes, and CipherText is created as a list of characters. Keys and IVs can be chosen at random (using for example crypto_n_random_bytes/2) or derived from input keying material (IKM) @@ -531,8 +558,7 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z], - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- - option(encoding(Encoding), Options, utf8), - encoding_bytes(Encoding, PlainText0, PlainText), + options_data_bytes(Options, PlainText0, PlainText), option(tag(Tag), Options, _), ( nonvar(Tag) -> must_be_bytes(Tag, crypto_data_encrypt/6) @@ -556,9 +582,9 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- Decrypt the given CipherText, using the symmetric algorithm Algorithm, key Key, and initialization vector IV, to give - PlainText. CipherText must be a list of bytes or characters, and - Key and IV must be lists of bytes. PlainText is created as a list - of characters. + PlainText. CipherText must be a list of characters, and Key and IV + must be lists of bytes. PlainText is created as a list of + characters. Currently, the only supported algorithm is 'chacha20-poly1305', a very secure, fast and versatile authenticated encryption method. @@ -581,6 +607,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), + must_be(atom, Encoding), + member(Encoding, [utf8,octet]), must_be(list, CipherText0), encoding_bytes(octet, CipherText0, CipherText1), append(CipherText1, Tag, CipherText), @@ -590,18 +618,70 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- '$crypto_data_decrypt'(CipherText, Key, IV, Encoding, PlainText). encoding_bytes(octet, Bs0, Bs) :- + must_be(list, Bs0), ( maplist(integer, Bs0) -> Bs0 = Bs ; maplist(char_code, Bs0, Bs) ), must_be_bytes(Bs, crypto_encoding). encoding_bytes(utf8, Cs, Bs) :- + must_be(list, Cs), ( maplist(atom, Cs) -> chars_bytes_(Cs, Bs, crypto_encoding) ; domain_error(encryption_encoding, Cs, crypto) ). -char_code(Char, Code) :- atom_codes(Char, [Code]). +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Digital signatures with Ed25519 + =============================== + + - ed25519_new_keypair(-Pair) + Yields a new Ed25519 key pair Pair, a list of characters. The + pair contains the private key and must be kept absolutely secret. + Pair can be used for signing. Its public key can be obtained + with ed25519_keypair_public_key/2. + + - ed25519_keypair_public_key(+Pair, -PublicKey) + PublicKey is the public key of the given key pair. The public key + can be used for signature verification, and can be shared freely. + The public key is represented as a list of characters. + + - ed25519_sign(+Key, +Data, -Signature, +Options) + Key and Data must be lists of characters. Key is a key pair in + PKCS#8 v2 format as generated by ed25519_new_keypair/1. Sign Data + with Key, yielding Signature as a list of hexadecimal characters. + + - ed25519_verify(+Key, +Data, +Signature, +Options) + Key and Data must be lists of characters. Key is a public key. + Succeeds if Data was signed with the private key corresponding to + Key, where Signature is a list of hexadecimal characters as + generated by ed25519_sign/4. Fails otherwise. + + Currently, the only option for signing and verifying is: + + - encoding(+Encoding) + The default encoding of Data is utf8. The alternative is octet, + which treats Data as a list of raw bytes. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +ed25519_new_keypair(Pair) :- + '$ed25519_new_keypair'(Pair). + +ed25519_keypair_public_key(Pair0, PublicKey) :- + encoding_bytes(octet, Pair0, Pair), + '$ed25519_keypair_public_key'(Pair, PublicKey). + +ed25519_sign(Key0, Data0, Signature, Options) :- + options_data_bytes(Options, Data0, Data), + encoding_bytes(octet, Key0, Key), + '$ed25519_sign'(Key, Data, Signature0), + hex_bytes(Signature, Signature0). + +ed25519_verify(Key0, Data0, Signature0, Options) :- + options_data_bytes(Options, Data0, Data), + encoding_bytes(octet, Key0, Key), + hex_bytes(Signature0, Signature), + '$ed25519_verify'(Key, Data, Signature). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Modular multiplicative inverse. diff --git a/src/prolog/lib/format.pl b/src/prolog/lib/format.pl index 17ab0bfc..18289094 100644 --- a/src/prolog/lib/format.pl +++ b/src/prolog/lib/format.pl @@ -49,6 +49,10 @@ The predicate format/2 is like format_//2, except that it outputs the text on the terminal instead of describing it declaratively. + format/3, used as format(Stream, FormatString, Arguments), outputs + the described string to the given Stream. If Stream is a binary + stream, then the code of each emitted character must be in 0..255. + If at all possible, format_//2 should be used, to stress pure parts that enable easy testing etc. If necessary, you can emit the list Ls with maplist(write, Ls). @@ -68,6 +72,7 @@ :- module(format, [format_//2, format/2, + format/3, portray_clause/1, listing/1 ]). @@ -359,6 +364,26 @@ format(Fs, Args) :- phrase(format_(Fs, Args), Cs), maplist(write, Cs). +format(Stream, Fs, Args) :- + phrase(format_(Fs, Args), Cs), + ( stream_property(Stream, type(binary)) -> + % maplist(char_code, Cs, Bytes) is currently a lot slower + % than first converting Cs to an atom, and then to codes. + % In the future, we can ideally avoid creating an atom here, + % since an atom leaves traces in the system. + atom_chars(A, Cs), + atom_codes(A, Bytes), + ( member(NonByte, Bytes), NonByte > 255 -> + char_code(Char, NonByte), + throw(error(representation_error(Char), format/3)) + ; true + ), + % For binary streams, we use a specialised internal predicate + % that uses only a single "write" operation for efficiency. + '$put_bytes'(Stream, Bytes) + ; maplist(put_char(Stream), Cs) + ). + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ?- phrase(cells("hello", [], 0, []), Cs). diff --git a/src/prolog/lib/pio.pl b/src/prolog/lib/pio.pl index 4f387625..4c0f9046 100644 --- a/src/prolog/lib/pio.pl +++ b/src/prolog/lib/pio.pl @@ -1,12 +1,24 @@ -:- module(pio, [phrase_from_file/2]). +:- module(pio, [phrase_from_file/2, + phrase_from_file/3]). :- use_module(library(dcgs)). :- use_module(library(error)). +:- use_module(library(lists), [member/2]). phrase_from_file(NT, File) :- - ( var(File) -> instantiation_error(phrase_from_file/2) + phrase_from_file(NT, File, []). + +phrase_from_file(NT, File, Options) :- + ( var(File) -> instantiation_error(phrase_from_file/3) ; (\+ atom(File) ; File = []) -> - domain_error(source_sink, File, phrase_from_file/2) - ; '$file_to_chars'(File, Chars), + domain_error(source_sink, File, phrase_from_file/3) + ; must_be(list, Options), + ( member(Var, Options), var(Var) -> instantiation_error(phrase_from_file/3) + ; member(type(Type), Options) -> + must_be(atom, Type), + member(Type, [text,binary]) + ; Type = text + ), + '$file_to_chars'(File, Chars, Type), phrase(NT, Chars) ). diff --git a/src/prolog/machine/arithmetic_ops.rs b/src/prolog/machine/arithmetic_ops.rs index ab604e32..ae3e3c62 100644 --- a/src/prolog/machine/arithmetic_ops.rs +++ b/src/prolog/machine/arithmetic_ops.rs @@ -398,7 +398,7 @@ impl MachineState { match (n1, n2) { (Number::Fixnum(n1), Number::Fixnum(n2)) => { - if n1 != 1 && n2 < 0 { + if n1 != 1 && n2 < -1 { let n = Number::from(n1); let stub = MachineError::functor_stub(clause_name!("^"), 2); @@ -424,7 +424,7 @@ impl MachineState { } } (Number::Fixnum(n1), Number::Integer(n2)) => { - if n1 != 1 && &*n2 < &0 { + if n1 != 1 && &*n2 < &-1 { let n = Number::from(n1); let stub = MachineError::functor_stub(clause_name!("^"), 2); @@ -442,7 +442,7 @@ impl MachineState { } } (Number::Integer(n1), Number::Fixnum(n2)) => { - if &*n1 != &1 && n2 < 0 { + if &*n1 != &1 && n2 < -1 { let n = Number::Integer(n1); let stub = MachineError::functor_stub(clause_name!("^"), 2); @@ -460,7 +460,7 @@ impl MachineState { } } (Number::Integer(n1), Number::Integer(n2)) => { - if &*n1 != &1 && &*n2 < &0 { + if &*n1 != &1 && &*n2 < &-1 { let n = Number::Integer(n1); let stub = MachineError::functor_stub(clause_name!("^"), 2); diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index c4842978..ac43d01c 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -348,7 +348,9 @@ fn compile_into_module( ); match compile_into_module_impl(wam, &mut compiler, module, src, indices) { - Ok(()) => EvalSession::EntrySuccess, + Ok(()) => { + EvalSession::EntrySuccess + } Err(e) => { compiler.drop_expansions(&mut wam.code_repo); EvalSession::from(e) diff --git a/src/prolog/machine/heap.rs b/src/prolog/machine/heap.rs index 68d4a2f9..91639423 100644 --- a/src/prolog/machine/heap.rs +++ b/src/prolog/machine/heap.rs @@ -189,6 +189,10 @@ impl HeapTemplate { #[inline] pub(crate) fn put_complete_string(&mut self, s: &str) -> Addr { + if s.is_empty() { + return Addr::EmptyList; + } + let addr = self.allocate_pstr(s); self.pop(); @@ -316,20 +320,7 @@ impl HeapTemplate { pub(crate) fn allocate_pstr(&mut self, src: &str) -> Addr { self.write_pstr(src) - .unwrap_or_else(|| { - let h = self.h(); - - self.push(HeapCellValue::PartialString( - PartialString::empty(), - true, - )); - - self.push(HeapCellValue::Addr( - Addr::HeapCell(h + 1) - )); - - Addr::PStrLocation(h, 0) - }) + .unwrap_or_else(|| Addr::EmptyList) } #[inline] diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index ec06f9fc..a26c4cde 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -1492,9 +1492,13 @@ impl MachineState { &QueryInstruction::PutPartialString(_, ref string, reg, has_tail) => { let pstr_addr = if has_tail { - let pstr_addr = self.heap.allocate_pstr(&string); - self.heap.pop(); // the tail will be added by the next instruction. - pstr_addr + if !string.is_empty() { + let pstr_addr = self.heap.allocate_pstr(&string); + self.heap.pop(); // the tail will be added by the next instruction. + pstr_addr + } else { + Addr::EmptyList + } } else { self.heap.put_complete_string(&string) }; diff --git a/src/prolog/machine/partial_string.rs b/src/prolog/machine/partial_string.rs index 2d821ac4..1413b37c 100644 --- a/src/prolog/machine/partial_string.rs +++ b/src/prolog/machine/partial_string.rs @@ -37,8 +37,8 @@ fn scan_for_terminator>(iter: Iter) -> usize { let mut terminator_idx = 0; for c in iter { - if c == '\u{0}' { - break; + if c == '\u{0}' && terminator_idx != 0 { + return terminator_idx; } terminator_idx += c.len_utf8(); @@ -98,37 +98,9 @@ impl PartialString { } } - #[inline] - pub(super) - fn empty() -> Self { - let mut pstr = PartialString { - buf: ptr::null(), - len: 0, - _marker: PhantomData, - }; - - unsafe { - let layout = alloc::Layout::from_size_align_unchecked( - '\u{0}'.len_utf8(), - mem::align_of::(), - ); - - pstr.buf = alloc::alloc(layout) as *const _; - pstr.len = '\u{0}'.len_utf8(); - - pstr.write_terminator_at(0); - } - - pstr - } - unsafe fn append_chars(mut self, src: &str) -> Option<(Self, &str)> { let terminator_idx = scan_for_terminator(src.chars()); - if terminator_idx == 0 { - return None; - } - let layout = alloc::Layout::from_size_align_unchecked( terminator_idx + '\u{0}'.len_utf8(), mem::align_of::(), @@ -145,8 +117,8 @@ impl PartialString { self.write_terminator_at(terminator_idx); - Some(if terminator_idx != src.len() { - (self, &src[terminator_idx + '\u{0}'.len_utf8() ..]) + Some(if terminator_idx != src.as_bytes().len() { + (self, &src[terminator_idx ..]) } else { (self, "") }) @@ -211,9 +183,7 @@ impl PartialString { #[inline] pub fn at_end(&self, end_n: usize) -> bool { - unsafe { - ptr::read((self.buf as usize + end_n) as *const u8) == 0u8 - } + end_n + 1 == self.len } #[inline] diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 9e6ea491..6308017d 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -688,9 +688,14 @@ impl MachineState { return Err(self.open_past_eos_error(stream.clone(), caller, arity)); } EOFAction::EOFCode => { - let end_of_stream = self.heap.to_unifiable( - HeapCellValue::Atom(clause_name!("end_of_file"), None) - ); + let end_of_stream = + if stream.options.stream_type == StreamType::Binary { + Addr::Fixnum(-1) + } else { + self.heap.to_unifiable( + HeapCellValue::Atom(clause_name!("end_of_file"), None) + ) + }; stream.set_past_end_of_stream(); Ok(self.unify(result, end_of_stream)) diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 4061bdbc..0fb224ad 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -40,8 +40,10 @@ use crate::crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers}; use crate::crossterm::terminal::{enable_raw_mode, disable_raw_mode}; use ring::rand::{SecureRandom, SystemRandom}; -use ring::{digest,hkdf,pbkdf2,aead,error}; +use ring::{digest,hkdf,pbkdf2,aead,signature::{self,KeyPair}}; use ripemd160::{Ripemd160, Digest}; +use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512}; +use blake2::{Blake2s, Blake2b}; pub fn get_key() -> KeyEvent { let key; @@ -1110,6 +1112,11 @@ impl MachineState { let h = self.heap.h(); + if atom.as_str().is_empty() { + self.fail = true; + return Ok(()); + } + let pstr = self.heap.allocate_pstr(atom.as_str()); let pstr_tail = self.heap[h + 1].as_addr(h + 1); @@ -1154,10 +1161,22 @@ impl MachineState { match pstr { Addr::PStrLocation(h, _) => { - let tail = self.heap[h + 1].as_addr(h + 1); - let target = self[temp_v!(2)]; + if let HeapCellValue::PartialString(_, true) = &self.heap[h] { + let tail = self.heap[h + 1].as_addr(h + 1); + let target = self[temp_v!(2)]; - self.unify(tail, target); + self.unify(tail, target); + } else { + self.fail = true; + return Ok(()); + } + } + Addr::Lis(h) => { + self.unify(Addr::HeapCell(h + 1), self[temp_v!(2)]); + } + Addr::EmptyList => { + self.fail = true; + return Ok(()); } _ => { unreachable!() @@ -1888,29 +1907,51 @@ impl MachineState { } }; - let complete_string = { - let mut buffer = String::new(); - match file.read_to_string(&mut buffer) { - Ok(_size) => { - self.heap.put_complete_string(&buffer) - } - Err(_e) => { - // This case if the data isn't UTF-8 valid. - let mut buffer = Vec::new(); - let _ = match file.read_to_end(&mut buffer) { - Ok(size) => size, - Err(_e) => unreachable!() - }; - let buffer = String::from_iter( - buffer.into_iter().map(|b| b as char) - ); - - self.heap.put_complete_string(&buffer) + let type_str = match self.store(self.deref(self[temp_v!(3)])) { + 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 complete_string = { + let mut buffer = String::new(); + match type_str { + "text" => { match file.read_to_string(&mut buffer) { + Ok(_size) => { + self.heap.put_complete_string(&buffer) + } + Err(_e) => { + // the data isn't valid UTF-8, so we fail. + self.fail = true; + return Ok(()); + + } + } + } + "binary" => { let mut buffer = Vec::new(); + let _ = match file.read_to_end(&mut buffer) { + Ok(size) => size, + Err(_e) => unreachable!() + }; + + let buffer = String::from_iter( + buffer.into_iter().map(|b| b as char) + ); + + self.heap.put_complete_string(&buffer) + } + _ => { unreachable!() } + } + }; + self.unify(complete_string, a2); } &SystemClauseType::PutCode => { @@ -2112,6 +2153,37 @@ impl MachineState { } } } + &SystemClauseType::PutBytes => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "$put_bytes", 2)?; + + let stub = MachineError::functor_stub(clause_name!("$put_bytes"), 2); + let bytes = self.integers_to_bytevec(temp_v!(2), stub); + + match stream.write(&bytes) { + Ok(_) => { + return return_from_clause!(self.last_call, self); + } + _ => { + let stub = MachineError::functor_stub( + clause_name!("$put_bytes"), + 2, + ); + + let addr = self.heap.to_unifiable( + HeapCellValue::Stream(stream.clone()), + ); + + return Err(self.error_form( + MachineError::existence_error( + self.heap.h(), + ExistenceError::Stream(addr), + ), + stub, + )); + } + } + } &SystemClauseType::GetByte => { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "get_byte", 2)?; @@ -2125,6 +2197,13 @@ impl MachineState { )?; if stream.past_end_of_stream() { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("get_byte"), + 2, + )?; + if EOFAction::Reset != stream.options.eof_action { return return_from_clause!(self.last_call, self); } else if self.fail { @@ -2132,12 +2211,6 @@ impl MachineState { } } - if stream.at_end_of_stream() { - stream.set_past_end_of_stream(); - self.unify(self[temp_v!(2)], Addr::Fixnum(-1)); - return return_from_clause!(self.last_call, self); - } - let addr = match self.store(self.deref(self[temp_v!(2)])) { addr if addr.is_ref() => { @@ -2197,18 +2270,9 @@ impl MachineState { } } _ => { - self.eof_action( - self[temp_v!(2)], - &mut stream, - clause_name!("get_byte"), - 2, - )?; - - if EOFAction::Reset != stream.options.eof_action { - return return_from_clause!(self.last_call, self); - } else if self.fail { - return Ok(()); - } + stream.set_past_end_of_stream(); + self.unify(self[temp_v!(2)], Addr::Fixnum(-1)); + return return_from_clause!(self.last_call, self); } } } @@ -5202,21 +5266,39 @@ impl MachineState { }; let ints_list = - if algorithm_str == "ripemd160" { - let mut context = Ripemd160::new(); - context.input(&bytes); - Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) - } else { - let ints = digest::digest( - match algorithm_str { - "sha256" => { &digest::SHA256 } - "sha384" => { &digest::SHA384 } - "sha512" => { &digest::SHA512 } - "sha512_256" => { &digest::SHA512_256 } - _ => { unreachable!() } - }, - &bytes); - Addr::HeapCell(self.heap.to_list(ints.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + match algorithm_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))))) } + "sha3_256" => { let mut context = Sha3_256::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } + "sha3_384" => { let mut context = Sha3_384::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } + "sha3_512" => { let mut context = Sha3_512::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } + "blake2s256" => { let mut context = Blake2s::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } + "blake2b512" => { let mut context = Blake2b::new(); + context.input(&bytes); + Addr::HeapCell(self.heap.to_list(context.result().as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) } + "ripemd160" => { let mut context = Ripemd160::new(); + 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 { + "sha256" => { &digest::SHA256 } + "sha384" => { &digest::SHA384 } + "sha512" => { &digest::SHA512 } + "sha512_256" => { &digest::SHA512_256 } + _ => { unreachable!() } + }, + &bytes); + Addr::HeapCell(self.heap.to_list(ints.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) + } }; self.unify(self[temp_v!(2)], ints_list); @@ -5248,11 +5330,12 @@ impl MachineState { usize::try_from(n).unwrap() } Ok(Number::Integer(n)) => { - n.to_usize().unwrap() - } - _ => { - unreachable!() + match n.to_usize() { + Some(u) => { u } + _ => { self.fail = true; return Ok(()); } + } } + _ => { unreachable!() } }; let ints_list = @@ -5261,14 +5344,17 @@ impl MachineState { "sha256" => { hkdf::HKDF_SHA256 } "sha384" => { hkdf::HKDF_SHA384 } "sha512" => { hkdf::HKDF_SHA512 } - _ => { unreachable!() } + _ => { self.fail = true; return Ok(()); } }; let salt = hkdf::Salt::new(digest_alg, &salt); let mut bytes : Vec = Vec::new(); bytes.resize(length, 0); - salt.extract(&data).expand(&[&info[..]], MyKey(length)).unwrap().fill(&mut bytes).unwrap(); + match salt.extract(&data).expand(&[&info[..]], MyKey(length)) { + Ok(r) => { r.fill(&mut bytes).unwrap(); } + _ => { self.fail = true; return Ok(()); } + } - Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) }; self.unify(self[temp_v!(6)], ints_list); @@ -5285,7 +5371,10 @@ impl MachineState { u64::try_from(n).unwrap() } Ok(Number::Integer(n)) => { - n.to_u64().unwrap() + match n.to_u64() { + Some(i) => { i } + None => { self.fail = true; return Ok(()); } + } } _ => { unreachable!() @@ -5298,7 +5387,7 @@ impl MachineState { NonZeroU32::new(iterations as u32).unwrap(), &salt, &data, &mut bytes); - Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))) + Addr::HeapCell(self.heap.to_list(bytes.iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))) }; self.unify(self[temp_v!(4)], ints_list); @@ -5312,18 +5401,18 @@ impl MachineState { let iv = self.integers_to_bytevec(temp_v!(3), stub3); let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); - let nonce_sequence = OneNonceSequence::new(aead::Nonce::try_assume_unique_for_key(&iv).unwrap()); - let mut key: aead::SealingKey = aead::BoundKey::new(unbound_key, nonce_sequence); + let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap(); + let key = aead::LessSafeKey::new(unbound_key); let mut in_out = data.clone(); let tag = - match key.seal_in_place_separate_tag(aead::Aad::empty(), &mut in_out) { + match key.seal_in_place_separate_tag(nonce, aead::Aad::empty(), &mut in_out) { Ok(d) => { d } _ => { self.fail = true; return Ok(()); } }; let tag_list = - Addr::HeapCell(self.heap.to_list(tag.as_ref().iter().map(|b| HeapCellValue::Integer(Rc::new(Integer::from(*b)))))); + Addr::HeapCell(self.heap.to_list(tag.as_ref().iter().map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))))); let complete_string = { let buffer = String::from_iter(in_out.iter().map(|b| *b as char)); @@ -5355,14 +5444,14 @@ impl MachineState { }; let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); - let nonce_sequence = OneNonceSequence::new(aead::Nonce::try_assume_unique_for_key(&iv).unwrap()); - let mut key: aead::OpeningKey = aead::BoundKey::new(unbound_key, nonce_sequence); + let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap(); + let key = aead::LessSafeKey::new(unbound_key); let mut in_out = data.clone(); let complete_string = { let decrypted_data = - match key.open_in_place(aead::Aad::empty(), &mut in_out) { + match key.open_in_place(nonce, aead::Aad::empty(), &mut in_out) { Ok(d) => { d } _ => { self.fail = true; return Ok(()); } }; @@ -5382,6 +5471,63 @@ impl MachineState { self.unify(self[temp_v!(5)], complete_string); } + &SystemClauseType::Ed25519NewKeyPair => { + let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(rng()).unwrap(); + let complete_string = { + let buffer = String::from_iter(pkcs8_bytes.as_ref().iter().map(|b| *b as char)); + self.heap.put_complete_string(&buffer) + }; + + self.unify(self[temp_v!(1)], complete_string); + } + &SystemClauseType::Ed25519KeyPairPublicKey => { + let stub1 = MachineError::functor_stub(clause_name!("ed25519_keypair_public_key"), 2); + let bytes = self.integers_to_bytevec(temp_v!(1), stub1); + + let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) { + Ok(kp) => { kp } + _ => { self.fail = true; return Ok(()); } + }; + + let complete_string = { + let buffer = String::from_iter(key_pair.public_key().as_ref().iter().map(|b| *b as char)); + self.heap.put_complete_string(&buffer) + }; + + self.unify(self[temp_v!(2)], complete_string); + } + &SystemClauseType::Ed25519Sign => { + let stub1 = MachineError::functor_stub(clause_name!("ed25519_sign"), 4); + let key = self.integers_to_bytevec(temp_v!(1), stub1); + let stub2 = MachineError::functor_stub(clause_name!("ed25519_sign"), 4); + let data = self.integers_to_bytevec(temp_v!(2), stub2); + + let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { + Ok(kp) => { kp } + _ => { self.fail = true; return Ok(()); } + }; + + let sig = key_pair.sign(&data); + + 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!(3)], sig_list); + } + &SystemClauseType::Ed25519Verify => { + let stub1 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4); + let key = self.integers_to_bytevec(temp_v!(1), stub1); + let stub2 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4); + let data = self.integers_to_bytevec(temp_v!(2), stub2); + let stub3 = MachineError::functor_stub(clause_name!("ed25519_verify"), 4); + let signature = self.integers_to_bytevec(temp_v!(3), stub3); + + let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key); + match peer_public_key.verify(&data, &signature) { + Ok(_) => { } + _ => { self.fail = true; return Ok(()); } + } + } }; return_from_clause!(self.last_call, self) @@ -5406,17 +5552,3 @@ impl hkdf::KeyType for MyKey { self.0 } } - -struct OneNonceSequence(Option); - -impl OneNonceSequence { - fn new(nonce: aead::Nonce) -> Self { - Self(Some(nonce)) - } -} - -impl aead::NonceSequence for OneNonceSequence { - fn advance(&mut self) -> Result { - self.0.take().ok_or(error::Unspecified) - } -}