void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { ge_p3 A; sha512(seed, 32, private_key); private_key[0] &= 248; private_key[31] &= 63; private_key[31] |= 64; ge_scalarmult_base(&A, private_key); ge_p3_tobytes(public_key, &A); }То есть считается private_key = sha512(seed), фиксируются несколько бит и проводятся операции над curve25519 и в итоге получаем public_key. Вот после sha512 и битовыми операциями - получаем так называемый expanded_key. И private key от tor v3 собственно и есть этот expanded_key. А. Теперь вспомнил почему забросил эту тему :) Вместо того, чтобы показать seed размером 32 байта - mkp224o показывает нам кучу всего ненужного: expanded_key размером 64 байта, public_key в 32 байта, который получается из expanded, и onion адрес, который производный от public_key (+ байт версии и 2 байта хешсуммы в конце). Так что хранить придётся 64 байта на ключ или перезапускать mkp224... Печаль.
vGimly
2023-01-12
Tor v3
2021-04-07
BLAKE3 (надеюсь доступно)
Чуток поковырялся с хеш алгоритмом BLAKE3.
Интересный хеш. Криптографически стойкий (пока не доказана эффективность но и обратного тоже нет). Но быстрее SHA-256, SHA-512, SHA1 и даже MD5.
Из полезного.
0. Очень быстрый алгоритм.
Операции проводятся над 32-битными целыми числами без знака.
Используются только операции:
ADD сложение "+",
XOR исключающее-или "^",
ROR циклический сдвиг ">>",
операция перемешивания слов по фиксированной таблице (всего 16 значений).
Всё это сопровождается весьма малым числом раундов (всего 7).
Используются блоки данных 64 байта (+ 64 байта внутреннего состояния с ключом и значениями хеша предыдущего звена цепи, флагов и счётчика чанков).
В итоге всё очень хорошо укладывается в целочисленные операции SIMD современных процессоров и их кеш память.
Но и этого разработчикам показалось мало, так как алгоритм ещё и практически неограниченно распараллеливается из-за использования бинарного дерева Меркла (блоками по 1024 байта).
2. Встроенные алгоритмы деривации (наследования) ключей KDF и MAK. Кому это надо - знают зачем. См. п.0. Фактически проводится стандартные раунды хеширования, меняются только флаги (и/или используется хеш ключевого материала в поле CV вместо IV).
3. При хранении значений бинарного дерева произвольного уровня можно не пересчитывать весь хеш заново (при отсутствии изменений внутри некоторых блоков(. Похоже на то, что используется в BitTorrent (там считается и хранится SHA1 хеш каждого блока отдельно), TTH и AICH. Но в отличии от них - хеши одинаковых блоков будут разными, если они находятся по разным смещениям (внутри блока состояния, участвующего в вычислении хеша есть меняющийся счётчик 1024-байтных блоков-чанков).
Ещё раз - алгоритм ОЧЕНЬ быстрый. Быстрее даже MD5. Авторы обещают, что большая длинна хеша важнее количества раундов - и алгоритм пройдёт инспекцию криптоаналитиков.
Очень на это надеюсь. И думаю что за алгоритмом есть хорошее будущее.
== tldr; BEGIN ==
Из плохого. Рефренная реализация на языке rust. Быстрая SIMD реализация b3sum и библиотека blake3 - на языке rust. Готовых модулей Perl пока нет. Встречались реализации на других языках (C#/Blake3.NET .NET 5.0 wtf), но они использовали внешнюю библиотеку blake3 на rust.
Зато есть реализация на C, но тоже оптимизированная под SIMD и с ассемблерным кодом под разные платформы/ассемблеры - но не многопоточная (хотя и это и не помогло в понимании работы алгоритма), однако позволило сделать версию под VS с выдачей отладочной информации о входах-выходах функции компрессии и изменениях внутреннего состояния в ней. Заодно пришло понимание запутанности C реализации из-за алгоритмических оптимизаций (гораздо больше почти однотипных на первый взгляд функций чем в рефрене) и куча кода непонятного на первый взгляд назначения.
В итоге таки пришлось чуток поплавать по поверхности этого странного языка, так как его рефренную реализацию оказалось проще понять и использовать для реализации на Perl. Многое в простом rust коде было понятно даже без начальных знаний о языке - а код на C был в этом лишь подспорьем.
Установка среды rust, настройка окружения, первая сборка. Копание в библиотеке blake3. Первый println! внутри функции компрессии. И до готового решения (очень пока грязного - не нашёл способа как потокобезопасно передавать открытые файлы вглубь функций, которые делают параллельные вычисления) чтобы b3sum выдала в mailslot на perl дерево Меркла блоков файла (который кладёт это дерево в sqlite базу). Переписывание библиотеки Win32::Mailslot - понимание того, что я не знаю perl xs и не знаю как её поправить чтобы понимала строки с нулями. Как использовать функцию newSVpn если нет нормальных примеров а автор Mailslot про неё вообще не знал (он честно сказал что это его первый подход к XS - и судя по коду он нормально знал только msvc). Неспроста этого модуля нет в cpan. Но тут вспомнил про Win32API::File - спасибо автору за чистый и понятный код, который я уже когда-то даже правил (очень нужная мне в моей считалке хешей функция SetEndOfFile).
И в итоге b3sum уделывает мою старую считалку стоя и в рост (которую, как я думал, ограничивает лишь производительность диска) - даже с чудовищно вкряченным и отвратительным решением, с тремя переходами на unsafe код и обратно, открывающим и закрывающим mailslot на каждый чих расчёта суммы блока дерева.
== tldr; END ==
Итак. Алгоритм BLAKE3.
Используемые обозначения:
блок - 64 байта, обработка хешируемых данных идёт такими "кусочками". Может быть меньше, если входных данных не хватает - тогда недостающая часть заполняется нулями до 64 байт.
чанк - 1024 байта (1 килобайт или по рекомендациям МЭК кибибайт 1 КиБ или KiB) - набор из 16 блоков по 64 байта. Может быть меньше (до 1), если блоков не хватает. Используется для "нулевого" уровня дерева Меркла - хеши чанков попарно объединяются для получения корневого хеша.
u8/u32/u64 - беззнаковые целые, соответственно 8 бит (байт), 32 бита (двойное слово, DWORD), 64 бита (unsigned long, QWORD).
1. IV инициализационный вектор - 256 бит или 8 u32.
Он используется много где - гугл даёт ссылки на него же в SHA256 и BitCoin - пишут это набор первых 32 бит дробной части (иррациональных) квадратных корней простых чисел по возрастанию: 2, 3, 5, 7, 11, 13, 17, 23):
67e6096a85ae67bb72f36e3c3af54fa57f520e518c68059babd9831f19cde05b
Ну или как он же записан как u32 в шестнадцатеричном виде:
const IV: [u32; 8] = [
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
];
Он используется как ключ (первые 4 u32 - кроме случаев MAK и KDF) и как начальный CV каждого чанка (все 8 слов).
2. Функция компрессии compress.
На вход подаются:
$block_words, # [u32; 16]
$chaining_value, # [u32; 8]
$counter, # u64
$block_len, # u32
$flags, # u8
Zero filled: если данных не достаточно (менее 64 байт), остаток блока заполняется нулями до 64 байт. Пустой блок соответственно 64 нулевых байта.
Сюда же подсовывается IV для первого блока или хеш ключа для HMAK/KDF.
CHUNK_START | CHUNK_END = x03
Если это окажется ещё и выходной хеш (для короткого или пустого файла):
CHUNK_START | CHUNK_END | ROOT = x0B
Все операции проводят над беззнаковыми целыми 32-битными числами u32.
Работающую реализацию для rust берите из рефрена.
2021-02-25
Подключение iSCSI: Authentication Failure OneWayCHAP / MutualCHAP (Решение)
<target iqn.2020-03.myhost.ru:lun1>backing-store /dev/disk/by-id/usb-Netac_OnlyDisk_6237111-0:0initiator-address 192.168.1.30incominguser iqn.1991-05.com.microsoft:serv.local SecretStringOneoutgoinguser iqn.1991-05.com.microsoft:serv.local SecretStringTwo</target>
PS C:\WINDOWS\system32>> Connect-IscsiTarget -NodeAddress iqn.2020-03.myhost.ru:lun1 -AuthenticationType OneWayCHAP -ChapUsername iqn.1991-05.com.microsoft:serv.local -ChapSecret SecretStringOneConnect-IscsiTarget : Authentication Failure.строка:1 знак:1+ Connect-IscsiTarget -NodeAddress "iqn.2020-03.h001.ru:lun1" -Authenti ...+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ CategoryInfo : NotSpecified: (MSFT_iSCSITarget:ROOT/Microsoft/...SFT_iSCSITarget) [Connect-IscsiTarget], CimException+ FullyQualifiedErrorId : HRESULT 0xefff0009,Connect-IscsiTarget
2019-03-26
Экспорт приватных ключей Гост. ч.2
Итак задача та же: нужно получить "бесплатно" закрытые ключи из "дефакто" стандартного гостовского провайдера в openssl.
Система Windows 10.0.17134.648
Ключ с привязанным секретным ключом установлен через CSP.
Запускаем certmgr.msc. Личное - Сертификаты.
Жмём ПКМ на сертификат с привязанным ключом гост (экспортируемым).
Все задачи - Экспорт.
Далее. - Да, экспортировать закрытый ключ - Далее
Файл обмена личной информацией - PKCS #12 (.PFX).
Галочки можно не трогать - но я снял все. Лишнее не надо.
Далее. Галочку на пароль. Вбиваем 123 и 123 в подтверждение.
Шифрование TripleDES-SHA1 (можно и AES256-SHA256 - hmac проверка будет чуть другой).
Далее - Имя файла выбираем 123-try3.pfx - Далее - "Экспорт выполнен" - Готово.
Файл с приватным ключом лежит на диске. Радуемся но не долго.
$ openssl pkcs12 -info -engine gost -password pass:123 -in 123-try3.pfx
engine "gost" set.
MAC: sha1, Iteration 2000
MAC length: 20, salt length: 20
PKCS7 Data
Shrouded Keybag: undefined, Iteration 2000
Bag Attributes
localKeyID: 01 00 00 00
friendlyName: 9d09886d-f53d-4e57-85b1-c3c25b0cd72e
Microsoft CSP Name: Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider
Error outputting keys and certificates
25769902144:error:06074079:digital envelope routines:EVP_PBE_CipherInit:unknown pbe algorithm:crypto/evp/evp_pbe.c:95:TYPE=1.2.840.113549.1.12.1.80
25769902144:error:23077073:PKCS12 routines:PKCS12_pbe_crypt:pkcs12 algor cipherinit error:crypto/pkcs12/p12_decr.c:41:
25769902144:error:2306A075:PKCS12 routines:PKCS12_item_decrypt_d2i:pkcs12 pbe crypt error:crypto/pkcs12/p12_decr.c:94:
Встречает наш знакомый oid=1.2.840.113549.1.12.1.80.
$ openssl asn1parse -inform der -in 123-try3.pfx
0:d=0 hl=4 l=1140 cons: SEQUENCE 4:d=1 hl=2 l= 1 prim: INTEGER :03 7:d=1 hl=4 l=1072 cons: SEQUENCE 11:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data 22:d=2 hl=4 l=1057 cons: cont [ 0 ] 26:d=3 hl=4 l=1053 prim: OCTET STRING [HEX DUMP]:
1083:d=1 hl=2 l= 59 cons: SEQUENCE 1085:d=2 hl=2 l= 31 cons: SEQUENCE 1087:d=3 hl=2 l= 7 cons: SEQUENCE 1089:d=4 hl=2 l= 5 prim: OBJECT :sha1 1096:d=3 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:
BA88838B4D071952A12D2AE49E91B16E9D1E4719 1118:d=2 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:
767C58C151C9125E72367411A97E9E706C95905C 1140:d=2 hl=2 l= 2 prim: INTEGER :07D0
Из предыдущего поста понятно как проверить целостность pkcs7-data - тут алгоритм видимо стандартный - но я его привёл perl скриптом. Скрипт всё ещё работает. Пароль кодируем в UCS2 - размножаем и дополняем номером версии (3) и солью, хешируем SHA1 ROUNDS раз и делаем HMAC_SHA1.
Номером версии=03
ROUNDS=0x7D0 (2000)
SALT=767C58C151C9125E72367411A97E9E706C95905C
HMAC=BA88838B4D071952A12D2AE49E91B16E9D1E4719
Если выводить с "AES256-SHA256" меняем алгоритм суммы SHA256 и HMAC_SHA256.
Но всё равно никаких 3Des и AES в файле не используется.
$VER=3; $PASS='123';
$ROUNDS=0x7D0; $SALT=pack 'H*', '767C58C151C9125E72367411A97E9E706C95905C'; $SHA1_NEED=pack 'H*','BA88838B4D071952A12D2AE49E91B16E9D1E4719'; $BLOB=pack 'H*', <<EOF;
EOF use Encode 'encode'; use Digest::SHA qw(hmac_sha1 sha1); $KEY=encode('UCS-2BE', $PASS."\0"); $KEY=((pack'C',$VER) x 64) . substr($SALT x 4, 0, 64) . substr($KEY x 32, 0, 64); $KEY=sha1($KEY) for 1..$ROUNDS; $SHA1_GOT=hmac_sha1($BLOB, $KEY);
use v5.10; say 'HMAC: ',unpack 'H*', $KEY; say 'NEED: ',unpack 'H*', $SHA1_NEED; say 'CALC: ',unpack 'H*', $SHA1_GOT;
Продолжаем. Берём в буфер обмена содержимое pkcs7-data - пишем в файл и парсим asn1.
$ xxd -r -p > 123-try3.pkcs7 (paste) $ openssl asn1parse -inform der -in 123-try3.pkcs7
0:d=0 hl=4 l=1049 cons: SEQUENCE 4:d=1 hl=4 l= 470 cons: SEQUENCE 8:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data 19:d=2 hl=4 l= 455 cons: cont [ 0 ] 23:d=3 hl=4 l= 451 prim: OCTET STRING [HEX DUMP]:
478:d=1 hl=4 l= 571 cons: SEQUENCE 482:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data 493:d=2 hl=4 l= 556 cons: cont [ 0 ] 497:d=3 hl=4 l= 552 prim: OCTET STRING [HEX DUMP]:
Видим теперь два объекта - второй это сертификат - и нам не нужен. Ключ в первом "мешке". Повторяем для него процедуру.
$ xxd -r -p > 123-try3.pkcs8 (paste) $ openssl asn1parse -inform der -in 123-try3.pkcs8
0:d=0 hl=4 l= 447 cons: SEQUENCE 4:d=1 hl=4 l= 443 cons: SEQUENCE 8:d=2 hl=2 l= 11 prim: OBJECT :pkcs8ShroudedKeyBag 21:d=2 hl=3 l= 178 cons: cont [ 0 ] 24:d=3 hl=3 l= 175 cons: SEQUENCE 27:d=4 hl=2 l= 36 cons: SEQUENCE 29:d=5 hl=2 l= 10 prim: OBJECT :1.2.840.113549.1.12.1.80 41:d=5 hl=2 l= 22 cons: SEQUENCE 43:d=6 hl=2 l= 16 prim: OCTET STRING [HEX DUMP]: C16E378ABE17ADBC7C29E4F5EA4EEED9 61:d=6 hl=2 l= 2 prim: INTEGER :07D0 65:d=4 hl=3 l= 134 prim: OCTET STRING [HEX DUMP]:
202:d=2 hl=3 l= 246 cons: SET 205:d=3 hl=2 l= 19 cons: SEQUENCE 207:d=4 hl=2 l= 9 prim: OBJECT :localKeyID 218:d=4 hl=2 l= 6 cons: SET 220:d=5 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:01000000 226:d=3 hl=2 l= 87 cons: SEQUENCE 228:d=4 hl=2 l= 9 prim: OBJECT :friendlyName 239:d=4 hl=2 l= 74 cons: SET 241:d=5 hl=2 l= 72 prim: BMPSTRING 315:d=3 hl=3 l= 133 cons: SEQUENCE 318:d=4 hl=2 l= 9 prim: OBJECT :Microsoft CSP Name 329:d=4 hl=2 l= 120 cons: SET 331:d=5 hl=2 l= 118 prim: BMPSTRINGВот мы и добрались до этого unknown gost 1.2.840.113549.1.12.1.80 штатными средствами openssl.
Если загрузить исходный pfx в онлайн просмотрщик - можно было увидеть всю структуру сразу.
0 30 1140: SEQUENCE { 4 02 1: INTEGER 3 7 30 1072: SEQUENCE { 11 06 9: OBJECT IDENTIFIER data (1.2.840.113549.1.7.1) 22 a0 1057: [0] { 26 04 1053: OCTET STRING, encapsulates { 30 30 1049: SEQUENCE { 34 30 470: SEQUENCE { 38 06 9: OBJECT IDENTIFIER data (1.2.840.113549.1.7.1) 49 a0 455: [0] { 53 04 451: OCTET STRING, encapsulates { 57 30 447: SEQUENCE { 61 30 443: SEQUENCE { 65 06 11: OBJECT IDENTIFIER pkcs8ShroudedKeyBag (1.2.840.113549.1.12.10.1.2) 78 a0 178: [0] { 81 30 175: SEQUENCE { 84 30 36: SEQUENCE { 86 06 10: OBJECT IDENTIFIER pbeUnknownGost (1.2.840.113549.1.12.1.80) 98 30 22: SEQUENCE { 100 04 16: OCTET STRING : c1 6e 37 8a be 17 ad bc 7c 29 e4 f5 ea 4e ee d9 118 02 2: INTEGER 2000 : } : } 122 04 134: OCTET STRING : c4 a7 be 9f 5d ec 55 36 9a 1e 0b 35 7b 2d 05 22 : 56 f2 05 2e 10 64 2b 47 cc 8a 56 73 51 e8 15 84 : ff d7 dc b7 d2 38 4e ec 45 66 a0 2a 67 f9 ef 75 : 04 d7 a8 fa 4c cc 51 72 2d 53 77 0f 2b 4b 4d 06 : 67 f0 30 e9 06 0a 6c 0f 4e d3 d7 33 42 c0 c1 c8 : 6f d9 66 9e 98 08 b9 8a 37 2b c0 af 89 19 cc 50 : 2f 9c 74 35 1c 60 b4 3a d8 9d 6f 41 55 03 5c da : 5e be 92 c3 6d 3d 2c 8e a5 62 f0 f5 58 45 29 55 : 22 58 aa c8 4c c4 : } : } 259 31 246: SET { 262 30 19: SEQUENCE { 264 06 9: OBJECT IDENTIFIER localKeyId (1.2.840.113549.1.9.21) 275 31 6: SET { 277 04 4: OCTET STRING, encapsulates { 279 01 0: BOOLEAN true : } : } : } 283 30 87: SEQUENCE { 285 06 9: OBJECT IDENTIFIER friendlyName (1.2.840.113549.1.9.20) 296 31 74: SET { 298 1e 72: BMPString "9d09886d-f53d-4e57-85b1-c3c25b0cd72e" : } : } 372 30 133: SEQUENCE { 375 06 9: OBJECT IDENTIFIER keyProviderNameAttr (1.3.6.1.4.1.311.17.1) 386 31 120: SET { 388 1e 118: BMPString "Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider" : } : } : } : } : } : } : } : } 508 30 571: SEQUENCE { 512 06 9: OBJECT IDENTIFIER data (1.2.840.113549.1.7.1) 523 a0 556: [0] { 527 04 552: OCTET STRING, encapsulates { 531 30 548: SEQUENCE { 535 30 544: SEQUENCE { 539 06 11: OBJECT IDENTIFIER certBag (1.2.840.113549.1.12.10.1.3) 552 a0 504: [0] { 556 30 500: SEQUENCE { 560 06 10: OBJECT IDENTIFIER x509Certificate (1.2.840.113549.1.9.22.1) 572 a0 484: [0] { 576 04 480: OCTET STRING, encapsulates { 580 30 476: SEQUENCE { 584 30 395: SEQUENCE { 588 a0 3: [0] { 590 02 1: INTEGER 2 : } 593 02 8: INTEGER : 66 1c 4e db fe 64 8d 91 603 30 8: SEQUENCE { 605 06 6: OBJECT IDENTIFIER id-GostR3411-94-with-GostR3410-2001 (1.2.643.2.2.3) : } 613 30 31: SEQUENCE { 615 31 11: SET { 617 30 9: SEQUENCE { 619 06 3: OBJECT IDENTIFIER countryName (2.5.4.6) 624 13 2: PrintableString "RU" : } : } 628 31 16: SET { 630 30 14: SEQUENCE { 632 06 3: OBJECT IDENTIFIER commonName (2.5.4.3) 637 13 7: PrintableString "pub2001" : } : } : } 646 30 30: SEQUENCE { 648 17 13: UTCTime Mon Mar 25 2019 19:43:00 GMT+0300 (Москва, стандартное время) 663 17 13: UTCTime Wed Mar 25 2020 19:43:00 GMT+0300 (Москва, стандартное время) : } 678 30 31: SEQUENCE { 680 31 11: SET { 682 30 9: SEQUENCE { 684 06 3: OBJECT IDENTIFIER countryName (2.5.4.6) 689 13 2: PrintableString "RU" : } : } 693 31 16: SET { 695 30 14: SEQUENCE { 697 06 3: OBJECT IDENTIFIER commonName (2.5.4.3) 702 13 7: PrintableString "pub2001" : } : } : } 711 30 99: SEQUENCE { 713 30 28: SEQUENCE { 715 06 6: OBJECT IDENTIFIER id-GostR3410-2001 (1.2.643.2.2.19) 723 30 18: SEQUENCE { 725 06 7: OBJECT IDENTIFIER id-GostR3410-2001-CryptoPro-XchA-ParamSet (1.2.643.2.2.36.0) 734 06 7: OBJECT IDENTIFIER id-GostR3411-94-CryptoProParamSet (1.2.643.2.2.30.1) : } : } 743 03 67: BIT STRING, unused 0 bits, encapsulates { 745 04 64: OCTET STRING : 32 71 b0 ab c9 cc 92 14 23 84 d0 b1 0a dc bc 41 : 24 a1 d5 a9 af d4 40 f6 a9 9a ad dd 88 2e 70 cf : db 9a ed 16 82 a9 45 df 95 6e 04 f8 c9 7e 83 33 : cf 83 9b 46 13 53 be d1 a0 4c d9 93 04 37 2b 71 : } : } 812 a3 168: [3] { 815 30 165: SEQUENCE { 818 30 14: SEQUENCE { 820 06 3: OBJECT IDENTIFIER keyUsage (2.5.29.15) 825 01 1: BOOLEAN true 828 04 4: OCTET STRING, encapsulates { 830 03 2: BIT STRING, unused 2 bits : 110111B : } : } 834 30 29: SEQUENCE { 836 06 3: OBJECT IDENTIFIER extKeyUsage (2.5.29.37) 841 04 22: OCTET STRING, encapsulates { 843 30 20: SEQUENCE { 845 06 8: OBJECT IDENTIFIER clientAuth (1.3.6.1.5.5.7.3.2) 855 06 8: OBJECT IDENTIFIER emailProtection (1.3.6.1.5.5.7.3.4) : } : } : } 865 30 29: SEQUENCE { 867 06 3: OBJECT IDENTIFIER subjectKeyIdentifier (2.5.29.14) 872 04 22: OCTET STRING, encapsulates { 874 04 20: OCTET STRING : 75 c3 bd ab 0e 1b 55 34 dd 35 45 7a 20 1a c7 f4 : 6d b1 df 55 : } : } 896 30 68: SEQUENCE { 898 06 3: OBJECT IDENTIFIER authorityKeyIdentifier (2.5.29.35) 903 04 61: OCTET STRING, encapsulates { 905 30 59: SEQUENCE { 907 80 20: [0] : 75 c3 bd ab 0e 1b 55 34 dd 35 45 7a 20 1a c7 f4 : 6d b1 df 55 929 a1 35: [1] { 931 a4 33: [4] { 933 30 31: SEQUENCE { 935 31 11: SET { 937 30 9: SEQUENCE { 939 06 3: OBJECT IDENTIFIER countryName (2.5.4.6) 944 13 2: PrintableString "RU" : } : } 948 31 16: SET { 950 30 14: SEQUENCE { 952 06 3: OBJECT IDENTIFIER commonName (2.5.4.3) 957 13 7: PrintableString "pub2001" : } : } : } : } : } : } : } : } 966 30 15: SEQUENCE { 968 06 3: OBJECT IDENTIFIER basicConstraints (2.5.29.19) 973 04 8: OCTET STRING, encapsulates { 975 30 6: SEQUENCE { 977 01 1: BOOLEAN true 980 02 1: INTEGER 1 : } : } : } : } : } : } 983 30 8: SEQUENCE { 985 06 6: OBJECT IDENTIFIER id-GostR3411-94-with-GostR3410-2001 (1.2.643.2.2.3) : } 993 03 65: BIT STRING, unused 0 bits : 56 d6 df 33 8f e5 d6 29 24 32 77 f3 1d b5 6f cb : 5b 7c bc af 8e fd 8f 86 44 cc b7 35 9e ac ce 86 : b0 21 31 c1 a5 02 97 50 f7 d8 31 b9 07 00 0d fe : 38 df cd 18 a9 82 03 77 33 84 c1 8b 48 90 fa d1 : } : } : } : } : } 1060 31 21: SET { 1062 30 19: SEQUENCE { 1064 06 9: OBJECT IDENTIFIER localKeyId (1.2.840.113549.1.9.21) 1075 31 6: SET { 1077 04 4: OCTET STRING, encapsulates { 1079 01 0: BOOLEAN true : } : } : } : } : } : } : } : } : } : } : } : } : } 1083 30 59: SEQUENCE { 1085 30 31: SEQUENCE { 1087 30 7: SEQUENCE { 1089 06 5: OBJECT IDENTIFIER sha1 (1.3.14.3.2.26) : } 1096 04 20: OCTET STRING : ba 88 83 8b 4d 07 19 52 a1 2d 2a e4 9e 91 b1 6e : 9d 1e 47 19 : } 1118 04 20: OCTET STRING : 76 7c 58 c1 51 c9 12 5e 72 36 74 11 a9 7e 9e 70 : 6c 95 90 5c 1140 02 2: INTEGER 2000 : } : }
Продолжаем снимать транспортную кодировку.
Получили в данный момент такие цифры:
PASS=123 SALT=C16E378ABE17ADBC7C29E4F5EA4EEED9 ROUNDS=0x7D0 ENCRYPTED KEYBAG:
use Digest::GOST::CryptoPro qw(gost);
$KEY=encode('UCS-2LE', $PASS);
for ($i=1;$i<=$ROUNDS;$i++){$KEY=gost($KEY.$SALT.pack('n',$i))}
say 'PASSWORD=',unpack 'H*',$KEY;
Для предыдущих цифр получаем ключ:KEY=c2bb428f981f30d8e623db786d9bd780e7d9f785a950211530142ae46fab8dceВот раунды хеширования если будете повторять: 310032003300 c16e378abe17adbc7c29e4f5ea4eeed9 0001 fb76e01671ac07908c159f59a0442815acaccc2e77fc25958b99a4626fc738fe c16e378abe17adbc7c29e4f5ea4eeed9 0002 cdae166434edddd770cbc0d7294f4031d83309206bc15a3089cbde622fba0ce7 c16e378abe17adbc7c29e4f5ea4eeed9 0003 ...9350e6ec445712728c6d7c6c02efb9068f295a3f451af8df33613bfc42412d71 c16e378abe17adbc7c29e4f5ea4eeed9 07d0Хеш сумму можно получить штатными средствами так:$ echo 310032003300 c16e378abe17adbc7c29e4f5ea4eeed9 0001 | xxd -r -p > round1$ gostsum round1fe38c76f62a4998b9525fc772eccacac152844a0599f158c9007ac7116e076fb round1$ openssl dgst -engine gost -md_gost94 -hex round1md_gost94(round1)= fb76e01671ac07908c159f59a0442815acaccc2e77fc25958b99a4626fc738fegostsum есть в пакетах, собирается из gost engine от openssl (есть на github) да, и он выдаёт другой порядок байт, что надо учитывать.Всё это было и в первой части - тут чуть подробнее.
Теперь продолжение:
Снимаем транспортную кодировку с помощью openssl.
IV=C16E378ABE17ADBC (игнорируем остаток 7C29E4F5EA4EEED9)MODE=CFB (Режим шифрования - он по умолчанию для gost89 в openssl)
IV (Начальный вектор): SALT(0:7) (соль больше блока шифрования магмы - для IV отрезаем её до 8 байт=16 шестнадцатеричных символов = 64 бита - или игнорируем warning)
KEY (Ключ - получили выше) 256 бит = 32 байта = 64 hex цифры.KEY=c2bb428f981f30d8e623db786d9bd780e7d9f785a950211530142ae46fab8dce$ xxd -r -p > 123-try3.enc(paste keybag C4A7BE9F5DEC55369A1E0B...)$ CRYPT_PARAMS=1.2.643.2.2.31.1 openssl enc -engine gost -d -gost89 -in 123-try3.enc -out 123-try3.dec \
-K c2bb428f981f30d8e623db786d9bd780e7d9f785a950211530142ae46fab8dce -iv C16E378ABE17ADBC7C29E4F5EA4EEED9На всякий случай задали параметры шифрования через env CRYPT_PARAMS но по умолчанию они же стоят. Должны получить asn1 c экспортным ключом (первый asci символ "0").$ openssl asn1parse -inform der -in 123-try3.dec0:d=0 hl=3 l= 131 cons: SEQUENCE 3:d=1 hl=2 l= 1 prim: INTEGER :00 6:d=1 hl=2 l= 10 cons: SEQUENCE 8:d=2 hl=2 l= 6 prim: OBJECT :GOST R 34.10-2001 16:d=2 hl=2 l= 0 prim: NULL 18:d=1 hl=2 l= 114 prim: OCTET STRING [HEX DUMP]: 0720000024AA00004D41473120000000
306030580408FEF0268E68E6A7EA302804207987275594A59F491E62F636F794BDD49A5CD547E0D901DF3AD58DA9EFFC2657040468369E8AA022030205A0A01C06062A8503020262301206072A85030202240006072A850302021E010404B30785AAСодержимое блоба - это то, что мы получаем, если экспорт приватного ключа сделать через winapi. То есть транспортную кодировку мы полностью сняли.Осталось то что получили внутрь openssl прописать :)Посмотрим что нам даёт криптопро в своей экспортной структуре.Заголовок структуры 07 - PRIVATEKEYBLOB (06 - PUBLICKEY).Такой параметр передаётся в функцию ExportKey.000020 - размер ключа? Или тут всё таки 00002007 ?АА24 - algid:
algInfo name:DH 34.10-2001
longName:GOST R 34.10-2001 256 DHalgId:43556(AA24)
type:KeyExchange
OID:1.2.643.2.2.98
OID.alg=43556(AA24)
OID_groupId:3:PUBKEY_ALG4D414731 = MAG1 "Магический заголовок"?00000020 - опять размер ключа?Дальше в структуре снова идёт asn1.Отрезаем заголовок и смотрим что там:
0:d=0 hl=2 l= 96 cons: SEQUENCE 2:d=1 hl=2 l= 88 cons: SEQUENCE 4:d=2 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:FEF0268E68E6A7EA 14:d=2 hl=2 l= 40 cons: SEQUENCE 16:d=3 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]: 7987275594A59F491E62F636F794BDD49A5CD547E0D901DF3AD58DA9EFFC2657 50:d=3 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:68369E8A 56:d=2 hl=2 l= 34 cons: cont [ 0 ] 58:d=3 hl=2 l= 2 prim: BIT STRING 62:d=3 hl=2 l= 28 cons: cont [ 0 ] 64:d=4 hl=2 l= 6 prim: OBJECT :GOST R 34.10-2001 DH 72:d=4 hl=2 l= 18 cons: SEQUENCE 74:d=5 hl=2 l= 7 prim: OBJECT :id-GostR3410-2001-CryptoPro-XchA-ParamSet 83:d=5 hl=2 l= 7 prim: OBJECT :id-GostR3411-94-CryptoProParamSet 92:d=1 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:B30785AAИли на онлайн сервисе:
0 30 96: SEQUENCE { 2 30 88: SEQUENCE { 4 04 8: OCTET STRING : fe f0 26 8e 68 e6 a7 ea 14 30 40: SEQUENCE { 16 04 32: OCTET STRING : 79 87 27 55 94 a5 9f 49 1e 62 f6 36 f7 94 bd d4 : 9a 5c d5 47 e0 d9 01 df 3a d5 8d a9 ef fc 26 57 50 04 4: OCTET STRING : 68 36 9e 8a : } 56 a0 34: [0] { 58 03 2: BIT STRING, unused 5 bits : 101B 62 a0 28: [0] { 64 06 6: OBJECT IDENTIFIER id-GostR3410-2001DH (1.2.643.2.2.98) 72 30 18: SEQUENCE { 74 06 7: OBJECT IDENTIFIER id-GostR3410-2001-CryptoPro-XchA-ParamSet (1.2.643.2.2.36.0) 83 06 7: OBJECT IDENTIFIER id-GostR3411-94-CryptoProParamSet (1.2.643.2.2.30.1) : } : } : } : } 92 04 4: OCTET STRING : b3 07 85 aa : }
id-GostR3410-2001-CryptoPro-XchA-ParamSet
id-GostR3410-2001DH
id-GostR3411-94-CryptoProParamSetТакже тут поработали такие не(до)документированные алгоритмы:CALG_PRO_EXPORT CALG_PRO12_EXPORT CALG_SIMPLE_EXPORT
Упоминается, что они "работают в соответствии с рекомендациями ТК26" - но как обратить их работу надо ещё смотреть.
И надо помнить, что у нас есть пароль на экспорт (может быть и не участвует) и публичный ключ (из сертификата).
Публичный ключ (получить легко)=
3271B0ABC9CC92142384D0B10ADCBC4124A1D5A9AFD440F6A99AADDD882E70CF DB9AED1682A945DF956E04F8C97E8333CF839B461353BED1A04CD99304372B71Части приватного ключа (при экспорте приватного в формате PEM)=00DC1DFB629158E86D246F26DADD3289562910A1D65FD74E523A636351B8C33D4597BEC52D5D4704(видимо что-то фокс зашифровал)Экспорт приватного ключа фоксом в pkcs8 без шифра даёт вот это:
{privateKeyAlgorithm: { name: "GOST R 34.10-2001", id: "id-GostR3410-2001", namedCurve: "X-256-A", sBox: "D-A" }, privateKey: [ 0x04, 0x20, 0x31, 0x96, 0x76, 0x58, 0xc0, 0x64, 0x7a, 0xcd, 0xbd, 0x2c, 0x8a, 0x8d, 0x55, 0xcc, 0x03, 0x18, 0x03, 0x20, 0x8d, 0x94, 0x29, 0x2c, 0x16, 0x22, 0x1f, 0x68, 0xd9, 0xd7, 0xca, 0xa5, 0xd7, 0x3e ] }Приватный ключ= 042031967658C0647ACDBD2C8A8D55CC031803208D94292C16221F68D9D7CAA5D73E
Осталось это получить из шифрованного ключа.
...
Дополнения.
По адресу: https://tc26.ru/standard/rs/%D0%A0%2050.1.111-2016.pdf https://www.s-terra.ru/upload/medialibrary/9bb/rekomendacii-po-standartizacii-kriptografia.pdf нашлось описание хотелок ТК26, оформленных в видеРЕКОМЕНДАЦИИ ПО СТАНДАРТИЗАЦИИ ИСПОЛЬЗОВАНИЕ КРИПТОГРАФИЧЕСКИХ АЛГОРИТМОВ, СОПУТСТВУЮЩИХ ПРИМЕНЕНИЮ СТАНДАРТОВ ГОСТ Р 34.10-2012 И ГОСТ Р 34.11-2012 Утверждены решением заседания технического комитета по стандартизации «Криптографическая защита информации» (Протокол №13 от 24.04.2014 г.)В которых в пункте 5.6 "Экспорт и импорт ключей" дают такой алгоритм :
1) Порождается случайный набор UKM. 2) С помощью функции диверсификации, использующей в качестве ключа диверсификации ключ экспорта Ke, и в качестве значения seed случайный набор UKM, производится формирование ключа, обозначаемого KEKe(UKM). KEKe(UKM) = KDF(Ke,label,UKM). 3) Вычисляется значение имитовставки по ГОСТ 28147-89 длины 4 байта от данных K на ключе KEKe(UKM), синхропосылка при этом полагается равной первым 8 байтам UKM. Полученный набор обозначается через CEK_MAC. Compute a 4-byte checksum value, gost28147IMIT (UKM, KEK(UKM), CEK). Call the result CEK_MAC. [rfc4357] 4) Ключ K зашифровывается по алгоритму ГОСТ 28147-89 в режиме простой замены с использованием ключа KEKe(UKM). Результат зашифрования обозначается через CEK_ENC. Encrypt CEK in ECB mode using KEK(UKM). Call the ciphertext CEK_ENC. [rfc4357] 5) Экспортным представлением ключа полагается набор (UKM | CEK_ENC | CEK_MAC). входной набор для порождения ключа: KDF(Ke,label,UKM) выходной набор: UKM, CEK_ENC, CEK_MAC label=26BDB878 (фиксированный набор - приведён в "рекомендациях") UKM=FEF0268E68E6A7EA (очевидно самая длинная посылка, т.к. её размер минимум 8 байт) CEK_ENC=7987275594A59F491E62F636F794BDD49A5CD547E0D901DF3AD58DA9EFFC2657 CEK_MAC=68369E8A или B30785AA (обе цифры длинной 4 байта - и могут быть MAC)В секции 5.4 дают функцию диверсификации KDF_GOSTR3411_2012_256:
KDF(Kin,label,seed) = HMAC256(Kin,0x01|label|0x00|seed|0x01|0x00)Осталось "угадать" значение Ke Читаем rfc4357 как "изуродовать" K до Ke:
6.5. CryptoPro KEK Diversification Algorithm Given a random 64-bit UKM and a GOST 28147-89 key K, this algorithm creates a new GOST 28147-89 key K(UKM). 1) Let K[0] = K; 2) UKM is split into components a[i,j]: UKM = a[0]|..|a[7] (a[i] - byte, a[i,0]..a[i,7] - it's bits) 3) Let i be 0. 4) K[1]..K[8] are calculated by repeating the following algorithm eight times: A) K[i] is split into components k[i,j]: K[i] = k[i,0]|k[i,1]|..|k[i,7] (k[i,j] - 32-bit integer) B) Vector S[i] is calculated: S[i] = ((a[i,0]*k[i,0] + ... + a[i,7]*k[i,7]) mod 2^32) | (((~a[i,0])*k[i,0] + ... + (~a[i,7])*k[i,7]) mod 2^32); C) K[i+1] = encryptCFB (S[i], K[i], K[i]) D) i = i + 1 5) Let K(UKM) be K[8]. 7. Secret Key Diversification This algorithm creates a GOST 28147-89 key Kd, given GOST R 34.10-94 or GOST R 34.10-2001 secret key K and diversification data D of size 4..40 bytes. 1) 40-byte blob B is created from D by cloning it enough times to fill all 40 bytes. For example, if D is 40-bytes long, B = D; If D is 6-bytes long, B = D|D|D|D|D|D|D[0..3]. 2) B is split into 8-byte UKM and 32-byte SRCKEY (B = UKM|SRCKEY). 3) The algorithm from Section 6.5 is used to create K(UKM) from key K and UKM, with two differences: * Instead of S[i], vector (0,0,0,UKM[i],ff,ff,ff,ff XOR UKM[i]) is used. * During each encryption step, only 8 out of 32 GOST 28147-89 rounds are done. 4) Kd is calculated: Kd = encryptCFB (UKM, K(UKM), SRCKEY).Лезем в код github/gost_engine Внутри test_keyexpimp.c проверочные значения одного из тестовых примеров из pdf tc26.
unsigned char kdftree_key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, }; unsigned char kdf_label[] = { 0x26, 0xBD, 0xB8, 0x78 }; unsigned char kdf_seed[] = { 0xAF, 0x21, 0x43, 0x41, 0x45, 0x65, 0x63, 0x78 }; const unsigned char kdf_etalon[] = { 0x22, 0xB6, 0x83, 0x78, 0x45, 0xC6, 0xBE, 0xF6, 0x5E, 0xA7, 0x16, 0x72, 0xB2, 0x65, 0x83, 0x10, 0x86, 0xD3, 0xC7, 0x6A, 0xEB, 0xE6, 0xDA, 0xE9, 0x1C, 0xAD, 0x51, 0xD8, 0x3F, 0x79, 0xD1, 0x6B, 0x07, 0x4C, 0x93, 0x30, 0x59, 0x9D, 0x7F, 0x8D, 0x71, 0x2F, 0xCA, 0x54, 0x39, 0x2F, 0x4D, 0xDD, 0xE9, 0x37, 0x51, 0x20, 0x6B, 0x35, 0x84, 0xC8, 0xF4, 0x3F, 0x9E, 0x6D, 0xC5, 0x15, 0x31, 0xF9 }; // код проверки: ret = gost_kdftree2012_256(kdf_result, 64, kdftree_key, 32, kdf_label, 4, kdf_seed, 8, 1); if (ret <= 0) { ERR_print_errors_fp(stderr); err = 5; } else { hexdump(stdout, "KDF TREE", kdf_result, 64); if (memcmp(kdf_result, kdf_etalon, 64) != 0) { fprintf(stdout, "ERROR! test failed\n"); err = 6; } } // добавляем "первое" контрольное значение: const unsigned char kdf_gost_etalon[] = { 0xa1, 0xaa, 0x5f, 0x7d, 0xe4, 0x02, 0xd7, 0xb3, 0xd3, 0x23, 0xf2, 0x99, 0x1c, 0x8d, 0x45, 0x34, 0x01, 0x31, 0x37, 0x01, 0x0a, 0x83, 0x75, 0x4f, 0xd0, 0xaf, 0x6d, 0x7c, 0xd4, 0x92, 0x2e, 0xd9 }; // и его код проверки: ret = gost_kdftree2012_256(kdf_result, 32, kdftree_key, 32, kdf_label, 4, kdf_seed, 8, 1); if (ret <= 0) { ERR_print_errors_fp(stderr); err = 9; } else { hexdump(stdout, "KDF_GOSTR3411_2012_256", kdf_result, 32); if (memcmp(kdf_result, kdf_gost_etalon, 32) != 0) { fprintf(stdout, "ERROR! test failed\n"); err = 10; } }Работающий код для вычисления KDF() у нас есть. Внутри gost_keywrap.c обнаруживаем функции деривации ключа. Контрольных значений не видно.
Находим контрольные примеры в рекомендациях (5.7 п13):
13) Экспорт и импорт ключей на параметрах szOID_Gost28147_89_TC26_Z_ParamSet Ключ K: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f Величина UKM: af 21 43 41 45 65 63 78 label: 26 bd b8 78 KEKe(UKM) = KDF(Ke,label,UKM): a1 aa 5f 7d e4 02 d7 b3 d3 23 f2 99 1c 8d 45 34 01 31 37 01 0a 83 75 4f d0 af 6d 7c d4 92 2e d9 CEK_MAC: 38 d5 8a a3 CEK_ENC: b9 fb 92 42 95 0f 84 3f 0f bd 5b 9a 5e cf 9f 17 f7 9e 6d 21 58 16 56 de 6d c5 85 dd 62 7a 44 0aK=042031967658C0647ACDBD2C8A8D55CC031803208D94292C16221F68D9D7CAA5D73E