crypto.pl -- Cryptography and authentication library
This library provides bindings to functionality of OpenSSL that is related to cryptography and authentication, not necessarily involving connections, sockets or streams.
The hash functionality of this library subsumes and extends that of
library(sha)
, library(hash_stream)
and library(md5)
by providing a
unified interface to all available digest algorithms.
The underlying OpenSSL library (libcrypto
) is dynamically loaded if
either library(crypto)
or library(ssl)
are loaded. Therefore, if
your application uses library(ssl)
, you can use library(crypto)
for
hashing without increasing the memory footprint of your application. In
other cases, the specialised hashing libraries are more lightweight but
less general alternatives to library(crypto)
.
- crypto_n_random_bytes(+N, -Bytes) is det
- Bytes is unified with a list of N cryptographically secure
pseudo-random bytes. Each byte is an integer between 0 and 255. If
the internal pseudo-random number generator (PRNG) has not been
seeded with enough entropy to ensure an unpredictable byte
sequence, an exception is thrown.
One way to relate such a list of bytes to an integer is to use CLP(FD) constraints as follows:
:- use_module(library(clpfd)). bytes_integer(Bs, N) :- foldl(pow, Bs, 0-0, N-_). pow(B, N0-I0, N-I) :- B in 0..255, N #= N0 + B*256^I0, I #= I0 + 1.
With this definition, you can generate a random 256-bit integer from a list of 32 random bytes:
?- crypto_n_random_bytes(32, Bs), bytes_integer(Bs, I). Bs = [98, 9, 35, 100, 126, 174, 48, 176, 246|...], I = 109798276762338328820827...(53 digits omitted).
The above relation also works in the other direction, letting you translate an integer to a list of bytes. In addition, you can use hex_bytes/2 to convert bytes to tokens that can be easily exchanged in your applications. This also works if you have compiled SWI-Prolog without support for large integers.
- crypto_data_hash(+Data, -Hash, +Options) is det
- Hash is the hash of Data. The conversion is controlled
by Options:
- algorithm(+Algorithm)
- One of
md5
(insecure),sha1
(insecure),ripemd160
,sha224
,sha256
,sha384
,sha512
,sha3_224
,sha3_256
,sha3_384
,sha3_512
,blake2s256
orblake2b512
. The BLAKE digest algorithms require OpenSSL 1.1.0 or greater, and the SHA-3 algorithms require OpenSSL 1.1.1 or greater. The default is a cryptographically secure algorithm. If you specify a variable, then that variable is unified with the algorithm that was used. - encoding(+Encoding)
- If Data is a sequence of character codes, this must be
translated into a sequence of bytes, because that is what
the hashing requires. The default encoding is
utf8
. The other meaningful value isoctet
, claiming that Data contains raw bytes. - hmac(+Key)
- If this option is specified, a hash-based message authentication code (HMAC) is computed, using the specified Key which is either an atom, string or list of bytes. Any of the available digest algorithms can be used with this option. The cryptographic strength of the HMAC depends on that of the chosen algorithm and also on the key. This option requires OpenSSL 1.1.0 or greater.
- crypto_file_hash(+File, -Hash, +Options) is det
- True if Hash is the hash of the content of File. For Options, see crypto_data_hash/3.
- crypto_context_new(-Context, +Options) is det
- Context is unified with the empty context, taking into account Options. The context can be used in crypto_data_context/3. For Options, see crypto_data_hash/3.
- crypto_data_context(+Data, +Context0, -Context) is det
- Context0 is an existing computation context, and Context is the
new context after hashing Data in addition to the previously
hashed data. Context0 may be produced by a prior invocation of
either crypto_context_new/2 or crypto_data_context/3 itself.
This predicate allows a hash to be computed in chunks, which may be important while working with Metalink (RFC 5854), BitTorrent or similar technologies, or simply with big files.
- crypto_context_hash(+Context, -Hash)
- Obtain the hash code of Context. Hash is an atom representing the hash code that is associated with the current state of the computation context Context.
- crypto_open_hash_stream(+OrgStream, -HashStream, +Options) is det
- Open a filter stream on OrgStream that maintains a hash. The hash
can be retrieved at any time using crypto_stream_hash/2. Available
Options in addition to those of crypto_data_hash/3 are:
- close_parent(+Bool)
- If
true
(default), closing the filter stream also closes the original (parent) stream.
- crypto_stream_hash(+HashStream, -Hash) is det
- Unify Hash with a hash for the bytes sent to or read from HashStream. Note that the hash is computed on the stream buffers. If the stream is an output stream, it is first flushed and the Digest represents the hash at the current location. If the stream is an input stream the Digest represents the hash of the processed input including the already buffered data.
- crypto_password_hash(+Password, ?Hash) is semidet
- If Hash is instantiated, the predicate succeeds iff the hash
matches the given password. Otherwise, the call is equivalent to
crypto_password_hash(Password, Hash, [])
and computes a password-based hash using the default options. - crypto_password_hash(+Password, -Hash, +Options) is det
- Derive Hash based on Password. This predicate is similar to
crypto_data_hash/3 in that it derives a hash from given data.
However, it is tailored for the specific use case of
passwords. One essential distinction is that for this use case,
the derivation of a hash should be as slow as possible to
counteract brute-force attacks over possible passwords.
Another important distinction is that equal passwords must yield, with very high probability, different hashes. For this reason, cryptographically strong random numbers are automatically added to the password before a hash is derived.
Hash is unified with an atom that contains the computed hash and all parameters that were used, except for the password. Instead of storing passwords, store these hashes. Later, you can verify the validity of a password with crypto_password_hash/2, comparing the then entered password to the stored hash. If you need to export this atom, you should treat it as opaque ASCII data with up to 255 bytes of length. The maximal length may increase in the future.
Admissible options are:
- algorithm(+Algorithm)
- The algorithm to use. Currently, the only available algorithm
is
pbkdf2-sha512
, which is therefore also the default. - cost(+C)
- C is an integer, denoting the binary logarithm of the number of iterations used for the derivation of the hash. This means that the number of iterations is set to 2^C. Currently, the default is 17, and thus more than one hundred thousand iterations. You should set this option as high as your server and users can tolerate. The default is subject to change and will likely increase in the future or adapt to new algorithms.
- salt(+Salt)
- Use the given list of bytes as salt. By default, cryptographically secure random numbers are generated for this purpose. The default is intended to be secure, and constitutes the typical use case of this predicate.
Currently, PBKDF2 with SHA-512 is used as the hash derivation function, using 128 bits of salt. All default parameters, including the algorithm, are subject to change, and other algorithms will also become available in the future. Since computed hashes store all parameters that were used during their derivation, such changes will not affect the operation of existing deployments. Note though that new hashes will then be computed with the new default parameters.
- crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det
- Concentrate possibly dispersed entropy of Data and then expand it to
the desired length. Bytes is unified with a list of bytes of
length Length, and is suitable as input keying material and
initialization vectors to the symmetric encryption predicates.
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.
- info(+Info)
- Optional context and application specific information, specified as an atom, string or list of bytes. The default is the zero length atom ''.
- salt(+List)
- Optionally, a list of bytes that are used as salt. The default is all zeroes.
- encoding(+Atom)
- Either
utf8
(default) oroctet
, denoting the representation of Data as in crypto_data_hash/3.
The info/1 option can be used to generate multiple keys from a single master key, using for example values such as
key
andiv
, or the name of a file that is to be encrypted.This predicate requires OpenSSL 1.1.0 or greater.
- ecdsa_sign(+Key, +Data, -Signature, +Options)
- Create an ECDSA signature for Data with EC private key Key.
Among the most common cases is signing a hash that was created
with crypto_data_hash/3 or other predicates of this library. For
this reason, the default encoding (
hex
) assumes that Data is an atom, string, character list or code list representing the data in hexadecimal notation. See rsa_sign/4 for an example.Options:
- encoding(+Encoding)
- Encoding to use for Data. Default is
hex
. Alternatives areoctet
,utf8
andtext
.
- ecdsa_verify(+Key, +Data, +Signature, +Options) is semidet
- True iff Signature can be verified as the ECDSA signature for
Data, using the EC public key Key.
Options:
- encoding(+Encoding)
- Encoding to use for Data. Default is
hex
. Alternatives areoctet
,utf8
andtext
.
- hex_bytes(?Hex, ?List) is det
- Relation between a hexadecimal sequence and a list of bytes. Hex
is an atom, string, list of characters or list of codes in
hexadecimal encoding. This is the format that is used by
crypto_data_hash/3 and related predicates to represent hashes.
Bytes is a list of integers between 0 and 255 that represent the
sequence as a list of bytes. At least one of the arguments must
be instantiated. When converting List to Hex, an atom is used
to represent the sequence of hexadecimal digits.
Example:
?- hex_bytes('501ACE', Bs). Bs = [80, 26, 206].
- rsa_private_decrypt(+PrivateKey, +CipherText, -PlainText, +Options) is det
- rsa_private_encrypt(+PrivateKey, +PlainText, -CipherText, +Options) is det
- rsa_public_decrypt(+PublicKey, +CipherText, -PlainText, +Options) is det
- rsa_public_encrypt(+PublicKey, +PlainText, -CipherText, +Options) is det
- RSA Public key encryption and decryption primitives. A string
can be safely communicated by first encrypting it and have the
peer decrypt it with the matching key and predicate. The length
of the string is limited by the key length.
Options:
- encoding(+Encoding)
- Encoding to use for Data. Default is
utf8
. Alternatives areutf8
andoctet
. - padding(+PaddingScheme)
- Padding scheme to use. Default is
pkcs1
. Alternatives arepkcs1_oaep
,sslv23
andnone
. Note thatnone
should only be used if you implement cryptographically sound padding modes in your application code as encrypting unpadded data with RSA is insecure
- rsa_sign(+Key, +Data, -Signature, +Options) is det
- Create an RSA signature for Data with private key Key. Options:
- type(+Type)
- SHA algorithm used to compute the digest. Values are
sha1
,sha224
,sha256
,sha384
orsha512
. The default is a cryptographically secure algorithm. If you specify a variable, then it is unified with the algorithm that was used. - encoding(+Encoding)
- Encoding to use for Data. Default is
hex
. Alternatives areoctet
,utf8
andtext
.
This predicate can be used to compute a
sha256WithRSAEncryption
signature as follows:sha256_with_rsa(PemKeyFile, Password, Data, Signature) :- Algorithm = sha256, read_key(PemKeyFile, Password, Key), crypto_data_hash(Data, Hash, [algorithm(Algorithm), encoding(octet)]), rsa_sign(Key, Hash, Signature, [type(Algorithm)]). read_key(File, Password, Key) :- setup_call_cleanup( open(File, read, In, [type(binary)]), load_private_key(In, Password, Key), close(In)).
Note that a hash that is computed by crypto_data_hash/3 can be directly used in rsa_sign/4 as well as ecdsa_sign/4.
- rsa_verify(+Key, +Data, +Signature, +Options) is semidet
- Verify an RSA signature for Data with public key Key.
Options:
- type(+Type)
- SHA algorithm used to compute the digest. Values are
sha1
,sha224
,sha256
,sha384
orsha512
. The default is the same as for rsa_sign/4. This option must match the algorithm that was used for signing. When operating with different parties, the used algorithm must be communicated over an authenticated channel. - encoding(+Encoding)
- Encoding to use for Data. Default is
hex
. Alternatives areoctet
,utf8
andtext
.
- crypto_data_decrypt(+CipherText, +Algorithm, +Key, +IV, -PlainText, +Options)
- Decrypt the given CipherText, using the symmetric algorithm
Algorithm, key Key, and initialization vector IV, to give PlainText.
CipherText must be a string, atom or list of codes or characters,
and PlainText is created as a string. Key and IV are typically
lists of bytes, though atoms and strings are also permitted.
Algorithm must be an algorithm which your copy of OpenSSL knows. See
crypto_data_encrypt/6 for an example.
- encoding(+Encoding)
- Encoding to use for CipherText. Default is
utf8
. Alternatives areutf8
andoctet
. - padding(+PaddingScheme)
- For block ciphers, the padding scheme to use. Default is
block
. You can disable padding by supplyingnone
here. - tag(+Tag)
- For authenticated encryption schemes, the tag must be specified as a list of bytes exactly as they were generated upon encryption. This option requires OpenSSL 1.1.0 or greater.
- min_tag_length(+Length)
- If the tag length is smaller than 16, this option must be used to permit such shorter tags. This is used as a safeguard against truncation attacks, where an attacker provides a short tag that is easier to guess.
- crypto_data_encrypt(+PlainText, +Algorithm, +Key, +IV, -CipherText, +Options)
- Encrypt the given PlainText, using the symmetric algorithm
Algorithm, key Key, and initialization vector (or nonce) IV, to give
CipherText.
PlainText must be a string, atom or list of codes or characters, and CipherText is created as a string. Key and IV are typically lists of bytes, though atoms and strings are also permitted. Algorithm must be an algorithm which your copy of OpenSSL knows about.
Keys and IVs can be chosen at random (using for example crypto_n_random_bytes/2) or derived from input keying material (IKM) using for example crypto_data_hkdf/4. This input is often a shared secret, such as a negotiated point on an elliptic curve, or the hash that was computed from a password via crypto_password_hash/3 with a freshly generated and specified salt.
Reusing the same combination of Key and IV typically leaks at least some information about the plaintext. For example, identical plaintexts will then correspond to identical ciphertexts. For some algorithms, reusing an IV with the same Key has disastrous results and can cause the loss of all properties that are otherwise guaranteed. Especially in such cases, an IV is also called a nonce (number used once). If an IV is not needed for your algorithm (such as
'aes-128-ecb'
) then any value can be provided as it will be ignored by the underlying implementation. Note that such algorithms do not provide semantic security and are thus insecure. You should use stronger algorithms instead.It is safe to store and transfer the used initialization vector (or nonce) in plain text, but the key must be kept secret.
Commonly used algorithms include:
'chacha20-poly1305'
- A powerful and efficient authenticated encryption scheme, providing secrecy and at the same time reliable protection against undetected modifications of the encrypted data. This is a very good choice for virtually all use cases. It is a stream cipher and can encrypt data of any length up to 256 GB. Further, the encrypted data has exactly the same length as the original, and no padding is used. It requires OpenSSL 1.1.0 or greater. See below for an example.
'aes-128-gcm'
- Also an authenticated encryption scheme. It uses a 128-bit (i.e., 16 bytes) key and a 96-bit (i.e., 12 bytes) nonce. It requires OpenSSL 1.1.0 or greater.
'aes-128-cbc'
- A block cipher that provides secrecy, but does not protect against unintended modifications of the cipher text. This algorithm uses 128-bit (16 bytes) keys and initialization vectors. It works with all supported versions of OpenSSL. If possible, consider using an authenticated encryption scheme instead.
Options:
- encoding(+Encoding)
- Encoding to use for PlainText. Default is
utf8
. Alternatives areutf8
andoctet
. - padding(+PaddingScheme)
- For block ciphers, the padding scheme to use. Default is
block
. You can disable padding by supplyingnone
here. If padding is disabled for block ciphers, then the length of the ciphertext must be a multiple of the block size. - tag(-List)
- For authenticated encryption schemes, List is unified with a list of bytes holding the tag. This tag must be provided for decryption. Authenticated encryption requires OpenSSL 1.1.0 or greater.
- tag_length(+Length)
- For authenticated encryption schemes, the desired length of the tag, specified as the number of bytes. The default is 16. Smaller numbers are not recommended.
For example, with OpenSSL 1.1.0 and greater, we can use 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, respectively:
?- Algorithm = 'chacha20-poly1305', crypto_n_random_bytes(32, Key), crypto_n_random_bytes(12, IV), crypto_data_encrypt("this is some input", Algorithm, Key, IV, CipherText, [tag(Tag)]), crypto_data_decrypt(CipherText, Algorithm, Key, IV, RecoveredText, [tag(Tag)]). Algorithm = 'chacha20-poly1305', Key = [65, 147, 140, 197, 27, 60, 198, 50, 218|...], IV = [253, 232, 174, 84, 168, 208, 218, 168, 228|...], CipherText = <binary string>, Tag = [248, 220, 46, 62, 255, 9, 178, 130, 250|...], RecoveredText = "this is some input".
In this example, we use crypto_n_random_bytes/2 to generate a key and nonce from cryptographically secure random numbers. For repeated applications, you must ensure that a nonce is only used once together with the same key. Note that for authenticated encryption schemes, the tag that was computed during encryption is necessary for decryption. It is safe to store and transfer the tag in plain text.
- crypto_modular_inverse(+X, +M, -Y) is det
- Compute the modular multiplicative inverse of the integer X. Y is unified with an integer such that X*Y is congruent to 1 modulo M.
- crypto_generate_prime(+N, -P, +Options) is det
- Generate a prime P with at least N bits. Options is a list of options.
Currently, the only supported option is:
- safe(Boolean)
- If Boolean is
true
(default isfalse
), then a safe prime is generated. This means that P is of the form 2*Q + 1 where Q is also prime.
- crypto_is_prime(+P, +Options) is semidet
- True iff P passes a probabilistic primality test. Options is a
list of options. Currently, the only supported option is:
- iterations(N)
- N is the number of iterations that are performed. If this option is not specified, a number of iterations is used such that the probability of a false positive is at most 2^(-80).
- crypto_name_curve(+Name, -Curve) is det
- Obtain a handle for a named elliptic curve. Name is an atom, and
Curve is unified with an opaque object that represents the curve.
Currently, only elliptic curves over prime fields are
supported. Examples of such curves are
prime256v1
andsecp256k1
.If you have OpenSSL installed, you can get a list of supported curves via:
$ openssl ecparam -list_curves
- crypto_curve_order(+Curve, -Order) is det
- Obtain the order of an elliptic curve. Order is an integer, denoting how many points on the curve can be reached by multiplying the curve's generator with a scalar.
- crypto_curve_generator(+Curve, -Point) is det
- Point is the generator of the elliptic curve Curve.
- crypto_curve_scalar_mult(+Curve, +N, +Point, -R) is det
- R is the result of N times Point on the elliptic curve Curve. N must be an integer, and Point must be a point on the curve.