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/kdf/HkdfSha256.lvt

namespace Livt.Crypto.Kdf
using Livt.Crypto.Mac


// HKDF-SHA-256 (RFC 5869) — single-block message model.
//
// HKDF has two stages:
//   Extract: PRK = HMAC-SHA256(salt, IKM)
//   Expand:  T(1) = HMAC-SHA256(PRK, info || 0x01)
//            T(N) = HMAC-SHA256(PRK, T(N-1) || info || N)   for N >= 2
//            OKM  = T(1) || T(2) || ... (truncated to L bytes)
//
// Caller responsibilities (single-block model):
//   - salt is provided as a 32-byte key (zero-pad if shorter than 32 bytes;
//     keys > 32 bytes should be SHA-256-hashed first).
//   - All message blocks are SHA-256-padded 64-byte arrays.  The caller
//     accounts for the 64-byte k_ipad block already consumed by Init().
//     Total inner SHA-256 input = 64 (k_ipad) + len(message).
//
//   ikmBlock (64 B):
//     IKM || 0x80 || zeros || be64((64 + len(IKM)) * 8)
//
//   t1Block (64 B):
//     info || 0x01 || 0x80 || zeros || be64((64 + len(info) + 1) * 8)
//
//   tnBlock (64 B):
//     T(N-1) || info || N || 0x80 || zeros || be64((64 + 32 + len(info) + 1) * 8)
//     Constraint: len(info) <= 31 so that the full message fits in 64 bytes.
component HkdfSha256
{
    public const PRK_SIZE:  int = 32
    public const OKM_BLOCK: int = 32

    hmac: HmacSha256

    new()
    {
        this.hmac = new HmacSha256()
    }

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

    // Extract: PRK = HMAC-SHA256(salt, ikmBlock)
    // salt    — 32-byte key (zero-padded from the raw salt)
    // ikmBlock — SHA-256-padded IKM (64 B); inner total = 64 + len(IKM) bytes
    public fn Extract(salt: byte[32], ikmBlock: byte[64]) byte[32]
    {
        this.hmac.SetKey(salt)
        this.hmac.Init()
        this.hmac.ProcessBlock(ikmBlock)
        return this.hmac.GetHmac()
    }

    // ExpandT1: T(1) = HMAC-SHA256(PRK, t1Block)
    // prk     — 32-byte pseudorandom key from Extract
    // t1Block — SHA-256-padded (info || 0x01) (64 B); inner total = 64 + len(info) + 1 bytes
    public fn ExpandT1(prk: byte[32], t1Block: byte[64]) byte[32]
    {
        this.hmac.SetKey(prk)
        this.hmac.Init()
        this.hmac.ProcessBlock(t1Block)
        return this.hmac.GetHmac()
    }

    // ExpandTN: T(N) = HMAC-SHA256(PRK, tnBlock) for N >= 2
    // prk     — 32-byte pseudorandom key from Extract
    // tnBlock — SHA-256-padded (T(N-1) || info || N) (64 B)
    //           inner total = 64 + 32 + len(info) + 1 bytes
    //           len(info) must be <= 31 so the message fits in 64 bytes.
    public fn ExpandTN(prk: byte[32], tnBlock: byte[64]) byte[32]
    {
        this.hmac.SetKey(prk)
        this.hmac.Init()
        this.hmac.ProcessBlock(tnBlock)
        return this.hmac.GetHmac()
    }
}