1 module dcrypt.crypto.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.crypto.random.prng;
10 import dcrypt.bitmanip;
11 import core.cpuid;
12 import std.range: chunks;
13 
14 unittest {
15 	if(RDRand.isSupported) {
16 		ubyte[71] buf1;
17 		ubyte[71] buf2;
18 
19 		RDRand rand;
20 
21 		rand.nextBytes(buf1);
22 		rand.nextBytes(buf2);
23 		
24 		assert(buf1 != buf2, "rdrand produced twice the same output!");
25 	}
26 }
27 
28 static assert(isRNG!RDRand);
29 
30 @safe
31 public struct RDRand {
32 
33 	public {
34 
35 		enum name = "RDRand";
36 
37 	}
38 
39 	/// Returns: `true` if your platform supports the rdrand instruction. Evaluated at runtime.
40 	@property
41 	public static bool isSupported() nothrow @nogc {
42 		return hasRdrand();
43 	}
44 	
45 	/// Generate random data with rdrand instruction.
46 	/// Params:
47 	/// buf = Buffer for random data.
48 	/// 
49 	/// Throws: Throws an Error if your platform does not support the RDRAND instruction.
50 	@trusted
51 	public static void nextBytes(ubyte[] buf) nothrow @nogc
52 	{
53 		
54 		if(!isSupported) {
55 			assert(false, "RDRAND is not supported by your platform!");
56 		}
57 		
58 		size_t len = buf.length / 8;
59 		
60 		asm nothrow @nogc {
61 			
62 			mov RDI, buf+8;	// load pointer to destination buffer
63 			mov RCX, len;	// set counter to len
64 			
65 			// fill buf with random data
66 		fillLoop:
67 			rdrand	R8;
68 			mov	R8, RAX;
69 			mov		[RDI], R8;
70 			add		RDI, 8;
71 			
72 			loop fillLoop;
73 		}
74 		
75 		
76 		if(buf.length % 8 > 0) {
77 			
78 			assert(buf.length-len*8 < 8);
79 			
80 			// fill remainder with random bytes
81 			ulong r = nextLong();
82 			
83 			foreach(ref b; buf[len*8 .. $]) {
84 				b = cast(ubyte) r;
85 				r >>= 8;
86 			}
87 		}
88 	}
89 
90 //	public void nextBytes(ubyte[] buf) nothrow @nogc 
91 //	{
92 //
93 //		if(!isSupported) {
94 //			assert(false, "RDRAND is not supported by your platform!");
95 //		}
96 //
97 //		while(buf.length > 0) {
98 //			long r = nextLong();
99 //			for(uint i = 0; i < 8 && buf.length > 0; ++i) {
100 //				buf[0] = cast(ubyte) r & 0xFF;
101 //				r >>= 8;
102 //			}
103 //		}
104 //	}
105 
106 	/// Returns: A uniformly random ulong.
107 	/// 
108 	// TODO: optimize to fill an array
109 	@trusted
110 	private static ulong nextLong() nothrow @nogc {
111 		ulong r;
112 
113 		asm nothrow @nogc {
114 			rdrand	R8;
115 			mov		r, R8;
116 		}
117 
118 		return r;
119 	}
120 
121 
122 	unittest {
123 		if(isSupported) {
124 			long r1 = nextLong();
125 			long r2 = nextLong();
126 
127 			assert(r1 != r2, "rdrand produced twice the same output!");
128 		}
129 	}
130 
131 }