CRYPTO_SIGN(3MONOCYPHER) | 3MONOCYPHER | CRYPTO_SIGN(3MONOCYPHER) |

# NAME

public key signatures```
#include
<monocypher.h>
```

`void`

`crypto_sign_public_key`

(`uint8_t
public_key[32]`, `const uint8_t
secret_key[32]`);

`void`

`crypto_sign`

(`uint8_t
signature[64]`, `const uint8_t secret_key[32]`,
`const uint8_t public_key[32]`, `const
uint8_t *message`, `size_t message_size`);

`int`

`crypto_check`

(`const uint8_t
signature[64]`, `const uint8_t public_key[32]`,
`const uint8_t *message`, `size_t
message_size`);

# DESCRIPTION

`crypto_sign`

()
and `crypto_check`

() provide EdDSA public key
signatures and verification.

The arguments are:

`signature`- The signature.
`secret_key`- A 32-byte random number known only to you. See intro() about random number generation (use your operating system's random number generator). Do not use the same private key for both signatures and key exchanges. The public keys are different and revealing both may leak information.
`public_key`- The public key, generated from
`secret_key`with`crypto_sign_public_key`

(). `message`- The message to sign.
`message_size`- Length of
`message`, in bytes.

`signature` and `message`
may overlap.

`crypto_sign_public_key`

()
computes the public key of the specified secret key.

`crypto_sign`

()
signs a message with `secret_key`. The public key is
optional and will be recomputed if not provided. This recomputation doubles
the execution time.
**Never sign a
message with the wrong public key**. Doing so would expose the private
key. Either provide `NULL`

as the
`public_key` or store the private and public keys
together as a single unit.

`crypto_check`

()
checks that a given signature is genuine. Meaning, only someone who had the
private key could have signed the message.
**It does not run in
constant time**. It does not have to in most threat models because
nothing is secret: everyone knows the public key, and the signature and
message are rarely secret. If the message needs to be secret, use a key
exchange protocol involving
crypto_x25519() and then
crypto_lock_aead()
instead.

An incremental interface is available; see crypto_sign_init_first_pass().

# RETURN VALUES

`crypto_sign_public_key`

() and
`crypto_sign`

() return nothing.

`crypto_check`

() returns 0 for legitimate
messages and -1 for forgeries.

# EXAMPLES

The following examples assume the existence of
`arc4random_buf`

(), which fills the given buffer with
cryptographically secure random bytes. If
`arc4random_buf`

() does not exist on your system, see
intro() for advice about how to
generate cryptographically secure random bytes.

Generate a public key from a random secret key:

uint8_t sk[32]; /* Random secret key */ uint8_t pk[32]; /* Matching public key */ arc4random_buf(sk, 32); crypto_sign_public_key(pk, sk); /* Wipe the secret key if it is no longer needed */ crypto_wipe(sk, 32);

Sign a message:

uint8_t sk [32]; /* Secret key from above */ const uint8_t message [11] = "Lorem ipsu"; /* Message to sign */ uint8_t signature[64]; crypto_sign(signature, sk, NULL, 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_check(signature, pk, message, 10)) { /* Message is corrupted, abort processing */ } else { /* Message is genuine */ }

To avoid recomputing the public key with each signature, store it next to the private key (“composite private key”). Make sure you treat that key pair as a single unit:

uint8_t sk[64]; /* Combined secret key */ uint8_t pk[32]; /* Public key */ arc4random_buf(sk, 32); /* Secret half */ crypto_sign_public_key(sk + 32, sk); /* Public half */ memcpy(pk, sk + 32, 32); /* Copy public key */ /* Wipe the secret key if it is no longer needed */ crypto_wipe(sk, 64);

That way signing can use the composite private key alone:

uint8_t sk [64]; /* Combined secret key from above */ const uint8_t message [11] = "Lorem ipsu"; /* Message to sign */ uint8_t signature[64]; crypto_sign(signature, sk, sk + 32, message, 10); /* Wipe the secret key if it is no longer needed */ crypto_wipe(sk, 64);

It is also possible to implement a safer API that provides a combined key abstraction:

void eddsa_sign_key_pair(uint8_t secret_key[64], uint8_t public_key[32], uint8_t seed[32]) { /* Copy seed to secret key then wipes the seed. */ /* Accounts for buffer overlaps. */ uint8_t sk[32]; memcpy(sk , seed, 32); crypto_wipe(seed, 32); memcpy(secret_key, sk , 32); crypto_wipe(sk , 32); crypto_sign_public_key(secret_key + 32, secret_key); memmove(public_key, secret_key + 32, 32); } void eddsa_sign(uint8_t signature [64], const uint8_t secret_key[64], const uint8_t *message, size_t message_size) { crypto_sign(signature, secret_key, secret_key + 32, message, message_size); }

With this API we can generate a key pair from a seed:

uint8_t seed[32]; /* Random seed */ uint8_t sk [64]; /* Combined secret key */ uint8_t pk [32]; /* Public key */ arc4random_buf(seed, 32); eddsa_sign_key_pair(sk, pk, seed); /* Wipe the secret key if it is no longer needed */ crypto_wipe(sk, 64);

Then we can sign with the composite private key:

uint8_t sk [64]; /* Combined secret key from above */ const uint8_t message [11] = "Lorem ipsu"; /* Message to sign */ uint8_t signature[64]; eddsa_sign(signature, sk, message, 10); /* Wipe the secret key if it is no longer needed */ crypto_wipe(sk, 64);

# SEE ALSO

# STANDARDS

These functions implement PureEdDSA with Curve25519 and BLAKE2b, as described in RFC 8032. This is the same as Ed25519, with BLAKE2b instead of SHA-512.

# HISTORY

The `crypto_sign`

(),
`crypto_check`

(), and
`crypto_sign_public_key`

() functions appeared in
Monocypher 0.2.

Starting with Monocypher 2.0.5, modified signatures abusing the
inherent signature malleability property of EdDSA now cause a non-zero
return value of `crypto_check`

(); in prior versions,
such signatures would be accepted.

**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.

# SECURITY CONSIDERATIONS

## Using the wrong public key exposes the private key

Performing two signatures on the same message with the same private key but with two different public keys instantly exposes the private key.

There are two ways to avoid this error. The easiest is to call
`crypto_sign`

() with a `NULL`

public key so it regenerates the correct one from the private key. This
method is slower, but in practice is often fast enough.

The fastest is to treat the private and public key as a single
unit: once generated, they must be stored together and treated as one
composite private key. When calling `crypto_sign`

(),
provide the public half of the composite private key. The public half can be
copied and and published separately, but the copy itself must never be used
for signatures.

## Signature malleability

Signature malleability is the ability of an attacker to produce a valid signature with knowledge of only an existing signature and the public key. That is, given a message, a signature and a public key, an attacker could generate a new signature for the same message that is valid under the same public key. Monocypher prevents signature malleability by only accepting signatures in canonical form.

On the other hand, EdDSA signatures are not unique like
cryptographic hashes. The signing procedure is deterministic by
specification and `crypto_sign`

() follows this
specification. However, someone with the private key can generate
arbitrarily many valid, canonical, and different signatures of the same
message. Because of this, never assume that signatures are unique.

## Fault injection and power analysis

Fault 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. If attackers are expected to have such access and have the relevant equipment, you could try using the incremental interface provided by crypto_sign_init_first_pass() to mitigate the side-channel attacks. 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.

July 9, 2022 | Debian |