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 }