Livt packages

Livt.Crypto

Version 1.0.5

Fixed-size cryptographic components for Livt, including AES, ChaCha20, SHA, MACs, and key derivation.

Published files (69)

src/mac/HmacSha256.lvt

namespace Livt.Crypto.Mac
using Livt.Crypto.Hash


// HMAC-SHA-256 (RFC 2104 + NIST FIPS 180-4) — single-block message model.
//
// Usage (per message):
//   1. SetKey(key)                 — derive k_ipad / k_opad blocks from 32-byte key
//      (key is zero-padded to the 64-byte block size internally)
//   2. Init()                      — Reset inner SHA and process k_ipad block
//   3. ProcessBlock(block)         — feed each additional 64-byte message block
//      Caller is responsible for SHA-256 padding of the last message block,
//      accounting for the 64-byte k_ipad prefix already consumed in Init().
//      Total inner length = 64 + |message_bytes|.
//   4. GetHmac() byte[32]          — finalise inner hash and run outer pass
//
// Key length convention: KEY_SIZE = 32 (= DIGEST_SIZE).  Keys ≤ 32 bytes are
// zero-padded to 64 bytes (BLOCK_SIZE) by SetKey.  Keys > 32 bytes should be
// SHA-256-hashed by the caller before passing to SetKey.
component HmacSha256
{
    public const KEY_SIZE:    int = 32
    public const BLOCK_SIZE:  int = 64
    public const DIGEST_SIZE: int = 32

    sha:   Sha256
    kIpad: byte[64]
    kOpad: byte[64]

    new()
    {
        this.sha = new Sha256()
    }

    // -----------------------------------------------------------------------
    // Public API
    // -----------------------------------------------------------------------

    // SetKey pads key to BLOCK_SIZE bytes then XORs with ipad (0x36) and
    // opad (0x5c) to produce the two keyed-block constants.
    public fn SetKey(key: byte[32])
    {
        // Bytes 0-31: XOR key byte with pad constants.
        var ip0:  byte = key[0] ^ 0x36; var op0:  byte = key[0] ^ 0x5c
        var ip1:  byte = key[1] ^ 0x36; var op1:  byte = key[1] ^ 0x5c
        var ip2:  byte = key[2] ^ 0x36; var op2:  byte = key[2] ^ 0x5c
        var ip3:  byte = key[3] ^ 0x36; var op3:  byte = key[3] ^ 0x5c
        var ip4:  byte = key[4] ^ 0x36; var op4:  byte = key[4] ^ 0x5c
        var ip5:  byte = key[5] ^ 0x36; var op5:  byte = key[5] ^ 0x5c
        var ip6:  byte = key[6] ^ 0x36; var op6:  byte = key[6] ^ 0x5c
        var ip7:  byte = key[7] ^ 0x36; var op7:  byte = key[7] ^ 0x5c
        var ip8:  byte = key[8] ^ 0x36; var op8:  byte = key[8] ^ 0x5c
        var ip9:  byte = key[9] ^ 0x36; var op9:  byte = key[9] ^ 0x5c
        var ip10: byte = key[10] ^ 0x36; var op10: byte = key[10] ^ 0x5c
        var ip11: byte = key[11] ^ 0x36; var op11: byte = key[11] ^ 0x5c
        var ip12: byte = key[12] ^ 0x36; var op12: byte = key[12] ^ 0x5c
        var ip13: byte = key[13] ^ 0x36; var op13: byte = key[13] ^ 0x5c
        var ip14: byte = key[14] ^ 0x36; var op14: byte = key[14] ^ 0x5c
        var ip15: byte = key[15] ^ 0x36; var op15: byte = key[15] ^ 0x5c
        var ip16: byte = key[16] ^ 0x36; var op16: byte = key[16] ^ 0x5c
        var ip17: byte = key[17] ^ 0x36; var op17: byte = key[17] ^ 0x5c
        var ip18: byte = key[18] ^ 0x36; var op18: byte = key[18] ^ 0x5c
        var ip19: byte = key[19] ^ 0x36; var op19: byte = key[19] ^ 0x5c
        var ip20: byte = key[20] ^ 0x36; var op20: byte = key[20] ^ 0x5c
        var ip21: byte = key[21] ^ 0x36; var op21: byte = key[21] ^ 0x5c
        var ip22: byte = key[22] ^ 0x36; var op22: byte = key[22] ^ 0x5c
        var ip23: byte = key[23] ^ 0x36; var op23: byte = key[23] ^ 0x5c
        var ip24: byte = key[24] ^ 0x36; var op24: byte = key[24] ^ 0x5c
        var ip25: byte = key[25] ^ 0x36; var op25: byte = key[25] ^ 0x5c
        var ip26: byte = key[26] ^ 0x36; var op26: byte = key[26] ^ 0x5c
        var ip27: byte = key[27] ^ 0x36; var op27: byte = key[27] ^ 0x5c
        var ip28: byte = key[28] ^ 0x36; var op28: byte = key[28] ^ 0x5c
        var ip29: byte = key[29] ^ 0x36; var op29: byte = key[29] ^ 0x5c
        var ip30: byte = key[30] ^ 0x36; var op30: byte = key[30] ^ 0x5c
        var ip31: byte = key[31] ^ 0x36; var op31: byte = key[31] ^ 0x5c

        this.kIpad[0]  = ip0;  this.kOpad[0]  = op0
        this.kIpad[1]  = ip1;  this.kOpad[1]  = op1
        this.kIpad[2]  = ip2;  this.kOpad[2]  = op2
        this.kIpad[3]  = ip3;  this.kOpad[3]  = op3
        this.kIpad[4]  = ip4;  this.kOpad[4]  = op4
        this.kIpad[5]  = ip5;  this.kOpad[5]  = op5
        this.kIpad[6]  = ip6;  this.kOpad[6]  = op6
        this.kIpad[7]  = ip7;  this.kOpad[7]  = op7
        this.kIpad[8]  = ip8;  this.kOpad[8]  = op8
        this.kIpad[9]  = ip9;  this.kOpad[9]  = op9
        this.kIpad[10] = ip10; this.kOpad[10] = op10
        this.kIpad[11] = ip11; this.kOpad[11] = op11
        this.kIpad[12] = ip12; this.kOpad[12] = op12
        this.kIpad[13] = ip13; this.kOpad[13] = op13
        this.kIpad[14] = ip14; this.kOpad[14] = op14
        this.kIpad[15] = ip15; this.kOpad[15] = op15
        this.kIpad[16] = ip16; this.kOpad[16] = op16
        this.kIpad[17] = ip17; this.kOpad[17] = op17
        this.kIpad[18] = ip18; this.kOpad[18] = op18
        this.kIpad[19] = ip19; this.kOpad[19] = op19
        this.kIpad[20] = ip20; this.kOpad[20] = op20
        this.kIpad[21] = ip21; this.kOpad[21] = op21
        this.kIpad[22] = ip22; this.kOpad[22] = op22
        this.kIpad[23] = ip23; this.kOpad[23] = op23
        this.kIpad[24] = ip24; this.kOpad[24] = op24
        this.kIpad[25] = ip25; this.kOpad[25] = op25
        this.kIpad[26] = ip26; this.kOpad[26] = op26
        this.kIpad[27] = ip27; this.kOpad[27] = op27
        this.kIpad[28] = ip28; this.kOpad[28] = op28
        this.kIpad[29] = ip29; this.kOpad[29] = op29
        this.kIpad[30] = ip30; this.kOpad[30] = op30
        this.kIpad[31] = ip31; this.kOpad[31] = op31

        // Bytes 32-63: zero-padded key XOR pad = constant.
        this.kIpad[32] = 0x36; this.kOpad[32] = 0x5c
        this.kIpad[33] = 0x36; this.kOpad[33] = 0x5c
        this.kIpad[34] = 0x36; this.kOpad[34] = 0x5c
        this.kIpad[35] = 0x36; this.kOpad[35] = 0x5c
        this.kIpad[36] = 0x36; this.kOpad[36] = 0x5c
        this.kIpad[37] = 0x36; this.kOpad[37] = 0x5c
        this.kIpad[38] = 0x36; this.kOpad[38] = 0x5c
        this.kIpad[39] = 0x36; this.kOpad[39] = 0x5c
        this.kIpad[40] = 0x36; this.kOpad[40] = 0x5c
        this.kIpad[41] = 0x36; this.kOpad[41] = 0x5c
        this.kIpad[42] = 0x36; this.kOpad[42] = 0x5c
        this.kIpad[43] = 0x36; this.kOpad[43] = 0x5c
        this.kIpad[44] = 0x36; this.kOpad[44] = 0x5c
        this.kIpad[45] = 0x36; this.kOpad[45] = 0x5c
        this.kIpad[46] = 0x36; this.kOpad[46] = 0x5c
        this.kIpad[47] = 0x36; this.kOpad[47] = 0x5c
        this.kIpad[48] = 0x36; this.kOpad[48] = 0x5c
        this.kIpad[49] = 0x36; this.kOpad[49] = 0x5c
        this.kIpad[50] = 0x36; this.kOpad[50] = 0x5c
        this.kIpad[51] = 0x36; this.kOpad[51] = 0x5c
        this.kIpad[52] = 0x36; this.kOpad[52] = 0x5c
        this.kIpad[53] = 0x36; this.kOpad[53] = 0x5c
        this.kIpad[54] = 0x36; this.kOpad[54] = 0x5c
        this.kIpad[55] = 0x36; this.kOpad[55] = 0x5c
        this.kIpad[56] = 0x36; this.kOpad[56] = 0x5c
        this.kIpad[57] = 0x36; this.kOpad[57] = 0x5c
        this.kIpad[58] = 0x36; this.kOpad[58] = 0x5c
        this.kIpad[59] = 0x36; this.kOpad[59] = 0x5c
        this.kIpad[60] = 0x36; this.kOpad[60] = 0x5c
        this.kIpad[61] = 0x36; this.kOpad[61] = 0x5c
        this.kIpad[62] = 0x36; this.kOpad[62] = 0x5c
        this.kIpad[63] = 0x36; this.kOpad[63] = 0x5c
    }

    // Init resets the inner SHA-256 state and processes the k_ipad block.
    // Must be called before the first ProcessBlock for each new message.
    public fn Init()
    {
        var ipadBlock: byte[64]
        ipadBlock[0]  = this.kIpad[0];  ipadBlock[1]  = this.kIpad[1];  ipadBlock[2]  = this.kIpad[2];  ipadBlock[3]  = this.kIpad[3]
        ipadBlock[4]  = this.kIpad[4];  ipadBlock[5]  = this.kIpad[5];  ipadBlock[6]  = this.kIpad[6];  ipadBlock[7]  = this.kIpad[7]
        ipadBlock[8]  = this.kIpad[8];  ipadBlock[9]  = this.kIpad[9];  ipadBlock[10] = this.kIpad[10]; ipadBlock[11] = this.kIpad[11]
        ipadBlock[12] = this.kIpad[12]; ipadBlock[13] = this.kIpad[13]; ipadBlock[14] = this.kIpad[14]; ipadBlock[15] = this.kIpad[15]
        ipadBlock[16] = this.kIpad[16]; ipadBlock[17] = this.kIpad[17]; ipadBlock[18] = this.kIpad[18]; ipadBlock[19] = this.kIpad[19]
        ipadBlock[20] = this.kIpad[20]; ipadBlock[21] = this.kIpad[21]; ipadBlock[22] = this.kIpad[22]; ipadBlock[23] = this.kIpad[23]
        ipadBlock[24] = this.kIpad[24]; ipadBlock[25] = this.kIpad[25]; ipadBlock[26] = this.kIpad[26]; ipadBlock[27] = this.kIpad[27]
        ipadBlock[28] = this.kIpad[28]; ipadBlock[29] = this.kIpad[29]; ipadBlock[30] = this.kIpad[30]; ipadBlock[31] = this.kIpad[31]
        ipadBlock[32] = this.kIpad[32]; ipadBlock[33] = this.kIpad[33]; ipadBlock[34] = this.kIpad[34]; ipadBlock[35] = this.kIpad[35]
        ipadBlock[36] = this.kIpad[36]; ipadBlock[37] = this.kIpad[37]; ipadBlock[38] = this.kIpad[38]; ipadBlock[39] = this.kIpad[39]
        ipadBlock[40] = this.kIpad[40]; ipadBlock[41] = this.kIpad[41]; ipadBlock[42] = this.kIpad[42]; ipadBlock[43] = this.kIpad[43]
        ipadBlock[44] = this.kIpad[44]; ipadBlock[45] = this.kIpad[45]; ipadBlock[46] = this.kIpad[46]; ipadBlock[47] = this.kIpad[47]
        ipadBlock[48] = this.kIpad[48]; ipadBlock[49] = this.kIpad[49]; ipadBlock[50] = this.kIpad[50]; ipadBlock[51] = this.kIpad[51]
        ipadBlock[52] = this.kIpad[52]; ipadBlock[53] = this.kIpad[53]; ipadBlock[54] = this.kIpad[54]; ipadBlock[55] = this.kIpad[55]
        ipadBlock[56] = this.kIpad[56]; ipadBlock[57] = this.kIpad[57]; ipadBlock[58] = this.kIpad[58]; ipadBlock[59] = this.kIpad[59]
        ipadBlock[60] = this.kIpad[60]; ipadBlock[61] = this.kIpad[61]; ipadBlock[62] = this.kIpad[62]; ipadBlock[63] = this.kIpad[63]

        this.sha.Reset()
        this.sha.ProcessBlock(ipadBlock)
    }

    // ProcessBlock feeds one 64-byte message block to the inner SHA-256 state.
    // The caller must pad the last block so the total inner-message byte count
    // (64 + all message bytes) is accounted for in the length field.
    public fn ProcessBlock(block: byte[64])
    {
        this.sha.ProcessBlock(block)
    }

    // GetHmac finalises the inner hash then runs the outer SHA-256 pass and
    // returns the 32-byte HMAC.
    // Outer SHA-256 input = k_opad (64 B) || inner_digest (32 B) = 96 B.
    // Outer block 1 = k_opad.
    // Outer block 2 = inner_digest || 0x80 || 0x00*23 || be64(768) (96*8 = 768 = 0x300).
    public fn GetHmac() byte[32]
    {
        // Capture inner digest before resetting SHA.
        var innerDigest: byte[32] = this.sha.GetDigest()
        var id0:  byte = innerDigest[0];  var id1:  byte = innerDigest[1];  var id2:  byte = innerDigest[2];  var id3:  byte = innerDigest[3]
        var id4:  byte = innerDigest[4];  var id5:  byte = innerDigest[5];  var id6:  byte = innerDigest[6];  var id7:  byte = innerDigest[7]
        var id8:  byte = innerDigest[8];  var id9:  byte = innerDigest[9];  var id10: byte = innerDigest[10]; var id11: byte = innerDigest[11]
        var id12: byte = innerDigest[12]; var id13: byte = innerDigest[13]; var id14: byte = innerDigest[14]; var id15: byte = innerDigest[15]
        var id16: byte = innerDigest[16]; var id17: byte = innerDigest[17]; var id18: byte = innerDigest[18]; var id19: byte = innerDigest[19]
        var id20: byte = innerDigest[20]; var id21: byte = innerDigest[21]; var id22: byte = innerDigest[22]; var id23: byte = innerDigest[23]
        var id24: byte = innerDigest[24]; var id25: byte = innerDigest[25]; var id26: byte = innerDigest[26]; var id27: byte = innerDigest[27]
        var id28: byte = innerDigest[28]; var id29: byte = innerDigest[29]; var id30: byte = innerDigest[30]; var id31: byte = innerDigest[31]

        // Pre-read kOpad into locals.
        var o0:  byte = this.kOpad[0];  var o1:  byte = this.kOpad[1];  var o2:  byte = this.kOpad[2];  var o3:  byte = this.kOpad[3]
        var o4:  byte = this.kOpad[4];  var o5:  byte = this.kOpad[5];  var o6:  byte = this.kOpad[6];  var o7:  byte = this.kOpad[7]
        var o8:  byte = this.kOpad[8];  var o9:  byte = this.kOpad[9];  var o10: byte = this.kOpad[10]; var o11: byte = this.kOpad[11]
        var o12: byte = this.kOpad[12]; var o13: byte = this.kOpad[13]; var o14: byte = this.kOpad[14]; var o15: byte = this.kOpad[15]
        var o16: byte = this.kOpad[16]; var o17: byte = this.kOpad[17]; var o18: byte = this.kOpad[18]; var o19: byte = this.kOpad[19]
        var o20: byte = this.kOpad[20]; var o21: byte = this.kOpad[21]; var o22: byte = this.kOpad[22]; var o23: byte = this.kOpad[23]
        var o24: byte = this.kOpad[24]; var o25: byte = this.kOpad[25]; var o26: byte = this.kOpad[26]; var o27: byte = this.kOpad[27]
        var o28: byte = this.kOpad[28]; var o29: byte = this.kOpad[29]; var o30: byte = this.kOpad[30]; var o31: byte = this.kOpad[31]
        var o32: byte = this.kOpad[32]; var o33: byte = this.kOpad[33]; var o34: byte = this.kOpad[34]; var o35: byte = this.kOpad[35]
        var o36: byte = this.kOpad[36]; var o37: byte = this.kOpad[37]; var o38: byte = this.kOpad[38]; var o39: byte = this.kOpad[39]
        var o40: byte = this.kOpad[40]; var o41: byte = this.kOpad[41]; var o42: byte = this.kOpad[42]; var o43: byte = this.kOpad[43]
        var o44: byte = this.kOpad[44]; var o45: byte = this.kOpad[45]; var o46: byte = this.kOpad[46]; var o47: byte = this.kOpad[47]
        var o48: byte = this.kOpad[48]; var o49: byte = this.kOpad[49]; var o50: byte = this.kOpad[50]; var o51: byte = this.kOpad[51]
        var o52: byte = this.kOpad[52]; var o53: byte = this.kOpad[53]; var o54: byte = this.kOpad[54]; var o55: byte = this.kOpad[55]
        var o56: byte = this.kOpad[56]; var o57: byte = this.kOpad[57]; var o58: byte = this.kOpad[58]; var o59: byte = this.kOpad[59]
        var o60: byte = this.kOpad[60]; var o61: byte = this.kOpad[61]; var o62: byte = this.kOpad[62]; var o63: byte = this.kOpad[63]

        // Outer pass block 1: k_opad.
        var opadBlock: byte[64]
        opadBlock[0]  = o0;  opadBlock[1]  = o1;  opadBlock[2]  = o2;  opadBlock[3]  = o3
        opadBlock[4]  = o4;  opadBlock[5]  = o5;  opadBlock[6]  = o6;  opadBlock[7]  = o7
        opadBlock[8]  = o8;  opadBlock[9]  = o9;  opadBlock[10] = o10; opadBlock[11] = o11
        opadBlock[12] = o12; opadBlock[13] = o13; opadBlock[14] = o14; opadBlock[15] = o15
        opadBlock[16] = o16; opadBlock[17] = o17; opadBlock[18] = o18; opadBlock[19] = o19
        opadBlock[20] = o20; opadBlock[21] = o21; opadBlock[22] = o22; opadBlock[23] = o23
        opadBlock[24] = o24; opadBlock[25] = o25; opadBlock[26] = o26; opadBlock[27] = o27
        opadBlock[28] = o28; opadBlock[29] = o29; opadBlock[30] = o30; opadBlock[31] = o31
        opadBlock[32] = o32; opadBlock[33] = o33; opadBlock[34] = o34; opadBlock[35] = o35
        opadBlock[36] = o36; opadBlock[37] = o37; opadBlock[38] = o38; opadBlock[39] = o39
        opadBlock[40] = o40; opadBlock[41] = o41; opadBlock[42] = o42; opadBlock[43] = o43
        opadBlock[44] = o44; opadBlock[45] = o45; opadBlock[46] = o46; opadBlock[47] = o47
        opadBlock[48] = o48; opadBlock[49] = o49; opadBlock[50] = o50; opadBlock[51] = o51
        opadBlock[52] = o52; opadBlock[53] = o53; opadBlock[54] = o54; opadBlock[55] = o55
        opadBlock[56] = o56; opadBlock[57] = o57; opadBlock[58] = o58; opadBlock[59] = o59
        opadBlock[60] = o60; opadBlock[61] = o61; opadBlock[62] = o62; opadBlock[63] = o63

        this.sha.Reset()
        this.sha.ProcessBlock(opadBlock)

        // Outer pass block 2: inner_digest || SHA-256 padding for 96-byte outer message.
        // 96 * 8 = 768 = 0x0000000000000300
        var ob2: byte[64]
        ob2[0]  = id0;  ob2[1]  = id1;  ob2[2]  = id2;  ob2[3]  = id3
        ob2[4]  = id4;  ob2[5]  = id5;  ob2[6]  = id6;  ob2[7]  = id7
        ob2[8]  = id8;  ob2[9]  = id9;  ob2[10] = id10; ob2[11] = id11
        ob2[12] = id12; ob2[13] = id13; ob2[14] = id14; ob2[15] = id15
        ob2[16] = id16; ob2[17] = id17; ob2[18] = id18; ob2[19] = id19
        ob2[20] = id20; ob2[21] = id21; ob2[22] = id22; ob2[23] = id23
        ob2[24] = id24; ob2[25] = id25; ob2[26] = id26; ob2[27] = id27
        ob2[28] = id28; ob2[29] = id29; ob2[30] = id30; ob2[31] = id31
        ob2[32] = 0x80
        ob2[33] = 0x00; ob2[34] = 0x00; ob2[35] = 0x00; ob2[36] = 0x00; ob2[37] = 0x00; ob2[38] = 0x00; ob2[39] = 0x00
        ob2[40] = 0x00; ob2[41] = 0x00; ob2[42] = 0x00; ob2[43] = 0x00; ob2[44] = 0x00; ob2[45] = 0x00; ob2[46] = 0x00; ob2[47] = 0x00
        ob2[48] = 0x00; ob2[49] = 0x00; ob2[50] = 0x00; ob2[51] = 0x00; ob2[52] = 0x00; ob2[53] = 0x00; ob2[54] = 0x00; ob2[55] = 0x00
        ob2[56] = 0x00; ob2[57] = 0x00; ob2[58] = 0x00; ob2[59] = 0x00
        ob2[60] = 0x00; ob2[61] = 0x00; ob2[62] = 0x03; ob2[63] = 0x00

        this.sha.ProcessBlock(ob2)
        return this.sha.GetDigest()
    }

    // -----------------------------------------------------------------------
}