AES-128-ECB/CBC 查表法 C#实现

遇到一段汇编代码,没认出来是查表法的AES。这里完全用字节处理,来实现AES加密计算,来加深一下对查表法AES的印象。

由于采用对字节的操作,会造成很多无畏的内存读写,运算速度肯定没有对uint(4字节)来的快。本来反汇编出来的就是uint的,这边只是想学习具体细节,才转换成字节的。后续有空了,再把整理后的uint操作的代码贴出来吧。

主要参考了:

查表法的理论计算过程

部分代码参考

部分代码参考2

字节操作函数

        /// <summary>
        /// 字节数组->uint,大端模式
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static uint ReadUint_BE(byte[] buf, int index)
        {
            uint d1 = buf[index + 3]; // buf的高位,成为retData的尾端
            uint d2 = (uint)(buf[index + 2] << 8);
            uint d3 = (uint)(buf[index + 1] << 16);
            uint d4 = (uint)(buf[index] << 24);
            return d1 | d2 | d3 | d4;
        }
        public static void WriteUint_BE(byte[] buf, int index, uint data)
        {
            buf[index + 3] = (byte)(data); // data低8位(尾端)写到buf的高位
            buf[index + 2] = (byte)(data >> 8);
            buf[index + 1] = (byte)(data >> 16);
            buf[index + 0] = (byte)(data >> 24); // data高8位写到buf的低位
        }
        /// <summary>
        /// 字节数组->uint,小端模式
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static uint ReadUint_LE(byte[] buf, int index)
        {
            uint d1 = buf[index]; // buf的低位,成为retData的尾端
            uint d2 = (uint)(buf[index + 1] << 8);
            uint d3 = (uint)(buf[index + 2] << 16);
            uint d4 = (uint)(buf[index + 3] << 24);
            return d1 | d2 | d3 | d4;
        }
        public static void WriteUint_LE(byte[] buf, int index, uint data)
        {
            buf[index] = (byte)data; // data低8位(尾端)写道buf的低位
            buf[index + 1] = (byte)(data >> 8);
            buf[index + 2] = (byte)(data >> 16);
            buf[index + 3] = (byte)(data >> 24);
        }
        public static byte HIBYTE(uint data) => (byte)((data >> 24) & 0xff);
        public static byte BYTE2(uint data) => (byte)((data >> 16) & 0xff);
        public static byte BYTE1(uint data) => (byte)((data >> 8) & 0xff);
        public static byte LOWBYTE(uint data) => (byte)(data & 0xff);

生成SBOX

为了避免位移运算,这里对sbox进行偏移,得到4个uint[]的sbox

        public static byte[] create_sbox()
        {
            byte[] a = { 0xf1, 0xe3, 0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8 };
            byte b = 0x63, x1, x, y, z, i = 0x00;

            byte[] sbox = new byte[0x100];
            int j, k;
            do
            {
                z = 0x00;
                x = calc_inverse(i);
                for (j = 0; j < 8; j++)
                {
                    z = (byte)(z >> 1);
                    y = (byte)(a[j] & x);
                    x1 = 0x00;
                    for (k = 0; k < 8; k++)
                    {
                        x1 ^= (byte)(y & 0x80);
                        y = (byte)(y << 1);
                    }
                    z ^= x1;
                }
                z ^= b;
                sbox[i] = z;
                i++;
            } while (i != 0x00);

            return sbox;
        }
        public static void create_shift_sbox(byte[] oriSbox, out uint[] sbox0, out uint[] sbox1, out uint[] sbox2, out uint[] sbox3)
        {
            sbox0 = new uint[256];
            for (int idx = 0; idx < 0x100; idx++)
                sbox0[idx] = oriSbox[idx];
            sbox1 = new uint[256];
            for (int idx = 0; idx < 0x100; idx++)
                sbox1[idx] = (uint)(oriSbox[idx] << 8);
            sbox2 = new uint[256];
            for (int idx = 0; idx < 0x100; idx++)
                sbox2[idx] = (uint)(oriSbox[idx] << 0x10);
            sbox3 = new uint[256];
            for (int idx = 0; idx < 0x100; idx++)
                sbox3[idx] = (uint)(oriSbox[idx] << 0x18);
        }

        /// <summary>
        /// 计算逆元
        /// </summary>
        private static byte calc_inverse(byte tar)
        {
            if (tar == 0x00)
                return 0x00;

            for (byte i = 0x01; i != 0x00; i++)
                if (gf_mul(tar, i) == 0x01)
                    return i;

            throw new Exception("不对劲,没有正常return");
        }
        /// <summary>
        /// 乘法运算
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static byte gf_mul(byte a, byte b)
        {
            byte[] data = new byte[8];
            data[0] = a;
            data[1] = (byte)(data[0] << 1);
            if ((data[0] & 0x80) == 0x80) data[1] ^= 0x1b;
            data[2] = (byte)(data[1] << 1);
            if ((data[1] & 0x80) == 0x80) data[2] ^= 0x1b;
            data[3] = (byte)(data[2] << 1);
            if ((data[2] & 0x80) == 0x80) data[3] ^= 0x1b;
            data[4] = (byte)(data[3] << 1);
            if ((data[3] & 0x80) == 0x80) data[4] ^= 0x1b;
            data[5] = (byte)(data[4] << 1);
            if ((data[4] & 0x80) == 0x80) data[5] ^= 0x1b;
            data[6] = (byte)(data[5] << 1);
            if ((data[5] & 0x80) == 0x80) data[6] ^= 0x1b;
            data[7] = (byte)(data[6] << 1);
            if ((data[6] & 0x80) == 0x80) data[7] ^= 0x1b;

            byte dat = 0x00;
            if ((b & 0x01) == 0x01) dat ^= data[0];
            if ((b & 0x02) == 0x02) dat ^= data[1];
            if ((b & 0x04) == 0x04) dat ^= data[2];
            if ((b & 0x08) == 0x08) dat ^= data[3];
            if ((b & 0x10) == 0x10) dat ^= data[4];
            if ((b & 0x20) == 0x20) dat ^= data[5];
            if ((b & 0x40) == 0x40) dat ^= data[6];
            if ((b & 0x80) == 0x80) dat ^= data[7];

            return dat;
        }

生成TBOX

        /// <summary>
        /// 2 1 1 3
        /// 3 2 1 1
        /// 1 3 2 1
        /// 1 1 3 2
        /// </summary>
        /// <param name="sbox"></param>
        /// <param name="tbox0">2 1 1 3</param>
        /// <param name="tbox1">3 2 1 1</param>
        /// <param name="tbox2">1 3 2 1</param>
        /// <param name="tbox3">1 1 3 2</param>
        public static void create_tbox(byte[] sbox, out uint[] tbox0, out uint[] tbox1, out uint[] tbox2, out uint[] tbox3)
        {
            tbox0 = new uint[256];
            tbox1 = new uint[256];
            tbox2 = new uint[256];
            tbox3 = new uint[256];

            for (int i = 0; i < 0x100; i++)
            {
                //byte mul1 = gf_mul(sbox[i], 1);
                byte mul1 = sbox[i];
                byte mul2 = gf_mul(sbox[i], 2);
                byte mul3 = gf_mul(sbox[i], 3);

                tbox0[i] = (uint)((mul2 << 0x18) | (mul1 << 0x10) | (mul1 << 8) | mul3);
                tbox1[i] = (uint)((mul3 << 0x18) | (mul2 << 0x10) | (mul1 << 8) | mul1);
                tbox2[i] = (uint)((mul1 << 0x18) | (mul3 << 0x10) | (mul2 << 8) | mul1);
                tbox3[i] = (uint)((mul1 << 0x18) | (mul1 << 0x10) | (mul3 << 8) | mul2);
            }
        }

秘钥拓展

        /// <summary>
        /// Rcon
        /// </summary>
        public static uint[] ROUND_PARAMS_B = new uint[10]
        {
            0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36,
        };

        public static byte[] create_round_keys_bytes(byte[] keys)
        {
            byte[] expKey = new byte[0xb0];
            for (int i = 0; i < 0x10; i += 4) // mem copy
            {
                // 大小端转换
                expKey[i] = keys[i + 3];
                expKey[i + 1] = keys[i + 2];
                expKey[i + 2] = keys[i + 1];
                expKey[i + 3] = keys[i];
            }

            for (int i = 0x10, rounds = 0; i < 0xb0; i += 0x10, rounds++)
            {
                // 前一个4字节uint
                byte b0 = expKey[i - 4];
                byte b1 = expKey[i - 3];
                byte b2 = expKey[i - 2];
                byte b3 = expKey[i - 1];

                expKey[i] = (byte)(expKey[i - 0x10] ^ SBOX0[b3]);
                expKey[i + 1] = (byte)(expKey[i - 0x10 + 1] ^ SBOX0[b0]);
                expKey[i + 2] = (byte)(expKey[i - 0x10 + 2] ^ SBOX0[b1]);
                expKey[i + 3] = (byte)(expKey[i - 0x10 + 3] ^ SBOX0[b2] ^ ROUND_PARAMS_B[rounds]);

                // 分组后12B,循环处理
                for (int j = 4; j < 0x10; j++)
                    expKey[i + j] = (byte)(expKey[i + j - 4] ^ expKey[i + j - 0x10]);
            }

            return expKey;
        }

分组加密

        /// <summary>
        /// 4个SBOX,分别对应:
        /// SBOX0[i]: SBOX_ori[i]
        /// SBOX1[i]: SBOX_ori[i] << 8
        /// SBOX2[i]: SBOX_ori[i] << 0x10
        /// SBOX3[i]: SBOX_ori[i] << 0x18
        /// </summary>
        public static uint[] SBOX0;
        public static uint[] SBOX1;
        public static uint[] SBOX2;
        public static uint[] SBOX3;
        /// <summary>
        /// 4个TBOX,根据矩阵运算得到
        /// </summary>
        public static uint[] TBOX0;
        public static uint[] TBOX1;
        public static uint[] TBOX2;
        public static uint[] TBOX3;

        public static void init_box()
        {
            byte[] oriSbox = create_sbox();
            create_shift_sbox(oriSbox, out SBOX0, out SBOX1, out SBOX2, out SBOX3);
            create_tbox(oriSbox, out TBOX0, out TBOX1, out TBOX2, out TBOX3);
        }

        /// <summary>
        /// 分组加密运算,aes-128,没个分组计算10轮
        /// </summary>
        public static void round_encrypt(byte[] data, byte[] expKey)
        {
            byte A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15;
            byte D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15;

            uint u0 = ReadUint_LE(expKey, 0) ^ ReadUint_BE(data, 0);
            uint u1 = ReadUint_LE(expKey, 4) ^ ReadUint_BE(data, 4);
            uint u2 = ReadUint_LE(expKey, 8) ^ ReadUint_BE(data, 8);
            uint u3 = ReadUint_LE(expKey, 0xc) ^ ReadUint_BE(data, 0xc);

            uint wa0, wa1, wa2, wa3;
            int halfRound = (0xa >> 1) - 1; // 一重循环做两次
            for (int i = 0x10; ; i += 0x20)
            {
                // 因为大小端的原因,这里的高位字节,其实是字节数组中的低位,高尾端模式:HI在低位。
                A0 = HIBYTE(u0);
                A1 = BYTE2(u0);
                A2 = BYTE1(u0);
                A3 = LOWBYTE(u0);

                A4 = HIBYTE(u1);
                A5 = BYTE2(u1);
                A6 = BYTE1(u1);
                A7 = LOWBYTE(u1);

                A8 = HIBYTE(u2);
                A9 = BYTE2(u2);
                A10 = BYTE1(u2);
                A11 = LOWBYTE(u2);

                A12 = HIBYTE(u3);
                A13 = BYTE2(u3);
                A14 = BYTE1(u3);
                A15 = LOWBYTE(u3);

                wa0 = TBOX0[A0] ^ TBOX1[A5] ^ TBOX2[A10] ^ TBOX3[A15] ^ ReadUint_LE(expKey, i);
                wa1 = TBOX0[A4] ^ TBOX1[A9] ^ TBOX2[A14] ^ TBOX3[A3] ^ ReadUint_LE(expKey, i + 4);
                wa2 = TBOX0[A8] ^ TBOX1[A13] ^ TBOX2[A2] ^ TBOX3[A7] ^ ReadUint_LE(expKey, i + 8);
                wa3 = TBOX0[A12] ^ TBOX1[A1] ^ TBOX2[A6] ^ TBOX3[A11] ^ ReadUint_LE(expKey, i + 0xc);

                // 还是要注意大小端。小端模式下:尾部LO在低位0
                D0 = HIBYTE(wa0);
                D1 = BYTE2(wa0);
                D2 = BYTE1(wa0);
                D3 = LOWBYTE(wa0);

                D4 = HIBYTE(wa1);
                D5 = BYTE2(wa1);
                D6 = BYTE1(wa1);
                D7 = LOWBYTE(wa1);

                D8 = HIBYTE(wa2);
                D9 = BYTE2(wa2);
                D10 = BYTE1(wa2);
                D11 = LOWBYTE(wa2);

                D12 = HIBYTE(wa3);
                D13 = BYTE2(wa3);
                D14 = BYTE1(wa3);
                D15 = LOWBYTE(wa3);

                if (halfRound == 0) break; // 为了循环能写简单一点、为了能精简汇编代码,原来的9轮循环,改成了现在2*4 + 1轮循环。实际上感觉这是汇编代码优化后的结果。不是刻意写成这样的。

                u0 = TBOX0[D0] ^ TBOX1[D5] ^ TBOX2[D10] ^ TBOX3[D15] ^ ReadUint_LE(expKey, i + 0x10);
                u1 = TBOX0[D4] ^ TBOX1[D9] ^ TBOX2[D14] ^ TBOX3[D3] ^ ReadUint_LE(expKey, i + 0x14);
                u2 = TBOX0[D8] ^ TBOX1[D13] ^ TBOX2[D2] ^ TBOX3[D7] ^ ReadUint_LE(expKey, i + 0x18);
                u3 = TBOX0[D12] ^ TBOX1[D1] ^ TBOX2[D6] ^ TBOX3[D11] ^ ReadUint_LE(expKey, i + 0x1c);

                halfRound = halfRound - 1;
            }

            uint v39 = SBOX0[D15] ^ SBOX1[D10] ^ SBOX2[D5] ^ SBOX3[D0] ^ ReadUint_LE(expKey, 0xa0);
            uint v40 = SBOX0[D3] ^ SBOX1[D14] ^ SBOX2[D9] ^ SBOX3[D4] ^ ReadUint_LE(expKey, 0xa4);
            uint v42 = SBOX0[D7] ^ SBOX1[D2] ^ SBOX2[D13] ^ SBOX3[D8] ^ ReadUint_LE(expKey, 0xa8);
            uint v35 = SBOX0[D11] ^ SBOX1[D6] ^ SBOX2[D1] ^ SBOX3[D12] ^ ReadUint_LE(expKey, 0xac);

            data[0] = HIBYTE(v39);
            data[1] = BYTE2(v39);
            data[2] = BYTE1(v39);
            data[3] = LOWBYTE(v39);

            data[4] = HIBYTE(v40);
            data[5] = BYTE2(v40);
            data[6] = BYTE1(v40);
            data[7] = LOWBYTE(v40);

            data[8] = HIBYTE(v42);
            data[9] = BYTE2(v42);
            data[10] = BYTE1(v42);
            data[11] = LOWBYTE(v42);

            data[12] = HIBYTE(v35);
            data[13] = BYTE2(v35);
            data[14] = BYTE1(v35);
            data[15] = LOWBYTE(v35);
        }

加密部分

这里采用in-place计算,加密结果直接写回到data。

        /// <summary>
        /// ECB模式
        /// </summary>
        /// <param name="data">数据部分保证16字节对齐</param>
        /// <param name="key">秘钥长度为16字节</param>
        public static void aes_128_ecb_encrypt(byte[] data, byte[] key)
        {
            byte[] expKey = create_round_keys_bytes(key);

            byte[] buf = new byte[0x10];
            for (int i = 0; i < data.Length; i += 0x10)
            {
                Array.Copy(data, i, buf, 0, 0x10);
                round_encrypt(buf, expKey);
                Array.Copy(buf, 0, data, i, 0x10);
            }
        }
        /// <summary>
        /// CBC模式
        /// </summary>
        /// <param name="data">数据部分保证16字节对齐</param>
        /// <param name="key">秘钥长度为16字节</param>
        public static void aes_128_cbc_encrypt(byte[] data, byte[] key, byte[] iv)
        {
            byte[] expKey = create_round_keys_bytes(key);

            byte[] buf = new byte[0x10];
            for (int i = 0; i < data.Length; i += 0x10)
            {
                Array.Copy(data, i, buf, 0, 0x10);

                for (int j = 0; j < 16; j++)
                    buf[j] = (byte)(iv[j] ^ buf[j]);

                round_encrypt(buf, expKey);

                Array.Copy(buf, 0, data, i, 0x10);
                Array.Copy(buf, 0, iv, i, 0x10);
            }
        }

测试程序

    class Program
    {
        static void Main(string[] args)
        {
            MyAes.init_box();
            byte[] keys = ByteUtils.FromHexString("2E82BFAFF4E0C26D1D032A8F90803EAE");
            byte[] iv = ByteUtils.FromHexString("7315254F862899A133B3A832C2F78E0E");

            // ecb测试
            byte[] testData = new byte[0x10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
            Console.WriteLine(ByteUtils.ToHexString(testData));
            Console.WriteLine(ByteUtils.ToHexString(keys));
            MyAes.aes_128_ecb_encrypt(testData, keys);
            Console.WriteLine(ByteUtils.ToHexString(testData));

            Console.WriteLine();

            // cbc测试
            testData = new byte[0x10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
            Console.WriteLine(ByteUtils.ToHexString(testData));
            Console.WriteLine(ByteUtils.ToHexString(keys));
            Console.WriteLine(ByteUtils.ToHexString(iv));
            MyAes.aes_128_cbc_encrypt(testData, keys, iv);
            Console.WriteLine(ByteUtils.ToHexString(testData));

            Console.ReadKey();
        }
    }

测试结果

 实际计算结果,和采用公共加密库计算得到的结果一致。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值