My goal is to use the crypto_box_easy
/ crypto_box_open_easy
routines where Alice and Bob each have their own Scalars / Points already previously generated with crypto_core_ed25519_scalar_random
and crypto_scalarmult_ed25519_base_noclamp
, respectively.
I did not want to use crypto_box_keypair
because I want to re-use the existing keys which Alice and Bob already have.
I've tried to use the crypto_sign_ed25519_sk_to_curve25519
/ crypto_sign_ed25519_pk_to_curve25519
routines to convert the Ed25519
scalar/points to x25519. However decryption fails, so this does not seem to be the correct approach.
This routine only seems to work when the keypairs were generated with crypto_sign_ed25519_keypair
, and not crypto_core_ed25519_scalar_random
and crypto_scalarmult_ed25519_base_noclamp
.
I am including two snippets here. The first D snippet I'm showing works OK (You can ignore the serialization part):
unittest
{
// alice: generate ed25519 key pair
ubyte[crypto_sign_ed25519_PUBLICKEYBYTES] ed25519_alice_pk;
ubyte[crypto_sign_ed25519_SECRETKEYBYTES] ed25519_alice_sk;
crypto_sign_ed25519_keypair(ed25519_alice_pk.ptr, ed25519_alice_sk.ptr);
// alice: convert ed25519 to x25519
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_alice_pk;
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_alice_sk;
crypto_sign_ed25519_sk_to_curve25519(x25519_alice_sk.ptr, ed25519_alice_sk.ptr);
crypto_sign_ed25519_pk_to_curve25519(x25519_alice_pk.ptr, ed25519_alice_pk.ptr);
// alice: generate ed25519 key pair
ubyte[crypto_sign_ed25519_PUBLICKEYBYTES] ed25519_bob_pk;
ubyte[crypto_sign_ed25519_SECRETKEYBYTES] ed25519_bob_sk;
crypto_sign_ed25519_keypair(ed25519_bob_pk.ptr, ed25519_bob_sk.ptr);
// alice: convert ed25519 to x25519
// secret key / point for x25519 encryption
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_bob_pk;
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_bob_sk;
crypto_sign_ed25519_sk_to_curve25519(x25519_bob_sk.ptr, ed25519_bob_sk.ptr);
crypto_sign_ed25519_pk_to_curve25519(x25519_bob_pk.ptr, ed25519_bob_pk.ptr);
static struct S
{
int x = 123;
}
S s;
const payload = s.serializeFull();
ubyte[crypto_box_NONCEBYTES] nonce;
randombytes_buf(nonce.ptr, nonce.length);
auto ciphertext_len = crypto_box_MACBYTES + payload.length;
ubyte[] ciphertext = new ubyte[](ciphertext_len);
if (crypto_box_easy(ciphertext.ptr, payload.ptr, payload.length, nonce.ptr,
x25519_bob_pk.ptr, x25519_alice_sk.ptr) != 0)
assert(0);
ubyte[] decrypted = new ubyte[](payload.length);
if (crypto_box_open_easy(decrypted.ptr, ciphertext.ptr, ciphertext_len, nonce.ptr,
x25519_alice_pk.ptr, x25519_bob_sk.ptr) != 0)
assert(0);
S deserialized = deserializeFull!S(decrypted);
assert(deserialized.x == s.x);
}
And here is an example using key-pairs generated with crypto_core_ed25519_scalar_random
/ crypto_scalarmult_ed25519_base_noclamp
which fails during decryption:
unittest
{
// alice: generate ed25519 key pair
ubyte[crypto_core_ed25519_BYTES] ed25519_alice_pk;
ubyte[crypto_core_ed25519_SCALARBYTES] ed25519_alice_sk;
crypto_core_ed25519_scalar_random(ed25519_alice_sk.ptr);
crypto_scalarmult_ed25519_base_noclamp(ed25519_alice_pk.ptr, ed25519_alice_sk.ptr);
// alice: convert ed25519 to x25519
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_alice_pk;
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_alice_sk;
crypto_sign_ed25519_sk_to_curve25519(x25519_alice_sk.ptr, ed25519_alice_sk.ptr);
crypto_sign_ed25519_pk_to_curve25519(x25519_alice_pk.ptr, ed25519_alice_pk.ptr);
// bob: generate ed25519 key pair
ubyte[crypto_core_ed25519_BYTES] ed25519_bob_pk;
ubyte[crypto_core_ed25519_SCALARBYTES] ed25519_bob_sk;
crypto_core_ed25519_scalar_random(ed25519_bob_sk.ptr);
crypto_scalarmult_ed25519_base_noclamp(ed25519_bob_pk.ptr, ed25519_bob_sk.ptr);
// bob: convert ed25519 to x25519
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_bob_pk;
ubyte[crypto_scalarmult_curve25519_BYTES] x25519_bob_sk;
crypto_sign_ed25519_sk_to_curve25519(x25519_bob_sk.ptr, ed25519_bob_sk.ptr);
crypto_sign_ed25519_pk_to_curve25519(x25519_bob_pk.ptr, ed25519_bob_pk.ptr);
static struct S
{
int x = 123;
}
S s;
const payload = s.serializeFull();
ubyte[crypto_box_NONCEBYTES] nonce;
randombytes_buf(nonce.ptr, nonce.length);
auto ciphertext_len = crypto_box_MACBYTES + payload.length;
ubyte[] ciphertext = new ubyte[](ciphertext_len);
if (crypto_box_easy(ciphertext.ptr, payload.ptr, payload.length, nonce.ptr,
x25519_bob_pk.ptr, x25519_alice_sk.ptr) != 0)
assert(0);
ubyte[] decrypted = new ubyte[](payload.length);
if (crypto_box_open_easy(decrypted.ptr, ciphertext.ptr, ciphertext_len, nonce.ptr,
x25519_alice_pk.ptr, x25519_bob_sk.ptr) != 0)
assert(0); // FAILS
S deserialized = deserializeFull!S(decrypted);
assert(deserialized.x == s.x);
}
The examples are otherwise equivalent. So the question is, what is the right way to derive a key-pair for encryption if I only have a key-pair for signing which was generated with crypto_core_ed25519_scalar_random
and crypto_scalarmult_ed25519_base_noclamp
?
question from:
https://stackoverflow.com/questions/65932254/how-do-i-convert-an-ed25519-scalar-into-x25519-for-encryption