Wolfgang Thallergcc 15.1.0ada185d1 year agoHistory
1556 lines · 44.2 KB · D
RawDownload
/** * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t