1
$\begingroup$

More of a theoretical question...

Taking a system where Lib Sodium has been used for asymmetric encryption, where a "crypto_box_keypair" has been generated, and the public key has been distributed to a number of different systems.

Would it be possible to sign a message using that keypair?

As in, the message remains in plain text, and a signature is generated with the secret key (possibly indirectly), and anyone in possession of (and trusts) the public key, can verify that the message hasn't been tampered with.

Considering that the "crypto_sign" function requires the use of "crypto_sign_keypair", not "crypto_box_keypair".


From a PHP implementation point of view, I was wondering if you could...

Take the keypair that has been previously generated by:

<?php
$key_pair = sodium_crypto_box_keypair();
$key_secret = sodium_crypto_box_secretkey($key_pair);
$key_public = sodium_crypto_box_publickey($key_pair);
?>

Then, for every message you want to sign, create a "crypto_sign_keypair", and use sodium_crypto_sign() as intended:

<?php

// $sign_seed = random_bytes(SODIUM_CRYPTO_SIGN_SEEDBYTES);
// $sign_pair = sodium_crypto_sign_seed_keypair($sign_seed);

$sign_pair = sodium_crypto_sign_keypair();
$sign_secret = sodium_crypto_sign_secretkey($sign_pair);
$sign_public = sodium_crypto_sign_publickey($sign_pair);

$message = 'Hello';

$message_signed = sodium_crypto_sign($message, $sign_secret);

?>

The $sign_secret and $sign_pair can be destroyed, because the recipient just needs to use $sign_public:

<?php
$message = sodium_crypto_sign_open($message_signed, $sign_public);
?>

The problem is that $sign_public needs to be sent to the recipient.

Where they need to verify it hasn't been tampered with, and that it came from the owner of the original $key_secret, by using their trusted copy of $key_public.

Noting that:

  • sodium_crypto_sign_detached is basically the same thing, but does not contain the message in its output.
  • sodium_crypto_box requires the recipient to have a keypair as well.
  • sodium_crypto_box_seal uses the public key to encrypt.
  • sodium_crypto_secretbox and sodium_crypto_aead_* use a shared key (symmetric).
  • sodium_crypto_auth uses a shared key.
$\endgroup$

2 Answers 2

1
$\begingroup$

It's possible to go the other way around (thanks Scott Arciszewski)

<?php

$sign_pair = sodium_crypto_sign_keypair();
$sign_secret = sodium_crypto_sign_secretkey($sign_pair);
$sign_public = sodium_crypto_sign_publickey($sign_pair);

$key_secret = sodium_crypto_sign_ed25519_sk_to_curve25519($sign_secret);
$key_public = sodium_crypto_sign_ed25519_pk_to_curve25519($sign_public);

$key_public = sodium_crypto_box_publickey_from_secretkey($key_secret);

?>
$\endgroup$
0
$\begingroup$

For two (overly-complex) solutions, starting with the crypto_box_keypair:

<?php
$key_pair = sodium_crypto_box_keypair();
$key_secret = sodium_crypto_box_secretkey($key_pair);
$key_public = sodium_crypto_box_publickey($key_pair);
?>

And using a throw-away keypair; where the public and private parts of this keypair can be seen by everyone (which seems a little odd)...


Version 1, still using sodium_crypto_sign(), along with sodium_crypto_box() to "encrypt" the public signing key (anyone can decrypt, but it verifies that it hasn't been tampered with).

We sign the message with:

<?php

function sign($key_secret, $message) {

    $sign_pair = sodium_crypto_sign_keypair(); // Different to sodium_crypto_box_keypair()
    $sign_secret = sodium_crypto_sign_secretkey($sign_pair);
    $sign_public = sodium_crypto_sign_publickey($sign_pair);

    $message_signed = sodium_crypto_sign($message, $sign_secret);

    $sign_box_secret = sodium_crypto_sign_ed25519_sk_to_curve25519($sign_secret); // Convert crypto_sign_keypair to crypto_box_keypair.
    $sign_box_public = sodium_crypto_sign_ed25519_pk_to_curve25519($sign_public);

    $sign_box_nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
    $sign_box_pair = sodium_crypto_box_keypair_from_secretkey_and_publickey($key_secret, $sign_box_public);
    $sign_box_encrypted = sodium_crypto_box($sign_public, $sign_box_nonce, $sign_box_pair);

    return [$message_signed, $sign_box_encrypted, $sign_box_nonce, $sign_box_secret];

}

$package = sign($key_secret, 'hello');

?>

Then verify and extract the message with:

<?php

function verify($key_public, $package) {

    list($message_signed, $sign_box_encrypted, $sign_box_nonce, $sign_box_secret) = $package;

    $sign_box_pair = sodium_crypto_box_keypair_from_secretkey_and_publickey($sign_box_secret, $key_public);
    $sign_public = sodium_crypto_box_open($sign_box_encrypted, $sign_box_nonce, $sign_box_pair);

    return sodium_crypto_sign_open($message_signed, $sign_public);

}

echo verify($key_public, $package);

?>

Version 2, we can skip sodium_crypto_sign(), as sodium_crypto_box() allows us to verify the message during decryption.

We sign the message with:

<?php

function sign($key_secret, $message) {

    $shared_pair = sodium_crypto_box_keypair();
    $shared_secret = sodium_crypto_box_secretkey($shared_pair);
    $shared_public = sodium_crypto_box_publickey($shared_pair);

    $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
    $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($key_secret, $shared_public);

    $message_signed = sodium_crypto_box($message, $nonce, $keypair);

    return [$message_signed, $shared_secret, $nonce];

}

$package = sign($key_secret, 'hello');

?>

Then verify and extract the message with:

<?php

function verify($key_public, $package) {

    list($message_signed, $shared_secret, $nonce) = $package;

    $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($shared_secret, $key_public);

    return sodium_crypto_box_open($message_signed, $nonce, $keypair);

}

echo verify($key_public, $package);

?>

The first version is more complex, but it does keep closer to the original concept of signing, as the message still remains in plain text... something you can see with:

echo substr($package[0], SODIUM_CRYPTO_SIGN_BYTES);
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.