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)

tests/aes/Aes256Test.lvt

namespace Livt.Crypto.Tests.Aes
using Livt.Crypto.Aes



@Test
component Aes256Test
{
    aes: EncryptBlock256

    new()
    {
        this.aes = new EncryptBlock256()
    }

    // NIST FIPS 197 Appendix C.3 AES-256 test vector.
    // Key:        000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
    // Plaintext:  00112233445566778899aabbccddeeff
    // Ciphertext: 8ea2b7ca516745bfeafc49904b496089
    // Verify: echo "00112233445566778899aabbccddeeff" | xxd -r -p | openssl enc -aes-256-ecb -K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f -nopad 2>/dev/null | xxd -p
    @Test
    fn TestNistFips197()
    {
        var key: byte[32] = [
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]

        var plaintext: byte[16] = [
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
            0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]

        this.aes.SetKey(key)
        this.aes.LoadBlock(plaintext)
        this.aes.EncryptBlock()

        var ct: byte[16] = this.aes.GetBlock()
        assert ct[0] == 0x8e
        assert ct[1] == 0xa2
        assert ct[2] == 0xb7
        assert ct[3] == 0xca
        assert ct[4] == 0x51
        assert ct[5] == 0x67
        assert ct[6] == 0x45
        assert ct[7] == 0xbf
        assert ct[8] == 0xea
        assert ct[9] == 0xfc
        assert ct[10] == 0x49
        assert ct[11] == 0x90
        assert ct[12] == 0x4b
        assert ct[13] == 0x49
        assert ct[14] == 0x60
        assert ct[15] == 0x89
    }

    // NIST SP 800-38A Appendix F.1.5 AES-256 ECB — two blocks with the same key,
    // verifying that EncryptBlock produces correct output on successive calls.
    // Key:         603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
    // Plaintext 1: 6bc1bee22e409f96e93d7e117393172a
    // Ciphertext 1: f3eed1bdb5d2a03c064b5a7e3db181f8
    // Verify: echo "6bc1bee22e409f96e93d7e117393172a" | xxd -r -p | openssl enc -aes-256-ecb -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -nopad 2>/dev/null | xxd -p
    // Plaintext 2: ae2d8a571e03ac9c9eb76fac45af8e51
    // Ciphertext 2: 591ccb10d410ed26dc5ba74a31362870
    // Verify: echo "ae2d8a571e03ac9c9eb76fac45af8e51" | xxd -r -p | openssl enc -aes-256-ecb -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -nopad 2>/dev/null | xxd -p
    @Test
    fn TestNistSp80038A()
    {
        var key2: byte[32] = [
            0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
            0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
            0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
            0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4]

        var pt1: byte[16] = [
            0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
            0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a]

        this.aes.SetKey(key2)
        this.aes.LoadBlock(pt1)
        this.aes.EncryptBlock()

        var ct1: byte[16] = this.aes.GetBlock()
        assert ct1[0] == 0xf3
        assert ct1[1] == 0xee
        assert ct1[2] == 0xd1
        assert ct1[3] == 0xbd
        assert ct1[4] == 0xb5
        assert ct1[5] == 0xd2
        assert ct1[6] == 0xa0
        assert ct1[7] == 0x3c
        assert ct1[8] == 0x06
        assert ct1[9] == 0x4b
        assert ct1[10] == 0x5a
        assert ct1[11] == 0x7e
        assert ct1[12] == 0x3d
        assert ct1[13] == 0xb1
        assert ct1[14] == 0x81
        assert ct1[15] == 0xf8

        // Second block with same key — verifies no stale state between calls.
        var pt2: byte[16] = [
            0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
            0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51]

        this.aes.LoadBlock(pt2)
        this.aes.EncryptBlock()

        var ct2: byte[16] = this.aes.GetBlock()
        assert ct2[0] == 0x59
        assert ct2[1] == 0x1c
        assert ct2[2] == 0xcb
        assert ct2[3] == 0x10
        assert ct2[4] == 0xd4
        assert ct2[5] == 0x10
        assert ct2[6] == 0xed
        assert ct2[7] == 0x26
        assert ct2[8] == 0xdc
        assert ct2[9] == 0x5b
        assert ct2[10] == 0xa7
        assert ct2[11] == 0x4a
        assert ct2[12] == 0x31
        assert ct2[13] == 0x36
        assert ct2[14] == 0x28
        assert ct2[15] == 0x70
    }

    // All-zero 256-bit key with all-zero plaintext.
    // Key:        0000000000000000000000000000000000000000000000000000000000000000
    // Plaintext:  00000000000000000000000000000000
    // Ciphertext: dc95c078a2408989ad48a21492842087
    // Verify: echo "00000000000000000000000000000000" | xxd -r -p | openssl enc -aes-256-ecb -K 0000000000000000000000000000000000000000000000000000000000000000 -nopad 2>/dev/null | xxd -p
    @Test
    fn TestAllZeroKeyAndPlaintext()
    {
        var key3: byte[32] = [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

        var pt3: byte[16] = [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

        this.aes.SetKey(key3)
        this.aes.LoadBlock(pt3)
        this.aes.EncryptBlock()

        var ct: byte[16] = this.aes.GetBlock()
        assert ct[0] == 0xdc
        assert ct[1] == 0x95
        assert ct[2] == 0xc0
        assert ct[3] == 0x78
        assert ct[4] == 0xa2
        assert ct[5] == 0x40
        assert ct[6] == 0x89
        assert ct[7] == 0x89
        assert ct[8] == 0xad
        assert ct[9] == 0x48
        assert ct[10] == 0xa2
        assert ct[11] == 0x14
        assert ct[12] == 0x92
        assert ct[13] == 0x84
        assert ct[14] == 0x20
        assert ct[15] == 0x87
    }

    // NIST SP 800-38A Appendix F.1.5 AES-256 ECB — blocks 3 and 4, same key as
    // TestNistSp80038A.  Verifies that EncryptBlock produces the correct output
    // for additional successive blocks with the same key schedule.
    // Key:         603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
    // Plaintext 3: 30c81c46a35ce411e5fbc1191a0a52ef
    // Ciphertext 3: b6ed21b99ca6f4f9f153e7b1beafed1d
    // Verify: echo "30c81c46a35ce411e5fbc1191a0a52ef" | xxd -r -p | openssl enc -aes-256-ecb -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -nopad 2>/dev/null | xxd -p
    // Plaintext 4: f69f2445df4f9b17ad2b417be66c3710
    // Ciphertext 4: 23304b7a39f9f3ff067d8d8f9e24ecc7
    // Verify: echo "f69f2445df4f9b17ad2b417be66c3710" | xxd -r -p | openssl enc -aes-256-ecb -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -nopad 2>/dev/null | xxd -p
    @Test
    fn TestNistSp80038ABlocks3And4()
    {
        var key: byte[32] = [
            0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
            0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
            0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
            0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4]

        var pt3: byte[16] = [
            0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
            0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef]

        this.aes.SetKey(key)
        this.aes.LoadBlock(pt3)
        this.aes.EncryptBlock()

        var ct1: byte[16] = this.aes.GetBlock()
        assert ct1[0] == 0xb6
        assert ct1[1] == 0xed
        assert ct1[2] == 0x21
        assert ct1[3] == 0xb9
        assert ct1[4] == 0x9c
        assert ct1[5] == 0xa6
        assert ct1[6] == 0xf4
        assert ct1[7] == 0xf9
        assert ct1[8] == 0xf1
        assert ct1[9] == 0x53
        assert ct1[10] == 0xe7
        assert ct1[11] == 0xb1
        assert ct1[12] == 0xbe
        assert ct1[13] == 0xaf
        assert ct1[14] == 0xed
        assert ct1[15] == 0x1d

        // Block 4 — same key, no SetKey needed.
        var pt4: byte[16] = [
            0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
            0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10]

        this.aes.LoadBlock(pt4)
        this.aes.EncryptBlock()

        var ct2: byte[16] = this.aes.GetBlock()
        assert ct2[0] == 0x23
        assert ct2[1] == 0x30
        assert ct2[2] == 0x4b
        assert ct2[3] == 0x7a
        assert ct2[4] == 0x39
        assert ct2[5] == 0xf9
        assert ct2[6] == 0xf3
        assert ct2[7] == 0xff
        assert ct2[8] == 0x06
        assert ct2[9] == 0x7d
        assert ct2[10] == 0x8d
        assert ct2[11] == 0x8f
        assert ct2[12] == 0x9e
        assert ct2[13] == 0x24
        assert ct2[14] == 0xec
        assert ct2[15] == 0xc7
    }

    // User behaviour: encrypting the same plaintext with the same key a second
    // time must produce identical ciphertext.  Verifies that LoadBlock + EncryptBlock
    // leave no stale state that would corrupt a repeated call.
    // Uses the NIST FIPS 197 Appendix C.3 vector.
    // Key:       000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
    // Plaintext: 00112233445566778899aabbccddeeff
    // Expected:  8ea2b7ca516745bfeafc49904b496089 (both encryptions)
    @Test
    fn TestDeterminism()
    {
        var key: byte[32] = [
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]

        var plaintext: byte[16] = [
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
            0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]

        this.aes.SetKey(key)

        // First encryption.
        this.aes.LoadBlock(plaintext)
        this.aes.EncryptBlock()

        var ct1: byte[16] = this.aes.GetBlock()
        assert ct1[0] == 0x8e
        assert ct1[1] == 0xa2
        assert ct1[2] == 0xb7
        assert ct1[3] == 0xca
        assert ct1[4] == 0x51
        assert ct1[5] == 0x67
        assert ct1[6] == 0x45
        assert ct1[7] == 0xbf
        assert ct1[8] == 0xea
        assert ct1[9] == 0xfc
        assert ct1[10] == 0x49
        assert ct1[11] == 0x90
        assert ct1[12] == 0x4b
        assert ct1[13] == 0x49
        assert ct1[14] == 0x60
        assert ct1[15] == 0x89

        // Second encryption of the same plaintext — must produce the same ciphertext.
        this.aes.LoadBlock(plaintext)
        this.aes.EncryptBlock()

        var ct2: byte[16] = this.aes.GetBlock()
        assert ct2[0] == 0x8e
        assert ct2[1] == 0xa2
        assert ct2[2] == 0xb7
        assert ct2[3] == 0xca
        assert ct2[4] == 0x51
        assert ct2[5] == 0x67
        assert ct2[6] == 0x45
        assert ct2[7] == 0xbf
        assert ct2[8] == 0xea
        assert ct2[9] == 0xfc
        assert ct2[10] == 0x49
        assert ct2[11] == 0x90
        assert ct2[12] == 0x4b
        assert ct2[13] == 0x49
        assert ct2[14] == 0x60
        assert ct2[15] == 0x89
    }

    // User behaviour: calling SetKey between encryptions must produce output
    // consistent with the new key.  Uses two independent NIST vectors:
    //   key1 (FIPS 197) + plaintext1 → ciphertext1 = 8ea2b7ca516745bfeafc49904b496089
    //   key2 (all-zero) + plaintext2 (all-zero) → ciphertext2 = dc95c078a2408989ad48a21492842087
    @Test
    fn TestKeyChangeWorkflow()
    {
        // First encryption — NIST FIPS 197 Appendix C.3.
        var key1: byte[32] = [
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]

        var pt1: byte[16] = [
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
            0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]

        this.aes.SetKey(key1)
        this.aes.LoadBlock(pt1)
        this.aes.EncryptBlock()

        var ct1: byte[16] = this.aes.GetBlock()
        assert ct1[0] == 0x8e
        assert ct1[1] == 0xa2
        assert ct1[2] == 0xb7
        assert ct1[3] == 0xca
        assert ct1[4] == 0x51
        assert ct1[5] == 0x67
        assert ct1[6] == 0x45
        assert ct1[7] == 0xbf
        assert ct1[8] == 0xea
        assert ct1[9] == 0xfc
        assert ct1[10] == 0x49
        assert ct1[11] == 0x90
        assert ct1[12] == 0x4b
        assert ct1[13] == 0x49
        assert ct1[14] == 0x60
        assert ct1[15] == 0x89

        // Change key and encrypt a different block — all-zero key + all-zero plaintext.
        var key2: byte[32] = [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

        var pt2: byte[16] = [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

        this.aes.SetKey(key2)
        this.aes.LoadBlock(pt2)
        this.aes.EncryptBlock()

        var ct2: byte[16] = this.aes.GetBlock()
        assert ct2[0] == 0xdc
        assert ct2[1] == 0x95
        assert ct2[2] == 0xc0
        assert ct2[3] == 0x78
        assert ct2[4] == 0xa2
        assert ct2[5] == 0x40
        assert ct2[6] == 0x89
        assert ct2[7] == 0x89
        assert ct2[8] == 0xad
        assert ct2[9] == 0x48
        assert ct2[10] == 0xa2
        assert ct2[11] == 0x14
        assert ct2[12] == 0x92
        assert ct2[13] == 0x84
        assert ct2[14] == 0x20
        assert ct2[15] == 0x87
    }

    // User behaviour: ClearKey zeroes the round key schedule so that key
    // material does not persist after a session ends.  Verifies by clearing
    // the key, loading a fresh key, and confirming correct ciphertext output.
    // A corrupted key schedule would produce wrong ciphertext.
    // Key:      000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
    // Expected: 8ea2b7ca516745bfeafc49904b496089 (NIST FIPS 197 Appendix C.3)
    @Test
    fn TestClearKey()
    {
        var key: byte[32] = [
            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]

        var plaintext: byte[16] = [
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
            0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]

        // Establish key, then clear it to simulate end-of-session zeroization.
        this.aes.SetKey(key)
        this.aes.ClearKey()

        // Reload the same key and encrypt — must produce correct NIST ciphertext.
        // If ClearKey left the schedule in a bad state this will produce wrong output.
        this.aes.SetKey(key)
        this.aes.LoadBlock(plaintext)
        this.aes.EncryptBlock()

        var ct: byte[16] = this.aes.GetBlock()
        assert ct[0] == 0x8e
        assert ct[1] == 0xa2
        assert ct[2] == 0xb7
        assert ct[3] == 0xca
        assert ct[4] == 0x51
        assert ct[5] == 0x67
        assert ct[6] == 0x45
        assert ct[7] == 0xbf
        assert ct[8] == 0xea
        assert ct[9] == 0xfc
        assert ct[10] == 0x49
        assert ct[11] == 0x90
        assert ct[12] == 0x4b
        assert ct[13] == 0x49
        assert ct[14] == 0x60
        assert ct[15] == 0x89
    }
}