Merge pull request #552 from mthom/master

Scryer Dockerfile
This commit is contained in:
Mark Thom
2020-05-22 16:04:03 -03:00
committed by GitHub
18 changed files with 499 additions and 256 deletions

View File

@@ -1,6 +0,0 @@
target
Dockerfile
README.md
.git
.gitignore
.gitmodules

55
Cargo.lock generated
View File

@@ -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"

View File

@@ -1,6 +1,6 @@
[package]
name = "scryer-prolog"
version = "0.8.122"
version = "0.8.123"
authors = ["Mark Thom <markjordanthom@gmail.com>"]
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"

View File

@@ -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"]

View File

@@ -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&nbsp;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&nbsp;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&nbsp;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

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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.

View File

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

View File

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

View File

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

View File

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

View File

@@ -189,6 +189,10 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
#[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<T: RawBlockTraits> HeapTemplate<T> {
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]

View File

@@ -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)
};

View File

@@ -37,8 +37,8 @@ fn scan_for_terminator<Iter: Iterator<Item = char>>(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::<u8>(),
);
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::<u8>(),
@@ -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]

View File

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

View File

@@ -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<u8> = 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<OneNonceSequence> = 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<OneNonceSequence> = 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<usize> {
self.0
}
}
struct OneNonceSequence(Option<aead::Nonce>);
impl OneNonceSequence {
fn new(nonce: aead::Nonce) -> Self {
Self(Some(nonce))
}
}
impl aead::NonceSequence for OneNonceSequence {
fn advance(&mut self) -> Result<aead::Nonce, error::Unspecified> {
self.0.take().ok_or(error::Unspecified)
}
}