CRYPTO_ELLIGATOR_MAP(3MONOCYPHER)

πŸ“¦ version 4.0.3, in nixos 26.05, nixpkgs unstable

πŸ’œ 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 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_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() returns 0 on success and -1 if the given curve argument is unsuitable for hiding.
  • crypto_elligator_map() and crypto_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