LoginSignup
2
0

More than 1 year has passed since last update.

RDRAND instructionを使う

Last updated at Posted at 2021-06-06

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

References

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0