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.util.pack;
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 	/// Dummy function. Has no effect at all.
46 	public void addSeed(in ubyte[]) nothrow @nogc pure {}
47 
48 	
49 	/// Generate random data with rdrand instruction.
50 	/// Params:
51 	/// buf = Buffer for random data.
52 	/// 
53 	/// Throws: Throws an Error if your platform does not support the RDRAND instruction.
54 	@trusted
55 	public static void nextBytes(ubyte[] buf) nothrow @nogc
56 	{
57 		
58 		if(!isSupported) {
59 			assert(false, "RDRAND is not supported by your platform!");
60 		}
61 		
62 		size_t len = buf.length / 8;
63 		
64 		asm nothrow @nogc {
65 			
66 			mov RDI, buf+8;	// load pointer to destination buffer
67 			mov RCX, len;	// set counter to len
68 			
69 			// fill buf with random data
70 		fillLoop:
71 			rdrand	R8;
72 			mov	R8, RAX;
73 			mov		[RDI], R8;
74 			add		RDI, 8;
75 			
76 			loop fillLoop;
77 		}
78 		
79 		
80 		if(buf.length % 8 > 0) {
81 			
82 			assert(buf.length-len*8 < 8);
83 			
84 			// fill remainder with random bytes
85 			ulong r = nextLong();
86 			
87 			foreach(ref b; buf[len*8 .. $]) {
88 				b = cast(ubyte) r;
89 				r >>= 8;
90 			}
91 		}
92 	}
93 
94 //	public void nextBytes(ubyte[] buf) nothrow @nogc 
95 //	{
96 //
97 //		if(!isSupported) {
98 //			assert(false, "RDRAND is not supported by your platform!");
99 //		}
100 //
101 //		while(buf.length > 0) {
102 //			long r = nextLong();
103 //			for(uint i = 0; i < 8 && buf.length > 0; ++i) {
104 //				buf[0] = cast(ubyte) r & 0xFF;
105 //				r >>= 8;
106 //			}
107 //		}
108 //	}
109 
110 	/// Returns: A uniformly random ulong.
111 	/// 
112 	// TODO: optimize to fill an array
113 	@trusted
114 	private static ulong nextLong() nothrow @nogc {
115 		ulong r;
116 
117 		asm nothrow @nogc {
118 			rdrand	R8;
119 			mov		r, R8;
120 		}
121 
122 		return r;
123 	}
124 
125 
126 	unittest {
127 		if(isSupported) {
128 			long r1 = nextLong();
129 			long r2 = nextLong();
130 
131 			assert(r1 != r2, "rdrand produced twice the same output!");
132 		}
133 	}
134 
135 }