Public-key signatures

Example (combined mode)

1

#define MESSAGE (const unsigned char *) "test"

2

#define MESSAGE_LEN 4

3

â€‹

4

unsigned char pk[crypto_sign_PUBLICKEYBYTES];

5

unsigned char sk[crypto_sign_SECRETKEYBYTES];

6

crypto_sign_keypair(pk, sk);

7

â€‹

8

unsigned char signed_message[crypto_sign_BYTES + MESSAGE_LEN];

9

unsigned long long signed_message_len;

10

â€‹

11

crypto_sign(signed_message, &signed_message_len,

12

MESSAGE, MESSAGE_LEN, sk);

13

â€‹

14

unsigned char unsigned_message[MESSAGE_LEN];

15

unsigned long long unsigned_message_len;

16

if (crypto_sign_open(unsigned_message, &unsigned_message_len,

17

signed_message, signed_message_len, pk) != 0) {

18

/* Incorrect signature! */

19

}

Copied!

Example (detached mode)

1

#define MESSAGE (const unsigned char *) "test"

2

#define MESSAGE_LEN 4

3

â€‹

4

unsigned char pk[crypto_sign_PUBLICKEYBYTES];

5

unsigned char sk[crypto_sign_SECRETKEYBYTES];

6

crypto_sign_keypair(pk, sk);

7

â€‹

8

unsigned char sig[crypto_sign_BYTES];

9

â€‹

10

crypto_sign_detached(sig, NULL, MESSAGE, MESSAGE_LEN, sk);

11

â€‹

12

if (crypto_sign_verify_detached(sig, MESSAGE, MESSAGE_LEN, pk) != 0) {

13

/* Incorrect signature! */

14

}

Copied!

Example (multi-part message)

1

#define MESSAGE_PART1 \

2

((const unsigned char *) "Arbitrary data to hash")

3

#define MESSAGE_PART1_LEN 22

4

â€‹

5

#define MESSAGE_PART2 \

6

((const unsigned char *) "is longer than expected")

7

#define MESSAGE_PART2_LEN 23

8

â€‹

9

unsigned char pk[crypto_sign_PUBLICKEYBYTES];

10

unsigned char sk[crypto_sign_SECRETKEYBYTES];

11

crypto_sign_keypair(pk, sk);

12

â€‹

13

crypto_sign_state state;

14

unsigned char sig[crypto_sign_BYTES];

15

â€‹

16

/* signature creation */

17

â€‹

18

crypto_sign_init(&state)

19

crypto_sign_update(&state, MESSAGE_PART1, MESSAGE_PART1_LEN);

20

crypto_sign_update(&state, MESSAGE_PART2, MESSAGE_PART2_LEN);

21

crypto_sign_final_create(&state, sig, NULL, sk);

22

â€‹

23

/* signature verification */

24

â€‹

25

crypto_sign_init(&state)

26

crypto_sign_update(&state, MESSAGE_PART1, MESSAGE_PART1_LEN);

27

crypto_sign_update(&state, MESSAGE_PART2, MESSAGE_PART2_LEN);

28

if (crypto_sign_final_verify(&state, sig, pk) != 0) {

29

/* message forged! */

30

}

Copied!

Purpose

In this system, a signer generates a key pair:

a secret key, that will be used to append a signature to any number of messages

a public key, that anybody can use to verify that the signature appended to a message was actually issued by the creator of the public key.

Verifiers need to already know and ultimately trust a public key before messages signed using it can be verified.

Warning: this is different from authenticated encryption. Appending a signature does not change the representation of the message itself.

Key pair generation

1

int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);

Copied!

The crypto_sign_keypair() function randomly generates a secret key and a corresponding public key. The public key is put into pk (crypto_sign_PUBLICKEYBYTES bytes) and the secret key into sk (crypto_sign_SECRETKEYBYTES bytes).

1

int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk,

2

const unsigned char *seed);

Copied!

Using crypto_sign_seed_keypair(), the key pair can also be deterministically derived from a single key seed (crypto_sign_SEEDBYTES bytes).

Combined mode

1

int crypto_sign(unsigned char *sm, unsigned long long *smlen_p,

2

const unsigned char *m, unsigned long long mlen,

3

const unsigned char *sk);

Copied!

The crypto_sign() function prepends a signature to a message m whose length is mlen bytes, using the secret key sk.

The signed message, which includes the signature + a plain copy of the message, is put into sm, and is crypto_sign_BYTES + mlen bytes long.

If smlen is not a NULL pointer, the actual length of the signed message is stored into smlen.

1

int crypto_sign_open(unsigned char *m, unsigned long long *mlen_p,

2

const unsigned char *sm, unsigned long long smlen,

3

const unsigned char *pk);

Copied!

The crypto_sign_open() function checks that the signed message sm whose length is smlen bytes has a valid signature for the public key pk.

If the signature is doesn't appear to be valid, the function returns -1.

On success, it puts the message with the signature removed into m, stores its length into mlen if mlen is not a NULL pointer, and returns 0.

Detached mode

In detached mode, the signature is stored without attaching a copy of the original message to it.

1

int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen_p,

2

const unsigned char *m, unsigned long long mlen,

3

const unsigned char *sk);

Copied!

The crypto_sign_detached() function signs the message m whose length is mlen bytes, using the secret key sk, and puts the signature into sig, which can be up to crypto_sign_BYTES bytes long.

The actual length of the signature is put into siglen if siglen is not NULL.

It is safe to ignore siglen and always consider a signature as crypto_sign_BYTES bytes long: shorter signatures will be transparently padded with zeros if necessary.

1

int crypto_sign_verify_detached(const unsigned char *sig,

2

const unsigned char *m,

3

unsigned long long mlen,

4

const unsigned char *pk);

Copied!

The crypto_sign_verify_detached() function verifies that sig is a valid signature for the message m whose length is mlen bytes, using the signer's public key pk.

It returns -1 if the signature fails verification, or 0 on success.

Multi-part messages

If the message doesn't fit in memory, it can be provided as a sequence of arbitrarily-sized chunks.

This will use the Ed25519ph signature system, that pre-hashes the message. In other words, what gets signed is not the message itself, but its image through a hash function.

If the message can fit in memory and can be supplied as a single chunk, the single-part API should be preferred.

Note: Ed25519ph(m) is intentionally not equivalent to Ed25519(SHA512(m)).

If, for some reason, you need to prehash the message yourself, use the multi-part crypto_generichash_*() APIs and sign the 512 bit output.

1

int crypto_sign_init(crypto_sign_state *state);

Copied!

The crypto_sign_init() function initializes the state state. This function must be called before the first crypto_sign_update() call.

1

int crypto_sign_update(crypto_sign_state *state,

2

const unsigned char *m, unsigned long long mlen);

Copied!

Add a new chunk m of length mlen bytes to the message that will eventually be signed.

After all parts have been supplied, one of the following functions can be called:

1

int crypto_sign_final_create(crypto_sign_state *state, unsigned char *sig,

2

unsigned long long *siglen_p,

3

const unsigned char *sk);

Copied!

The crypto_sign_final_create() function computes a signature for the previously supplied message, using the secret key sk and puts it into sig.

If siglen_p is not NULL, the length of the signature is stored at this address.

It is safe to ignore siglen and always consider a signature as crypto_sign_BYTES bytes long: shorter signatures will be transparently padded with zeros if necessary.

1

int crypto_sign_final_verify(crypto_sign_state *state, const unsigned char *sig,

2

const unsigned char *pk);

Copied!

The crypto_sign_final_verify() function verifies that sig is a valid signature for the message whose content has been previously supplied using crypto_update(), using the public key pk.

Extracting the seed and the public key from the secret key

The secret key actually includes the seed (either a random seed or the one given to crypto_sign_seed_keypair()) as well as the public key.

While the public key can always be derived from the seed, the precomputation saves a significant amount of CPU cycles when signing.

If required, Sodium provides two functions to extract the seed and the public key from the secret key:

1

int crypto_sign_ed25519_sk_to_seed(unsigned char *seed,

2

const unsigned char *sk);

3

â€‹

4

int crypto_sign_ed25519_sk_to_pk(unsigned char *pk, const unsigned char *sk);

Copied!

The crypto_sign_ed25519_sk_to_seed() function extracts the seed from the secret key sk and copies it into seed (crypto_sign_SEEDBYTES bytes).

The crypto_sign_ed25519_sk_to_pk() function extracts the public key from the secret key sk and copies it into pk (crypto_sign_PUBLICKEYBYTES bytes).

Data structures

crypto_sign_state, whose size can be retrieved using crypto_sign_statebytes()

Constants

crypto_sign_PUBLICKEYBYTES

crypto_sign_SECRETKEYBYTES

crypto_sign_BYTES

crypto_sign_SEEDBYTES

Algorithm details

Single-part signature: Ed25519

Multi-part signature: Ed25519ph

References

Notes

crypto_sign_verify() and crypto_sign_verify_detached() are only designed to verify signatures computed using crypto_sign() and crypto_sign_detached().

The original NaCl crypto_sign_open() implementation overwrote 64 bytes after the message. The libsodium implementation doesn't write past the end of the message.

Ed25519ph (used by the multi-part API) was implemented in libsodium 1.0.12.

The Ed25519 system was designed to compute deterministic signatures.

Non-deterministic (but also non-standard) signatures can be produced by compiling libsodium with the ED25519_NONDETERMINISTIC macro defined.

Last modified 6d ago