1. Introduction
Intel® Data Protection Technology with Intel® Secure Key provides two instructions: RDRAND, which is useful for generating high-quality keys for cryptographic protocols, and RDSEED, which is for seeding software-based pseudo random number generators (PRNGs). More information about the Digital Random Number Generator (DRNG) in Secure Key is available at Intel® Digital Random Number Generator (DRNG) Software Implementation Guide by John Mechalas.
The RDRAND library has been updated to include RDSEED support and has been renamed to the DRNG library. This document includes the following sections:
Section 2: Structure of DRNG Library and Prerequisites
Section 3: Changes in RDRAND APIs
Section 4: Design decisions for RDSEED APIs
Section 5: Sample user code to use DRNG library
Programmers who are new to the DRNG should first read reference 1, as this documentation only discusses the DRNG library implementation.
2. Structure of DRNG Library and Prerequisites
The DRNG library provides easy access to the RDRAND and RDSEED instructions and is compatible with Linux*, Windows*, and OS X*. In addition, it can be built with the Intel® Compiler, GNU Compiler (GCC), and Microsoft Compiler in 32-bit as well as 64-bit machines.
2.1 Structure
The DRNG library contains files that make it compatible with multiple platforms. Short descriptions of all the files are given below:
Filename | Description |
/lib | Folder with precompiled libraries |
drng.h | The macros and function definitions used in the library |
rdseed.c | The API for the RDSEED instruction |
rdrand.c | The API for the RDRAND instruction |
main.c | Program to test RDRAND and RDSEED API calls |
configure | Used in Linux to generate the Makefile |
configure.ac | GNU autoconfig file |
drng.vcxproj | Visual Studio* 13 project file for DRNG library |
drng.sln | Visual Studio 13 solution file for DRNG library |
test.vcxproj | Visual Studio 13 test project |
test.sln | Visual Studio 13 solution file for test project |
Makefile.in | Makefile template for Linux and OS X systems |
2.2 Prerequisites
Please make sure the following prerequisites are completed before using the library.
- Hardware Prerequisite
Although the DRNG library checks for the support of instructions, it is a good idea to check your system before downloading the library.
DRNG instructions are available beginning with 3rd generation Intel® Core™ processors and Intel® Xeon® v2 processors. - Linux Software Prerequisite
Make sure you are using GCC version 4.8.
- Windows Software Prerequisite
Visual Studio is required to build the test program in Windows [Visual Studio 13 is preferred]
Reference 2 contains the details to download Visual Studio 13. - OS X Software Prerequisite
Make sure you are using GCC version 4.8
- Microsoft Compiler
RDRAND intrinsics are available in Visual Studio 12 and RDSEED intrinsics are available in Visual Studio 13. Hence, it is highly recommended to use Visual Studio 13.
- Intel Compiler
Developers can install Intel Compiler version 15.0 for any platform to use DRNG library.
Reference 3 contains the details about installing the Intel Compiler.
3. Changes in the RDRAND API
A few minor changes have been made in RDRAND library to improve the efficiency of programs. These changes minimally affect users’ API calls.
3.1 The drng header file
The RDRAND header file (rdrand.h) has been replaced by the DRNG header file (drng.h). This change has been made to wrap up the new API calls for RDSEED API with existing macros and API calls of RDRAND library.
Consequently, the names of macros have been changed accordingly (drng.h). Some of the changes are mentioned below:
Old Name | New Name |
---|---|
RDRAND_SUCCESS | DRNG_SUCCESS |
RDRAND_SUPPORTED | DRNG_SUPPORTED |
RDRAND_NOT_READY | DRNG_NOT_READY |
3.2 Caching of DRNG_SUPPORT
To increase the run time efficiency of the library, the number of calls to __cpuid() has been reduced. Now, the library will call __cpuid() only once during the program scope, and the result will be cached in a variable for further references.
4. Design Decisions for RDSEED
One of the major differences in software implementation between RDSEED and RDRAND is the retry value. RDRAND is guaranteed to generate a random number within 10 retries on a working CPU. On the other hand, because the RDSEED instruction does not have a fairness mechanism built into it, there are no guarantees as to how often a thread should retry the instruction, or how many retries might be needed to obtain a random seed. In practice, the number of retries depends on the number of hardware threads on the CPU and how aggressively they are calling RDSEED.
4.1 Reentrant Code
To mitigate the uncertainty of the RDSEED instruction, the APIs have been designed to be reentrant. This helps the RDSEED instruction to attain 100% success rate without a busy wait.
To use this, a “skip” parameter has been introduced that needs to be passed while calling the RDSEED APIs. Code Snippet 1 illustrates the use.
Code Snippet 1
skip = 0; skip = rdseed_get_n_32(RDSEED_CUTOFF, array32, skip, MAX_RETRY_LIMIT);
The API returns the total number of seeds generated by the call. So, when calling the API for the first time, the “skip” value should be zero, as no seeds have been generated yet.
If the desired number of seeds was generated from the first call, then the program can continue. Otherwise, the API needs to be called again with an updated value of “skip” until the desired number (RDSEED_CUTOFF) of seeds is generated. Code Snippet 2 illustrates the use. New seeds will be appended to the buffer without affecting the previously generate seeds.
Code Snippet 2
while (skip < RDSEED_CUTOFF) { printf("\nRSEED numbers %d uint32's:\n", skip); skip = rdseed_get_n_32(RDSEED_CUTOFF, array32, skip, MAX_RETRY_LIMIT); }
4.2 Retry Value
A high “retry” value and a call similar to RDRAND library can keep the processor busy for a long time and hence decrease its efficiency. In a worst case, a retry value of 100 to generate ten 32-bit seeds can cause 1000 retries.
As a solution to this, users can provide a MAX_RETRY_VALUE. This value will determine the total number of retries that will be made by the library if a RDSEED instruction fails to return a value. The scope of MAX_RETRY_VALUE will be a single call to the API. For a simple direct call to generate a single seed value, the API will retry MAX_RETRY_VALUE times to generate the seed. Code Example 1 shows this implementation.
Code Example 1
int rdseed_16(uint16_t* x, int retry) { if (RdSeed_isSupported()) { if (_rdseed16_step(x)) return DRNG_SUCCESS; else { retry_counter = retry; while (retry_counter > 0) { retry_counter--; if (_rdseed16_step(x)) return DRNG_SUCCESS; } return DRNG_NOT_READY; } } }
While using the rdseed_get_n_32(),rdseed_get_n_64() , or rdseed_get_bytes() APIs the MAX_RETRY_VALUE determines the maximum number of RDSEED instruction attempts in total, that is, the scope of MAX_RETRY _VALUE exists until the API call exits. Hence the library is controlling the overall retries that were made in the call instead of passing a fresh retry value for each rdseed_32() or rdseed_64 call. Code Example 2 illustrates the implementation.
Code Example 2
int rdseed_get_n_32(unsigned int n, uint32_t *dest, unsigned int skip, unsigned int retry) { int success; unsigned int i; unsigned int success_count = 0; retry_counter = retry; if (skip) { n = n - skip; dest = &(dest[skip]); success_count = skip; } for (i = 0; i<n; i++) { success = rdseed_32(dest, retry_counter); if (success != DRNG_SUCCESS) return ((success == DRNG_UNSUPPORTED) ? success : success_count); dest = &(dest[1]); success_count++; } return success_count; }
4.3 Windows Compiler
The Intel Compiler and GNU Compiler allow rdrand_64() and rdseed_64() even in a 32-bit compilation by concatenating two 32-bit values together. The Windows Compiler does not include the 64-bit API calls in its 32-bit compiler intrinsics, hence calls to the 64-bit API should be done using “#ifndefs” or other equivalent solutions.
5. Sample User Code
RdRand_isSupported() and RdSeed_isSupported() APIs can be used to determine the support for RDRAND and RDSEED instruction by system. If the function returns 1, then the given instruction is supported by the system. Otherwise, if the return value is 0, then the instruction is not supported by the system.
5.1 Inline Assembly
Code Example 3 shows the implementation for 16-, 32-, and 64-bit invocations of RDSEED using inline assembly.
Code Example 3
int rdseed16_step(uint16_t *seed) { unsigned char ok; asm volatile ("rdseed /* .byte 0x66; .byte 0x0f; .byte 0xc7 */ %0; setc %1" : "=r" (*seed), "=qm" (ok)); return (int)ok; } int rdseed32_step(uint32_t *seed) { unsigned char ok; asm volatile ("rdseed /* .byte 0x0f; .byte 0xc7; .byte 0xf8 */ %0; setc %1" : "=r" (*seed), "=qm" (ok)); return (int)ok; } int rdseed64_step(uint64_t *seed) { unsigned char ok; asm volatile ("rdseed/*.byte 0x48; .byte 0x0f; .byte 0xc7; .byte 0xf8*/ %0; setc %1": "=r" (*seed), "=qm" (ok)); return (int)ok; }
5.2 A Simple Call
Code Example 4 shows the implementation for 16-, 32-, and 64-bit invocations of RDSEED using the DRNG library. MAX_RETRY_LIMIT is used to set the number of retries that will made before the API call returns failure. The function will return DRNG_SUCCESS ( = 1) if successful, otherwise it will return an error.
Code Example 4
/* Maximum retry value of rdseed instruction in a single call*/ #define MAX_RETRY_LIMIT 75 uint16_t u16; uint32_t u32; uint64_t u64; r = rdseed_16(&u16, MAX_RETRY_LIMIT); if (r != DRNG_SUCCESS) printf("rdseed instruction failed with code %d\n", r); printf("RDSEED uint16: %u\n", u16); r = rdseed_32(&u32, MAX_RETRY_LIMIT); if (r != DRNG_SUCCESS) printf("rdseed instruction failed with code %d\n", r); printf("RDSEED uint32: %u\n", u32); r = rdseed_64(&u64, MAX_RETRY_LIMIT); if (r != DRNG_SUCCESS) printf("rdseed instruction failed with code %d\n", r); printf("RDSEED uint64: %llu\n", u64);
5.3 Generating n 32- or 64-bit Seeds
Code Example 5 illustrates the use of rdseed_get_n_32 and rdseed_get_n_64. These functions are used to generate the 32- or 64-bit seeds and store them in a buffer of size ‘n’. As explained in section 4 these codes are reentrant, and the results will get appended to the buffer if the call is resumed. Also, MAX_RETRY_LIMIT has the scope of one call. The API call returns the total number of seeds generated by the call.
Code Example 5
/* Minimum number of results required*/ #define BUFFSIZE 1275 /* Minimum number of results required*/ #define RDSEED_CUTOFF 10 /* Maximum retry value of rdseed instruction in a single call*/ #define MAX_RETRY_LIMIT 75 int i; int r; uint32_t array32[10]; uint64_t array64[10]; unsigned char buffer[BUFFSIZE]; r = 0; r = rdseed_get_n_32(RDSEED_CUTOFF, array32, r, MAX_RETRY_LIMIT); if (r == DRNG_UNSUPPORTED) printf("RDSEED is not supported by system\n"); while (r < RDSEED_CUTOFF) { printf("\nRSEED numbers %d uint32's:\n", r); r = rdseed_get_n_32(RDSEED_CUTOFF, array32, r, MAX_RETRY_LIMIT); } printf("\nRDSEED numbers %d uint32's:\n", r); for (i = 0; i < RDSEED_CUTOFF; ++i) { printf("%u\n", array32[i]); } r = 0; r = rdseed_get_n_64(RDSEED_CUTOFF, array64, r, MAX_RETRY_LIMIT); if (r == DRNG_UNSUPPORTED) printf("RDSEED is not supported by system\n"); while (r < RDSEED_CUTOFF) { printf("\nRDSEED numbers %d uint64's:\n", r); r = rdseed_get_n_64(RDSEED_CUTOFF, array64, r, MAX_RETRY_LIMIT); } printf("\nRDSEED numbers %d uint64's:\n", r); for (i = 0; i < RDSEED_CUTOFF; ++i) { printf("%llu\n", array64[i]); }
5.4 Generating n Bytes of Seed
Similarly, Code Example 6 illustrates the use of rdseed_get_bytes. This function can be used to generate seeds of n bytes long. As explained in section 4 these codes are reentrant, and the results will get appended to the buffer if the call is resumed. Also, MAX_RETRY_LIMIT has the scope of one call. The API call returns the size of seeds generated by the call.
Code Example 6
memset(buffer, 0, BUFFSIZE); r = 0; r = rdseed_get_bytes(BUFFSIZE, buffer, r, MAX_RETRY_LIMIT); if (r == DRNG_UNSUPPORTED) printf("RDSEED is not supported by system\n"); else { while (r < BUFFSIZE) { printf("\nRDSEED generated %d bytes:\n", r); r = rdseed_get_bytes(BUFFSIZE, buffer, r, MAX_RETRY_LIMIT); } int i, j; printf("\nTotal generated RDSEED Buffer of %d bytes:\n", r); j = 0; for (i = 0; i < BUFFSIZE; ++i) { printf("%02x ", buffer[i]); ++j; if (j == 16) { j = 0; printf("\n"); } else if (j == 8) printf(""); } printf("\n"); }
Summary
The DRNG library provides random numbers with excellent statistical qualities, highly unpredictable random sequences, and high performance thanks to Intel® Data Protection Technology with Intel® Secure Key. The library is very portable as it is designed to be used on any platform with multiple compilers. Accessible via 2 simple instructions and 12 user-friendly APIs, it is also very easy to use.
References
- Intel® Digital Random Number Generator (DRNG) Software Implementation Guide.John Mechalas.
https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide - Installing Visual Studio. Microsoft Corp.
https://msdn.microsoft.com/en-us/library/e2h7fzkw.aspx Intel Compilers for Windows Silent Installation Guide. Steven Lionel.
https://software.intel.com/en-us/articles/intel-compilers-for-windows-silent-installation-guidesIntel Compilers for Linux and OS X* Compiler Installation Help. Ronald W Green.
https://software.intel.com/en-us/articles/intel-compilers-for-linux-and-mac-os-x-compiler-installation-help