| Category | Functions |
|---|---|
| Template API | isDigest DigestType hasPeek hasBlockSize ExampleDigest digest hexDigest makeDigest |
| OOP API | Digest |
| Helper functions | toHexString secureEqual |
| Implementation helpers | digestLength WrapperDigest |
APIsThere are two APIs for digests: The template API and the OOP API. The template API uses structs and template helpers likeisDigest. The OOP API implements digests as classes inheriting theDigest interface. All digests are named so that the template API struct is called "x" and the OOP API class is called "xDigest". For example we haveMD5 <-->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 theDigest classes still have to be created usingnew 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 "x" structs directly.Sourcestd/digest/package.d
CTFEDigests do not work in CTFE
TODODigesting 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)
import std.digest.crc;//Simple examplechar[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog");writeln(hexHash);// "39A34F41"//Simple example, using the API manuallyCRC32 context = makeDigest!CRC32();context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog");ubyte[4] hash = context.finish();writeln(toHexString(hash));// "39A34F41"
//Generating the hashes of a file, idiomatic D wayimport 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); }}
//Generating the hashes of a file using the template APIimport 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 nowforeach (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); }}
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 nowforeach (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); }}
ExampleDigest;Note
//Using the OutputRange featureimport 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!writeln(ctx.finish().toHexString());// "7707D6AE4E027C70EEA2A935C2296F21"
put(scope const(ubyte)[]data...);put must work for any type which passesisDigest:Example
ExampleDigest dig;dig.put(cast(ubyte) 0);//single ubytedig.put(cast(ubyte) 0,cast(ubyte) 0);//variadicubyte[10] buf;dig.put(buf);//buffer
start();finish();NoteThe 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.
isDigest(T);NoteThis is very useful as a template constraint (see examples)
import std.digest.crc;staticassert(isDigest!CRC32);
import std.digest.crc;void myFunction(T)()if (isDigest!T){ T dig; dig.start();auto result = dig.finish();}myFunction!CRC32();
DigestType(T)import std.digest.crc;assert(is(DigestType!(CRC32) ==ubyte[4]));
import std.digest.crc;CRC32 dig;dig.start();DigestType!CRC32 result = dig.finish();
hasPeek(T);Note
import std.digest.crc, std.digest.md;assert(!hasPeek!(MD5));assert(hasPeek!CRC32);
import std.digest.crc;void myFunction(T)()if (hasPeek!T){ T dig; dig.start();auto result = dig.peek();}myFunction!CRC32();
hasBlockSize(T) if (isDigest!T)import std.digest.hmac, std.digest.md;staticassert(hasBlockSize!MD5 && MD5.blockSize == 512);staticassert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512);
digest(Hash, Range)(auto ref Rangerange)Rangerange | anInputRange withElementTypeubyte,ubyte[] orubyte[num] |
import std.digest.md;import std.range : repeat;auto testRange = repeat!ubyte(cast(ubyte)'a', 100);auto md5 =digest!MD5(testRange);
digest(Hash, T...)(scope const Tdata)data)));Tdata | one or more arrays of any type |
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");writeln(toHexString(crc32));// "39A34F41"
import std.digest.crc;auto crc32 =digest!CRC32("The quick ","brown ","fox jumps over the lazy dog");writeln(toHexString(crc32));// "39A34F41"
hexDigest(Hash, Order order = Order.increasing, Range)(ref Rangerange)| order | the order in which the bytes are processed (seetoHexString) |
Rangerange | anInputRange withElementTypeubyte,ubyte[] orubyte[num] |
import std.digest.md;import std.range : repeat;auto testRange = repeat!ubyte(cast(ubyte)'a', 100);writeln(hexDigest!MD5(testRange));// "36A92CC94A9E0FA21F625F8BFB007ADF"
hexDigest(Hash, Order order = Order.increasing, T...)(scope const Tdata)data)));| order | the order in which the bytes are processed (seetoHexString) |
Tdata | one or more arrays of any type |
import std.digest.crc;// "414FA339"writeln(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog"));
import std.digest.crc;// "414FA339"writeln(hexDigest!(CRC32, Order.decreasing)("The quick ","brown ","fox jumps over the lazy dog"));
makeDigest(Hash)();import std.digest.md;auto md5 =makeDigest!MD5();md5.put(0);writeln(toHexString(md5.finish()));// "93B885ADFE0DA089CDF634904FD59F71"
Digest;NoteA Digest implementation is always anOutputRange
//Using the OutputRange featureimport 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);writeln(ctx.finish().toHexString());// "7707D6AE4E027C70EEA2A935C2296F21"
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");writeln(crcHexString(crc32));// "414FA339"
import std.digest.crc;ubyte[] crc32 = (new CRC32Digest()).digest("The quick ","brown ","fox jumps over the lazy dog");writeln(crcHexString(crc32));// "414FA339"
void test(Digest dig){ dig.put(cast(ubyte) 0);//single ubyte dig.put(cast(ubyte) 0,cast(ubyte) 0);//variadicubyte[10] buf; dig.put(buf);//buffer}
put(scope const(ubyte)[]data...);Example
void test(Digest dig){ dig.put(cast(ubyte) 0);//single ubyte dig.put(cast(ubyte) 0,cast(ubyte) 0);//variadicubyte[10] buf; dig.put(buf);//buffer}
reset();length() const;finish();finish(ubyte[]buf);digest(scope const(void[])[]data...);Order: bool;import std.digest.crc : CRC32;auto crc32 = digest!CRC32("The quick ","brown ","fox jumps over the lazy dog");writeln(crc32.toHexString!(Order.decreasing));// "414FA339"writeln(crc32.toHexString!(LetterCase.lower,Order.decreasing));// "414fa339"
increasingdecreasingtoHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper)(const ubyte[num]digest);toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num]digest);toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper)(in ubyte[]digest);toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[]digest);NoteThe 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.
import std.digest.crc;//Test with template API:auto crc32 =digest!CRC32("The quick ","brown ","fox jumps over the lazy dog");//Lower case variant:writeln(toHexString!(LetterCase.lower)(crc32));// "39a34f41"//Usually CRCs are printed in this order, though:writeln(toHexString!(Order.decreasing)(crc32));// "414FA339"writeln(toHexString!(LetterCase.lower, Order.decreasing)(crc32));// "414fa339"
import std.digest.crc;// With OOP APIauto crc32 = (new CRC32Digest()).digest("The quick ","brown ","fox jumps over the lazy dog");//Usually CRCs are printed in this order, though:writeln(toHexString!(Order.decreasing)(crc32));// "414FA339"
WrapperDigest(T) if (isDigest!T): Digest;import std.digest.md;//Simple exampleauto hash =newWrapperDigest!MD5();hash.put(cast(ubyte) 0);auto result = hash.finish();
//using a supplied bufferimport std.digest.md;ubyte[16] buf;auto hash =newWrapperDigest!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
put(scope const(ubyte)[]data...);reset();length() const;finish(ubyte[]buf);finish();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
peek(ubyte[]buf) const;peek() const;secureEqual(R1, R2)(R1r1, R2r2)R1r1 | A digest representation |
R2r2 | A digest representation |
import std.digest.hmac : hmac;import std.digest.sha : SHA1;import std.string : representation;// a typical HMAC data integrity verificationauto secret ="A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation;auto data ="data".representation;auto hex1 = data.hmac!SHA1(secret).toHexString;auto hex2 = data.hmac!SHA1(secret).toHexString;auto hex3 ="data1".representation.hmac!SHA1(secret).toHexString;assert(secureEqual(hex1[], hex2[]));assert(!secureEqual(hex1[], hex3[]));
isHexString(String)(Stringhex)Stringhex | hexdecimal encoded byte array |
assert(isHexString("0x0123456789ABCDEFabcdef"));assert(isHexString("0123456789ABCDEFabcdef"));assert(!isHexString("g"));assert(!isHexString("#"));
fromHexStringAsRange(String)(Stringhex)Stringhex | String representation of a hexdecimal-encoded byte array. |
import std.range.primitives : ElementType, isForwardRange;import std.traits : ReturnType;// The decoder implements a forward range.staticassert(isForwardRange!(ReturnType!(fromHexStringAsRange!string)));staticassert(isForwardRange!(ReturnType!(fromHexStringAsRange!wstring)));staticassert(isForwardRange!(ReturnType!(fromHexStringAsRange!dstring)));// The element type of the range is always `ubyte`.staticassert(is(ElementType!(ReturnType!(fromHexStringAsRange!string)) ==ubyte));staticassert(is(ElementType!(ReturnType!(fromHexStringAsRange!wstring)) ==ubyte));staticassert(is(ElementType!(ReturnType!(fromHexStringAsRange!dstring)) ==ubyte));
fromHexString(String)(Stringhex)Stringhex | String representation of a hexdecimal-encoded byte array. |
Example
ubyte[] dby ="0xBA".fromHexString;
// Single bytewriteln("0xff".fromHexString);// [255]writeln("0xff"w.fromHexString);// [255]writeln("0xff"d.fromHexString);// [255]writeln("0xC0".fromHexString);// [192]writeln("0x00".fromHexString);// [0]// Nothingwriteln("".fromHexString);// []writeln(""w.fromHexString);// []writeln(""d.fromHexString);// []// Nothing but a prefixwriteln("0x".fromHexString);// []writeln("0x"w.fromHexString);// []writeln("0x"d.fromHexString);// []// Half a bytewriteln("0x1".fromHexString);// [0x01]writeln("0x1"w.fromHexString);// [0x01]writeln("0x1"d.fromHexString);// [0x01]// Mixed case is fine.writeln("0xAf".fromHexString);// [0xAF]writeln("0xaF".fromHexString);// [0xAF]// Multiple byteswriteln("0xfff".fromHexString);// [0x0F, 0xFF]writeln("0x123AaAa".fromHexString);// [0x01, 0x23, 0xAA, 0xAA]writeln("EBBBBF".fromHexString);// [0xEB, 0xBB, 0xBF]// md5 sumassert("d41d8cd98f00b204e9800998ecf8427e".fromHexString == [ 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E,]);
// Cycle self-testconstubyte[] initial = [0x00, 0x12, 0x34, 0xEB];writeln(initial);// initial.toHexString().fromHexString()