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/internal/Ghash.lvt

namespace Livt.Crypto.Internal

// Ghash — GHASH function for GCM-based authenticated encryption (NIST SP 800-38D).
//
// GHASH accumulates a sequence of 16-byte blocks into a 16-byte authenticator.
// The hash key H = AES_Encrypt(0^128) is set via SetHashKey.
// Usage:
//   1. SetHashKey(h)         — set the AES-derived hash key H
//   2. Reset()               — zero the accumulator Y
//   3. For each block b:
//        UpdateBlock(b)      — Y = GfMul128(Y XOR b, H)
//   4. var tag = GetY()      — read the accumulator
//
// GF(2^128) field polynomial: x^128 + x^7 + x^2 + x + 1 (GCM spec, R = E1 00..00).
// Bit ordering follows GCM spec: the most-significant bit of the first byte is
// the coefficient of x^0.
component Ghash
{
    H:  byte[16]
    Y:  byte[16]

    new()
    {
    }

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

    public fn SetHashKey(h: byte[16])
    {
        for (var i = 0; i < 16; i++)
        {
            this.H[i] = h[i]
        }
    }

    public fn Reset()
    {
        for (var i = 0; i < 16; i++)
        {
            this.Y[i] = 0x00
        }
    }

    // UpdateBlock: Y = GfMul128(Y XOR block, H)
    public fn UpdateBlock(block: byte[16])
    {
        // XOR accumulator with block.
        var xb0:  byte = this.Y[0] ^ block[0]
        var xb1:  byte = this.Y[1] ^ block[1]
        var xb2:  byte = this.Y[2] ^ block[2]
        var xb3:  byte = this.Y[3] ^ block[3]
        var xb4:  byte = this.Y[4] ^ block[4]
        var xb5:  byte = this.Y[5] ^ block[5]
        var xb6:  byte = this.Y[6] ^ block[6]
        var xb7:  byte = this.Y[7] ^ block[7]
        var xb8:  byte = this.Y[8] ^ block[8]
        var xb9:  byte = this.Y[9] ^ block[9]
        var xb10: byte = this.Y[10] ^ block[10]
        var xb11: byte = this.Y[11] ^ block[11]
        var xb12: byte = this.Y[12] ^ block[12]
        var xb13: byte = this.Y[13] ^ block[13]
        var xb14: byte = this.Y[14] ^ block[14]
        var xb15: byte = this.Y[15] ^ block[15]

        // GF(2^128) multiply (xb, H) into result R.
        var result: byte[16] = this.GfMul128(xb0, xb1, xb2, xb3, xb4, xb5, xb6, xb7,
                                             xb8, xb9, xb10, xb11, xb12, xb13, xb14, xb15)

        for (var i = 0; i < 16; i++)
        {
            this.Y[i] = result[i]
        }
    }

    public fn GetY() byte[16]
    {
        return this.Y
    }

    // -----------------------------------------------------------------------
    // GF(2^128) carry-less multiplication.
    // Inputs X (16 bytes, passed as individual args to avoid expression
    // boundary issues) and H (from this.H).
    // Algorithm: right-to-left Russian-peasant over the 128 bits of X,
    // processing bit i of X (MSB-first per GCM spec: bit 0 is MSB of byte 0).
    //
    // Z = 0 (accumulator), V = H.
    // For each bit i of X (i = 0..127):
    //   if bit i of X == 1: Z = Z XOR V
    //   if LSB of V == 1:   V = (V >> 1) XOR R   where R[0]=0xe1, R[1..15]=0
    //   else:               V = V >> 1
    // Return Z.
    // -----------------------------------------------------------------------

    fn GfMul128(x0: byte, x1: byte, x2: byte, x3: byte,
                x4: byte, x5: byte, x6: byte, x7: byte,
                x8: byte, x9: byte, x10: byte, x11: byte,
                x12: byte, x13: byte, x14: byte, x15: byte) byte[16]
    {
        // Accumulator Z (128 bits).
        var z0:  int = 0
        var z1:  int = 0
        var z2:  int = 0
        var z3:  int = 0
        var z4:  int = 0
        var z5:  int = 0
        var z6:  int = 0
        var z7:  int = 0
        var z8:  int = 0
        var z9:  int = 0
        var z10: int = 0
        var z11: int = 0
        var z12: int = 0
        var z13: int = 0
        var z14: int = 0
        var z15: int = 0

        // V = H (working copy of H).
        var v0:  int = this.H[0]  as int
        var v1:  int = this.H[1]  as int
        var v2:  int = this.H[2]  as int
        var v3:  int = this.H[3]  as int
        var v4:  int = this.H[4]  as int
        var v5:  int = this.H[5]  as int
        var v6:  int = this.H[6]  as int
        var v7:  int = this.H[7]  as int
        var v8:  int = this.H[8]  as int
        var v9:  int = this.H[9]  as int
        var v10: int = this.H[10] as int
        var v11: int = this.H[11] as int
        var v12: int = this.H[12] as int
        var v13: int = this.H[13] as int
        var v14: int = this.H[14] as int
        var v15: int = this.H[15] as int

        // Build a flat 128-element int array from the 16 X bytes (each bit as 0/1).
        // xBits[0] = MSB of x0, xBits[127] = LSB of x15.
        var xBits: int[128]
        var xInt0:  int = x0  as int
        var xInt1:  int = x1  as int
        var xInt2:  int = x2  as int
        var xInt3:  int = x3  as int
        var xInt4:  int = x4  as int
        var xInt5:  int = x5  as int
        var xInt6:  int = x6  as int
        var xInt7:  int = x7  as int
        var xInt8:  int = x8  as int
        var xInt9:  int = x9  as int
        var xInt10: int = x10 as int
        var xInt11: int = x11 as int
        var xInt12: int = x12 as int
        var xInt13: int = x13 as int
        var xInt14: int = x14 as int
        var xInt15: int = x15 as int

        xBits[0]   = (xInt0  / 128) % 2
        xBits[1]   = (xInt0  / 64)  % 2
        xBits[2]   = (xInt0  / 32)  % 2
        xBits[3]   = (xInt0  / 16)  % 2
        xBits[4]   = (xInt0  / 8)   % 2
        xBits[5]   = (xInt0  / 4)   % 2
        xBits[6]   = (xInt0  / 2)   % 2
        xBits[7]   =  xInt0         % 2
        xBits[8]   = (xInt1  / 128) % 2
        xBits[9]   = (xInt1  / 64)  % 2
        xBits[10]  = (xInt1  / 32)  % 2
        xBits[11]  = (xInt1  / 16)  % 2
        xBits[12]  = (xInt1  / 8)   % 2
        xBits[13]  = (xInt1  / 4)   % 2
        xBits[14]  = (xInt1  / 2)   % 2
        xBits[15]  =  xInt1         % 2
        xBits[16]  = (xInt2  / 128) % 2
        xBits[17]  = (xInt2  / 64)  % 2
        xBits[18]  = (xInt2  / 32)  % 2
        xBits[19]  = (xInt2  / 16)  % 2
        xBits[20]  = (xInt2  / 8)   % 2
        xBits[21]  = (xInt2  / 4)   % 2
        xBits[22]  = (xInt2  / 2)   % 2
        xBits[23]  =  xInt2         % 2
        xBits[24]  = (xInt3  / 128) % 2
        xBits[25]  = (xInt3  / 64)  % 2
        xBits[26]  = (xInt3  / 32)  % 2
        xBits[27]  = (xInt3  / 16)  % 2
        xBits[28]  = (xInt3  / 8)   % 2
        xBits[29]  = (xInt3  / 4)   % 2
        xBits[30]  = (xInt3  / 2)   % 2
        xBits[31]  =  xInt3         % 2
        xBits[32]  = (xInt4  / 128) % 2
        xBits[33]  = (xInt4  / 64)  % 2
        xBits[34]  = (xInt4  / 32)  % 2
        xBits[35]  = (xInt4  / 16)  % 2
        xBits[36]  = (xInt4  / 8)   % 2
        xBits[37]  = (xInt4  / 4)   % 2
        xBits[38]  = (xInt4  / 2)   % 2
        xBits[39]  =  xInt4         % 2
        xBits[40]  = (xInt5  / 128) % 2
        xBits[41]  = (xInt5  / 64)  % 2
        xBits[42]  = (xInt5  / 32)  % 2
        xBits[43]  = (xInt5  / 16)  % 2
        xBits[44]  = (xInt5  / 8)   % 2
        xBits[45]  = (xInt5  / 4)   % 2
        xBits[46]  = (xInt5  / 2)   % 2
        xBits[47]  =  xInt5         % 2
        xBits[48]  = (xInt6  / 128) % 2
        xBits[49]  = (xInt6  / 64)  % 2
        xBits[50]  = (xInt6  / 32)  % 2
        xBits[51]  = (xInt6  / 16)  % 2
        xBits[52]  = (xInt6  / 8)   % 2
        xBits[53]  = (xInt6  / 4)   % 2
        xBits[54]  = (xInt6  / 2)   % 2
        xBits[55]  =  xInt6         % 2
        xBits[56]  = (xInt7  / 128) % 2
        xBits[57]  = (xInt7  / 64)  % 2
        xBits[58]  = (xInt7  / 32)  % 2
        xBits[59]  = (xInt7  / 16)  % 2
        xBits[60]  = (xInt7  / 8)   % 2
        xBits[61]  = (xInt7  / 4)   % 2
        xBits[62]  = (xInt7  / 2)   % 2
        xBits[63]  =  xInt7         % 2
        xBits[64]  = (xInt8  / 128) % 2
        xBits[65]  = (xInt8  / 64)  % 2
        xBits[66]  = (xInt8  / 32)  % 2
        xBits[67]  = (xInt8  / 16)  % 2
        xBits[68]  = (xInt8  / 8)   % 2
        xBits[69]  = (xInt8  / 4)   % 2
        xBits[70]  = (xInt8  / 2)   % 2
        xBits[71]  =  xInt8         % 2
        xBits[72]  = (xInt9  / 128) % 2
        xBits[73]  = (xInt9  / 64)  % 2
        xBits[74]  = (xInt9  / 32)  % 2
        xBits[75]  = (xInt9  / 16)  % 2
        xBits[76]  = (xInt9  / 8)   % 2
        xBits[77]  = (xInt9  / 4)   % 2
        xBits[78]  = (xInt9  / 2)   % 2
        xBits[79]  =  xInt9         % 2
        xBits[80]  = (xInt10 / 128) % 2
        xBits[81]  = (xInt10 / 64)  % 2
        xBits[82]  = (xInt10 / 32)  % 2
        xBits[83]  = (xInt10 / 16)  % 2
        xBits[84]  = (xInt10 / 8)   % 2
        xBits[85]  = (xInt10 / 4)   % 2
        xBits[86]  = (xInt10 / 2)   % 2
        xBits[87]  =  xInt10        % 2
        xBits[88]  = (xInt11 / 128) % 2
        xBits[89]  = (xInt11 / 64)  % 2
        xBits[90]  = (xInt11 / 32)  % 2
        xBits[91]  = (xInt11 / 16)  % 2
        xBits[92]  = (xInt11 / 8)   % 2
        xBits[93]  = (xInt11 / 4)   % 2
        xBits[94]  = (xInt11 / 2)   % 2
        xBits[95]  =  xInt11        % 2
        xBits[96]  = (xInt12 / 128) % 2
        xBits[97]  = (xInt12 / 64)  % 2
        xBits[98]  = (xInt12 / 32)  % 2
        xBits[99]  = (xInt12 / 16)  % 2
        xBits[100] = (xInt12 / 8)   % 2
        xBits[101] = (xInt12 / 4)   % 2
        xBits[102] = (xInt12 / 2)   % 2
        xBits[103] =  xInt12        % 2
        xBits[104] = (xInt13 / 128) % 2
        xBits[105] = (xInt13 / 64)  % 2
        xBits[106] = (xInt13 / 32)  % 2
        xBits[107] = (xInt13 / 16)  % 2
        xBits[108] = (xInt13 / 8)   % 2
        xBits[109] = (xInt13 / 4)   % 2
        xBits[110] = (xInt13 / 2)   % 2
        xBits[111] =  xInt13        % 2
        xBits[112] = (xInt14 / 128) % 2
        xBits[113] = (xInt14 / 64)  % 2
        xBits[114] = (xInt14 / 32)  % 2
        xBits[115] = (xInt14 / 16)  % 2
        xBits[116] = (xInt14 / 8)   % 2
        xBits[117] = (xInt14 / 4)   % 2
        xBits[118] = (xInt14 / 2)   % 2
        xBits[119] =  xInt14        % 2
        xBits[120] = (xInt15 / 128) % 2
        xBits[121] = (xInt15 / 64)  % 2
        xBits[122] = (xInt15 / 32)  % 2
        xBits[123] = (xInt15 / 16)  % 2
        xBits[124] = (xInt15 / 8)   % 2
        xBits[125] = (xInt15 / 4)   % 2
        xBits[126] = (xInt15 / 2)   % 2
        xBits[127] =  xInt15        % 2

        // Main loop: 128 iterations, one per bit of X.
        for (var i = 0; i < 128; i++)
        {
            // If X bit i is 1: Z = Z XOR V.
            var xBit: int = xBits[i]
            if (xBit == 1)
            {
                var zv0:  byte = z0  as byte
                var zv1:  byte = z1  as byte
                var zv2:  byte = z2  as byte
                var zv3:  byte = z3  as byte
                var zv4:  byte = z4  as byte
                var zv5:  byte = z5  as byte
                var zv6:  byte = z6  as byte
                var zv7:  byte = z7  as byte
                var zv8:  byte = z8  as byte
                var zv9:  byte = z9  as byte
                var zv10: byte = z10 as byte
                var zv11: byte = z11 as byte
                var zv12: byte = z12 as byte
                var zv13: byte = z13 as byte
                var zv14: byte = z14 as byte
                var zv15: byte = z15 as byte
                var vv0:  byte = v0  as byte
                var vv1:  byte = v1  as byte
                var vv2:  byte = v2  as byte
                var vv3:  byte = v3  as byte
                var vv4:  byte = v4  as byte
                var vv5:  byte = v5  as byte
                var vv6:  byte = v6  as byte
                var vv7:  byte = v7  as byte
                var vv8:  byte = v8  as byte
                var vv9:  byte = v9  as byte
                var vv10: byte = v10 as byte
                var vv11: byte = v11 as byte
                var vv12: byte = v12 as byte
                var vv13: byte = v13 as byte
                var vv14: byte = v14 as byte
                var vv15: byte = v15 as byte
                var xr0:  byte = zv0 ^ vv0
                var xr1:  byte = zv1 ^ vv1
                var xr2:  byte = zv2 ^ vv2
                var xr3:  byte = zv3 ^ vv3
                var xr4:  byte = zv4 ^ vv4
                var xr5:  byte = zv5 ^ vv5
                var xr6:  byte = zv6 ^ vv6
                var xr7:  byte = zv7 ^ vv7
                var xr8:  byte = zv8 ^ vv8
                var xr9:  byte = zv9 ^ vv9
                var xr10: byte = zv10 ^ vv10
                var xr11: byte = zv11 ^ vv11
                var xr12: byte = zv12 ^ vv12
                var xr13: byte = zv13 ^ vv13
                var xr14: byte = zv14 ^ vv14
                var xr15: byte = zv15 ^ vv15
                var nz0:  int = xr0  as int
                var nz1:  int = xr1  as int
                var nz2:  int = xr2  as int
                var nz3:  int = xr3  as int
                var nz4:  int = xr4  as int
                var nz5:  int = xr5  as int
                var nz6:  int = xr6  as int
                var nz7:  int = xr7  as int
                var nz8:  int = xr8  as int
                var nz9:  int = xr9  as int
                var nz10: int = xr10 as int
                var nz11: int = xr11 as int
                var nz12: int = xr12 as int
                var nz13: int = xr13 as int
                var nz14: int = xr14 as int
                var nz15: int = xr15 as int
                z0  = nz0
                z1  = nz1
                z2  = nz2
                z3  = nz3
                z4  = nz4
                z5  = nz5
                z6  = nz6
                z7  = nz7
                z8  = nz8
                z9  = nz9
                z10 = nz10
                z11 = nz11
                z12 = nz12
                z13 = nz13
                z14 = nz14
                z15 = nz15
            }

            // Right-shift V by 1 (GCM: MSB is x^0, shift-right moves toward x^127).
            // New bit 0 (MSB of byte 0) = 0 (shift in a zero).
            // If old bit 127 (LSB of byte 15) was 1: XOR with R=E1 00..00.
            var lsb: int = v15 % 2

            var sv0:  int = v0  / 2 + (0)   * 128
            var sv1:  int = v1  / 2 + (v0  % 2) * 128
            var sv2:  int = v2  / 2 + (v1  % 2) * 128
            var sv3:  int = v3  / 2 + (v2  % 2) * 128
            var sv4:  int = v4  / 2 + (v3  % 2) * 128
            var sv5:  int = v5  / 2 + (v4  % 2) * 128
            var sv6:  int = v6  / 2 + (v5  % 2) * 128
            var sv7:  int = v7  / 2 + (v6  % 2) * 128
            var sv8:  int = v8  / 2 + (v7  % 2) * 128
            var sv9:  int = v9  / 2 + (v8  % 2) * 128
            var sv10: int = v10 / 2 + (v9  % 2) * 128
            var sv11: int = v11 / 2 + (v10 % 2) * 128
            var sv12: int = v12 / 2 + (v11 % 2) * 128
            var sv13: int = v13 / 2 + (v12 % 2) * 128
            var sv14: int = v14 / 2 + (v13 % 2) * 128
            var sv15: int = v15 / 2 + (v14 % 2) * 128

            // If lsb == 1: XOR with R = E1 00..00 (byte 0 = 0xe1, rest = 0x00).
            if (lsb == 1)
            {
                var sv0b:  byte = sv0  as byte
                var r0b:   byte = 0xe1
                var xv0b:  byte = sv0b ^ r0b
                var xv0i:  int = xv0b as int
                sv0 = xv0i
            }

            v0  = sv0
            v1  = sv1
            v2  = sv2
            v3  = sv3
            v4  = sv4
            v5  = sv5
            v6  = sv6
            v7  = sv7
            v8  = sv8
            v9  = sv9
            v10 = sv10
            v11 = sv11
            v12 = sv12
            v13 = sv13
            v14 = sv14
            v15 = sv15
        }

        // Assemble result.
        var res: byte[16]
        var rz0:  byte = z0  as byte
        var rz1:  byte = z1  as byte
        var rz2:  byte = z2  as byte
        var rz3:  byte = z3  as byte
        var rz4:  byte = z4  as byte
        var rz5:  byte = z5  as byte
        var rz6:  byte = z6  as byte
        var rz7:  byte = z7  as byte
        var rz8:  byte = z8  as byte
        var rz9:  byte = z9  as byte
        var rz10: byte = z10 as byte
        var rz11: byte = z11 as byte
        var rz12: byte = z12 as byte
        var rz13: byte = z13 as byte
        var rz14: byte = z14 as byte
        var rz15: byte = z15 as byte
        res[0]  = rz0
        res[1]  = rz1
        res[2]  = rz2
        res[3]  = rz3
        res[4]  = rz4
        res[5]  = rz5
        res[6]  = rz6
        res[7]  = rz7
        res[8]  = rz8
        res[9]  = rz9
        res[10] = rz10
        res[11] = rz11
        res[12] = rz12
        res[13] = rz13
        res[14] = rz14
        res[15] = rz15
        return res
    }
}