Ориентация главы
Примитивы двух предыдущих глав симметричны — обе стороны используют один ключ. Но самая первая фраза застревает: как вручить симметричный ключ другой стороне? Отправка открытым текстом по недоверяемому каналу равна публикации; встретиться заранее для обмена требует уже существующего защищённого канала. Асимметричная криптография разрубает узел: ключи ходят парами — публичный распространяется свободно, приватный никогда не покидает руки. Глава покрывает две взаимно независимые линии — обмен ключами (ECDH на x25519/x448 и P-256/P-384: каждая сторона держит приватный ключ, обменивается публичными, вычисляет один и тот же секрет) и цифровые подписи (Ed25519/ECDSA/RSA-PSS: приватный подписывает, публичный проверяет, заявления становятся неотказуемыми). Они — сырьё сертификатов главы 78 (сертификат есть «подпись поверх публичного ключа») и движок рукопожатия TLS главы 82. После главы вы должны на любую потребность протокола отвечать «какая кривая, какая схема подписи и почему».
76.1引入
Два сценария. Первый: ваш клиент должен установить шифрованный канал с сервером, которого никогда не видел — по полностью публичному каналу каждая сторона говорит «вот мой публичный ключ», затем каждая вычисляет своим приватным и публичным другой стороны, приходя к полностью одинаковому 32-байтовому секрету; ни один подслушивающий не вычислит его из всех публичных сообщений. Это обмен DH (Diffie-Hellman); x25519 — его современная форма 2026 года. Второй: издатель ПО должен доказать «это обновление действительно от меня» — подписать дайджест пакета ключом, который есть только у него, а весь мир проверяет публично распространённым ключом. Различие подписи и MAC (глава 74) — модель доверия: проверяющий MAC обязан держать ключ (симметрия); проверяющему подписи достаточно публичного ключа (публичность) — что позволяет «одному ко многим»: один издатель, миллион проверяющих.
Обе линии выходят на одну сцену в рукопожатии TLS 1.3: x25519 обменивается общим секретом, а сервер подписывает дайджест рукопожатия приватным ключом сертификата (RSA-PSS или ECDSA), доказывая подлинность. Четыре примера главы соответствуют этим ролям.
76.2概念
Обмен ключами: симметрия ECDH
Магию Диффи-Хеллмана можно нарисовать одной симметричной диаграммой:
Ассоциативность умножения гарантирует равенство произведений, а подслушивающий видел лишь два публичных ключа — восстановление sA из pubA есть задача дискретного логарифмирования на эллиптической кривой, в этом вся стойкость. XRT покрывает три семейства:
- x25519 (32 байта приватный/публичный/общий): RFC 7748, группа по умолчанию TLS 1.3, без зависимости от источника случайности;
xrtX25519Public(приватный → публичный),xrtX25519(голое скалярное умножение),xrtX25519Shared(безопасный вход общего секрета — дополнительно обрабатывает злодейские входы публичного ключа вроде всех нулей и точек низкого порядка),xrtX25519KeyPair(удобная комбинация на безопасном источнике случайности). - x448 (56-байтовая форма): изоморфная версия с большим запасом прочности, API полностью симметричен x25519 (
xrtX448Public/Shared/KeyPair). - P-256/P-384: кривые NIST (65/97 байт несжатой точки); кроме ECDH открыта и точечная арифметика (
Validпроверка точки,Addсложение точек,Multiplyскалярное умножение) — некоторые протоколы (реализации EC-подписей, доказательства с нулевым разглашением) требуют прямых операций с точками.
Общая дисциплина: вычисленный общий секрет никогда напрямую не симметричный ключ — его форма нефиксирована и, возможно, различима; он обязан пройти HKDF главы 74, чтобы стать «ключевым материалом» (сессионный пример главы 77 — стандартная цепь). Приватный ключ x25519 — просто 32 случайных байта (клэмпинг выполняется внутри xrtX25519KeyPair); скаляр P-кривых — целое большого конца; фиксированный материал служит только воспроизводимости тестов без утечки, продакшн-приватные ключи всегда из безопасного источника случайности.
Линия подписей одна: Ed25519 (современное умолчание)
Ed25519 — представитель «нового поколения» подписей: подпись 64 байта, публичный ключ/сид 32 байта, детерминированная подпись (одно сообщение + один ключ всегда дают одну подпись — без случайности, из корня уничтожая весь класс аварий «деградация случайности утекает приватный ключ»; инцидент ECDSA у Sony 2010 года — поучительный пример). API в два слоя: поштучный вход xrtEd25519Sign(种子, 消息, 签名) — верbatim-токен ниже — и состояниее xrtEd25519KeyInit/SignKey/KeyClear: последний предразвёртывает промежуточные величины подписи, заметно быстрее при многих подписях одним ключом (выпуск сертификатов, пакетная авторизация); по завершении KeyClear вытирает чувствительное промежуточное состояние. Проверка xrtEd25519Verify(公钥, 消息, 签名) — чисто публичная операция без ключа к вычищению. Попадание в тестовые векторы RFC 8032 — доказательство интероперабельности: подписи XRT проверяет OpenSSL и наоборот.
Линия подписей два: ECDSA (P-256/P-384)
ECDSA — рабочая лошадка сертификатов TLS и существующей PKI-экосистемы. Реализация XRT режется в четыре слоя: ECDSA_VERIFY/ECDSA_SIGN (не зависящее от кривой ядро) → ECDSA_P256/P384 (raw-проверка: 64-байтовая r||s фиксированной длины) → ECDSA_DER (слой преобразования raw ↔ канонический DER) → криво-специфичные DER-слои удобства. Подпись детерминированная по RFC 6979 — как и Ed25519, без внешней случайности. Компромисс raw и DER: DER — проводной формат сертификатов и существующих протоколов (переменной длины, TLV-кодирование); raw — форма внутренних вычислений и компактных протоколов (фиксированная длина); XRT выносит преобразование в отдельный слой, открыты оба мира.
Линия подписей три: RSA (гигант инсталляционной базы)
Стойкость RSA стоит на трудности факторизации больших целых; ключи велики (от 2048 бит), операции медленны, но установленная база сертификатов широчайшая. Расслоение RSA у XRT лучше всего показывает границу «алгоритм против протокола»:
xrtRsaPublic/xrtRsaPrivate: голое модульное возведение в степень — математический фундамент; открытый текст нельзя «шифровать» так напрямую (без набивки нет безопасности; пример использует это лишь для доказательства самосогласованности фундамента).- RSA-PSS (
xrtRsaPssSign/PssVerify): современный стандарт, единственный RSA-режим подписи TLS 1.3. Объект подписи — дайджест сообщения (два хеш-параметра: дайджест содержимого и маска MGF1). Salt опционален: явныйPssSignSalt(для тестовых векторов и детерминированных протоколов), вход удобства со случайной солью (по умолчанию), верификатор может использоватьXRT_RSA_PSS_SALT_ANYбез ограничения длины соли. - RSA-PKCS1 v1.5 (
xrtRsaPkcs1Verifyи др.): слой совместимости с историческими протоколами, каноническое кодирование EMSA.
Результат каждой операции приватного ключа перепроверяется публичной экспонентой — любой внутренний сбой обнаруживается, а не молча производит неверную подпись; образец оборонительной реализации.
Быстрый вердикт выбора
Новые системы подписывают с Ed25519 вперёд (быстро, мало, детерминированно, без привычек побочных каналов); для встраивания в существующую PKI — ECDSA P-256; к RSA-PSS — только при стыковке с легаси; обмен ключами всегда x25519 (x448 при требованиях большего запаса), P-256/P-384 — только когда правительство/комплаенс предписывает кривые NIST. Эта таблица приоритетов совпадает с порядком согласования групп TLS 1.3 — глава 82 покажет посадку.
76.3示例
Первая полная программа: симметрия ECDH и самопроверка арифметики кривых
Следующая программа из examples/crypto/ecdh_tour/main.c за один прогон проверяет совпадение общих секретов обеих сторон на x25519/x448 и самосогласованность арифметики P-кривых:
*/ #include <stdio.h> #include <string.h> #include <xrt.h> int main(void) { /* 两方私钥(演示用固定值;生产应来自安全随机源)。 */ static const uint8 arrSecretA[56] = { 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 52u, 53u, 54u, 55u, 56u }; static const uint8 arrSecretB[56] = { 56u, 55u, 54u, 53u, 52u, 51u, 50u, 49u, 48u, 47u, 46u, 45u, 44u, 43u, 42u, 41u, 40u, 39u, 38u, 37u, 36u, 35u, 34u, 33u, 32u, 31u, 30u, 29u, 28u, 27u, 26u, 25u, 24u, 23u, 22u, 21u, 20u, 19u, 18u, 17u, 16u, 15u, 14u, 13u, 12u, 11u, 10u, 9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u }; uint8 arrPubA[64]; uint8 arrPubB[64]; uint8 arrSharedA[64]; uint8 arrSharedB[64]; uint8 arrScalarTwo[48]; uint8 arrPoint[97]; uint8 arrSum[97]; uint8 arrDouble[97]; uint8 arrBad[97]; int iResult = 1; /* ---- X25519:公钥导出 + 双方共享密钥对称。 ---- */ if ( !xrtX25519Public(arrSecretA, arrPubA) || !xrtX25519Public(arrSecretB, arrPubB) || !xrtX25519(arrSecretA, arrPubB, arrSharedA) || !xrtX25519(arrSecretB, arrPubA, arrSharedB) || (memcmp(arrSharedA, arrSharedB, 32u) != 0) ) { goto Cleanup; } printf("ecdh: x25519 public + shared-secret symmetric ok\n"); /* ---- X448:56 字节私钥同构闭环。 ---- */ if ( !xrtX448Public(arrSecretA, arrPubA) || !xrtX448Public(arrSecretB, arrPubB) || !xrtX448(arrSecretA, arrPubB, arrSharedA) || !xrtX448(arrSecretB, arrPubA, arrSharedB) || (memcmp(arrSharedA, arrSharedB, 56u) != 0) ) { goto Cleanup; } printf("ecdh: x448 public + shared-secret symmetric ok\n"); /* ---- P-256:公钥有效 + 倍点一致 + 非法点被拒。 ---- */ memset(arrScalarTwo, 0, sizeof(arrScalarTwo)); arrScalarTwo[31] = 2u; /* 大端 32 字节标量 2。 */ if ( !xrtP256Public(arrSecretA, arrPoint) || !xrtP256Valid(arrPoint) || !xrtP256Add(arrPoint, arrPoint, arrSum) || !xrtP256Multiply(arrScalarTwo, arrPoint, arrDouble) || (memcmp(arrSum, arrDouble, 65u) != 0) ) { goto Cleanup; } /* 未知点前缀 0xFF → Valid 必须拒绝。 */ memset(arrBad, 0xFFu, sizeof(arrBad)); if ( xrtP256Valid(arrBad) ) { goto Cleanup; } printf("ecdh: p256 public/valid + add == multiply-by-2 ok\n"); /* ---- P-384:48 字节标量 / 97 字节点同构闭环。 ---- */ /* 清掉 P-256 段写入的字节 31,48 字节标量才恰好为 2。 */ arrScalarTwo[31] = 0u; arrScalarTwo[47] = 2u; if ( !xrtP384Public(arrSecretA, arrPoint) || !xrtP384Valid(arrPoint) || !xrtP384Add(arrPoint, arrPoint, arrSum) || !xrtP384Multiply(arrScalarTwo, arrPoint, arrDouble) || (memcmp(arrSum, arrDouble, 97u) != 0) ) { goto Cleanup; } memset(arrBad, 0xFFu, sizeof(arrBad)); if ( xrtP384Valid(arrBad) ) { goto Cleanup; } printf("ecdh: p384 public/valid + add == multiply-by-2 ok\n"); iResult = 0; Cleanup: return iResult; }
Что только что произошло. (1) Приватные ключи обеих сторон на фиксированных последовательностях (1..56 и обратной) — воспроизводимость; в продакшне входы вроде xrtX25519KeyPair сэмплируют из безопасного источника. (2) Проверка симметрии — ключевое утверждение ECDH: xrtX25519(sA, pubB) и xrtX25519(sB, pubA) побайтно равны — математически оба есть sA·sB·G. (3) Секция P-кривых делает две самопроверки: Valid принимает легальную точку, экспортированную из публичного ключа, и отвергает псевдоточку из всех 0xFF (проверка точки — часть безопасности ECDH: непроверенные точки допускают атаки малых подгрупп); Add(P,P) == Multiply(2,P) проверяет согласованность точечной арифметики без предзаданных констант базовой точки. (4) Заметьте: точки NIST кодируются большим концом с длиной по кривой (65/97 байт), тогда как x25519 фиксированно 32 байта — различие байтовых форм двух семейств держите в голове при проектировании протоколов.
Вторая полная программа: подпись Ed25519 на состоянии
Вторая программа из examples/crypto/ed25519_sign/main.c: подпись одного сообщения на развёрнутом состоянии ключа:
#include <stdio.h> #include <xrt.h> */ /* 使用可复用展开密钥签署一条消息。 */ int main(void) { static const char Message[] = "xrt ed25519"; uint8 Seed[XRT_ED25519_SEED_SIZE] = { 0 }; uint8 Signature[XRT_ED25519_SIGNATURE_SIZE]; xed25519key Key; bool bSigned; if ( !xrtEd25519KeyInit(&Key, Seed) ) { return 1; } bSigned = xrtEd25519SignKey( &Key, Message, sizeof(Message) - 1u, Signature ); xrtEd25519KeyClear(&Key); printf("signed: %s\n", bSigned ? "yes" : "no"); return bSigned ? 0 : 1; }
Что только что произошло. (1) xrtEd25519KeyInit предразвёртывает промежуточные величины подписи из 32-байтового сида — сид из нулей только ради воспроизводимости; в сценариях многих подписей одним ключом (CA пакетно подписывает сертификаты, сервис токенов) это переиспользование состояния — ключ к производительности. (2) Подпись детерминирована: повторные прогоны дают то же значение, случайность не участвует — значит, результат подписи можно использовать как тестовый вектор. (3) KeyClear немедленно вытирает развёрнутое состояние (в нём материал, из которого приватный ключ выводится), и только затем печатается результат — очистка нужна и на пути успеха, не только на пути ошибки. Парный examples/crypto/ed25519_verify проверяет публичную подпись вектором пустого сообщения RFC 8032 §7.1 — попадание в стандартный вектор есть доказательство интероперабельности.
Третья полная программа: RSA в три слоя — фундамент, явная соль, случайная соль
Третья программа из examples/crypto/rsa_pss/main.c: от голого модульного возведения до PSS со случайной солью за один проход:
#include "../rsa_fixture.h" #include <stdio.h> */ /* 展示 RSA 原始运算、显式盐 PSS 和随机盐便利入口。 */ int main(void) { __xrt_example_rsa_fixture Fixture; uint8 Raw[128] = { 0 }; uint8 Cipher[128]; uint8 Plain[128]; uint8 Signature[128]; bool bValid = false; /* 原始运算只用于说明底层层次,普通消息不得直接这样加密。 */ Raw[sizeof(Raw) - 1u] = 2u; if ( !__xrtExampleRsaInit(&Fixture) || !xrtRsaPublic( &Fixture.Key.Public, Raw, sizeof(Raw), Cipher ) || !xrtRsaPrivate( &Fixture.Key, Cipher, sizeof(Cipher), Plain ) || !xrtConstTimeEqual(Raw, Plain, sizeof(Raw)) ) { goto cleanup; } /* 显式盐入口适合协议层,随机盐入口覆盖常见签名路径。 */ if ( !xrtRsaPssSignSalt( &Fixture.Key, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, Fixture.Salt, sizeof(Fixture.Salt), Fixture.Hash, Signature ) || !xrtRsaPssVerify( &Fixture.Key.Public, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, sizeof(Fixture.Salt), Fixture.Hash, Signature, sizeof(Signature) ) || !xrtRsaPssSign( &Fixture.Key, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, Fixture.Hash, Signature ) || !xrtRsaPssVerify( &Fixture.Key.Public, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, XRT_RSA_PSS_SALT_ANY, Fixture.Hash, Signature, sizeof(Signature) ) ) { goto cleanup; } bValid = true; cleanup: xrtSecureZero(&Fixture, sizeof(Fixture)); xrtSecureZero(Plain, sizeof(Plain)); xrtSecureZero(Signature, sizeof(Signature)); printf("RSA-PSS: %s\n", bValid ? "valid" : "invalid"); return bValid ? 0 : 1; }
Что только что произошло. (1) Первая секция нарочно демонстрирует голое модульное возведение и прогоняет туда-обратно, доказывая математику — комментарии заголовка и кода оба подчёркивают «обычные сообщения нельзя шифровать так»: RSA без набивки — учебниковый пример, решаемый прямой математической атакой; секция существует, чтобы вы увидели дистанцию между фундаментом и защищённым протоколом. (2) PSS подписывает дважды: явная соль PssSignSalt (один вход — воспроизводимая подпись, форма протокольного тестирования) и случайная соль PssSign (каждый раз новая, продакшн-умолчание); проверка одним и тем же PssVerify, второй раз с XRT_RSA_PSS_SALT_ANY без ограничения длины соли — верификатору обычно не следует предполагать солевую политику подписанта. (3) Объект подписи — Fixture.Hash (дайджест), не исходное сообщение — RSA всегда подписывает дайджесты; два параметра XCRYPTO_HASH_SHA256 — дайджест содержимого и хеш маски MGF1. (4) Секция очистки вытирает xrtSecureZero-ом структуру ключа, буферы открытого текста и подпись — в структуре приватного ключа RSA лежат параметры CRT и простые множители, поверхность чувствительного куда шире симметричного ключа.
76.4契约
- Семейство обмена: x25519/x448/P-256/P-384 — везде экспорт приватный → публичный и общий секрет двух сторон;
Shared/безопасные входы обрабатывают злодейские публичные ключи (точки низкого порядка, нулевые выходы), а голые входы скалярного умножения (xrtX25519) входов не проверяют — протокольные слои используют формуShared; точечная арифметика (Valid/Add/Multiply) открыта только для P-кривых. - Граница источника случайности: голый обмен и ядро подписей не зависят от источника случайности (приватный ключ x25519 — просто случайное число; Ed25519/ECDSA детерминированы); зависят только слои удобства
KeyPairи случайная соль PSS —RANDOM_SECURE; при обрезке источник случайности заходит только с этими входами. - Представления подписей: Ed25519 фиксированно 64 байта; ECDSA raw фиксированной длины r||s, DER — проводной формат сертификатов, слой преобразования независим; длина подписи RSA-PSS/PKCS1 равна длине модуля (2048 бит → 256 байт).
- Расслоение RSA: голое модульное возведение демонстрирует только слой — без набивки нет безопасности; PSS — современный стандарт (единственный RSA-режим TLS 1.3); явная соль для воспроизводимости, случайная по умолчанию, проверка может использовать
XRT_RSA_PSS_SALT_ANY; результаты операций приватного ключа перепроверяются публичной экспонентой. - Детерминированная подпись: Ed25519 и ECDSA (RFC 6979) не зависят от внешней случайности — повторная подпись того же сообщения постоянна и годится как тестовый вектор.
- Очистка чувствительного материала:
xed25519key—KeyClearпосле использования; структуры приватных ключей RSA, сиды и общие секреты —xrtSecureZero; проверка — публичная операция, вычищать нечего. - Ошибки: неудачи объявляются возвращаемым значением; нелегальные длины параметров/кодировки точек отвергаются сразу; проверка точек (Valid) отвергает вне-кривые точки и псевдокодировки.
Типичные ошибки
Симптомы: ревью безопасности отметает с первого взгляда; при серьёзной атаке открытый текст безнабивочного RSA восстанавливается классическими приёмами (малая экспонента, подбор шифртекста) — учебниковый RSA не есть схема шифрования.
Причина: xrtRsaPublic/xrtRsaPrivate — математический фундамент (модульное возведение) без какой-либо набивки и рандомизации. Уровень стойкости нулевой: одинаковый открытый текст всегда даёт одинаковый шифротекст, открытый текст и шифротекст алгебраически взаимно преобразуемы.
uint8 Cipher[128]; xrtRsaPublic(&Key.Public, SecretMessage, 128, Cipher); send(Cipher); /* RSA без набивки: открытый текст падает прямой математической атаке */
2) Подпись: xrtRsaPssSign (единственный RSA-режим подписи TLS 1.3) */ uint8 Signature[128]; xrtRsaPssSign(&Key, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, Digest, Signature);
Симптомы: ревью не проходит; в некоторых сценариях младшие биты общего секрета статистически смещены, и прямая подача в таблицу ключей AES ослабляет стойкость; протокол также не может вывести несколько ключей по назначениям.
Причина: выход DH — «математический артефакт», а не «годный ключевой материал» — форма, распределение и привязка назначения не дотягивают. HKDF спроектирован ровно для этого преобразования.
uint8 Shared[32]; xrtX25519Shared(sA, pubB, Shared); xaesgcm State; xrtAesGcmInit(&State, Shared, 32, 16); /* сырой секрет прямо как ключ */
uint8 Shared[32], SessionKey[32]; xrtX25519Shared(sA, pubB, Shared); xrtHkdfSha256(Salt, iSalt, Shared, sizeof(Shared), Transcript, iTranscript, SessionKey, sizeof(SessionKey)); xrtSecureZero(Shared, sizeof(Shared)); /* промежуточный секрет выбрасывается сразу */ xrtAesGcmInit(&State, SessionKey, 32, 16);
Симптомы: функция проверки явно вернула false, а код идёт по ветке успеха; или пытается «сравнить» подпись с ожидаемым значением — подписи несут случайную соль (PSS) или хотя детерминированы (Ed25519), но логика кода записала «проверку» как «сравнение».
Причина: проверка — предикатная функция (входы публичный ключ/сообщение/подпись, выход bool), а не «вычислить ожидание и сравнить». Случайная соль PSS означает: две подписи одного сообщения одним ключом законно различаются — «переподписать и сравнить» всегда падает на подписях со случайной солью.
uint8 Expect[128]; xrtRsaPssSign(&Key, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, Digest, Expect); /* переподпись */ if ( memcmp(Expect, Received, 128) == 0 ) { /* при случайной соли никогда не равны */ accept(); }
if ( xrtRsaPssVerify(&Key.Public, XCRYPTO_HASH_SHA256, XCRYPTO_HASH_SHA256, XRT_RSA_PSS_SALT_ANY, Digest, Received, 128) ) { accept(); /* проверка — предикат: ответ за один шаг */ }
Практические задания
Зафиксируйте приватные ключи обеих сторон (например, 32 байта 0x01 и 0x02), пройдите Public → 互发 → Shared полностью, напечатайте оба общих секрета в hex и подтвердите совпадение. Затем поменяйте роли приватных ключей и перегоните — симметрия не зависит от «кто инициатор». Критерий приёмки: два прогона побитово идентичны (детерминизм).
Подпишите три разных сообщения входами Ed25519 на состоянии, выведя таблицу векторов «сообщение в hex + подпись в hex»; затем переподпишите поштучным входом xrtEd25519Sign и проверьте побитовое совпадение с результатами на состоянии. Подсказка: фиксированный сид для воспроизводимости; это интероперационные векторы для коллег — любая реализация RFC 8032 (например OpenSSL) должна проверить их все.
На петлевом TCP реализуйте рукопожатие двух сторон: каждая генерирует эфемерную пару xrtX25519KeyPair, обмен публичными ключами (достаточно открытого JSON), обе стороны выводят сессионный ключ через Shared + HKDF (info включает «мини-протокол v1» и оба публичных ключа), затем обмениваются одним шифрованным сообщением ChaCha20-Poly1305 для проверки. Атака: человек-в-середине подменяет публичные ключи — объясните, почему голое согласование не стойко к MITM (ответ — сертификаты главы 79); вывод «нужны сертификаты» запишите в README. Критерий приёмки: весь ключевой материал (приватные ключи/общий секрет/сессионный ключ) после использования — xrtSecureZero; ноль утечек по статистике главы 6.
Итоги
| Тема | Краткая справка |
|---|---|
| Две линии | обмен (x25519/x448/P-256/P-384 ECDH) и подписи (Ed25519/ECDSA/RSA) взаимно независимы, режутся по нужде |
| Симметрия ECDH | sA·pubB == sB·pubA == sA·sB·G; подслушивающий видит только публичные ключи и секрет не вычислит |
| Входы обмена | голое скалярное умножение (без проверки) против Shared безопасного входа (обрабатывает злодейские публичные ключи) против KeyPair удобства (со случайностью) |
| Ed25519 | 32 сид/32 публичный/64 подписи; детерминировано; KeyInit/SignKey/KeyClear ускоряют многие подписи на состоянии |
| ECDSA | P-256/P-384; raw фиксированной длины r‖s и DER проводной формат сертификатов, независимый слой преобразования; RFC 6979 детерминировано |
| Расслоение RSA | голое модульное возведение (только фундамент) → PSS (единственный RSA-режим TLS 1.3) → PKCS1 (совместимость); результат перепроверяется публичной экспонентой |
| Соль PSS | явная воспроизводима, случайная по умолчанию, проверка XRT_RSA_PSS_SALT_ANY без ограничений |
| Выбор | новые системы Ed25519 + x25519; встраивание в PKI — ECDSA P-256; к легаси — только тогда RSA-PSS |
| Железные правила | общий секрет обязан через HKDF; RSA никогда не шифрует данные напрямую; проверка — предикат, не сравнение |
| Очистка | развёрнутые ключи KeyClear; сиды/приватные/общие секреты xrtSecureZero; проверке вычищать нечего |
📖 Справочники API в этой главе:crypto (140 записей)random (41 записей)
Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки