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()
}
// -----------------------------------------------------------------------
}