Реализация алгоритма SHA-256

SHA-256 一 криптографическая хэш-функция, разработанная Агенством национальной безопасности США. SHA расшифровывается как Secure Hash Algorithm. Хэш-функция 一 это математическая операция, выполняемая с цифровыми данными. Сравнивая вычисленный «хэш» (результат выполнения алгоритма) с известным и ожидаемым хэш-значением, можно определить целостность данных. Односторонний хэш может быть создан из любого фрагмента данных, но не наоборот. Алгоритм SHA-256 основан на структуре Меркля-Дамгарда, согласно которой начальный индекс делится на блоки сразу после внесения изменений, а те, в свою очередь, на 16 слов.

Далее приведен полный код реализации SHA-256. В коде, для уменьшения размера исполняемого файла, применены в основном функции из стандартной библиотеки C, которая является также частью стандартной библиотеки D. Некоторые из функций импортированы с помощью директивы import, другие же извлечены из стандартной библиотеки C напрямую, что можно наблюдать в коде как функции с указанием extern(C) и описанием только их сигнатуры, приведенной к схеме описания функций в D.

За основу реализации взят код из проекта redo-c. Код был адаптирован для D, добавлена D-обёртка для удобного хеширования, которая за собой скрывает детали реализации алгоритма. Код без процедуры main и sha256sum может быть без каких либо изменений использован в режиме BetterC компилятора и не требует D Runtime.

Файлы подаются как аргументы командной строки, в ответ выводиться хэш-сумма и путь до файла.

version (Posix)
{
    extern (C)
    {
        import core.sys.posix.fcntl : O_RDONLY;
        import core.sys.posix.sys.types : off_t, ssize_t;

        import std.conv : to;

        extern (C) int open(scope const(char*) pathname, int flags) pure nothrow @nogc;
        extern (C) ssize_t pread(int fd, void* buf, size_t count, off_t offset);
        extern (C) int close(int fd);

        extern (C) void* memset(scope return void* s, int c, ulong n) pure nothrow @nogc;
        extern (C) void* memcpy(scope return void* s1, scope const(void*) s2, ulong n) pure nothrow @nogc;

        struct sha256
        {
            ulong len;
            uint[8] h;
            ubyte[64] buf;
        }

        uint ror(uint n, int k) pure nothrow @nogc
        {
            return (n >> k) | (n << (32 - k));
        }

        uint Ch(uint x, uint y, uint z) pure nothrow @nogc
        {
            return (z ^ (x & (y ^ z)));
        }

        uint Maj(uint x, uint y, uint z) pure nothrow @nogc
        {
            return ((x & y) | (z & (x | y)));
        }

        uint S0(uint x) pure nothrow @nogc
        {
            return (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22));
        }

        uint S1(uint x) pure nothrow @nogc
        {
            return (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25));
        }

        uint R0(uint x) pure nothrow @nogc
        {
            return (ror(x, 7) ^ ror(x, 18) ^ (x >> 3));
        }

        uint R1(uint x) pure nothrow @nogc
        {
            return (ror(x, 17) ^ ror(x, 19) ^ (x >> 10));
        }

        enum uint[64] K = [
                0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
                0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
                0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
                0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
                0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
                0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
                0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
                0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
                0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
                0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
                0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
                0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
                0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
            ];

        void processblock(ref sha256 s, ubyte* buf) nothrow
        {
            uint[64] W;
            uint t1, t2, a, b, c, d, e, f, g, h;
            int i;

            for (i = 0; i < 16; i++)
            {
                W[i] = cast(uint)(buf[4 * i] << 24);
                W[i] |= cast(uint)(buf[4 * i + 1] << 16);
                W[i] |= cast(uint)(buf[4 * i + 2] << 8);
                W[i] |= buf[4 * i + 3];
            }
            for (; i < 64; i++)
                W[i] = R1(W[i - 2]) + W[i - 7] + R0(W[i - 15]) + W[i - 16];
            a = s.h[0];
            b = s.h[1];
            c = s.h[2];
            d = s.h[3];
            e = s.h[4];
            f = s.h[5];
            g = s.h[6];
            h = s.h[7];
            for (i = 0; i < 64; i++)
            {
                t1 = h + S1(e) + Ch(e, f, g) + K[i] + W[i];
                t2 = S0(a) + Maj(a, b, c);
                h = g;
                g = f;
                f = e;
                e = d + t1;
                d = c;
                c = b;
                b = a;
                a = t1 + t2;
            }
            s.h[0] += a;
            s.h[1] += b;
            s.h[2] += c;
            s.h[3] += d;
            s.h[4] += e;
            s.h[5] += f;
            s.h[6] += g;
            s.h[7] += h;
        }

        void pad(ref sha256 s)
        {
            uint r = s.len % 64;
            auto ptr = s.buf.ptr;

            s.buf[r++] = 0x80;
            if (r > 56)
            {
                memset(ptr + r, 0, 64 - r);
                r = 0;
                processblock(s, s.buf.ptr);
            }
            memset(ptr + r, 0, 56 - r);
            s.len *= 8;
            s.buf[56] = cast(ubyte)(s.len >> 56);
            s.buf[57] = cast(ubyte)(s.len >> 48);
            s.buf[58] = cast(ubyte)(s.len >> 40);
            s.buf[59] = cast(ubyte)(s.len >> 32);
            s.buf[60] = cast(ubyte)(s.len >> 24);
            s.buf[61] = cast(ubyte)(s.len >> 16);
            s.buf[62] = cast(ubyte)(s.len >> 8);
            s.buf[63] = cast(ubyte)(s.len);
            processblock(s, s.buf.ptr);
        }

        void sha256_init(ref sha256 s) pure nothrow @nogc
        {
            s.len = 0;
            s.h[0] = 0x6a09e667;
            s.h[1] = 0xbb67ae85;
            s.h[2] = 0x3c6ef372;
            s.h[3] = 0xa54ff53a;
            s.h[4] = 0x510e527f;
            s.h[5] = 0x9b05688c;
            s.h[6] = 0x1f83d9ab;
            s.h[7] = 0x5be0cd19;
        }

        void sha256_sum(ref sha256 s, ubyte* md)
        {
            int i;

            pad(s);
            for (i = 0; i < 8; i++)
            {
                md[4 * i] = cast(ubyte)(s.h[i] >> 24);
                md[4 * i + 1] = cast(ubyte)(s.h[i] >> 16);
                md[4 * i + 2] = cast(ubyte)(s.h[i] >> 8);
                md[4 * i + 3] = cast(ubyte)(s.h[i]);
            }
        }

        void sha256_update(ref sha256 s, const void* m, ulong len)
        {
            ubyte* p = cast(ubyte*) m;
            uint r = s.len % 64;
            auto ptr = s.buf.ptr;

            s.len += len;
            if (r)
            {
                if (len < 64 - r)
                {
                    memcpy(ptr + r, p, len);
                    return;
                }
                memcpy(ptr + r, p, 64 - r);
                len -= 64 - r;
                p += 64 - r;
                processblock(s, s.buf.ptr);
            }
            for (; len >= 64; len -= 64, p += 64)
                processblock(s, p);
            memcpy(ptr, p, len);
        }

        char* hashfile(int fd)
        {
            static char[16] hex = "0123456789abcdef";
            static char[65] asciihash;

            sha256 ctx;
            ulong off = 0;
            char[4096] buf;
            char* a;
            char[32] hash;
            int i;
            ssize_t r;

            sha256_init(ctx);

            while ((r = pread(fd, cast(void*) buf, buf.sizeof, off)) > 0)
            {
                sha256_update(ctx, cast(void*) buf, r);
                off += r;
            }

            sha256_sum(ctx, cast(ubyte*) hash);

            for (i = 0, a = asciihash.ptr; i < 32; i++)
            {
                *a++ = hex[hash[i] / 16];
                *a++ = hex[hash[i] % 16];
            }
            *a = 0;

            return asciihash.ptr;
        }

        string sha256sum(string filepath)
        {

            char* filename = cast(char*) filepath.dup;
            int fd = open(filename, O_RDONLY);

            scope (exit)
            {
                fd.close;
            }

            char* hash = hashfile(fd);

            return hash.to!string;
        }
    }
}

void main(string[] args)
{
    import std.file : exists;
    import std.stdio : writefln;

    if (args.length < 2)
    {
        writefln(`Usage: sha256d <file1 file2 ... fileN>`);
    }
    else
    {
        foreach (f; args[1 .. $])
        {
            if (f.exists)
            {
                writefln(`%s %s`, sha256sum(f), f);
            }
        }
    }
}

Используемые источники:

  1. https://github.com/leahneukirchen/redo-c
  2. https://ru.bitcoinwiki.org/wiki/SHA-256
  3. Пошагово объясняем, как работает алгоритм хеширования SHA-2 (SHA-256)

aquaratixc

Программист-самоучка и программист-любитель

Добавить комментарий