1 module passwd.securewipe;
2 
3 @safe:
4 
5 import std.traits;
6 
7 /**
8 	Guarantee value's memory is wiped with zeroes (even with optimisations enabled)
9 
10 	It's conservative about the types that are allowed.  If your type isn't allowed, you should possibly consider using a different one for sensitive data.
11 	For example, it won't erase immutable types like strings (use `char[]` instead) or classes (use a plain-old-data struct instead) or anything containing pointers.
12 */
13 void secureWipe(T)(ref T value) @trusted if (isJustData!T && isMutable!T)
14 {
15 	explicit_bzero(&value, T.sizeof);
16 }
17 
18 /// ditto
19 void secureWipe(T)(T[] value) @trusted if (isJustData!T && isMutable!T)
20 {
21 	// This should be impossible, but @safe is @safe
22 	if (value.length > size_t.max / T.sizeof) throw new Error("Array too large to be handled safely");
23 
24 	explicit_bzero(value.ptr, T.sizeof * value.length);
25 }
26 
27 private:
28 
29 extern(C) void explicit_bzero(void*, size_t) @nogc nothrow;
30 
31 /**
32 	True only for types that are raw data value types with no pointers, etc.
33 
34 	It's meant to test for types that are safe to wipe.
35 */
36 template isJustData(T)
37 {
38 	static if (isScalarType!T)
39 	{
40 		enum isJustData = true;
41 	}
42 	else static if (isStaticArray!T)
43 	{
44 		enum isJustData = isJustData!(ForeachType!T);
45 	}
46 	else static if (is(T == struct) || is(T == union))
47 	{
48 		enum isJustData = __traits(isPOD, T) && !isNested!T && allFieldsJustData!T;
49 	}
50 	else
51 	{
52 		enum isJustData = false;
53 	}
54 }
55 
56 /**
57 	Helper for isJustData
58 
59 	Required because of D's scoping rules.
60 */
61 template allFieldsJustData(S)
62 {
63 	import std.meta : allSatisfy;
64 	enum allFieldsJustData = allSatisfy!(isJustData, Fields!S);
65 }