RDRAND instructionを使う
はじめに
RDRANDおよびRDSEED instructionの使い方を説明します。
乱数および乱数SEEDを取得できます。Intel 64 and IA-32 instruction setで利用できます。
RDRAND / RDSEED instruction
指定したregisterに乱数および乱数SEEDをloadします。ビット長は16, 32, 64が指定できます。
有効な値を取得できない場合があります。CF(Carry Flag)で有効かどうかを確認できます。
Sample Code
rdrand / rdseedを指定した回数実行し、結果を表示する例を示します。
makefileは次の通りです。
CC=gcc
CFLAGS=-I. -Wall -Werror -O2
INCS=
ifeq ($(shell uname),Linux)
OBJS=test.o test_linux.o
else
OBJS=test.o test_win.o
endif
LIBS=
TARGET=test
all: $(TARGET)
%.o: %.s $(INCS)
$(CC) $(CFLAGS) -c -o $@ $<
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
clean:
rm -rf $(TARGET) *.o
// test.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#define TYPE_RAND 0
#define TYPE_SEED 1
#define SIZE_16BIT 16
#define SIZE_32BIT 32
#define SIZE_64BIT 64
#ifdef __linux__
#define EXE_NAME "test"
#else
#define EXE_NAME "test.exe"
#endif
#define STRCMP(value, label) strncmp(value, label, strlen(label)+1)
extern uint16_t rdrand64(uint64_t* value);
extern uint16_t rdrand32(uint32_t* value);
extern uint16_t rdrand16(uint16_t* value);
extern uint16_t rdseed64(uint64_t* value);
extern uint16_t rdseed32(uint32_t* value);
extern uint16_t rdseed16(uint16_t* value);
void usage() {
printf("usage:\n");
printf(" ./"EXE_NAME" type size num\n");
printf(" type : rand or seed\n");
printf(" size : 16 or 32 or 64\n");
printf("ex:\n");
printf(" ./"EXE_NAME" rand 16 1000\n");
}
int main(int argc, char* argv[])
{
int num = 0, type = TYPE_RAND, size = SIZE_32BIT, i;
if (argc != 4) {
usage();
return -1;
}
if (STRCMP(argv[1], "rand") == 0) {
type = TYPE_RAND;
} else if (STRCMP(argv[1], "seed") == 0) {
type = TYPE_SEED;
} else {
usage();
return -1;
}
size = atoi(argv[2]);
num = atoi(argv[3]);
printf("type=%d ", type);
printf("size=%d ", size);
printf("num=%d\n", num);
// c rrrr
printf("c ");
for (i=0; i<(size>>2); i++) {
printf("r");
}
printf(" c:CF(1:valid), r:rand or seed value\n");
if (type == TYPE_RAND) {
if (size == SIZE_16BIT) {
uint16_t v;
uint16_t r;
for (i=0; i<num; i++) {
r = rdrand16(&v);
printf("%d %04"PRIx16"\n", (r>>8)&0x01, v);
}
} else if (size == SIZE_32BIT) {
uint32_t v;
uint16_t r;
for (i=0; i<num; i++) {
r = rdrand32(&v);
printf("%d %08"PRIx32"\n", (r>>8)&0x01, v);
}
} else if (size == SIZE_64BIT) {
uint64_t v;
uint16_t r;
for (i=0; i<num; i++) {
r = rdrand64(&v);
printf("%d %016"PRIx64"\n", (r>>8)&0x01, v);
}
} else {
usage();
return -1;
}
} else {
// type = TYPE_SEED
if (size == SIZE_16BIT) {
uint16_t v;
uint16_t r;
for (i=0; i<num; i++) {
r = rdseed16(&v);
printf("%d %04"PRIx16"\n", (r>>8)&0x01, v);
}
} else if (size == SIZE_32BIT) {
uint32_t v;
uint16_t r;
for (i=0; i<num; i++) {
r = rdseed32(&v);
printf("%d %08"PRIx32"\n", (r>>8)&0x01, v);
}
} else if (size == SIZE_64BIT) {
uint64_t v;
uint16_t r;
for (i=0; i<num; i++) {
r = rdseed64(&v);
printf("%d %016"PRIx64"\n", (r>>8)&0x01, v);
}
} else {
usage();
return -1;
}
}
return 0;
}
windowsのassembly codeは次の通りです。
# test_win.s
.globl rdrand64
.globl rdrand32
.globl rdrand16
.globl rdseed64
.globl rdseed32
.globl rdseed16
# Microsoft x64 calling convention
# rcx(1st) rdx(2nd) r8(3rd) r9(4th)
# uint16_t rdrand64(uint64_t* value);
rdrand64:
rdrand %rdx
lahf
mov %rdx, (%rcx)
ret
# uint16_t rdrand32(uint32_t* value);
rdrand32:
rdrand %edx
lahf
mov %edx, (%rcx)
ret
# uint16_t rdrand16(uint16_t* value);
rdrand16:
rdrand %dx
lahf
mov %dx, (%rcx)
ret
# uint16_t rdseed64(uint64_t* value);
rdseed64:
rdseed %rdx
lahf
mov %rdx, (%rcx)
ret
# uint16_t rdseed32(uint32_t* value);
rdseed32:
rdseed %edx
lahf
mov %edx, (%rcx)
ret
# uint16_t rdseed16(uint16_t* value);
rdseed16:
rdseed %dx
lahf
mov %dx, (%rcx)
ret
linuxのassembly codeは次の通りです。
# test_linux.s
.globl rdrand64
.globl rdrand32
.globl rdrand16
.globl rdseed64
.globl rdseed32
.globl rdseed16
# System V AMD64 ABI
# rdi(1st) rsi(2nd) rdx(3rd) rcx(4th)
# uint16_t rdrand64(uint64_t* value);
rdrand64:
rdrand %rsi
lahf
mov %rsi, (%rdi)
ret
# uint16_t rdrand32(uint32_t* value);
rdrand32:
rdrand %esi
lahf
mov %esi, (%rdi)
ret
# uint16_t rdrand16(uint16_t* value);
rdrand16:
rdrand %si
lahf
mov %si, (%rdi)
ret
# uint16_t rdseed64(uint64_t* value);
rdseed64:
rdseed %rsi
lahf
mov %rsi, (%rdi)
ret
# uint16_t rdseed32(uint32_t* value);
rdseed32:
rdseed %esi
lahf
mov %esi, (%rdi)
ret
# uint16_t rdseed16(uint16_t* value);
rdseed16:
rdseed %si
lahf
mov %si, (%rdi)
ret
windows(Mingw-w64)でビルドします。
$ gcc --version
gcc.exe (Rev2, Built by MSYS2 project) 10.3.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ make
gcc -I. -Wall -Werror -O2 -c -o test.o test.c
gcc -I. -Wall -Werror -O2 -c -o test_win.o test_win.s
gcc -I. -Wall -Werror -O2 -o test test.o test_win.o
linuxでビルドします。
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.5 LTS"
$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ make
gcc -I. -Wall -Werror -O2 -c -o test.o test.c
gcc -I. -Wall -Werror -O2 -c -o test_linux.o test_linux.s
gcc -I. -Wall -Werror -O2 -o test test.o test_linux.o
プログラムを実行します。type, size, numを指定します。
typeにはinstructionを指定します。rand / seedが選択できます。
sizeにはビット長を指定します。16 / 32 / 64を指定します。
numには実行回数を指定します。
$ ./test.exe
usage:
./test.exe type size num
type : rand or seed
size : 16 or 32 or 64
ex:
./test.exe rand 16 1000
$ ./test.exe rand 16 10
type=0 size=16 num=10
c rrrr c:CF(1:valid), r:rand or seed value
1 6b57
1 0611
1 66d4
1 7efc
1 6e46
1 2257
1 c5c7
1 4a3f
1 84ce
1 f471
なお、type=seed, size=16を設定するとCF=0になることがありました。
$ ./test.exe seed 16 100
type=1 size=16 num=100
c rrrr c:CF(1:valid), r:rand or seed value
1 5212
...
1 d32a
0 0000
0 0000
1 9064
...
1 2e4b
0 0000
1 7a3c
...
1 b2e7
0 0000
1 9760