crypto_curve_to_hidden,
crypto_hidden_to_curve,
crypto_hidden_key_pair —
hiding of X25519 public keys
#include
<monocypher.h>
int
crypto_curve_to_hidden(
uint8_t
hidden[32],
const uint8_t curve[32],
uint8_t tweak);
void
crypto_hidden_to_curve(
uint8_t
curve[32],
const uint8_t hidden[32]);
void
crypto_hidden_key_pair(
uint8_t
hidden[32],
uint8_t secret_key[32],
uint8_t seed[32]);
These functions allow obfuscating X25519 public keys by making them appear
effectively indistinguishable from random noise. This is of interest for key
exchange protocols that require indistinguishability from randomness, such as
padded uniform random blobs (PURBs). They are intended for ephemeral
(short-lived, possibly just one-time) X25519 keys, not for long-term public
keys. After an initial key exchange involving hidden keys, subsequent key
exchange messages should be encrypted instead; see, for example, the Noise
protocol. This is an
advanced feature –
unless you are implementing an protocol that requires indistinguishability of
all communications from random noise, consider
crypto_key_exchange_public_key(3monocypher)
instead.
For understanding what these functions do, it is important to note that a
“public key” in this context refers to a
point on Curve25519. This also means that these
functions are not compatible with
crypto_sign(3monocypher)
and related functions.
crypto_curve_to_hidden() takes a public key
curve and a
tweak, hiding the public key it so that it is
effectively indistinguishable from random noise. Note that only
crypto_x25519_dirty_fast(3monocypher)
or
crypto_x25519_dirty_small(3monocypher)
can generate a suitable public key; the
crypto_x25519(3monocypher)
function is insufficient. The
tweak must be
chosen at random. Even then, this operation
may
fail: Not all curve points are capable of being hidden. In this case,
crypto_curve_to_hidden() must be tried again with
a new key pair; the
tweak does not need to be
changed. On average, two attempts are needed. Once a suitable public key has
been found,
crypto_curve_to_hidden() always
succeeds for it. Given the same values for
tweak and
curve,
crypto_curve_to_hidden() yields the same output
value
hidden.
crypto_hidden_to_curve() performs the inverse
operation: It decodes a hidden point to a curve point on Curve25519.
crypto_hidden_key_pair() is a convenience function
that generates a secret key and its corresponding public key, which is
effectively indistinguishable from random noise, from a random seed.
The execution time of this function is
unpredictable because it may take many failures until a key pair could be
generated successfully.
crypto_hidden_key_pair()
uses
crypto_x25519_dirty_fast(3monocypher)
internally; if code size is an important concern, its functionality can be
replicated with
crypto_x25519_dirty_small(3monocypher)
instead.
The arguments are:
-
-
- curve
- A point on the curve, which is a Curve25519 public key
generated with either
crypto_x25519_dirty_fast(3monocypher)
or
crypto_x25519_dirty_small(3monocypher).
-
-
- hidden
- The hidden encoding of a point on the curve which is
effectively indistinguishable from random.
-
-
- secret_key
- The secret key that was generated from the given
seed.
-
-
- seed
- A 32-byte random number from which to derive a key pair.
See intro(3monocypher) for
advice about generating random bytes (use the operating system's random
number generator). The seed is wiped
automatically.
-
-
- tweak
- A 1-byte random number, which influences the final output
of crypto_curve_to_hidden().
The
hidden and
curve arguments may overlap or point at the
same buffer.
crypto_curve_to_hidden() returns 0 on success, -1
if the given
curve argument is unsuitable for
hiding.
crypto_hidden_to_curve() and
crypto_hidden_key_pair() return nothing; they
cannot fail.
Generate a key pair manually using
crypto_x25519_dirty_small(3monocypher)
instead of its fast variant:
uint8_t sk [32]; /* Secret key output */
uint8_t pk [32]; /* Hidden public key output */
uint8_t tweak; /* Random tweak input */
arc4random_buf(&tweak, 1);
for (;;) {
arc4random_buf(sk, 32);
crypto_x25519_dirty_small(pk, sk);
if (crypto_curve_to_hidden(pk, pk, tweak) == 0)
break;
}
/* Now save the secret key and send the hidden public key. */
Performing a key exchange with the other party's public key having been hidden:
uint8_t hidden_pk [32]; /* Their hidden public key */
uint8_t their_pk [32]; /* Their unhidden public key */
uint8_t your_sk [32]; /* Your secret key */
uint8_t shared_key[32]; /* Shared session key */
crypto_hidden_to_curve(their_pk, hidden_pk);
crypto_key_exchange(shared_key, your_sk, their_pk);
/* Wipe secrets if they are no longer needed */
crypto_wipe(your_sk, 32);
crypto_key_exchange(3monocypher),
crypto_x25519(3monocypher),
crypto_x25519_dirty_small(3monocypher),
intro(3monocypher)
These functions implement the Elligator 2 mapping for Curve25519. This mapping
is incompatible with both the hash-to-curve Internet draft and the
implementation of Elligator 2 in libsodium. Elligator 2 was described in:
Daniel J. Bernstein,
Mike Hamburg, Anna Krasnova,
and Tanja Lange, Elligator:
Elliptic-curve points indistinguishable from uniform random strings,
Association for Computing Machinery, CCS
'13: Proceedings of the 2013 ACM SIGSAC conference on Computer &
communications security, pp. 967–980,
2013.
The
crypto_curve_to_hidden(),
crypto_hidden_to_curve(), and
crypto_hidden_key_pair() functions first appeared
in Monocypher 3.1.0.
The secret keys for the public keys fed into
crypto_curve_to_hidden()
must be chosen randomly, rather than
deterministically. Otherwise, the timing information given by the required
number of retries also leaks information on the secret keys.
These functions
help build highly
difficult-to-analyze protocols, but are insufficient by themselves: Other
metadata, such as the amount of bytes sent in a packet or the size of the
32-byte random-looking string that represents the curve point itself, can be
very strong indicators of the use of cryptography. Consider using appropriate
padding algorithms, such as PADME, and obscure other metadata as much as
possible.