1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
|
## Introduction {#crypto-introduction}
This library provides bindings to functionality of OpenSSL that is
related to cryptography and authentication, not necessarily involving
connections, sockets or streams.
## Design principle: Secure default algorithms {#crypto-secure-defaults}
A basic design principle of this library is that its _default algorithms
are cryptographically secure_ at the time of this writing. We will
_change_ the default algorithms if an attack on them becomes known, and
replace them by new defaults that are deemed appropriate at that time.
This may mean, for example, that where `sha256` is currently the default
algorithm, `blake2s256` or some other algorithm may become the default
in the future.
To preserve interoperability and compatibility and at the same time
allow us to transparently update default algorithms of this library, the
following conventions are used:
1. If an explicit algorithm is specified as an option, then that
algorithm is used.
2. If _no_ algorithm is specified, then a cryptographically secure
algorithm is used.
3. If an option that normally specifies an algorithm is present,
and a _logical variable_ appears instead of a concrete algorithm,
then that variable is unified with the secure default value.
This allows application programmers to inspect _which_ algorithm was
actually used, and store it for later reference.
For example:
==
?- crypto_data_hash(test, Hash, [algorithm(A)]).
Hash = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
A = sha256.
==
This shows that at the time of this writing, `sha256` was deemed
sufficiently secure, and was used as default algorithm for hashing.
You therefore must not rely on _which_ concrete algorithm is being used
by default. However, you can rely on the fact that the default
algorithms are secure. In other words, if they are _not_ secure, then
this is a mistake in this library, and we ask you to please report such
a situation as an urgent security issue.
## Representing binary data {#crypto-binary-data}
In the context of this library, _bytes_ can be represented as lists of
integers between 0 and 255. Such lists can be converted to and from
_hexadecimal notation_ with the following bidirectional relation:
* [[hex_bytes/2]]
## Cryptographically secure random numbers {#crypto-random}
Almost all cryptographic applications require the availability of
numbers that are sufficiently unpredictable. Examples are the creation
of keys, nonces and salts. With this library, you can generate
cryptographically strong pseudo-random numbers for such use cases:
* [[crypto_n_random_bytes/2]]
## Hashes {#crypto-hash}
A **hash**, also called **digest**, is a way to verify the integrity of
data. In typical cases, a hash is significantly shorter than the data
itself, and already miniscule changes in the data lead to different
hashes.
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)`.
### Hashes of data and files {#crypto-hash-basic}
The most important predicates to compute hashes are:
* [[crypto_data_hash/3]]
* [[crypto_file_hash/3]]
### Hashes of passwords {#crypto-hash-password}
For the important case of deriving hashes from _passwords_, the
following specialised predicates are provided:
* [[crypto_password_hash/2]]
* [[crypto_password_hash/3]]
### HMAC-based key derivation function (HKDF) {#crypto-hkdf}
The following predicate implements the _Hashed Message Authentication
Code (HMAC)-based key derivation function_, abbreviated as HKDF. It
supports a wide range of applications and requirements by concentrating
possibly dispersed entropy of the input keying material and then
expanding it to the desired length. The number and lengths of the output
keys depend on the specific cryptographic algorithms for which the keys
are needed.
* [[crypto_data_hkdf/4]]
### Hashing incrementally {#crypto-hash-incremental}
The following predicates are provided for building hashes
_incrementally_. This works by first creating a **context** with
crypto_context_new/2, then using this context with crypto_data_context/3
to incrementally obtain further contexts, and finally extract the
resulting hash with crypto_context_hash/2.
* [[crypto_context_new/2]]
* [[crypto_data_context/3]]
* [[crypto_context_hash/2]]
The following hashing predicates work over _streams_:
* [[crypto_open_hash_stream/3]]
* [[crypto_stream_hash/2]]
## Digital signatures {#crypto-signatures}
A digital **signature** is a relation between a key and data that only
someone who knows the key can compute.
_Signing_ uses a _private_ key, and _verifying_ a signature uses the
corresponding _public_ key of the signing entity. This library supports
both RSA and ECDSA signatures. You can use load_private_key/3 and
load_public_key/2 to load keys from files and streams.
In typical cases, we use this mechanism to sign the _hash_ of data. See
[hashing](<#crypto-hash>). For this reason, the following predicates
work on the _hexadecimal_ representation of hashes that is also used by
crypto_data_hash/3 and related predicates.
Signatures are also represented in hexadecimal notation, and you can
use hex_bytes/2 to convert them to and from lists of bytes (integers).
### ECDSA {#crypto-ECDSA}
* [[ecdsa_sign/4]]
* [[ecdsa_verify/4]]
### RSA {#crypto-RSA}
* [[rsa_sign/4]]
* [[rsa_verify/4]]
## Asymmetric encryption and decryption {#crypto-asymmetric}
The following predicates provide _asymmetric_ RSA encryption and
decryption. This means that the key that is used for _encryption_ is
different from the one used to _decrypt_ the data:
* [[rsa_private_decrypt/4]]
## Symmetric encryption and decryption {#crypto-symmetric}
The following predicates provide _symmetric_ encryption and
decryption. This means that the _same_ key is used in both cases.
* [[crypto_data_encrypt/6]]
* [[crypto_data_decrypt/6]]
## Number theory {#crypto-numbertheory}
This library provides operations from number theory that frequently
arise in cryptographic applications, complementing the existing
built-ins and GMP bindings:
* [[crypto_modular_inverse/3]]
* [[crypto_generate_prime/3]]
* [[crypto_is_prime/2]]
## Elliptic curves {#crypto-ec}
This library provides functionality for reasoning over _elliptic
curves_. Elliptic curves are represented as opaque objects. You acquire
a handle for an elliptic curve via crypto_name_curve/2.
A _point_ on a curve is represented by the Prolog term =|point(X, Y)|=,
where `X` and `Y` are integers that represent the point's affine
coordinates.
The following predicates are provided for reasoning over elliptic
curves:
* [[crypto_name_curve/2]]
* [[crypto_curve_order/2]]
* [[crypto_curve_generator/2]]
* [[crypto_curve_scalar_mult/4]]
## Example: Establishing a shared secret {#crypto-shared-secret}
As one example that involves most predicates of this library, we explain
a way to establish a _shared secret_ over an insecure channel. We shall
use _elliptic curves_ for this purpose.
Suppose Alice wants to establish an encrypted connection with Bob. To
achieve this even over a channel that may be subject to eavesdrooping
and man-in-the-middle attacks, Bob performs the following steps:
1. Choose an elliptic curve `C`, using crypto_name_curve/2.
2. Pick a random integer _k_ such that _k_ is greater than 0 and
smaller than the order of `C`. This can be done using
crypto_curve_order/2 and crypto_n_random_bytes/2.
3. Use crypto_curve_generator/2 to obtain the generator `G` of `C`, and
use crypto_curve_scalar_mult/4 to compute the scalar product _k*G_.
We call this result `R`, denoting a point on the curve.
4. Sign `R` (using for example rsa_sign/4 or ecdsa_sign/4) and
send this to Alice.
This mechanism hinges on a way for Alice to establish the _authenticity_
of the signed message (using predicates like rsa_verify/4 and
ecdsa_verify/4), for example by means of a public key that was
previously exchanged or is signed by a trusted party in such a way that
Alice can be sufficiently certain that it belongs to Bob. However, none
of these steps require any encryption!
Alice in turn performs the following steps:
1. Create a random integer _j_ such that _j_ is greater than 0 and
smaller than the order of C. Alice can also use
crypto_curve_order/2 and crypto_n_random_bytes/2 for this.
2. Compute the scalar product _j*G_, where `G` is again the generator
of `C` as obtained via crypto_curve_generator/2.
3. Further, compute the scalar product _j*R_, which is a point on
the curve that we shall call Q. We can derive a _shared secret_
from `Q`, using for example crypto_data_hkdf/4, and encrypt any
message with it (using for example crypto_data_encrypt/6).
4. Send the point _j*G_ and the encrypted message to Bob.
Bob receives _j*G_ in plain text and can arrive at the same shared
secret by performing the calculation _k*(j*G)_, which is - by
associativity and commutativity of scalar multiplication - identical to
the point _j*(k*G)_, which is again Q from which the shared secret can
be derived, and the message can be decrypted with crypto_data_decrypt/6.
This method is known as Diffie-Hellman-Merkle key exchange over elliptic
curves, abbreviated as ECDH. It provides forward secrecy (FS): Even if
the private key that was used to establish the _authenticity_ of Bob is
later compromised, the encrypted messages cannot be decrypted with it.
A major attraction of using elliptic curves for this purpose is found in
the comparatively small key size that suffices to make any attacks
unrealistic as far as we currently know. In particular, given any point
on the curve, we currently have no efficient way to determine by which
scalar the generator was multiplied to obtain that point. The method
described above relies on the hardness of this so-called _elliptic curve
discrete logarithm problem_ (ECDLP). On the other hand, some of the
named curves have been suspected to be chosen in such a way that they
could be prone to attacks that are not publicly known.
As an alternative to ECDH, you can use the original DH key exchange
scheme, where the prime field GF(p) is used instead of an elliptic
curve, and _exponentiation_ of a suitable generator is used instead of
scalar multiplication. You can use crypto_generate_prime/3 to generate
a sufficiently large prime for this purpose.
|