std1.0.0-beta-7 SafeC docs

Source crypto/hmac.h

View in the repository

#pragma once
#include <std/crypto/buf.h>
// SafeC Standard Library — HMAC (RFC 2104) over any hash.
//
// Hmac<H> is the HMAC construction written once over hash.h's BlockHash, so
// HMAC-SHA1 / -SHA224 / -SHA256 / -SHA384 / -SHA512 are the same code with a
// different hash context. It streams like a hash, and is a Hasher itself
// (update / finish / digest_size): hash_bytes and any generic<H: Hasher>
// code take it.
//
//   struct Hmac<Sha512Ctx> mac = Hmac::new(Sha512Ctx::new(), key, keyLen);
//   mac.update(msg, msgLen);
//   mac.finish(&tag[0]);                       // 64 bytes: mac.digest_size()
//
// hmac_sha256 is the one-shot form for HMAC-SHA256.
#include <std/crypto/hash.h>

#define HMAC_SHA256_SIZE 32 // bytes
#define HMAC_MAX_BLOCK_  128 // largest block_size() among std's hashes (SHA-512)
#define HMAC_MAX_DIGEST_ 64  // largest digest_size() (SHA-512)

namespace std {

generic<H: BlockHash>
struct Hmac {
    H inner;   // H((key ^ ipad) || message ...)
    H outer;   // H((key ^ opad) || ...), fed the inner digest by finish()

    // An HMAC keyed by 'key' over 'fresh', a new context of the hash to use
    // (Sha256Ctx::new(), Sha512Ctx::new_384(), ...). A key longer than the
    // hash's block is hashed first, as RFC 2104 specifies.
    static struct Hmac<H> new(H fresh, []const unsigned char key);
    void          update([]const unsigned char data);
    // Writes the digest_size()-byte MAC into mac (a shorter slice panics).
    // The context is spent after.
    void          finish([]unsigned char mac);
    unsigned long digest_size() const;
};

generic<H: BlockHash>
struct Hmac<H> Hmac::new(H fresh, []const unsigned char key) {
    unsigned long block = fresh.block_size();
    unsigned char keyBlock[HMAC_MAX_BLOCK_];
    unsigned long i = 0UL;
    if (key.len > block) {
        hash_bytes(fresh, key, keyBlock[..]);
        i = fresh.digest_size();
    } else {
        while (i < key.len) { keyBlock[i] = key[i]; i = i + 1UL; }
    }
    while (i < block) { keyBlock[i] = (unsigned char)0; i = i + 1UL; }

    unsigned char ipad[HMAC_MAX_BLOCK_];
    unsigned char opad[HMAC_MAX_BLOCK_];
    i = 0UL;
    while (i < block) {
        ipad[i] = keyBlock[i] ^ (unsigned char)0x36;
        opad[i] = keyBlock[i] ^ (unsigned char)0x5c;
        i = i + 1UL;
    }
    struct Hmac<H> m;
    m.inner = fresh;
    m.inner.update(ipad[0..block]);
    m.outer = fresh;
    m.outer.update(opad[0..block]);
    return m;
}

generic<H: BlockHash>
void Hmac::update([]const unsigned char data) { self.inner.update(data); }

generic<H: BlockHash>
void Hmac::finish([]unsigned char mac) {
    unsigned char innerDigest[HMAC_MAX_DIGEST_];
    self.inner.finish(innerDigest[..]);
    self.outer.update(innerDigest[0..self.inner.digest_size()]);
    self.outer.finish(mac);
}

generic<H: BlockHash>
unsigned long Hmac::digest_size() const { return self.outer.digest_size(); }

// Writes the 32-byte HMAC-SHA256 of 'data' (length 'dataLen') keyed by
// 'key' (length 'keyLen') into 'out' (must be >= 32 bytes).
void hmac_sha256([]const unsigned char key, []const unsigned char data, []unsigned char out);
// internal: the (reference, length) core of hmac_sha256()
void hmac_sha256_n_(const &unsigned char key, unsigned long keyLen,
                  const &unsigned char data, unsigned long dataLen,
                  &unsigned char out);

} // namespace std