Алгоритм Ed25519 относится к семейству схем цифровой подписи на основе эллиптических кривых (EdDSA). В экосистеме TweetNaCl.js он реализован как основной механизм асимметричной аутентификации данных, обеспечивающий высокую скорость, компактные ключи и устойчивость к целому классу криптоаналитических атак.
Ключевая особенность Ed25519 заключается в том, что он не предназначен для шифрования данных. Его задача — подтверждение целостности и подлинности сообщений через цифровую подпись. В связке с моделью доверия это становится фундаментом для построения цепочек сертификатов.
В криптографическом смысле доверие не передаётся «магически». Оно строится через проверяемые утверждения:
Именно эта рекурсивная структура приводит к модели цепочек доверия.
Библиотека TweetNaCl.js предоставляет минималистичный интерфейс для работы с Ed25519:
Типовой набор операций выглядит следующим образом:
const nacl = require('tweetnacl');
const util = require('tweetnacl-util');
// генерация ключей
const keyPair = nacl.sign.keyPair();
// сообщение
const message = util.decodeUTF8("critical data");
// подпись
const signature = nacl.sign.detached(message, keyPair.secretKey);
// проверка подписи
const isValid = nacl.sign.detached.verify(
message,
signature,
keyPair.publicKey
);
Важно учитывать, что secretKey в Ed25519 включает в себя
как приватную, так и публичную часть, тогда как publicKey
используется отдельно.
TweetNaCl.js реализует криптографический примитивный слой. Он не включает инфраструктуру:
Это сознательное решение: библиотека ограничивается криптографией, оставляя протокольную и инфраструктурную часть разработчику.
Поэтому сертификаты и цепочки доверия строятся поверх Ed25519 вручную.
Сертификат в контексте NaCl-подобных систем представляет собой структуру данных, содержащую:
Пример структуры:
{
"subject": "device-42",
"publicKey": "BASE64_PUBLIC_KEY",
"issuedAt": 1710000000,
"expiresAt": 1740000000,
"issuer": "ca-root",
"signature": "BASE64_SIGNATURE"
}
Подпись формируется не на JSON-строку как таковую, а на каноническое представление данных.
Одна из ключевых проблем любых криптографических систем поверх JSON — неоднозначность сериализации.
Разные сериализации одного и того же объекта могут давать разные байтовые представления, что ломает проверку подписи.
Поэтому вводится правило канонизации:
Простейшая стратегия:
function canonicalize(obj) {
return JSON.stringify(obj, Object.keys(obj).sort());
}
Далее результат переводится в Uint8Array:
const message = util.decodeUTF8(canonicalize(certData));
Процесс выпуска сертификата включает следующие шаги:
function signCertificate(cert, issuerSecretKey) {
const dataToSign = {
subject: cert.subject,
publicKey: cert.publicKey,
issuedAt: cert.issuedAt,
expiresAt: cert.expiresAt,
issuer: cert.issuer
};
const message = util.decodeUTF8(canonicalize(dataToSign));
const signature = nacl.sign.detached(message, issuerSecretKey);
return {
...dataToSign,
signature: util.encodeBase64(signature)
};
}
Проверка включает несколько этапов:
function verifyCertificate(cert, issuerPublicKey) {
const dataToVerify = {
subject: cert.subject,
publicKey: cert.publicKey,
issuedAt: cert.issuedAt,
expiresAt: cert.expiresAt,
issuer: cert.issuer
};
const message = util.decodeUTF8(canonicalize(dataToVerify));
const signature = util.decodeBase64(cert.signature);
const signatureValid = nacl.sign.detached.verify(
message,
signature,
issuerPublicKey
);
const now = Math.floor(Date.now() / 1000);
const timeValid = now >= cert.issuedAt && now <= cert.expiresAt;
return signatureValid && timeValid;
}
Цепочка доверия строится через рекурсивную верификацию сертификатов:
Каждый уровень подписывает следующий.
Корневой ключ считается доверенным по определению системы. Его публичный ключ встраивается в приложение или распространяется через защищённый канал.
const rootCA = {
publicKey: util.decodeBase64(ROOT_PUBLIC_KEY),
name: "root-ca"
};
Корневой CA подписывает промежуточные сертификаты:
Алгоритм проверки строится от листа к корню:
function verifyChain(leafCert, intermediateCert, rootCA) {
const leafValid = verifyCertificate(
leafCert,
util.decodeBase64(intermediateCert.publicKey)
);
const intermediateValid = verifyCertificate(
intermediateCert,
rootCA.publicKey
);
return leafValid && intermediateValid;
}
Цепочки позволяют строить системы делегирования:
Пример ограничений в метаданных:
{
"permissions": ["sign:messages", "auth:device"],
"scope": "iot-network"
}
Эти поля также включаются в подписываемую часть сертификата.
В реальных системах сертификаты на Ed25519 используются в:
Типичный сценарий:
Сертификат даёт не только идентичность, но и возможность подписывать сообщения:
const message = util.decodeUTF8("request-data");
const signature = nacl.sign.detached(
message,
deviceSecretKey
);
Сервер проверяет:
Ключевая проблема долгоживущих систем — обновление ключей.
Подходы:
Сценарий миграции:
В NaCl нет встроенного механизма отзыва, поэтому он реализуется отдельно:
Пример структуры:
{
"revoked": [
"device-42",
"device-99"
]
}
При проверке сертификата выполняется дополнительная проверка идентификатора.
В системах на основе TweetNaCl.js часто возникают ошибки:
Приватные ключи Ed25519 требуют строгого обращения:
Типовая схема включает:
Поток доверия:
Ed25519 обеспечивает криптографическую основу, но не определяет:
Эти аспекты формируют отдельный уровень — протокольную инфраструктуру поверх TweetNaCl.js, где сертификаты и цепочки доверия становятся основным механизмом управления идентичностью.