Сам я криптографией не занимаюсь, но кому то вполне может пригодится мое небольшое исследование. Решил разобраться со встроенными в процессор функциями RDRAND и RDSEED. Компилятор Delphi сказал Undeclared identifier. Хмм. Уже давно существует BMI, BMI2, AVX, AVX2 и даже AVX-512, а дельфийцы остановились на SSE4.2. Не беда. Скомпилируем код сами.
Сначала сделал проверку на поддержку данных функций процессором. Конечно же CPUID. Использовать CPUID можно начиная с первых Pentium процессоров. Надеюсь никто не додумается запустить CPUID на 486 машине, ибо ее там еще не было. Кстати RDRAND и RDSEED до процессоров IvyBridge также не существует.
function CPU_support_RDRAND: Boolean;
asm
mov rax, $01
cpuid
test ecx, 40000000h //тестируем 30-й бит
setne al
end;
function CPU_support_RDSEED: Boolean;
asm
mov rcx, 0
mov rax, $07 //страница №7
cpuid
test ebx, 40000h //тестируем 18-й бит
setne al
end;
Оказалось, что мой Core i7 G6950X Extreme поддерживает данные функции. Поэтому дальше решил скомпилировать байт-код вручную. Для опытных приведу код REX и REX.W префиксов. Возможно вы захотите записать результат в другой регистр:
const
REX_RDRAND32: Byte = $F0; //(11b:REG, 110b:OPCODE, 000b:EAX)
REX_RDSEED32: Byte = $F8; //(11b:REG, 111b:OPCODE, 000b:EAX)
REX_W_RDRAND64: Byte = $48; //(11b:REG, 110b:OPCODE, 000b:RAX)
REX_W_RDSEED64: Byte = $48; //(11b:REG, 111b:OPCODE, 000b:RAX)
Функции могут работать как в 32-х битном режиме, так и в 64-х битном. Поэтому сделал обе и даже в двух вариантах. В итоге получилось 4 функции:
function RDRand32: DWord;
asm
@Retry:
db $0F, $C7, $F0 //RDRAND EAX (CF = 1 говорит о корректности данных)
jnc @Retry
end;
function RDSeed32: DWord;
asm
@Retry:
db $0F, $C7, $F8 //RDSEED EAX (CF = 1 говорит о корректности данных)
jnc @Retry
end;
function RDRand64: QWord;
asm
.NOFRAME
@Retry:
db $48, $0F, $C7, $F0 //RDRAND RAX (CF = 1 говорит о корректности данных)
jnc @Retry
end;
function RDSeed64: QWord;
asm
.NOFRAME
@Retry:
db $48, $0F, $C7, $F8 //RDSEED RAX (CF = 1 говорит о корректности данных)
jnc @Retry
end;
По скорости они медленней, чем библиотечная функция Random. RDRand примерно на 35%, а RDSeed процентов на 50% и даже более, но качество уникальности генерируемых значений значительно выше. На данном ресурсе есть неплохие статьи на эту тему, ну а моя миссия (сделать функции доступными в Delphi) завершена. В Lazarus не тестировал, но скорее всего будет работать без каких либо изменений. В конец объявления функции нужно лишь добавить резервное слово assembler.
Здесь исходные тексты тестового консольного приложения. Там можно найти прототип функций Random32 и Random64 на основе встроенных в процессор. Возможно это то, что вы искали. Всем пока!
Комментарии (15)
powerman
23.02.2019 04:54+2качество уникальности генерируемых значений значительно выше
Интересно, кто и как это определил?
Вообще, как раз для криптографии очень нежелательно использовать один источник случайных чисел, особенно встроенный в процессор. Не знаю, как под виндой, но в линухе используется смесь из нескольких источников, чтобы возможные закладки в любом из них были нивелированы: вышеупомянутый RDRAND плюс события от клавы/мыши/диска/etc.
eDmk Автор
23.02.2019 11:32Эти функции сертифицированы.
RDRAND: Стандарт NIST SP 800-90A
RDSEED: Стандарт NIST SP 800-90B and NIST SP800-90C.
Стандарты конечно не наши, но все же.powerman
23.02.2019 16:22+1И как это отвечает на мой вопрос? Наличие сертификата говорит только о том, что кто-то вложил ресурсы в сертификацию. Сама по себе сертификация качество продукта не изменяет, и ничего не говорит о качестве этого продукта относительно других.
eDmk Автор
23.02.2019 16:36И как это отвечает на мой вопрос?
Вы пишите: используется смесь из нескольких источников...
В этой статье как раз описывается как устроены данные функции:
habr.com/ru/post/128666
Они тоже берут данные из нескольких источников.powerman
23.02.2019 16:58Это не имеет значения, потому что работа RDRAND полностью контролируется одной компанией, и провести аудит как она на самом деле работает в конкретном куске кремния — крайне проблематично.
Nagg
23.02.2019 16:06Была даже петиция на тему убрать rdrand из
/dev/random
:-) но Торвальдс заявляет что если там есть бэкдор то это никак не повляет т.к используются и другие способы как вы упомянули.powerman
23.02.2019 16:23Я в курсе. И, на мой взгляд, он абсолютно прав. Что не отменяет возможности того, что RDRAND содержит бэкдор.
scg
23.02.2019 09:48Первый раз вижу инструкцию, которая не возвращает сразу правильное значение. Прям асинхронный ассемблер какой-то получается: пока генерируется случайное число процессор может заняться чем-то еще.
DistortNeo
23.02.2019 12:37+1Видимо, вы не застали математический сопроцессор x87 в те времена, когда он был отдельным чипом.
Yermack
23.02.2019 10:19Здраствуйте, увидел у Вас похожие штуки и решил спросить. Где почитать и найти таблицы с объяснениями всех команд, чисто чтоб подобное
.text
Filename: In[35]
pushq %rbp
movq %rsp, %rbp
Source line: 1
mulsd %xmm0, %xmm0
popq %rbp
retq
nopw (%rax,%rax)
не представлялось какой-то магией, чтоб можно было такую вещь окинуть взором и сделать вывод, де, тут сумма и цикл, можно сделать и лучшеeDmk Автор
23.02.2019 10:45На сайте Intel . А как обычно все делают: опыт — сын ошибок трудных. В вашем случае вроде ассемблер AT&T.
masterspline
23.02.2019 23:34> По скорости они медленней, чем библиотечная функция Random.
Я написал функцию для заполнения 32-х байт случайными данными. Получилось:
Haswell 1400 тиков процессора (3.3 гигатика в секунду, Linux на голом железе)
Sandy Bridge EP 45 тиков (1.7 гигатика, Xeon в VMWare на Aruba)
Таким образом на разных процессорах разница на 2 порядка (1400 тактов и 45) при разнице в тактовой частоте процессоров всего в 2 раза. Однако, я не знаю, как оценить влияние виртуализации во втором случае (кроме как запустить на голом железе). Так что выкладывайте свои результаты (проц, количество тактов, количество гигатиков по результатам калибровки).
Код программы/* * Copyright 2019 Alex Syrnikov <san@masterspline.eu> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // clang++-7 -O3 -Wall -Wextra -pedantic -march=native -mtune=native -o rdrand32bytes ./rdrand.cpp #include <cstdio> #include <cstdint> #include <chrono> #include <thread> #include <algorithm> inline uint64_t rdtsc() { uint32_t tickl, tickh; __asm__ __volatile__("rdtsc":"=a"(tickl),"=d"(tickh)); return ( static_cast<uint64_t>(tickh) << 32) | tickl; } void calibrate_tsc() { printf( "tsc calibration\n" ); auto chrono_begin = std::chrono::steady_clock::now(); uint64_t begin_tsc = rdtsc(); std::this_thread::sleep_for( std::chrono::milliseconds(100) ); uint64_t end_tsc = rdtsc(); auto chrono_end = std::chrono::steady_clock::now(); std::chrono::duration<double> diff = chrono_end - chrono_begin; auto ticks_per_second = static_cast<size_t>((end_tsc-begin_tsc)/diff.count()); printf( "tsc per second %zd\n", ticks_per_second ); } int main() { calibrate_tsc(); uint64_t begin_tsc, end_tsc; uint8_t rnd[32]; std::fill( rnd, rnd+sizeof(rnd), 0 ); begin_tsc = rdtsc(); for( size_t i = 0; i < sizeof(rnd)/8; ++i ) __builtin_ia32_rdrand64_step( reinterpret_cast<unsigned long long*>(rnd+8*i) ); end_tsc = rdtsc(); printf( "rdrand64 for buffer got %ld ticks\n", end_tsc-begin_tsc ); printf( "rnd:" ); for( auto el : rnd ) { printf( " %02x", el ); } printf( "\n" ); return 0; }
eDmk Автор
24.02.2019 01:04Проц Intel i7 G6950X Extreme 3.0ГГц (3.4 ГГц при работе). Разгон до 4 ГГц выключил.
Монитор откалиброван, а больше мне ничего калибровать не надо.
Генерация данных:
Заголовок спойлераВсего итераций (x64): 100 000 000 Функция: RDRand32 Время: 8,47 сек. 11 807 769,51 в сек. Функция: RDRand64 Время: 8,25 сек. 12 121 212,12 в сек. Функция: System.Random Время: 0,23 сек. 427 350 427,35 в сек. Всего итераций (x64): 10 000 000 Функция: RDSeed32 Время: 9,86 сек. 1 014 301,65 в сек. Функция: RDSeed64 Время: 9,86 сек. 1 014 198,78 в сек.
mistergrim
На каких-то не было, а на каких-то и была.