| /** |
| * This module describes the _digest APIs used in Phobos. All digests follow |
| * these APIs. Additionally, this module contains useful helper methods which |
| * can be used with every _digest type. |
| * |
| $(SCRIPT inhibitQuickIndex = 1;) |
| |
| $(DIVC quickindex, |
| $(BOOKTABLE , |
| $(TR $(TH Category) $(TH Functions) |
| ) |
| $(TR $(TDNW Template API) $(TD $(MYREF isDigest) $(MYREF DigestType) $(MYREF hasPeek) |
| $(MYREF hasBlockSize) |
| $(MYREF ExampleDigest) $(MYREF _digest) $(MYREF hexDigest) $(MYREF makeDigest) |
| ) |
| ) |
| $(TR $(TDNW OOP API) $(TD $(MYREF Digest) |
| ) |
| ) |
| $(TR $(TDNW Helper functions) $(TD $(MYREF toHexString)) |
| ) |
| $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDigest)) |
| ) |
| ) |
| ) |
| |
| * APIs: |
| * There are two APIs for digests: The template API and the OOP API. The template API uses structs |
| * and template helpers like $(LREF isDigest). The OOP API implements digests as classes inheriting |
| * the $(LREF Digest) interface. All digests are named so that the template API struct is called "$(B x)" |
| * and the OOP API class is called "$(B x)Digest". For example we have $(D MD5) <--> $(D MD5Digest), |
| * $(D CRC32) <--> $(D CRC32Digest), etc. |
| * |
| * The template API is slightly more efficient. It does not have to allocate memory dynamically, |
| * all memory is allocated on the stack. The OOP API has to allocate in the finish method if no |
| * buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate, |
| * but the $(LREF Digest) classes still have to be created using $(D new) which allocates them using the GC. |
| * |
| * The OOP API is useful to change the _digest function and/or _digest backend at 'runtime'. The benefit here |
| * is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible. |
| * |
| * If just one specific _digest type and backend is needed, the template API is usually a good fit. |
| * In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs |
| * directly. |
| * |
| * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). |
| * Authors: |
| * Johannes Pfau |
| * |
| * Source: $(PHOBOSSRC std/_digest/_package.d) |
| * |
| * CTFE: |
| * Digests do not work in CTFE |
| * |
| * TODO: |
| * Digesting single bits (as opposed to bytes) is not implemented. This will be done as another |
| * template constraint helper (hasBitDigesting!T) and an additional interface (BitDigest) |
| */ |
| /* Copyright Johannes Pfau 2012. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or copy at |
| * http://www.boost.org/LICENSE_1_0.txt) |
| */ |
| module std.digest; |
| |
| public import std.ascii : LetterCase; |
| import std.meta : allSatisfy; |
| import std.range.primitives; |
| import std.traits; |
| |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| |
| //Simple example |
| char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog"); |
| assert(hexHash == "39A34F41"); |
| |
| //Simple example, using the API manually |
| CRC32 context = makeDigest!CRC32(); |
| context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog"); |
| ubyte[4] hash = context.finish(); |
| assert(toHexString(hash) == "39A34F41"); |
| } |
| |
| /// |
| @system unittest |
| { |
| //Generating the hashes of a file, idiomatic D way |
| import std.digest.crc, std.digest.md, std.digest.sha; |
| import std.stdio; |
| |
| // Digests a file and prints the result. |
| void digestFile(Hash)(string filename) |
| if (isDigest!Hash) |
| { |
| auto file = File(filename); |
| auto result = digest!Hash(file.byChunk(4096 * 1024)); |
| writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result)); |
| } |
| |
| void main(string[] args) |
| { |
| foreach (name; args[1 .. $]) |
| { |
| digestFile!MD5(name); |
| digestFile!SHA1(name); |
| digestFile!CRC32(name); |
| } |
| } |
| } |
| /// |
| @system unittest |
| { |
| //Generating the hashes of a file using the template API |
| import std.digest.crc, std.digest.md, std.digest.sha; |
| import std.stdio; |
| // Digests a file and prints the result. |
| void digestFile(Hash)(ref Hash hash, string filename) |
| if (isDigest!Hash) |
| { |
| File file = File(filename); |
| |
| //As digests imlement OutputRange, we could use std.algorithm.copy |
| //Let's do it manually for now |
| foreach (buffer; file.byChunk(4096 * 1024)) |
| hash.put(buffer); |
| |
| auto result = hash.finish(); |
| writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result)); |
| } |
| |
| void uMain(string[] args) |
| { |
| MD5 md5; |
| SHA1 sha1; |
| CRC32 crc32; |
| |
| md5.start(); |
| sha1.start(); |
| crc32.start(); |
| |
| foreach (arg; args[1 .. $]) |
| { |
| digestFile(md5, arg); |
| digestFile(sha1, arg); |
| digestFile(crc32, arg); |
| } |
| } |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc, std.digest.md, std.digest.sha; |
| import std.stdio; |
| |
| // Digests a file and prints the result. |
| void digestFile(Digest hash, string filename) |
| { |
| File file = File(filename); |
| |
| //As digests implement OutputRange, we could use std.algorithm.copy |
| //Let's do it manually for now |
| foreach (buffer; file.byChunk(4096 * 1024)) |
| hash.put(buffer); |
| |
| ubyte[] result = hash.finish(); |
| writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result)); |
| } |
| |
| void umain(string[] args) |
| { |
| auto md5 = new MD5Digest(); |
| auto sha1 = new SHA1Digest(); |
| auto crc32 = new CRC32Digest(); |
| |
| foreach (arg; args[1 .. $]) |
| { |
| digestFile(md5, arg); |
| digestFile(sha1, arg); |
| digestFile(crc32, arg); |
| } |
| } |
| } |
| |
| version (StdDdoc) |
| version = ExampleDigest; |
| |
| version (ExampleDigest) |
| { |
| /** |
| * This documents the general structure of a Digest in the template API. |
| * All digest implementations should implement the following members and therefore pass |
| * the $(LREF isDigest) test. |
| * |
| * Note: |
| * $(UL |
| * $(LI A digest must be a struct (value type) to pass the $(LREF isDigest) test.) |
| * $(LI A digest passing the $(LREF isDigest) test is always an $(D OutputRange)) |
| * ) |
| */ |
| struct ExampleDigest |
| { |
| public: |
| /** |
| * Use this to feed the digest with data. |
| * Also implements the $(REF isOutputRange, std,range,primitives) |
| * interface for $(D ubyte) and $(D const(ubyte)[]). |
| * The following usages of $(D put) must work for any type which |
| * passes $(LREF isDigest): |
| * Example: |
| * ---- |
| * ExampleDigest dig; |
| * dig.put(cast(ubyte) 0); //single ubyte |
| * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic |
| * ubyte[10] buf; |
| * dig.put(buf); //buffer |
| * ---- |
| */ |
| @trusted void put(scope const(ubyte)[] data...) |
| { |
| |
| } |
| |
| /** |
| * This function is used to (re)initialize the digest. |
| * It must be called before using the digest and it also works as a 'reset' function |
| * if the digest has already processed data. |
| */ |
| @trusted void start() |
| { |
| |
| } |
| |
| /** |
| * The finish function returns the final hash sum and resets the Digest. |
| * |
| * Note: |
| * The actual type returned by finish depends on the digest implementation. |
| * $(D ubyte[16]) is just used as an example. It is guaranteed that the type is a |
| * static array of ubytes. |
| * |
| * $(UL |
| * $(LI Use $(LREF DigestType) to obtain the actual return type.) |
| * $(LI Use $(LREF digestLength) to obtain the length of the ubyte array.) |
| * ) |
| */ |
| @trusted ubyte[16] finish() |
| { |
| return (ubyte[16]).init; |
| } |
| } |
| } |
| |
| /// |
| @system unittest |
| { |
| //Using the OutputRange feature |
| import std.algorithm.mutation : copy; |
| import std.digest.md; |
| import std.range : repeat; |
| |
| auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); |
| auto ctx = makeDigest!MD5(); |
| copy(oneMillionRange, &ctx); //Note: You must pass a pointer to copy! |
| assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21"); |
| } |
| |
| /** |
| * Use this to check if a type is a digest. See $(LREF ExampleDigest) to see what |
| * a type must provide to pass this check. |
| * |
| * Note: |
| * This is very useful as a template constraint (see examples) |
| * |
| * BUGS: |
| * $(UL |
| * $(LI Does not yet verify that put takes scope parameters.) |
| * $(LI Should check that finish() returns a ubyte[num] array) |
| * ) |
| */ |
| template isDigest(T) |
| { |
| import std.range : isOutputRange; |
| enum bool isDigest = isOutputRange!(T, const(ubyte)[]) && isOutputRange!(T, ubyte) && |
| is(T == struct) && |
| is(typeof( |
| { |
| T dig = void; //Can define |
| dig.put(cast(ubyte) 0, cast(ubyte) 0); //varags |
| dig.start(); //has start |
| auto value = dig.finish(); //has finish |
| })); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| static assert(isDigest!CRC32); |
| } |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| void myFunction(T)() |
| if (isDigest!T) |
| { |
| T dig; |
| dig.start(); |
| auto result = dig.finish(); |
| } |
| myFunction!CRC32(); |
| } |
| |
| /** |
| * Use this template to get the type which is returned by a digest's $(LREF finish) method. |
| */ |
| template DigestType(T) |
| { |
| static if (isDigest!T) |
| { |
| alias DigestType = |
| ReturnType!(typeof( |
| { |
| T dig = void; |
| return dig.finish(); |
| })); |
| } |
| else |
| static assert(false, T.stringof ~ " is not a digest! (fails isDigest!T)"); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| assert(is(DigestType!(CRC32) == ubyte[4])); |
| } |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| CRC32 dig; |
| dig.start(); |
| DigestType!CRC32 result = dig.finish(); |
| } |
| |
| /** |
| * Used to check if a digest supports the $(D peek) method. |
| * Peek has exactly the same function signatures as finish, but it doesn't reset |
| * the digest's internal state. |
| * |
| * Note: |
| * $(UL |
| * $(LI This is very useful as a template constraint (see examples)) |
| * $(LI This also checks if T passes $(LREF isDigest)) |
| * ) |
| */ |
| template hasPeek(T) |
| { |
| enum bool hasPeek = isDigest!T && |
| is(typeof( |
| { |
| T dig = void; //Can define |
| DigestType!T val = dig.peek(); |
| })); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc, std.digest.md; |
| assert(!hasPeek!(MD5)); |
| assert(hasPeek!CRC32); |
| } |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| void myFunction(T)() |
| if (hasPeek!T) |
| { |
| T dig; |
| dig.start(); |
| auto result = dig.peek(); |
| } |
| myFunction!CRC32(); |
| } |
| |
| /** |
| * Checks whether the digest has a $(D blockSize) member, which contains the |
| * digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest,hmac). |
| */ |
| |
| template hasBlockSize(T) |
| if (isDigest!T) |
| { |
| enum bool hasBlockSize = __traits(compiles, { size_t blockSize = T.blockSize; }); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.hmac, std.digest.md; |
| static assert(hasBlockSize!MD5 && MD5.blockSize == 512); |
| static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512); |
| } |
| |
| package template isDigestibleRange(Range) |
| { |
| import std.digest.md; |
| import std.range : isInputRange, ElementType; |
| enum bool isDigestibleRange = isInputRange!Range && is(typeof( |
| { |
| MD5 ha; //Could use any conformant hash |
| ElementType!Range val; |
| ha.put(val); |
| })); |
| } |
| |
| /** |
| * This is a convenience function to calculate a hash using the template API. |
| * Every digest passing the $(LREF isDigest) test can be used with this function. |
| * |
| * Params: |
| * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) |
| */ |
| DigestType!Hash digest(Hash, Range)(auto ref Range range) |
| if (!isArray!Range |
| && isDigestibleRange!Range) |
| { |
| import std.algorithm.mutation : copy; |
| Hash hash; |
| hash.start(); |
| copy(range, &hash); |
| return hash.finish(); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.md; |
| import std.range : repeat; |
| auto testRange = repeat!ubyte(cast(ubyte)'a', 100); |
| auto md5 = digest!MD5(testRange); |
| } |
| |
| /** |
| * This overload of the digest function handles arrays. |
| * |
| * Params: |
| * data= one or more arrays of any type |
| */ |
| DigestType!Hash digest(Hash, T...)(scope const T data) |
| if (allSatisfy!(isArray, typeof(data))) |
| { |
| Hash hash; |
| hash.start(); |
| foreach (datum; data) |
| hash.put(cast(const(ubyte[]))datum); |
| return hash.finish(); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc, std.digest.md, std.digest.sha; |
| auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog"); |
| auto sha1 = digest!SHA1( "The quick brown fox jumps over the lazy dog"); |
| auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog"); |
| assert(toHexString(crc32) == "39A34F41"); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog"); |
| assert(toHexString(crc32) == "39A34F41"); |
| } |
| |
| /** |
| * This is a convenience function similar to $(LREF digest), but it returns the string |
| * representation of the hash. Every digest passing the $(LREF isDigest) test can be used with this |
| * function. |
| * |
| * Params: |
| * order= the order in which the bytes are processed (see $(LREF toHexString)) |
| * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) |
| */ |
| char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range) |
| if (!isArray!Range && isDigestibleRange!Range) |
| { |
| return toHexString!order(digest!Hash(range)); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.md; |
| import std.range : repeat; |
| auto testRange = repeat!ubyte(cast(ubyte)'a', 100); |
| assert(hexDigest!MD5(testRange) == "36A92CC94A9E0FA21F625F8BFB007ADF"); |
| } |
| |
| /** |
| * This overload of the hexDigest function handles arrays. |
| * |
| * Params: |
| * order= the order in which the bytes are processed (see $(LREF toHexString)) |
| * data= one or more arrays of any type |
| */ |
| char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, T...)(scope const T data) |
| if (allSatisfy!(isArray, typeof(data))) |
| { |
| return toHexString!order(digest!Hash(data)); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339"); |
| } |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339"); |
| } |
| |
| /** |
| * This is a convenience function which returns an initialized digest, so it's not necessary to call |
| * start manually. |
| */ |
| Hash makeDigest(Hash)() |
| { |
| Hash hash; |
| hash.start(); |
| return hash; |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.md; |
| auto md5 = makeDigest!MD5(); |
| md5.put(0); |
| assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71"); |
| } |
| |
| /*+*************************** End of template part, welcome to OOP land **************************/ |
| |
| /** |
| * This describes the OOP API. To understand when to use the template API and when to use the OOP API, |
| * see the module documentation at the top of this page. |
| * |
| * The Digest interface is the base interface which is implemented by all digests. |
| * |
| * Note: |
| * A Digest implementation is always an $(D OutputRange) |
| */ |
| interface Digest |
| { |
| public: |
| /** |
| * Use this to feed the digest with data. |
| * Also implements the $(REF isOutputRange, std,range,primitives) |
| * interface for $(D ubyte) and $(D const(ubyte)[]). |
| * |
| * Example: |
| * ---- |
| * void test(Digest dig) |
| * { |
| * dig.put(cast(ubyte) 0); //single ubyte |
| * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic |
| * ubyte[10] buf; |
| * dig.put(buf); //buffer |
| * } |
| * ---- |
| */ |
| @trusted nothrow void put(scope const(ubyte)[] data...); |
| |
| /** |
| * Resets the internal state of the digest. |
| * Note: |
| * $(LREF finish) calls this internally, so it's not necessary to call |
| * $(D reset) manually after a call to $(LREF finish). |
| */ |
| @trusted nothrow void reset(); |
| |
| /** |
| * This is the length in bytes of the hash value which is returned by $(LREF finish). |
| * It's also the required size of a buffer passed to $(LREF finish). |
| */ |
| @trusted nothrow @property size_t length() const; |
| |
| /** |
| * The finish function returns the hash value. It takes an optional buffer to copy the data |
| * into. If a buffer is passed, it must be at least $(LREF length) bytes big. |
| */ |
| @trusted nothrow ubyte[] finish(); |
| ///ditto |
| nothrow ubyte[] finish(ubyte[] buf); |
| //@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549 |
| /*in |
| { |
| assert(buf.length >= this.length); |
| }*/ |
| |
| /** |
| * This is a convenience function to calculate the hash of a value using the OOP API. |
| */ |
| final @trusted nothrow ubyte[] digest(scope const(void[])[] data...) |
| { |
| this.reset(); |
| foreach (datum; data) |
| this.put(cast(ubyte[]) datum); |
| return this.finish(); |
| } |
| } |
| |
| /// |
| @system unittest |
| { |
| //Using the OutputRange feature |
| import std.algorithm.mutation : copy; |
| import std.digest.md; |
| import std.range : repeat; |
| |
| auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); |
| auto ctx = new MD5Digest(); |
| copy(oneMillionRange, ctx); |
| assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21"); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc, std.digest.md, std.digest.sha; |
| ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog"); |
| ubyte[] sha1 = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog"); |
| ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog"); |
| assert(crcHexString(crc32) == "414FA339"); |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.crc; |
| ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog"); |
| assert(crcHexString(crc32) == "414FA339"); |
| } |
| |
| @system unittest |
| { |
| import std.range : isOutputRange; |
| assert(!isDigest!(Digest)); |
| assert(isOutputRange!(Digest, ubyte)); |
| } |
| |
| /// |
| @system unittest |
| { |
| void test(Digest dig) |
| { |
| dig.put(cast(ubyte) 0); //single ubyte |
| dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic |
| ubyte[10] buf; |
| dig.put(buf); //buffer |
| } |
| } |
| |
| /*+*************************** End of OOP part, helper functions follow ***************************/ |
| |
| /** |
| * See $(LREF toHexString) |
| */ |
| enum Order : bool |
| { |
| increasing, /// |
| decreasing /// |
| } |
| |
| |
| /** |
| * Used to convert a hash value (a static or dynamic array of ubytes) to a string. |
| * Can be used with the OOP and with the template API. |
| * |
| * The additional order parameter can be used to specify the order of the input data. |
| * By default the data is processed in increasing order, starting at index 0. To process it in the |
| * opposite order, pass Order.decreasing as a parameter. |
| * |
| * The additional letterCase parameter can be used to specify the case of the output data. |
| * By default the output is in upper case. To change it to the lower case |
| * pass LetterCase.lower as a parameter. |
| * |
| * Note: |
| * The function overloads returning a string allocate their return values |
| * using the GC. The versions returning static arrays use pass-by-value for |
| * the return value, effectively avoiding dynamic allocation. |
| */ |
| char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper) |
| (in ubyte[num] digest) |
| { |
| static if (letterCase == LetterCase.upper) |
| { |
| import std.ascii : hexDigits = hexDigits; |
| } |
| else |
| { |
| import std.ascii : hexDigits = lowerHexDigits; |
| } |
| |
| |
| char[num*2] result; |
| size_t i; |
| |
| static if (order == Order.increasing) |
| { |
| foreach (u; digest) |
| { |
| result[i++] = hexDigits[u >> 4]; |
| result[i++] = hexDigits[u & 15]; |
| } |
| } |
| else |
| { |
| size_t j = num - 1; |
| while (i < num*2) |
| { |
| result[i++] = hexDigits[digest[j] >> 4]; |
| result[i++] = hexDigits[digest[j] & 15]; |
| j--; |
| } |
| } |
| return result; |
| } |
| |
| ///ditto |
| char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest) |
| { |
| return toHexString!(order, num, letterCase)(digest); |
| } |
| |
| ///ditto |
| string toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper) |
| (in ubyte[] digest) |
| { |
| static if (letterCase == LetterCase.upper) |
| { |
| import std.ascii : hexDigits = hexDigits; |
| } |
| else |
| { |
| import std.ascii : hexDigits = lowerHexDigits; |
| } |
| |
| auto result = new char[digest.length*2]; |
| size_t i; |
| |
| static if (order == Order.increasing) |
| { |
| foreach (u; digest) |
| { |
| result[i++] = hexDigits[u >> 4]; |
| result[i++] = hexDigits[u & 15]; |
| } |
| } |
| else |
| { |
| import std.range : retro; |
| foreach (u; retro(digest)) |
| { |
| result[i++] = hexDigits[u >> 4]; |
| result[i++] = hexDigits[u & 15]; |
| } |
| } |
| import std.exception : assumeUnique; |
| // memory was just created, so casting to immutable is safe |
| return () @trusted { return assumeUnique(result); }(); |
| } |
| |
| ///ditto |
| string toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest) |
| { |
| return toHexString!(order, letterCase)(digest); |
| } |
| |
| //For more example unittests, see Digest.digest, digest |
| |
| /// |
| @safe unittest |
| { |
| import std.digest.crc; |
| //Test with template API: |
| auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog"); |
| //Lower case variant: |
| assert(toHexString!(LetterCase.lower)(crc32) == "39a34f41"); |
| //Usually CRCs are printed in this order, though: |
| assert(toHexString!(Order.decreasing)(crc32) == "414FA339"); |
| assert(toHexString!(LetterCase.lower, Order.decreasing)(crc32) == "414fa339"); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.digest.crc; |
| // With OOP API |
| auto crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog"); |
| //Usually CRCs are printed in this order, though: |
| assert(toHexString!(Order.decreasing)(crc32) == "414FA339"); |
| } |
| |
| @safe unittest |
| { |
| ubyte[16] data; |
| assert(toHexString(data) == "00000000000000000000000000000000"); |
| |
| assert(toHexString(cast(ubyte[4])[42, 43, 44, 45]) == "2A2B2C2D"); |
| assert(toHexString(cast(ubyte[])[42, 43, 44, 45]) == "2A2B2C2D"); |
| assert(toHexString!(Order.decreasing)(cast(ubyte[4])[42, 43, 44, 45]) == "2D2C2B2A"); |
| assert(toHexString!(Order.decreasing, LetterCase.lower)(cast(ubyte[4])[42, 43, 44, 45]) == "2d2c2b2a"); |
| assert(toHexString!(Order.decreasing)(cast(ubyte[])[42, 43, 44, 45]) == "2D2C2B2A"); |
| } |
| |
| /*+*********************** End of public helper part, private helpers follow ***********************/ |
| |
| /* |
| * Used to convert from a ubyte[] slice to a ref ubyte[N]. |
| * This helper is used internally in the WrapperDigest template to wrap the template API's |
| * finish function. |
| */ |
| ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "") |
| { |
| assert(source.length >= N, errorMsg); |
| return *cast(T[N]*) source.ptr; |
| } |
| |
| /* |
| * Returns the length (in bytes) of the hash value produced by T. |
| */ |
| template digestLength(T) |
| if (isDigest!T) |
| { |
| enum size_t digestLength = (ReturnType!(T.finish)).length; |
| } |
| |
| @safe pure nothrow @nogc |
| unittest |
| { |
| import std.digest.md : MD5; |
| import std.digest.sha : SHA1, SHA256, SHA512; |
| assert(digestLength!MD5 == 16); |
| assert(digestLength!SHA1 == 20); |
| assert(digestLength!SHA256 == 32); |
| assert(digestLength!SHA512 == 64); |
| } |
| |
| /** |
| * Wraps a template API hash struct into a Digest interface. |
| * Modules providing digest implementations will usually provide |
| * an alias for this template (e.g. MD5Digest, SHA1Digest, ...). |
| */ |
| class WrapperDigest(T) |
| if (isDigest!T) : Digest |
| { |
| protected: |
| T _digest; |
| |
| public final: |
| /** |
| * Initializes the digest. |
| */ |
| this() |
| { |
| _digest.start(); |
| } |
| |
| /** |
| * Use this to feed the digest with data. |
| * Also implements the $(REF isOutputRange, std,range,primitives) |
| * interface for $(D ubyte) and $(D const(ubyte)[]). |
| */ |
| @trusted nothrow void put(scope const(ubyte)[] data...) |
| { |
| _digest.put(data); |
| } |
| |
| /** |
| * Resets the internal state of the digest. |
| * Note: |
| * $(LREF finish) calls this internally, so it's not necessary to call |
| * $(D reset) manually after a call to $(LREF finish). |
| */ |
| @trusted nothrow void reset() |
| { |
| _digest.start(); |
| } |
| |
| /** |
| * This is the length in bytes of the hash value which is returned by $(LREF finish). |
| * It's also the required size of a buffer passed to $(LREF finish). |
| */ |
| @trusted nothrow @property size_t length() const pure |
| { |
| return digestLength!T; |
| } |
| |
| /** |
| * The finish function returns the hash value. It takes an optional buffer to copy the data |
| * into. If a buffer is passed, it must have a length at least $(LREF length) bytes. |
| * |
| * Example: |
| * -------- |
| * |
| * import std.digest.md; |
| * ubyte[16] buf; |
| * auto hash = new WrapperDigest!MD5(); |
| * hash.put(cast(ubyte) 0); |
| * auto result = hash.finish(buf[]); |
| * //The result is now in result (and in buf). If you pass a buffer which is bigger than |
| * //necessary, result will have the correct length, but buf will still have it's original |
| * //length |
| * -------- |
| */ |
| nothrow ubyte[] finish(ubyte[] buf) |
| in |
| { |
| assert(buf.length >= this.length); |
| } |
| body |
| { |
| enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ |
| "big, check " ~ typeof(this).stringof ~ ".length!"; |
| asArray!(digestLength!T)(buf, msg) = _digest.finish(); |
| return buf[0 .. digestLength!T]; |
| } |
| |
| ///ditto |
| @trusted nothrow ubyte[] finish() |
| { |
| enum len = digestLength!T; |
| auto buf = new ubyte[len]; |
| asArray!(digestLength!T)(buf) = _digest.finish(); |
| return buf; |
| } |
| |
| version (StdDdoc) |
| { |
| /** |
| * Works like $(D finish) but does not reset the internal state, so it's possible |
| * to continue putting data into this WrapperDigest after a call to peek. |
| * |
| * These functions are only available if $(D hasPeek!T) is true. |
| */ |
| @trusted ubyte[] peek(ubyte[] buf) const; |
| ///ditto |
| @trusted ubyte[] peek() const; |
| } |
| else static if (hasPeek!T) |
| { |
| @trusted ubyte[] peek(ubyte[] buf) const |
| in |
| { |
| assert(buf.length >= this.length); |
| } |
| body |
| { |
| enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ |
| "big, check " ~ typeof(this).stringof ~ ".length!"; |
| asArray!(digestLength!T)(buf, msg) = _digest.peek(); |
| return buf[0 .. digestLength!T]; |
| } |
| |
| @trusted ubyte[] peek() const |
| { |
| enum len = digestLength!T; |
| auto buf = new ubyte[len]; |
| asArray!(digestLength!T)(buf) = _digest.peek(); |
| return buf; |
| } |
| } |
| } |
| |
| /// |
| @system unittest |
| { |
| import std.digest.md; |
| //Simple example |
| auto hash = new WrapperDigest!MD5(); |
| hash.put(cast(ubyte) 0); |
| auto result = hash.finish(); |
| } |
| |
| /// |
| @system unittest |
| { |
| //using a supplied buffer |
| import std.digest.md; |
| ubyte[16] buf; |
| auto hash = new WrapperDigest!MD5(); |
| hash.put(cast(ubyte) 0); |
| auto result = hash.finish(buf[]); |
| //The result is now in result (and in buf). If you pass a buffer which is bigger than |
| //necessary, result will have the correct length, but buf will still have it's original |
| //length |
| } |
| |
| @safe unittest |
| { |
| // Test peek & length |
| import std.digest.crc; |
| auto hash = new WrapperDigest!CRC32(); |
| assert(hash.length == 4); |
| hash.put(cast(const(ubyte[]))"The quick brown fox jumps over the lazy dog"); |
| assert(hash.peek().toHexString() == "39A34F41"); |
| ubyte[5] buf; |
| assert(hash.peek(buf).toHexString() == "39A34F41"); |
| } |
| |
| /** |
| * Securely compares two digest representations while protecting against timing |
| * attacks. Do not use `==` to compare digest representations. |
| * |
| * The attack happens as follows: |
| * |
| * $(OL |
| * $(LI An attacker wants to send harmful data to your server, which |
| * requires a integrity HMAC SHA1 token signed with a secret.) |
| * $(LI The length of the token is known to be 40 characters long due to its format, |
| * so the attacker first sends `"0000000000000000000000000000000000000000"`, |
| * then `"1000000000000000000000000000000000000000"`, and so on.) |
| * $(LI The given HMAC token is compared with the expected token using the |
| * `==` string comparison, which returns `false` as soon as the first wrong |
| * element is found. If a wrong element is found, then a rejection is sent |
| * back to the sender.) |
| * $(LI Eventually, the attacker is able to determine the first character in |
| * the correct token because the sever takes slightly longer to return a |
| * rejection. This is due to the comparison moving on to second item in |
| * the two arrays, seeing they are different, and then sending the rejection.) |
| * $(LI It may seem like too small of a difference in time for the attacker |
| * to notice, but security researchers have shown that differences as |
| * small as $(LINK2 http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf, |
| * 20µs can be reliably distinguished) even with network inconsistencies.) |
| * $(LI Repeat the process for each character until the attacker has the whole |
| * correct token and the server accepts the harmful data. This can be done |
| * in a week with the attacker pacing the attack to 10 requests per second |
| * with only one client.) |
| * ) |
| * |
| * This function defends against this attack by always comparing every single |
| * item in the array if the two arrays are the same length. Therefore, this |
| * function is always $(BIGOH n) for ranges of the same length. |
| * |
| * This attack can also be mitigated via rate limiting and banning IPs which have too |
| * many rejected requests. However, this does not completely solve the problem, |
| * as the attacker could be in control of a bot net. To fully defend against |
| * the timing attack, rate limiting, banning IPs, and using this function |
| * should be used together. |
| * |
| * Params: |
| * r1 = A digest representation |
| * r2 = A digest representation |
| * Returns: |
| * `true` if both representations are equal, `false` otherwise |
| * See_Also: |
| * $(LINK2 https://en.wikipedia.org/wiki/Timing_attack, The Wikipedia article |
| * on timing attacks). |
| */ |
| bool secureEqual(R1, R2)(R1 r1, R2 r2) |
| if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && |
| (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) && |
| !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void)) |
| { |
| static if (hasLength!R1 && hasLength!R2) |
| if (r1.length != r2.length) |
| return false; |
| |
| int result; |
| |
| static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && |
| hasLength!R1 && hasLength!R2) |
| { |
| foreach (i; 0 .. r1.length) |
| result |= r1[i] ^ r2[i]; |
| } |
| else static if (hasLength!R1 && hasLength!R2) |
| { |
| // Lengths are the same so we can squeeze out a bit of performance |
| // by not checking if r2 is empty |
| for (; !r1.empty; r1.popFront(), r2.popFront()) |
| { |
| result |= r1.front ^ r2.front; |
| } |
| } |
| else |
| { |
| // Generic case, walk both ranges |
| for (; !r1.empty; r1.popFront(), r2.popFront()) |
| { |
| if (r2.empty) return false; |
| result |= r1.front ^ r2.front; |
| } |
| if (!r2.empty) return false; |
| } |
| |
| return result == 0; |
| } |
| |
| /// |
| @system pure unittest |
| { |
| import std.digest.hmac : hmac; |
| import std.digest.sha : SHA1; |
| import std.string : representation; |
| |
| // a typical HMAC data integrity verification |
| auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation; |
| auto data = "data".representation; |
| |
| string hex1 = data.hmac!SHA1(secret).toHexString; |
| string hex2 = data.hmac!SHA1(secret).toHexString; |
| string hex3 = "data1".representation.hmac!SHA1(secret).toHexString; |
| |
| assert( secureEqual(hex1, hex2)); |
| assert(!secureEqual(hex1, hex3)); |
| } |
| |
| @system pure unittest |
| { |
| import std.internal.test.dummyrange : ReferenceInputRange; |
| import std.range : takeExactly; |
| import std.string : representation; |
| import std.utf : byWchar, byDchar; |
| |
| { |
| auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation; |
| auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".representation; |
| assert(!secureEqual(hex1, hex2)); |
| } |
| { |
| auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018"w.representation; |
| auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018"d.representation; |
| assert(secureEqual(hex1, hex2)); |
| } |
| { |
| auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byWchar; |
| auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; |
| assert(secureEqual(hex1, hex2)); |
| } |
| { |
| auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".byWchar; |
| auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; |
| assert(!secureEqual(hex1, hex2)); |
| } |
| { |
| auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); |
| auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); |
| assert(secureEqual(hex1, hex2)); |
| } |
| { |
| auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); |
| auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 9]).takeExactly(9); |
| assert(!secureEqual(hex1, hex2)); |
| } |
| } |