Skip to content

jedisct1/libsodium-signcryption

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 

Repository files navigation

signcryption using libsodium

An implementation of the Toorani-Beheshti signcryption scheme, instantiated over the Ristretto255 group.

Why

Traditional authenticated encryption with a shared key allows two or more parties to decrypt a ciphertext and verify that it was created by a member of the group knowing that secret key.

However, it doesn't allow verification of who in a group originally created a message.

In order to do so, authenticated encryption has to be combined with signatures.

The Toorani-Beheshti signcryption scheme achieves this using a single key pair per device, with forward security and public verifiability.

Parameter definitions

  • sender_id: an identifier for a sender. It may be the sender's public key but it doesn't have to. It can also be an account number, or anything that can uniquely identify a user. It doesn't need to be secret, nor have high entropy. A user can send messages from multiple devices, each with their own key pair, with sender_id remaining the same.
  • recipient_id: an identifier for the recipient of a message. It can represent a specific party, or, for a message sent to a group, a group identifier.
  • info: this describes the context in which a message was sent. Signature verification will fail if the context expected by the verifier doesn't match the one the signature was originally created for.
  • shared_key: a shared secret key, used for encryption. The scheme only generates shared secrets; applications are free to use them with the encryption system of their choice.

Algorithm summary

(a, A) = sender_sk, sender_pk
(b, B) = recipent_sk, recipient_pk

    r = H("nonce", sender_sk, recipient_pk, noise, plaintext)
    R = rG
    K = (Ra + r)B
    x = H("shared_key", K, sender_id, recipient_id, info)
    y = H("sign_key", R, sender_id, recipient_id, info, ciphertext)
    R = rG
    s = ya - r

signature = (R, s) = (rG, ya - r)

    K = b(RA + R)
    S = (sG + R)A

alternatively:

    S = A^2 + R

Source code

  • The src/tbsbr directory contains the main source code, with the scheme implemented using the BLAKE2b hash function and the Ristretto255 group. This is the recommended version.
  • As an alternative, the src/tbsbe directory contains a version using the standard edwards25519 encoding.

The API decription below assumes that the tbsbr version is being used, but both versions have the exact same API with a different prefix.

Key pair creation

void crypto_signcrypt_tbsbr_keygen(unsigned char pk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES],
                                   unsigned char sk[crypto_signcrypt_tbsbr_SECRETKEYBYTES]);

Create a new key pair, putting the public key into pk and the secret into sk.

void crypto_signcrypt_tbsbr_seed_keygen(unsigned char pk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES],
                                        unsigned char sk[crypto_signcrypt_tbsbr_SECRETKEYBYTES],
                                        const unsigned char seed[crypto_signcrypt_tbsbr_SEEDBYTES]);

Create a deterministic key pair from the seed seed.

Signcryption

These functions are called by the sender.

int crypto_signcrypt_tbsbr_sign_before(
    unsigned char st[crypto_signcrypt_tbsbr_STATEBYTES],
    unsigned char shared_key[crypto_signcrypt_tbsbr_SHAREDBYTES],
    const unsigned char *sender_id, size_t sender_id_len,
    const unsigned char *recipient_id, size_t recipient_id_len,
    const unsigned char *info, size_t info_len,
    const unsigned char sender_sk[crypto_signcrypt_tbsbr_SECRETKEYBYTES],
    const unsigned char recipient_pk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES],
    const unsigned char *m, size_t m_len);

This function computes a shared key shared_key that can later be used to encrypt a message from sender_id to recipient_id in the info context.

m is the message to be encrypted, and m_len its size in bytes. On a system with a reliable secure random number generator, this is optional and m can be set to NULL, with m_len set to 0.

shared_key can then be used to encrypt the message with any authenticated encryption system.

st will contain the state, required for the sign_after step.

The function returns -1 or error, 0 on success.

int crypto_signcrypt_tbsbr_sign_after(
    unsigned char       st[crypto_signcrypt_tbsbr_STATEBYTES],
    unsigned char       sig[crypto_signcrypt_tbsbr_SIGNBYTES],
    const unsigned char sender_sk[crypto_signcrypt_tbsbr_SECRETKEYBYTES],
    const unsigned char *c, size_t c_len);

Once the message has been encrypted, it must be signed with this function. c is the ciphertext, and c_len its length.

The signature is stored into sig.

The function returns -1 or error, 0 on success.

A typical signcryption sequence is thus:

  1. crypto_signcrypt_tbsbr_sign_before()
  2. encrypt with shared_key
  3. crypto_signcrypt_tbsbr_sign_after()

Unsigncryption

The functions are called by the recipient.

int crypto_signcrypt_tbsbr_verify_before(
    unsigned char       st[crypto_signcrypt_tbsbr_STATEBYTES],
    unsigned char       shared_key[crypto_signcrypt_tbsbr_SHAREDBYTES],
    const unsigned char sig[crypto_signcrypt_tbsbr_SIGNBYTES],
    const unsigned char *sender_id, size_t sender_id_len,
    const unsigned char *recipient_id, size_t recipient_id_len,
    const unsigned char *info, size_t info_len,
    const unsigned char sender_pk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES],
    const unsigned char recipient_sk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES]);

This function creates a state st and recovers the encryption shared key shared_key from the signature sig, the message sender identifier sender_id, the recipient recipient_len, the context info, the sender's public key sender_pk and the recipent's secret key recipient_sk.

The shared key can then be used to decrypt the ciphertext.

The function returns -1 or error, 0 on success.

int crypto_signcrypt_tbsbr_verify_after(
    unsigned char       st[crypto_signcrypt_tbsbr_STATEBYTES],
    const unsigned char sig[crypto_signcrypt_tbsbr_SIGNBYTES],
    const unsigned char sender_pk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES],
    const unsigned char *c, size_t c_len);

This function verifies that the signature sig is valid for the ciphertext c of length c_len bytes, the sender's public key sender_pk and the previously computed state st.

It returns -1 is the verification failed, and 0 if it succeeded.

A typical unsigncryption sequence is thus:

  1. crypto_signcrypt_tbsbr_verify_before()
  2. decrypt with shared_key
  3. crypto_signcrypt_tbsbr_verify_after() - The return of that function must be checked.

Public verification

The fact that a message was sent by a specific sender to a specific recipient in a specific context can also be publicly verified, without giving the ability to decrypt the ciphertext.

int crypto_signcrypt_tbsbr_verify_public(
    const unsigned char sig[crypto_signcrypt_tbsbr_SIGNBYTES],
    const unsigned char *sender_id, size_t sender_id_len,
    const unsigned char *recipient_id, size_t recipient_id_len,
    const unsigned char *info, size_t info_len,
    const unsigned char sender_pk[crypto_signcrypt_tbsbr_PUBLICKEYBYTES],
    const unsigned char *c, size_t c_len);

This function verifies that sig is a valid signature for the ciphertext c of length c_len bytes, the sender identifier sender_id, the recipient recipient_id, the context info, and the sender's public key sender_pk.

Constants

  • crypto_signcrypt_tbsbr_SECRETKEYBYTES = 32
  • crypto_signcrypt_tbsbr_PUBLICKEYBYTES = 32
  • crypto_signcrypt_tbsbr_SHAREDBYTES = 32
  • crypto_signcrypt_tbsbr_SEEDBYTES = 64
  • crypto_signcrypt_tbsbr_SIGNBYTES = 64
  • crypto_signcrypt_tbsbr_STATEBYTES = 512

Other implementations

References

  • A Directly Public Verifiable Signcryption Scheme based on Elliptic Curves [PDF] (M. Toorani, A. Beheshti).

Releases

No releases published

Packages

No packages published

Languages