经典密码学算法实现

python 复制代码
# AES-128 加密算法的规范实现(不使用外部库)
# ECB模式

S_BOX = [
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
    0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
    0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
    0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
    0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
    0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
    0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
    0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
    0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
    0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
    0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
    0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
    0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
    0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
    0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
    0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
    0xB0, 0x54, 0xBB, 0x16
]

RCON = [
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
]

def sub_bytes(state):
    # 字节替代
    for i in range(4):
        for j in range(4):
            state[i][j] = S_BOX[state[i][j]]

def shift_rows(state):
    # 行移位
    state[1] = state[1][1:] + state[1][:1]
    state[2] = state[2][2:] + state[2][:2]
    state[3] = state[3][3:] + state[3][:3]

def xtime(a):
    # GF(2^8)乘法
    return ((a << 1) ^ 0x1B) & 0xFF if a & 0x80 else a << 1

def mix_single_column(a):
    # 单列混合
    t = a[0] ^ a[1] ^ a[2] ^ a[3]
    u = a[0]
    a[0] ^= t ^ xtime(a[0] ^ a[1])
    a[1] ^= t ^ xtime(a[1] ^ a[2])
    a[2] ^= t ^ xtime(a[2] ^ a[3])
    a[3] ^= t ^ xtime(a[3] ^ u)

def mix_columns(state):
    # 列混合
    for i in range(4):
        col = [state[row][i] for row in range(4)]
        mix_single_column(col)
        for row in range(4):
            state[row][i] = col[row]

def add_round_key(state, round_key):
    # 密钥加
    for i in range(4):
        for j in range(4):
            state[i][j] ^= round_key[i][j]

def key_expansion(key):
    # 密钥扩展
    expanded = [key[i:i+4] for i in range(0, 16, 4)]
    for i in range(4, 44):
        word = expanded[i-1][:]
        if i % 4 == 0:
            word = word[1:] + word[:1]
            word = [S_BOX[b] for b in word]
            word[0] ^= RCON[i//4]
        expanded.append([expanded[i-4][j] ^ word[j] for j in range(4)])
    return [expanded[4*i:4*(i+1)] for i in range(11)]

def encrypt_block(plain, key_schedule):
    # 单个区块(16字节)AES加密
    state = [plain[i:i+4] for i in range(0, 16, 4)]

    add_round_key(state, key_schedule[0])

    for rnd in range(1, 10):
        sub_bytes(state)
        shift_rows(state)
        mix_columns(state)
        add_round_key(state, key_schedule[rnd])

    sub_bytes(state)
    shift_rows(state)
    add_round_key(state, key_schedule[10])

    return [byte for row in state for byte in row]

def AES_encrypt(plaintext, key):
    # AES-128 ECB加密,plaintext和key为16字节
    assert len(plaintext) == 16 and len(key) == 16, "Plaintext and key must be 16 bytes."

    key_schedule = key_expansion(key)
    ciphertext = encrypt_block(plaintext, key_schedule)
    return ciphertext

'''
AES 解密算法
'''
INV_S_BOX = [
    0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB,
    0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB,
    0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E,
    0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25,
    0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92,
    0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84,
    0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06,
    0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B,
    0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73,
    0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E,
    0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B,
    0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4,
    0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F,
    0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF,
    0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61,
    0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D
]


def inv_sub_bytes(state):
    for i in range(4):
        for j in range(4):
            state[i][j] = INV_S_BOX[state[i][j]]

def inv_shift_rows(state):
    state[1] = state[1][-1:] + state[1][:-1]
    state[2] = state[2][-2:] + state[2][:-2]
    state[3] = state[3][-3:] + state[3][:-3]

def inv_mix_single_column(a):
    u = xtime(xtime(a[0] ^ a[2]))
    v = xtime(xtime(a[1] ^ a[3]))
    a[0] ^= u
    a[1] ^= v
    a[2] ^= u
    a[3] ^= v

    mix_single_column(a)

def inv_mix_columns(state):
    for i in range(4):
        col = [state[row][i] for row in range(4)]
        inv_mix_single_column(col)
        for row in range(4):
            state[row][i] = col[row]

def decrypt_block(cipher, key_schedule):
    state = [cipher[i:i+4] for i in range(0, 16, 4)]

    add_round_key(state, key_schedule[10])
    for rnd in range(9, 0, -1):
        inv_shift_rows(state)
        inv_sub_bytes(state)
        add_round_key(state, key_schedule[rnd])
        inv_mix_columns(state)

    inv_shift_rows(state)
    inv_sub_bytes(state)
    add_round_key(state, key_schedule[0])

    return [byte for row in state for byte in row]

def AES_decrypt(ciphertext, key):
    key_schedule = key_expansion(key)
    plaintext = decrypt_block(ciphertext, key_schedule)
    return plaintext


# 在你的完整AES加解密函数定义之后,添加如下代码进行测试:

def main():
    # 原始明文(16字节)
    plaintext = [0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d,
                 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34]

    # 密钥(16字节)
    key = [0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
           0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]

    # 加密
    ciphertext = AES_encrypt(plaintext, key)
    ciphertext_hex = ''.join('{:02x}'.format(b) for b in ciphertext)
    print('AES加密后的密文:', ciphertext_hex)

    # 解密
    decrypted_text = AES_decrypt(ciphertext, key)
    decrypted_hex = ''.join('{:02x}'.format(b) for b in decrypted_text)
    print('AES解密后的明文:', decrypted_hex)

    # 验证解密结果与原文是否一致
    if decrypted_text == plaintext:
        print(' 解密成功,明文与原始数据一致。')
    else:
        print(' 解密失败,明文与原始数据不一致。')

if __name__ == '__main__':
    main()
python 复制代码
# 标准DES实现(无任何外部库依赖)
# 完整实现DES算法,包括IP、IP逆置换、Feistel轮函数和密钥扩展
# 完全符合FIPS 46-3标准,代码规范、清晰易读

IP = [
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6,
    64, 56, 48, 40, 32, 24, 16, 8,
    57, 49, 41, 33, 25, 17, 9, 1,
    59, 51, 43, 35, 27, 19, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7
]


IP_INV = [
    40, 8, 48, 16, 56, 24, 64, 32,
    39, 7, 47, 15, 55, 23, 63, 31,
    38, 6, 46, 14, 54, 22, 62, 30,
    37, 5, 45, 13, 53, 21, 61, 29,
    36, 4, 44, 12, 52, 20, 60, 28,
    35, 3, 43, 11, 51, 19, 59, 27,
    34, 2, 42, 10, 50, 18, 58, 26,
    33, 1, 41, 9, 49, 17, 57, 25
]


S_BOX = [
    # S1
    [
        [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
        [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
        [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
        [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
    ],
    # S2
    [
        [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
        [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
        [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
        [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
    ],
    # S3
    [
        [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
        [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
        [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
        [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
    ],
    # S4
    [
        [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
        [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
        [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
        [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
    ],
    # S5
    [
        [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
        [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
        [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
        [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
    ],
    # S6
    [
        [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
        [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
        [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
        [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
    ],
    # S7
    [
        [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
        [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
        [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
        [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
    ],
    # S8
    [
        [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
        [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
        [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
        [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
    ]
]

def permute(block, table):
    return [block[i-1] for i in table]

def shift_left(key_half, shifts):
    return key_half[shifts:] + key_half[:shifts]

def xor(bits1, bits2):
    return [b1 ^ b2 for b1, b2 in zip(bits1, bits2)]

def sbox_substitution(block48):
    output = []
    for i in range(8):
        chunk = block48[i*6:(i+1)*6]
        row = (chunk[0] << 1) | chunk[5]
        col = (chunk[1] << 3) | (chunk[2] << 2) | (chunk[3] << 1) | chunk[4]
        val = S_BOX[i][row][col]
        bin_val = [int(b) for b in f'{val:04b}']
        output += bin_val
    return output

def generate_subkeys(key64):
    # PC-1, PC-2为标准密钥置换表,C和D为分半后的密钥
    PC1 = [
        57, 49, 41, 33, 25, 17, 9,
        1, 58, 50, 42, 34, 26, 18,
        10, 2, 59, 51, 43, 35, 27,
        19, 11, 3, 60, 52, 44, 36,
        63, 55, 47, 39, 31, 23, 15,
        7, 62, 54, 46, 38, 30, 22,
        14, 6, 61, 53, 45, 37, 29,
        21, 13, 5, 28, 20, 12, 4
    ]

    PC2 = [
        14, 17, 11, 24, 1, 5,
        3, 28, 15, 6, 21, 10,
        23, 19, 12, 4, 26, 8,
        16, 7, 27, 20, 13, 2,
        41, 52, 31, 37, 47, 55,
        30, 40, 51, 45, 33, 48,
        44, 49, 39, 56, 34, 53,
        46, 42, 50, 36, 29, 32
    ]

    SHIFT_SCHEDULE = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]

    key56 = permute(key64, PC1)
    C, D = key56[:28], key56[28:]
    subkeys = []
    for shift in SHIFT_SCHEDULE:
        C, D = shift_left(C, shift), shift_left(D, shift)
        subkey = permute(C + D, PC2)
        subkeys.append(subkey)
    return subkeys

def feistel(R, subkey):
    E = [
        32, 1, 2, 3, 4, 5,
        4, 5, 6, 7, 8, 9,
        8, 9, 10, 11, 12, 13,
        12, 13, 14, 15, 16, 17,
        16, 17, 18, 19, 20, 21,
        20, 21, 22, 23, 24, 25,
        24, 25, 26, 27, 28, 29,
        28, 29, 30, 31, 32, 1
    ]

    P = [
        16, 7, 20, 21,
        29, 12, 28, 17,
        1, 15, 23, 26,
        5, 18, 31, 10,
        2, 8, 24, 14,
        32, 27, 3, 9,
        19, 13, 30, 6,
        22, 11, 4, 25
    ]
    expanded_R = permute(R, E)
    xor_result = xor(expanded_R, subkey)
    substituted = sbox_substitution(xor_result)
    return permute(substituted, P)

def DES_encrypt(plaintext64, key64):
    permuted_text = permute(plaintext64, IP)
    L, R = permuted_text[:32], permuted_text[32:]
    subkeys = generate_subkeys(key64)

    for i in range(16):
        temp_R = R
        R = xor(L, feistel(R, subkeys[i]))
        L = temp_R

    preoutput = permute(R + L, IP_INV)  # 注意最后交换一次
    return preoutput

def DES_decrypt(ciphertext64, key64):
    """与加密过程完全对称,子密钥倒序"""
    permuted = permute(ciphertext64, IP)
    L, R = permuted[:32], permuted[32:]
    # 子密钥倒序
    for subkey in reversed(generate_subkeys(key64)):
        temp_R = R
        R = xor(L, feistel(R, subkey))  # ⚠ 这里必须用旧的 R
        L = temp_R
    # 轮结束后再交换一次,顺序与加密保持一致
    return permute(R + L, IP_INV)


# 辅助函数,将字符串转化为二进制位数组
def text_to_bits(text):
    return [int(bit) for char in text for bit in f'{ord(char):08b}']

def bits_to_text(bits):
    chars = [chr(int(''.join(map(str, bits[i:i+8])), 2)) for i in range(0, len(bits), 8)]
    return ''.join(chars)

# 3DES -- EDE( Encrypt ▸ Decrypt ▸ Encrypt ) 实现
def triple_des_encrypt(block64, key1, key2, key3=None):
    """
    3DES EDE 加密单个 64‑bit 块
    - key1, key2, key3:  各自为 64‑bit 位列表
    - 若未提供 key3,则自动回退为 2‑Key 3DES (K3 = K1)
    """
    if key3 is None:
        key3 = key1                  # 2‑Key 3DES: K1, K2, K1

    step1 = DES_encrypt(block64, key1)   # 第 1 步:E_K1
    step2 = DES_decrypt(step1, key2)     # 第 2 步:D_K2
    step3 = DES_encrypt(step2, key3)     # 第 3 步:E_K3
    return step3

def triple_des_decrypt(block64, key1, key2, key3=None):
    """
    3DES EDE 解密单个 64‑bit 块
    顺序为 D_K3 ▸ E_K2 ▸ D_K1
    """
    if key3 is None:
        key3 = key1                  # 2‑Key 3DES 解密同理

    step1 = DES_decrypt(block64, key3)   # D_K3
    step2 = DES_encrypt(step1, key2)     # E_K2
    step3 = DES_decrypt(step2, key1)     # D_K1
    return step3

if __name__ == "__main__":
    # ------------ 测试数据 ------------
    plaintext = "ABCDEFGH"          # 8 字节(64bit)明文
    k1_ascii  = "12345678"
    k2_ascii  = "23456789"
    k3_ascii  = "34567890"          # 换成 None 可测试 2‑Key 3DES

    # ---- ASCII 转位列表 ----
    def str_to_bits(s):
        return [int(b) for ch in s for b in f"{ord(ch):08b}"]

    def bits_to_hex(bits):
        return hex(int("".join(map(str, bits)), 2))[2:].upper().zfill(16)

    def bits_to_str(bits):
        return "".join(
            chr(int("".join(map(str, bits[i:i+8])), 2))
            for i in range(0, len(bits), 8)
        )

    pt_bits = str_to_bits(plaintext)
    k1_bits, k2_bits, k3_bits = map(str_to_bits, (k1_ascii, k2_ascii, k3_ascii))

    # ------------ 3DES 加密 ------------
    ct_bits = triple_des_encrypt(pt_bits, k1_bits, k2_bits, k3_bits)
    # ------------ 3DES 解密 ------------
    pt_back_bits = triple_des_decrypt(ct_bits, k1_bits, k2_bits, k3_bits)

    # ------------ 输出结果 ------------
    print("密文 (Hex) :", bits_to_hex(ct_bits))
    print("解密后明文 :", bits_to_str(pt_back_bits))
python 复制代码
from typing import ByteString

# Pure Python implementation of the ChaCha20 stream cipher (RFC 8439)
# No third-party libraries used.

def _rotl32(x: int, n: int) -> int:
    """
    Rotate 32-bit integer x left by n bits.
    """
    return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n))


def _quarter_round(state: list[int], a: int, b: int, c: int, d: int) -> None:
    """
    Perform the ChaCha20 quarter-round operation on the state.
    Mutates the state in place.
    """
    state[a] = (state[a] + state[b]) & 0xFFFFFFFF
    state[d] ^= state[a]
    state[d] = _rotl32(state[d], 16)

    state[c] = (state[c] + state[d]) & 0xFFFFFFFF
    state[b] ^= state[c]
    state[b] = _rotl32(state[b], 12)

    state[a] = (state[a] + state[b]) & 0xFFFFFFFF
    state[d] ^= state[a]
    state[d] = _rotl32(state[d], 8)

    state[c] = (state[c] + state[d]) & 0xFFFFFFFF
    state[b] ^= state[c]
    state[b] = _rotl32(state[b], 7)


def _chacha20_block(key: ByteString, counter: int, nonce: ByteString) -> bytes:
    """
    Generate a single 64-byte ChaCha20 block.

    :param key: 32-byte key
    :param counter: 32-bit block counter
    :param nonce: 12-byte nonce
    :return: 64-byte keystream block
    """
    if len(key) != 32:
        raise ValueError("Key must be 32 bytes")
    if len(nonce) != 12:
        raise ValueError("Nonce must be 12 bytes")

    # Constants: "expand 32-byte k" in little-endian words
    constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]

    # Convert key into 8 little-endian 32-bit words
    key_words = [int.from_bytes(key[i:i+4], 'little') for i in range(0, 32, 4)]

    # Convert nonce into 3 little-endian 32-bit words
    nonce_words = [int.from_bytes(nonce[i:i+4], 'little') for i in range(0, 12, 4)]

    # Initial state: 16 words
    state = (
        constants +
        key_words +
        [counter] +
        nonce_words
    )
    working_state = list(state)

    # 20 rounds: 10 iterations of two rounds each
    for _ in range(10):
        # Column rounds
        _quarter_round(working_state, 0, 4, 8, 12)
        _quarter_round(working_state, 1, 5, 9, 13)
        _quarter_round(working_state, 2, 6, 10, 14)
        _quarter_round(working_state, 3, 7, 11, 15)
        # Diagonal rounds
        _quarter_round(working_state, 0, 5, 10, 15)
        _quarter_round(working_state, 1, 6, 11, 12)
        _quarter_round(working_state, 2, 7, 8, 13)
        _quarter_round(working_state, 3, 4, 9, 14)

    # Add original state to working state and serialize
    block = bytearray()
    for i in range(16):
        word = (working_state[i] + state[i]) & 0xFFFFFFFF
        block += word.to_bytes(4, 'little')

    return bytes(block)


def chacha20_encrypt(key: ByteString, nonce: ByteString, plaintext: ByteString, counter: int = 1) -> bytes:
    """
    Encrypt or decrypt data using ChaCha20.
    Same function for encryption and decryption (XOR with keystream).

    :param key: 32-byte key
    :param nonce: 12-byte nonce
    :param plaintext: data to encrypt/decrypt
    :param counter: initial block counter (default 1)
    :return: ciphertext/plaintext as bytes
    """
    output = bytearray()
    block_count = (len(plaintext) + 63) // 64

    for block_index in range(block_count):
        block = _chacha20_block(key, counter + block_index, nonce)
        segment = plaintext[block_index*64:(block_index+1)*64]
        for i in range(len(segment)):
            output.append(segment[i] ^ block[i])

    return bytes(output)

def main():
    # 256-bit(32 字节)密钥
    key = bytes.fromhex(
        '000102030405060708090a0b0c0d0e0f'
        '101112131415161718191a1b1c1d1e1f'
    )
    # 96-bit(12 字节)随机数(Nonce)
    nonce = bytes.fromhex('000000090000004a00000000')

    # 待加密的明文
    plaintext = b"Hello, ChaCha20!"

    print("Plaintext:", plaintext)

    # 加密
    ciphertext = chacha20_encrypt(key, nonce, plaintext)
    print("Ciphertext (hex):", ciphertext.hex())

    # 解密(使用同样的 key 和 nonce)
    decrypted = chacha20_encrypt(key, nonce, ciphertext)
    print("Decrypted:", decrypted)

    # 验证解密结果
    assert decrypted == plaintext, "解密结果与明文不一致!"
    print(" 解密成功,明文一致。")

if __name__ == "__main__":
    main()
python 复制代码
# -*- coding: utf-8 -*-
"""
SM4 (GM4) 算法纯 Python 实现
标准来源:GM/T 0002-2012《分组密码算法 SM4 算法》&#8203;:contentReference[oaicite:2]{index=2}&#8203;:contentReference[oaicite:3]{index=3}
"""

# 1. 基础参数定义

# 1.1 S-盒
SBOX = [
    0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
    0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
    0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
    0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
    0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
    0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
    0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
    0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
    0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
    0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
    0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
    0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
    0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
    0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
    0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
    0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
]

# 1.2 系统参数 FK(32 位常量)
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# 1.3 固定参数 CK(32 × 轮常量)
CK = [
    0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
    0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
    0xe0e7eeF5,0xfc030a11,0x181f262d,0x343b4249,
    0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
    0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
    0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
    0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
    0x10171e25,0x2c333a41,0x484f565d,0x646b7279
]

# 2. 辅助函数
def _rol(x, n):
    """32 位循环左移"""
    return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n))

def _tau(a):
    """非线性变换:对 32 位分 4×8 位,通过 SBOX 替代"""
    b = 0
    for i in range(4):
        byte = (a >> (24 - 8*i)) & 0xFF
        sb = SBOX[byte]
        b |= sb << (24 - 8*i)
    return b

def _L(b):
    """线性变换 L"""
    return b ^ _rol(b, 2) ^ _rol(b,10) ^ _rol(b,18) ^ _rol(b,24)

def _L_key(b):
    """轮密钥线性变换 L'"""
    return b ^ _rol(b,13) ^ _rol(b,23)

def _T(x):
    """轮函数 T = L∘τ"""
    return _L(_tau(x))

def _T_key(x):
    """密钥扩展变换 T' = L'∘τ"""
    return _L_key(_tau(x))

# 3. 轮密钥扩展
def _expand_key(MK):
    """MK: 用户输入 128 位主密钥,4×32 位整数列表"""
    K = [MK[i] ^ FK[i] for i in range(4)]
    rk = []
    for i in range(32):
        tmp = K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i]
        K.append(K[i] ^ _T_key(tmp))
        rk.append(K[i+4])
    return rk

# 4. 单块加密/解密

def sm4_encrypt_block(plaintext, rk):
    """
    plaintext: 128-bit 明文,4×32 位整数列表
    rk: 32 轮子密钥列表
    返回 4×32 位整数列表为密文
    """
    X = plaintext.copy()
    for i in range(32):
        tmp = X[i] ^ _T(X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[i])
        X.append(tmp)
    return [X[35], X[34], X[33], X[32]]

def sm4_decrypt_block(ciphertext, rk):
    """解密时轮密钥倒序"""
    X = ciphertext.copy()
    for i in range(32):
        tmp = X[i] ^ _T(X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[31-i])
        X.append(tmp)
    return [X[35], X[34], X[33], X[32]]

# 5. 示例测试

if __name__ == "__main__":
    # 测试向量(NIST/GM/T 0002-2012 推荐)
    MK_hex = "0123456789abcdeffedcba9876543210"
    PT_hex = "0123456789abcdeffedcba9876543210"
    # 期望密文:681EDF34D206965E86B3E94F536E4246

    # 转 4×32 位整数
    def hex2u32s(h):
        h = h.replace(" ", "")
        v = int(h, 16)
        return [(v >> (96 - 32*i)) & 0xFFFFFFFF for i in range(4)]
    def u32s2hex(u32s):
        v = 0
        for w in u32s:
            v = (v << 32) | (w & 0xFFFFFFFF)
        return f"{v:032X}"

    MK = hex2u32s(MK_hex)
    PT = hex2u32s(PT_hex)
    rk = _expand_key(MK)

    CT = sm4_encrypt_block(PT, rk)
    PT_back = sm4_decrypt_block(CT, rk)

    print("密文 (hex):", u32s2hex(CT))
    print("解密后:", u32s2hex(PT_back))
相关推荐
apcipot_rain19 分钟前
【应用密码学】实验四 公钥密码1——数学基础
密码学
补三补四23 分钟前
遗传算法(GA)
人工智能·算法·机器学习·启发式算法
dot to one1 小时前
C++ 渗透 数据结构中的二叉搜索树
数据结构·c++·算法·visual studio
好易学·数据结构2 小时前
可视化图解算法36: 序列化二叉树-I(二叉树序列化与反序列化)
数据结构·算法·leetcode·二叉树·力扣·序列化·牛客
孙同学_3 小时前
【递归,搜索与回溯算法篇】专题(一) - 递归
算法·leetcode
Tummer83634 小时前
C语言与C++的区别
c语言·c++·算法
MSTcheng.4 小时前
【数据结构】算法的复杂度
数据结构·算法
了不起的杰4 小时前
【算法】:滑动窗口
算法
2301_807611494 小时前
47. 全排列 II
c++·算法·leetcode·回溯