/** * 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) $(MYREF secureEqual)))$(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 `MD5` <--> `MD5Digest`, * `CRC32` <--> `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 `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 `OutputRange`) * ) */ struct ExampleDigest { public: /** * Use this to feed the digest with data. * Also implements the $(REF isOutputRange, std,range,primitives) * interface for `ubyte` and `const(ubyte)[]`. * The following usages of `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. * `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 `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 `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 `InputRange` with `ElementType` `ubyte`, `ubyte[]` or `ubyte[num]` */DigestType!Hash digest(Hash, Range)(auto ref Range range)if (!isArray!Range && isDigestibleRange!Range){ Hash hash; hash.start(); alias E = ElementType!Range; // Not necessarily ubyte. Could be ubyte[N] or ubyte[] or something w/alias this. static if (!(__traits(isScalar, E) && E.sizeof == 1)) { foreach (e; range) hash.put(e); return hash.finish(); } else { static if (hasBlockSize!Hash) enum bufferBytes = Hash.blockSize >= (8192 * 8) ? 8192 : Hash.blockSize <= 64 ? 8 : (Hash.blockSize / 8); else enum bufferBytes = 8; ubyte[bufferBytes] buffer = void; static if (isRandomAccessRange!Range && hasLength!Range) { const end = range.length; size_t i = 0; while (end - i >= buffer.length) { foreach (ref e; buffer) e = range[i++]; hash.put(buffer); } if (const remaining = end - i) { foreach (ref e; buffer[0 .. remaining]) e = range[i++]; hash.put(buffer[0 .. remaining]); } return hash.finish(); } else { for (;;) { size_t n = buffer.length; foreach (i, ref e; buffer) { if (range.empty) { n = i; break; } e = range.front; range.popFront(); } if (n) hash.put(buffer[0 .. n]); if (n != buffer.length) 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);Lines 1–500 of 1556. Load more
/** * 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) $(MYREF secureEqual)) ) $(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 `MD5` <--> `MD5Digest`, * `CRC32` <--> `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 `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 `OutputRange`) * ) */ struct ExampleDigest { public: /** * Use this to feed the digest with data. * Also implements the $(REF isOutputRange, std,range,primitives) * interface for `ubyte` and `const(ubyte)[]`. * The following usages of `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. * `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 `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 `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 `InputRange` with `ElementType` `ubyte`, `ubyte[]` or `ubyte[num]` */ DigestType!Hash digest(Hash, Range)(auto ref Range range) if (!isArray!Range && isDigestibleRange!Range) { Hash hash; hash.start(); alias E = ElementType!Range; // Not necessarily ubyte. Could be ubyte[N] or ubyte[] or something w/alias this. static if (!(__traits(isScalar, E) && E.sizeof == 1)) { foreach (e; range) hash.put(e); return hash.finish(); } else { static if (hasBlockSize!Hash) enum bufferBytes = Hash.blockSize >= (8192 * 8) ? 8192 : Hash.blockSize <= 64 ? 8 : (Hash.blockSize / 8); else enum bufferBytes = 8; ubyte[bufferBytes] buffer = void; static if (isRandomAccessRange!Range && hasLength!Range) { const end = range.length; size_t i = 0; while (end - i >= buffer.length) { foreach (ref e; buffer) e = range[i++]; hash.put(buffer); } if (const remaining = end - i) { foreach (ref e; buffer[0 .. remaining]) e = range[i++]; hash.put(buffer[0 .. remaining]); } return hash.finish(); } else { for (;;) { size_t n = buffer.length; foreach (i, ref e; buffer) { if (range.empty) { n = i; break; } e = range.front; range.popFront(); } if (n) hash.put(buffer[0 .. n]); if (n != buffer.length) 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);