Ed25519 и Curve25519 относятся к одной криптографической семье эллиптических кривых, но используются в разных задачах.
В TweetNaCl.js эти алгоритмы реализованы раздельно, и ключи между ними напрямую не совместимы без преобразования.
В реальных приложениях часто возникает сценарий:
Проблема в том, что:
Поэтому для повторного использования одной пары ключей требуется преобразование.
const keyPair = nacl.sign.keyPair();
Результат:
publicKey — 32 байтаsecretKey — 64 байта (seed + public key)const boxKeyPair = nacl.box.keyPair();
Результат:
publicKey — 32 байтаsecretKey — 32 байтаКонвертация строится на извлечении seed (первых 32 байт) из Ed25519 ключа.
Далее seed используется для построения Curve25519 ключа.
Самый корректный способ работы с конвертацией — начинать с seed:
const seed = nacl.randomBytes(32);
const edKeyPair = nacl.sign.keyPair.fromSeed(seed);
Здесь:
TweetNaCl.js не предоставляет официальную полноценную высокоуровневую функцию конвертации как libsodium, но на практике используется следующий подход.
const edKeyPair = nacl.sign.keyPair.fromSeed(seed);
const edSecretKey = edKeyPair.secretKey;
const curveKeyPair = nacl.box.keyPair.fromSecretKey(edSecretKey);
В строгом криптографическом смысле:
Однако в TweetNaCl.js используется упрощённая модель, где:
Поэтому более безопасная практика:
const seed = edSecretKey.slice(0, 32);
const curveKeyPair = nacl.box.keyPair.fromSecretKey(seed);
// генерация Ed25519 ключей
const edKeyPair = nacl.sign.keyPair();
// извлечение seed
const seed = edKeyPair.secretKey.slice(0, 32);
// генерация Curve25519 ключей
const curveKeyPair = nacl.box.keyPair.fromSecretKey(seed);
// результат
console.log(curveKeyPair.publicKey);
console.log(curveKeyPair.secretKey);
После конвертации можно выполнять обмен сообщениями:
const message = new TextEncoder().encode("secret data");
const nonce = nacl.randomBytes(24);
const encrypted = nacl.box(
message,
nonce,
curveKeyPair.publicKey,
recipientCurvePublicKey
);
Обратное преобразование не является прямым.
Curve25519 ключ:
Поэтому:
Часто применяют следующую модель:
seed (32 байта) — корневой секрет
из него:
const seed = nacl.randomBytes(32);
const signKeyPair = nacl.sign.keyPair.fromSeed(seed);
const boxKeyPair = nacl.box.keyPair.fromSecretKey(seed);
nacl.box.keyPair.fromSecretKey(edSecretKey) // может работать некорректно
Проблема:
nacl.box.keyPair.fromSecretKey(edPublicKey) // ошибка
В libsodium существует явная функция:
crypto_sign_ed25519_sk_to_curve25519TweetNaCl.js не предоставляет прямого аналога, поэтому:
export function ed25519ToCurve25519(edSecretKey) {
const seed = edSecretKey.slice(0, 32);
return nacl.box.keyPair.fromSecretKey(seed);
}
Конвертация используется в:
После конвертации необходимо учитывать:
Некоторые системы используют подход:
один seed
два производных ключа:
Это позволяет: