![]() |
![]()
| ![]() |
![]()
NAME
SYNOPSIS
void
int
void
void
void
void
void
void
int
DESCRIPTIONHigh level API
signature and message may overlap.
Conversion to X25519
Low-level building blocks
These functions are dangerous, using them directly allows many kinds of catastrophic misuse. The following descriptions are kept concise and technical on purpose. If you do not understand them, do not not use those functions.
It then returns 0 if all checks hold, -1 otherwise. Note that A and R are allowed to have low order, and their encoding is allowed to be non-canonical. This function does not run in constant time, do not use it with secret inputs. RETURN VALUESHigh level API
Conversion to X25519
Low-level building blocks
EXAMPLESThe following examples assume the existence of
Generate a key pair: uint8_t seed[32]; /* Random seed */ uint8_t sk [64]; /* secret key */ uint8_t pk [32]; /* Matching public key */ arc4random_buf(seed, 32); crypto_eddsa_key_pair(sk, pk, seed); /* Wipe the secret key if it is no longer needed */ /* The seed is wiped automatically */ crypto_wipe(sk, 32); Sign a message: uint8_t sk [64]; /* Secret key from above */ const uint8_t message [11] = "Lorem ipsu"; /* Message to sign */ uint8_t signature[64]; crypto_eddsa_sign(signature, sk, message, 10); /* Wipe the secret key if it is no longer needed */ crypto_wipe(sk, 32); Check the above: const uint8_t pk [32]; /* Their public key */ const uint8_t message [11] = "Lorem ipsu"; /* Signed message */ const uint8_t signature[64]; /* Signature to check */ if (crypto_eddsa_check(signature, pk, message, 10)) { /* Message is corrupted, do not trust it */ } else { /* Message is genuine */ } Implement XEdDSA (signatures with X25519 keys normally used for key exchange) with the low-level building blocks #include <monocypher.h> #include <monocypher-ed25519.h> #include <string.h> void xed25519_sign(uint8_t signature[64], const uint8_t secret_key[32], const uint8_t random[64], const uint8_t *message, size_t message_size) { static const uint8_t zero [32] = {0}; static const uint8_t minus_1[32] = { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, }; static const uint8_t prefix[32] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /* Key pair (a, A) */ uint8_t A[32]; /* XEdDSA public key */ uint8_t a[32]; /* XEdDSA private key */ crypto_eddsa_trim_scalar(a, secret_key); crypto_eddsa_scalarbase(A, a); int is_negative = A[31] & 0x80; /* Retrieve sign bit */ A[31] &= 0x7f; /* Clear sign bit */ if (is_negative) { /* a = -a */ crypto_eddsa_mul_add(a, a, minus_1, zero); } /* Secret nonce r */ uint8_t r[64]; crypto_sha512_ctx ctx; crypto_sha512_init (&ctx); crypto_sha512_update(&ctx, prefix , 32); crypto_sha512_update(&ctx, a , 32); crypto_sha512_update(&ctx, message, message_size); crypto_sha512_update(&ctx, random , 64); crypto_sha512_final (&ctx, r); crypto_eddsa_reduce(r, r); /* First half of the signature R */ uint8_t R[32]; crypto_eddsa_scalarbase(R, r); /* hash(R || A || M) */ uint8_t H[64]; crypto_sha512_init (&ctx); crypto_sha512_update(&ctx, R , 32); crypto_sha512_update(&ctx, A , 32); crypto_sha512_update(&ctx, message, message_size); crypto_sha512_final (&ctx, H); crypto_eddsa_reduce(H, H); /* Signature */ memcpy(signature, R, 32); crypto_eddsa_mul_add(signature + 32, a, H, r); /* Wipe secrets (A, R, and H are not secret) */ crypto_wipe(a, 32); crypto_wipe(r, 32); } int xed25519_verify(const uint8_t signature[64], const uint8_t public_key[32], const uint8_t *message, size_t message_size) { /* Convert X25519 key to EdDSA */ uint8_t A[32]; crypto_x25519_to_eddsa(A, public_key); /* hash(R || A || M) */ uint8_t H[64]; crypto_sha512_ctx ctx; crypto_sha512_init (&ctx); crypto_sha512_update(&ctx, signature, 32); crypto_sha512_update(&ctx, A , 32); crypto_sha512_update(&ctx, message , message_size); crypto_sha512_final (&ctx, H); crypto_eddsa_reduce(H, H); /* Check signature */ return crypto_eddsa_check_equation(signature, A, H); } SEE ALSOcrypto_blake2b(3monocypher), crypto_x25519(3monocypher), crypto_aead_lock(3monocypher), intro(3monocypher) STANDARDS
HISTORYThe Starting with Monocypher 2.0.5, modified signatures abusing the
inherent signature malleability property of EdDSA now cause a non-zero
return value of A critical security vulnerability that caused all-zero signatures to be accepted was introduced in Monocypher 0.3; it was fixed in Monocypher 1.1.1 and 2.0.4. In Monocypher 4.0.0
CAVEATSMonocypher 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. SECURITY CONSIDERATIONSSignature malleabilitySignature malleability is the ability of an attacker to produce a valid signature with knowledge of only an existing signature and the public key. Monocypher prevents that by checking the encoding of the signature, and guarantees that generating new signatures requires the private key. On the other hand, EdDSA signatures are not unique like
cryptographic hashes. The signing procedure is deterministic by
specification and Fault injection and power analysisFault injection (also known as glitching) and power analysis may be used to manipulate the resulting signature and recover the secret key in some cases. This requires hardware access. We can try to mitigate this attack by prefixing all hashes a random data block, in a construction similar to Ed25519ctx. Note that there may still be other power-related side channels (such as if the CPU leaks information when an operation overflows a register) that must be considered.
|