1 module dcrypt.random.rdrand; 2 3 4 /// The RDRand PRNG generates random data with intels rdrand instruction. 5 /// Use `RDRand.isSupported` to check at runntime if rdrand is supported. 6 /// 7 /// TODO: This module is not yet tested. 8 9 import dcrypt.random.prng; 10 import dcrypt.bitmanip; 11 import core.cpuid; 12 13 unittest { 14 if(RDRand.isSupported) { 15 ubyte[71] buf1; 16 ubyte[71] buf2; 17 18 RDRand rand; 19 20 rand.nextBytes(buf1); 21 rand.nextBytes(buf2); 22 23 assert(buf1 != buf2, "rdrand produced twice the same output!"); 24 25 ubyte[32] buf3; // Test multiple of 8. 26 rand.nextBytes(buf3); 27 } 28 } 29 30 // OOP wrapper 31 alias WrapperPRNG!RDRand RDRandRNG; 32 33 static assert(isRNG!RDRand); 34 35 @safe 36 public struct RDRand { 37 38 public { 39 40 enum name = "RDRand"; 41 42 } 43 44 /// Returns: `true` if your platform supports the rdrand instruction. Evaluated at runtime. 45 @property 46 public static bool isSupported() nothrow @nogc { 47 return hasRdrand(); 48 } 49 50 /// Generate random data with rdrand instruction. 51 /// Params: 52 /// buf = Buffer for random data. 53 /// 54 /// Throws: Throws an Error if your platform does not support the RDRAND instruction. 55 @trusted 56 public static void nextBytes(ubyte[] buf) nothrow @nogc 57 { 58 59 if(!isSupported) { 60 assert(false, "RDRAND is not supported by your platform!"); 61 } 62 63 while(buf.length >= 8) { 64 ulong r = nextLong(); 65 toLittleEndian!long(r, buf); 66 buf = buf[8..$]; 67 } 68 69 if(buf.length > 0) { 70 71 assert(buf.length < 8); 72 73 // fill remainder with random bytes 74 ulong r = nextLong(); 75 76 foreach(ref b; buf) { 77 b = cast(ubyte) r; 78 r >>= 8; 79 } 80 } 81 } 82 83 /// RDRAND is not seedable. 84 public void addSeed(in ubyte[] seed...) nothrow @nogc { 85 // Don't do anything. 86 } 87 88 /// Returns: A uniformly random ulong. 89 /// 90 // TODO: optimize to fill an array 91 @trusted 92 private static ulong nextLong() nothrow @nogc { 93 94 version(X86_64) { 95 96 ulong r; 97 98 version(LDC) { 99 asm nothrow @nogc { 100 db 0x49, 0x0f, 0xc7, 0xf0; // rdrand R8; 101 mov r, R8; 102 } 103 } else { 104 asm nothrow @nogc { 105 rdrand R8; 106 mov r, R8; 107 } 108 } 109 110 return r; 111 112 } else { 113 assert(false, "RDRAND is supported on x86_64 only."); 114 } 115 } 116 117 118 unittest { 119 if(isSupported) { 120 long r1 = nextLong(); 121 long r2 = nextLong(); 122 123 assert(r1 != r2, "rdrand produced twice the same output!"); 124 } 125 } 126 127 }