CRYPTO_ELLIGATOR_MAP(3MONOCYPHER)
π CRYPTO_ELLIGATOR_MAP π
okay so bestie, this is the 3MONOCYPHER manual page for crypto_elligator_map, crypto_elligator_rev and crypto_elligator_key_pair β aka the hiding of X25519 public keys trio π«Ά. advanced territory, but you're gonna slay it.
β¨ SYNOPSIS β¨
#include <monocypher.h>
void
crypto_elligator_map(uint8_t curve[32], const uint8_t hidden[32]);
int
crypto_elligator_rev(uint8_t hidden[32], const uint8_t curve[32],
uint8_t tweak);
void
crypto_elligator_key_pair(uint8_t hidden[32], uint8_t secret_key[32],
uint8_t seed[32]);
πΈ DESCRIPTION πΈ
So these functions let you obfuscate X25519 public keys by making them look effectively indistinguishable from random noise β¨. it's giving "nothing to see here". this is a whole vibe for key exchange protocols that need to be indistinguishable from randomness β think padded uniform random blobs (PURBs) π.
heads up bestie: these are 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 Framework. periodt.
this is an advanced feature fr fr. unless you're implementing a protocol that requires indistinguishability of all communications from random noise, consider crypto_x25519(3monocypher) instead π. both this family of functions and crypto_x25519(3monocypher) should be used as a building block to implement a key exchange protocol.
to actually understand what these functions do, it's important to note that a βpublic keyβ in this context refers to a point on Curve25519 π
. which also means these functions are not compatible with crypto_eddsa_sign(3monocypher) and related functions. not the same vibe, don't mix them.
crypto_elligator_rev() takes a public key curve and a tweak, hiding the public key so it's effectively indistinguishable from random noise π¦. and ngl there's a catch: 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, because not all curve points are capable of being hidden π. in that case, crypto_elligator_rev() must be tried again with a new key pair, though tweak does not need to be changed. on average, two attempts are needed. once a suitable public key has been found, crypto_elligator_rev() always succeeds for it π₯. given the same values for tweak and curve, crypto_elligator_rev() yields the same output value hidden.
crypto_elligator_map() performs the inverse operation: it decodes a hidden point to a curve point on Curve25519 π.
crypto_elligator_key_pair() is a convenience function that generates a secret key and its corresponding public key β 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_elligator_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 π
curveβ a point on the curve which is a Curve25519 public key generated with eithercrypto_x25519_dirty_fast(3monocypher)orcrypto_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 givenseed.seedβ a 32-byte random number from which to derive a key pair. seeintro(3monocypher)for advice about generating random bytes (use the operating system's random number generator). theseedis wiped automatically π§Ό.tweakβ a 1-byte random number, which influences the final output ofcrypto_elligator_rev().
and the cute part: the hidden and curve arguments may overlap or point at the same buffer π. in-place is fine, bestie.
β RETURN VALUES β
crypto_elligator_rev()returns0on success and-1if the givencurveargument is unsuitable for hiding.crypto_elligator_map()andcrypto_elligator_key_pair()return nothing. they cannot fail π.
π¦ EXAMPLES π¦
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_elligator_rev(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_elligator_map(their_pk, hidden_pk);
crypto_x25519(shared_key, your_sk, their_pk);
/* Wipe secrets if they are no longer needed */
crypto_wipe(your_sk, 32);
π SEE ALSO π
crypto_x25519(3monocypher), crypto_x25519_dirty_small(3monocypher), intro(3monocypher)
π STANDARDS π
these functions implement the Elligator 2 mapping for Curve25519. important: this mapping is incompatible with both the hash-to-curve Internet draft and the implementation of Elligator 2 in libsodium π ββοΈ. don't assume they match, they do not.
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 π.
Monocypher's Elligator 2 representatives are encoded as little-endian 254-bit numbers. the two most significant bits (254 and 255) are not used. crypto_elligator_map() ignores them, and crypto_elligator_rev() sets them at random π². the mapping uses 2 as the non-square Z, and [0..2^254-10] as the set of non-negative field elements. see https://elligator.org/map for more details.
π°οΈ HISTORY π°οΈ
the crypto_curve_to_hidden(), crypto_hidden_to_curve(), and crypto_hidden_key_pair() functions first appeared in Monocypher 3.1.0. in Monocypher 4.1.0, they were renamed crypto_elligator_rev(), crypto_elligator_map(), and crypto_elligator_key_pair() respectively π«. glow-up, honestly.
π CAVEATS π
Monocypher does not perform any input validation. any deviation from the specified input and output length ranges results in undefined behaviour π. make sure your inputs are correct, bestie, like actually.
no cap: no validation means no safety net. undefined behaviour is not a vibe.
π SECURITY CONSIDERATIONS π
the secret keys for the public keys fed into crypto_elligator_rev() 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. yes, the retry count is a side channel, and it's messy.
these functions help build highly difficult-to-analyse protocols but are insufficient by themselves. other metadata β such as the number 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. hide the vibes, not just the key π .
and that's the whole page, bestie π«Ά β this was the CRYPTO_ELLIGATOR_MAP(3MONOCYPHER) man page from the Debian build, dated March 6, 2023. go forth and hide some points β¨ππ
π¦ where this page comes from
- nixos 26.05: monocypher 4.0.2
- nixpkgs unstable: monocypher 4.0.3
- π the page's license: BSD-2-Clause OR CC0-1.0
- Copyright (c) 2020, 2022 Fabio Scotoni
- Copyright (c) 2023 Loup Vaillant
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms