原始 SPN 实现

原理与算法

这里我们实现了教材上的原始 SPN 算法,相关的数据(秘钥,秘钥编排算法,S盒,P盒,轮数)均保持一致,并在程序内部定义。

程序设计

  • 常量规定
变量 意义 类型
x 明文 整形
piS S盒 整形字典
piP P盒 整形字典
primitiveKey 主秘钥 整形
nr 轮数 整形
m S盒个数 整形
l S盒大小 整形

In [1]:
m, l = 4, 4 # m S-Boxes, l bits in each

piS = {0: 14, 1: 4, 2: 13, 3: 1,
       4: 2, 5: 15, 6: 11, 7: 8,
       8: 3, 9: 10, 10: 6, 11: 12,
       12: 5, 13: 9, 14: 0, 15: 7}
piP = {1: 1, 2: 5, 3: 9, 4: 13,
       5: 2, 6: 6, 7: 10, 8: 14,
       9: 3, 10: 7, 11: 11, 12: 15,
       13: 4, 14: 8, 15: 12, 16: 16}
nr = 4
primitiveKeyStr = '0011 1010 1001 0100 1101 0110 0011 1111'\
.replace(' ','')
primitiveKey = int(primitiveKeyStr, 2)
K = {}
K[1] = (primitiveKey >> 16) & 0xFFFF
K[2] = (primitiveKey >> 12) & 0xFFFF
K[3] = (primitiveKey >> 8) & 0xFFFF
K[4] = (primitiveKey >> 4) & 0xFFFF
K[5] = primitiveKey & 0xFFFF

piSInv = dict((v,k) for k,v in piS.items())
piPInv = dict((v,k) for k,v in piP.items())
  • S-Box 函数定义

In [2]:
# subsitution for each bits according to sBoxDict
def subsitutionFunc(ur, sBoxDict):
    currentUrBinStrMTimesLBits = \
    bin(ur).replace('0b', '').zfill(m * l)
    for i in range(1, m + 1):
        # S box subsitution by each
        # get the bits ready for subsitution
        # i to m from left to right
        currentSboxInput = \
        int(currentUrBinStrMTimesLBits\
            [l * (i - 1): l * i], 2) # dec int now
        currentSboxOutput = \
        sBoxDict[currentSboxInput] # dec int now
        # give it to vr in format available
        # vr should be int 
        if i == 1:
            vrStrInProgress = \
            bin(currentSboxOutput).replace('0b', '').zfill(l) 
            # 4-bit bin str notation
        else:
            vrStrInProgress += \
            bin(currentSboxOutput).replace('0b', '').zfill(l) 
            # 8,12,finally 16-bit bin str
    vr = int(vrStrInProgress, 2) 
    return vr # return a int
  • P-Box 函数定义

In [3]:
def permutationFunc(vr, pBoxDict):        
    currentVrBinStrMTimesLBits = \
    bin(vr).replace('0b','').zfill(m * l)
    wr = [None] * (m * l)
    for i in range(1, m * l + 1): # 1 - 16
        wr[pBoxDict[i] - 1] = \
        currentVrBinStrMTimesLBits[i-1:i]
    wr = int(''.join(wr), 2) # wr is a int now   
    return wr # return a int
  • 加密函数

In [4]:
def spnFromTextEncryption(x, piS=piS, piP=piP, K=K):
    nr = len(K) - 1
    w, u, v = {}, {}, {}    
    # keys and values in u, v, w are int
    w[0] = x
    for r in range(1, nr): # 1 to nr -1
        ## xor round key    
        u[r] = w[r - 1] ^ K[r]

        ## subsitution
        v[r] = subsitutionFunc(u[r], piS)

        ## permutation
        w[r] = permutationFunc(v[r], piP)
        
    u[nr] = w[nr - 1] ^ K[nr]
    v[nr] = subsitutionFunc(u[nr], piS)
    y = v[nr] ^ K[nr + 1]
    return y
  • 解密函数

In [5]:
def spnFromTextDecryption(y,
                          piSInv=piSInv,
                          piPInv=piPInv,
                          K=K):
    nr = len(K) - 1
    w, u, v = {}, {}, {}    
    # keys and values in u, v, w are int

    v[nr] = y ^ K[nr + 1]
    u[nr] = subsitutionFunc(v[nr], piSInv)
    w[nr - 1] = u[nr] ^ K[nr]

    for r in range(nr - 1, 0, -1):

        ## permutation Inverse
        v[r] = permutationFunc(w[r], piPInv)
        
        ## subsitution Inverse
        u[r] = subsitutionFunc(v[r], piSInv)

        ## xor round key    
        w[r - 1] = u[r] ^ K[r]    

        
    x = w[0]
    return x

测试

测试时使用教材上的测试用例:

意义 变量
明文 PLAINTEXT 0010 0110 1011 0111
密文 CIPHERTEXT 1011 1100 1101 0110

In [6]:
PLAINTEXT = int('0010 0110 1011 0111'.replace(' ' ,''), 2)
CIPHERTEXT = int('1011 1100 1101 0110'.replace(' ', ''), 2)
if spnFromTextEncryption(PLAINTEXT, piS, piP, K) \
== CIPHERTEXT:
    print('SPN From Text ENCRYPTION Correctly Implemented!')
else:
    print('WARNING!','SPN From Text Implementation INCORRECT')

if spnFromTextDecryption(CIPHERTEXT, piSInv, piPInv, K) \
== PLAINTEXT:
    print('SPN From Text DECRYPTION Correctly Implemented!')
else:
    print('WARNING!','SPN From Text Implementation INCORRECT')


SPN From Text ENCRYPTION Correctly Implemented!
SPN From Text DECRYPTION Correctly Implemented!

SPN 密码分析

密码分析预函数

  • 16位数字分4块函数

In [7]:
def splitBits(toSplit, index):
    if index == 4:
        shift = 0
    elif index == 3:
        shift = 4
    elif index == 2:
        shift = 8
    elif index == 1:
        shift = 12
    else :
        raise ValueError("index supposed to be 1, 2, 3 or 4")
    return ((toSplit >> shift) & 0b1111)
  • 明密文对生成函数

In [8]:
import random
def pcPairGenRand():
    plain = random.randint(2**0-1, 2**16-1)
    cipher = spnFromTextEncryption(x=plain)
    return (plain, cipher)

线性密码分析

原理与数据

  • IO

    • Input

      • 明密文对 $ \tau $

      • 明密文对数量 T

      • S-Box 逆代换

    • Output

      • 对应最大计数器的秘钥
  • 数据结构

    • 明密文对使用 list ,每对形成一个元组 (P, C) , 明密文使用定长 int

    • S-Box 逆代换使用字典数据结构

  • 算法

    教材算法 3.2

程序设计

  • 线性攻击函数异或集中模块

In [9]:
def zxor(x, u4_2, u4_4):    
    x5 = (x >> (16 - 5)) & 0b1
    x7 = (x >> (16 - 7)) & 0b1
    x8 = (x >> (16 - 8)) & 0b1
    
    u4__6 = (u4_2 >> 2) & 0b1
    u4__8 = (u4_2 >> 0) & 0b1
    u4__14 = (u4_4 >> 2) & 0b1
    u4__16 = (u4_4 >> 0) & 0b1
    z = x5 ^ x7 ^ x8 ^ u4__6 ^ u4__8 ^ u4__14 ^ u4__16
    return z
  • 线性攻击函数

In [10]:
import copy
def linearAttack(pairs, piSInv=piSInv):
    pairsCount = len(pairs)
    Count = [[0 for l2 in range(16)] for l1 in range(16)]
    for (x, y) in pairs:
        for (l1, l2) in \
        [(l1, l2) for l1 in range(16) for l2 in range(16)]:
                v4_2 = l1 ^ splitBits(y, 2)
                v4_4 = l2 ^ splitBits(y, 4)
                u4_2 = piSInv[v4_2]
                u4_4 = piSInv[v4_4]
                z = zxor(x, u4_2, u4_4)
                if z == 0: Count[l1][l2] += 1 
    max = -1
    for (l1, l2) in \
    [(l1, l2) for l1 in range(16) for l2 in range(16)]:
        Count[l1][l2] = \
        abs(Count[l1][l2] - int( pairsCount / 2 ))
        if Count[l1][l2] > max:
            max = Count[l1][l2]
            maxkey = copy.copy((l1, l2))
    return maxkey

差分密码分析

原理与数据

  • IO

    • Input

      • 明密文对 $ \tau $

      • 明密文对数量 T

      • S-Box 逆代换

    • Output

      • 对应最大计数器的秘钥
  • 数据结构

    • 明密文对使用 list ,每对形成一个元组 (P, C) , 明密文使用定长 int

    • S-Box 逆代换使用字典数据结构

  • 算法

    教材算法 3.3

程序设计

  • 明密文对生成函数

In [11]:
def xorPlainPairsGen(count=100):
    pairs = []
    while True:
        plain1 = random.randint(2**0-1, 2**16-1)
        plain2 = 0b0000101100000000 ^ plain1
        cipher1 = spnFromTextEncryption(x=plain1)
        cipher2 = spnFromTextEncryption(x=plain2)
        pairs.append((plain1, plain2, cipher1, cipher2))
        if len(pairs) == count:
            return pairs
  • 差分攻击函数

    由于同名符号过多,采取约定进行命名:

教材表示 程序变量表示
$v4_{<2>}$ v4_2
$(v4_{<2>})*$ vv4_2
$(v4_{<2>})'$ vvv4_2

In [12]:
def differentialAttack(pairs, piSInv=piSInv):
# if True:
    pairsCount = len(pairs)
    Count = [[0 for l2 in range(16)] for l1 in range(16)]
    for (x, y, xx, yy) in [(x, y, xx, yy) \
        for x, y in pairs for xx, yy in pairs \
        if (x, y) != (xx, yy)]:
        if splitBits(y, 1) != splitBits(yy, 1) \
        or splitBits(y, 3) != splitBits(yy, 3):
            print('Failure')
        else:
            for (l1, l2) in \
            [(l1, l2) for l1 in range(16) \
             for l2 in range(16)]:
                v4_2 = l1 ^ splitBits(y, 2)
                v4_4 = l2 ^ splitBits(y, 4)
                u4_2 = piSInv[v4_2]
                u4_4 = piSInv[v4_4]

                vv4_2 = l1 ^ splitBits(yy, 2)
                vv4_4 = l2 ^ splitBits(yy, 4)
                uu4_2 = piSInv[vv4_2]
                uu4_4 = piSInv[vv4_4]
                
                uuu4_2 = u4_2 ^ uu4_2
                uuu4_4 = u4_4 ^ uu4_4
                
                print(v4_2, v4_4, u4_2, u4_4)
                print(vv4_2, vv4_4, uu4_2, uu4_4)
                print(uuu4_2, uuu4_4)

                if uuu4_2 == 0b0110 \
                and uuu4_4 == 0b0110 :
                    Count[l1][l2] += 1
    max = -1
    for (l1, l2) in \
    [(l1, l2) for l1 in range(16) \
     for l2 in range(16)]:
        if Count[l1][l2] > max:
            max = Count[l1][l2]
            maxkey = copy.copy((l1, l2))
#     return maxkey
    print(maxkey)
  • 实施密码分析

In [13]:
%%time
#     generate 8000 pairs
pairs = []
for i in range(8000):
    pairs.append(pcPairGenRand())
#     lin attack
partialKey = linearAttack(pairs)


CPU times: user 4.36 s, sys: 5.65 ms, total: 4.37 s
Wall time: 4.37 s

In [14]:
%%time
def bruteForce(partialKey=partialKey, pairs=pairs):
    print("start brute force")
    for bits20 in range(2 ** 20):
        for bits4 in range(2 ** 4):
            primitiveKeyLocal = \
            (bits20 << 12)| (partialKey[0] << 8) \
            | (bits4 << 4)|(partialKey[1] << 0)
            Kl = {}
            Kl[1] = (primitiveKey >> 16) & 0xFFFF
            Kl[2] = (primitiveKey >> 12) & 0xFFFF
            Kl[3] = (primitiveKey >> 8) & 0xFFFF
            Kl[4] = (primitiveKey >> 4) & 0xFFFF
            Kl[5] = primitiveKey & 0xFFFF
            neq = False
            paircount = len(pairs)
            for index in range(paircount):
                (plain, cipher) = pairs[index]
                if spnFromTextEncryption\
                (x=plain, K=Kl) != cipher:
                    break
                elif index == paircount:
                    print(primitiveKeyLocal)
    print("""done""")


CPU times: user 4 µs, sys: 0 ns, total: 4 µs
Wall time: 5.72 µs

SPN 增强实现

增强措施

  • 增加分组长度

    分组长度由16位加长到64位,可以以8字节为单位进行文件加密

  • 增加密钥的长度

    主密钥长度由32位加长到2048位,由系统随机函数生成

  • 改进S盒

    S盒由系统随机函数生成,对不能通过随机性检测的S盒予以抛弃,并重新生成。

    检测表明,S盒随机性得到明显改善,可有效抵抗各种密码分析。

  • 增加轮数

    由4轮增加到32轮

程序设计

  • 字典洗牌函数定义

In [15]:
import random
def shuffleDictRand(d):
    k, v = list(d.keys()), list(d.values())
    random.shuffle(v) # shuffle values
    return dict(zip(k, v))
  • S-Box P-Box 函数定义

In [16]:
def genPiSRand(size=16):
    k, v = \
    list(range(size)), list(range(size))
    # 0-size-1
    random.shuffle(v)
    return dict(zip(k, v))
def genPiPRand(size=16):
    k, v = \
    list(range(1, size + 1)), list(range(1, size + 1))
    # 0-size-1
    random.shuffle(v)
    return dict(zip(k, v))
  • 密钥随机生成函数

In [17]:
import random
def genKeyRand(keysize=2048):
    return random.randint(0, 2 ** keysize - 1)
  • 常量规定

In [18]:
m, l = 16, 4 # m S-Boxes, l bits in each
nr = 32

piS = genPiSRand(l ** 2)
piP = genPiPRand(m * l)
primitiveKeyLen = (nr + 1) * (m * l)
primitiveKey = genKeyRand(primitiveKeyLen)
scheduledKeys = []
for r in range(1, (nr + 1) + 1):
    cKey = int(bin(primitiveKey).replace('0b', '')\
               .zfill(primitiveKeyLen)\
               [(r - 1) * m * l: r * m * l - 1], 2)
    scheduledKeys.append(cKey)
    # K is a dict from 1 to nr + 1
K = {}
for r in range(len(scheduledKeys)):
    K[r + 1] = scheduledKeys[r]

piSInv = dict((v,k) for k,v in piS.items())
piPInv = dict((v,k) for k,v in piP.items())
  • 加密函数

In [19]:
def spnReinforceEncryption(x, piS, piP, K):
    nr = len(K) - 1
    w, u, v = {}, {}, {}    
    # keys and values in u, v, w are int
    w[0] = x
    for r in range(1, nr): # 1 to nr -1
        ## xor round key    
        u[r] = w[r - 1] ^ K[r]

        ## subsitution
        v[r] = subsitutionFunc(u[r], piS)

        ## permutation
        w[r] = permutationFunc(v[r], piP)
        
    u[nr] = w[nr - 1] ^ K[nr]
    v[nr] = subsitutionFunc(u[nr], piS)
    y = v[nr] ^ K[nr + 1]
    return y
  • 解密函数

In [20]:
def spnReinforceDecryption(y, piSInv, piPInv, K):
    nr = len(K) - 1
    w, u, v = {}, {}, {}    
    # keys and values in u, v, w are int

    v[nr] = y ^ K[nr + 1]
    u[nr] = subsitutionFunc(v[nr], piSInv)
    w[nr - 1] = u[nr] ^ K[nr]

    for r in range(nr - 1, 0, -1):

        ## permutation Inverse
        v[r] = permutationFunc(w[r], piPInv)
        
        ## subsitution Inverse
        u[r] = subsitutionFunc(v[r], piSInv)

        ## xor round key    
        w[r - 1] = u[r] ^ K[r]    

        
    x = w[0]
    return x

文件加解密

原理与标准

文件加密采取迭代式方法分块读取文件,并分别进行加密或解密。

加解密使用前面实现的 增强SPN 加解密模块。

加密时生成密钥,并存储在文件中。解密时从文件读取密钥,并进行解密。

  • 分组密码工作模式

    采取电子密码本(Electric Codebook) 模式

    需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密。

  • 字节填充

    遵循 ISO/IEC 10118-1 & ISO/IEC 9797-1 标准。

    采取 zero padding,填充 \x00

    All the bytes that are required to be padded are padded with zero. The zero padding scheme has not been standardized for encryption, although it is specified for hashes and MACs as Padding Method 1 in ISO/IEC 10118-1 and ISO/IEC 9797-1.

程序设计

  • 常量规定
变量 解释
path2original 加密源文件路径
path2encrypted 加密后文件路径
path2decrypted 解密后文件路径
path2key 密钥读取或写入路径
BLOCKSIZE 密码分块大小
ENDIAN 加解密时均使用大端,不论文件实际存储所采用的方式

In [21]:
path2original = \
'~/Course_Project_of_Cryptography_HUST_2017/code/classicPrime.py'
path2encrypted = \
'~/Course_Project_of_Cryptography_HUST_2017/code/encrypted.py'

path2decrypted = \
'~/Course_Project_of_Cryptography_HUST_2017/code/decrypted.py'
path2key = \
'~/Course_Project_of_Cryptography_HUST_2017/code/key'

BLOCKSIZE = 8 # bytes
ENDIAN = 'big' # big endian default
  • 秘钥存入文件

In [23]:
import pickle
with open(path2key, 'wb') as fkey:
    pickle.dump(primitiveKey, fkey)
    fkey.close()
  • 加密

In [24]:
path2read = path2original
path2write = path2encrypted
from functools import partial
with open(path2read, 'rb') as fin:
    with open(path2write, 'wb') as fout:
        
        byteBlocks = iter(partial(fin.read, BLOCKSIZE), b'')
        for index, value in enumerate(byteBlocks):
            # convert to 64-bit int
            # pad if not BLOCKSIZE
            padTime = 0
            while len(value) != BLOCKSIZE:
                ####### ZERO PADDING ######
                value += b'\x00'
                padTime += 1
            byteBlockInt = int.from_bytes(
                value, byteorder = ENDIAN)
            # encryption
            cipherText = spnReinforceEncryption(
                byteBlockInt, piS, piP, K)
            # convert to bytes
            byteBlock2Write = cipherText.to_bytes(BLOCKSIZE
                                , byteorder = ENDIAN)
            fout.write(byteBlock2Write)
        
#         write a byte block to indicate padding info
        fout.write((padTime).to_bytes(BLOCKSIZE, ENDIAN))
        fout.close()
    fin.close()
  • 从文件读取密钥

In [25]:
import pickle
with open(path2key, 'rb') as fkey:
    primitiveKey = pickle.load(fkey)
    fkey.close()
  • 解密

In [26]:
path2read = path2encrypted
path2write = path2decrypted
from functools import partial

# read last block for padding info
with open(path2read, 'rb') as fp:
    padTime = 0
    for lastBlock \
    in reversed(list(iter(partial(fp.read, 8), b''))):
        padTime = \
        int.from_bytes(lastBlock, byteorder = ENDIAN)
        break
    fp.close()

import os
fileSize = os.stat(path2read).st_size
blockCount = int(fileSize / BLOCKSIZE)

with open(path2read, 'rb') as fin:
    with open(path2write, 'wb') as fout:
        byteBlocks = iter(partial(fin.read, BLOCKSIZE), b'')
        for index, value in enumerate(byteBlocks):
            # if reaches last block of padding info
            if index + 1 == blockCount:
                break
            # convert to 64-bit int
            byteBlockInt = int.from_bytes(
                value, byteorder = ENDIAN)
            # decryption
            cipherText = spnReinforceDecryption(
                byteBlockInt, piSInv, piPInv, K)
            # convert to bytes
            byteBlock2Write = cipherText.to_bytes(BLOCKSIZE
                                , byteorder = ENDIAN)
            # unpad if reaches last - 1 block
            if index + 2 == blockCount:
                byteBlock2Write = \
                byteBlock2Write[0: BLOCKSIZE - padTime]
                
            fout.write(byteBlock2Write)
        fout.close()
    fin.close()

测试(包含随机性检测)

直接运行上述模块,然后观察磁盘上的文件:

  • 源文件

In [27]:
!hexdump classicPrime.py


0000000 0a 69 6d 70 6f 72 74 20 6d 61 74 68 09 0a 23 20
0000010 44 65 74 65 72 6d 69 6e 65 20 50 72 69 6d 65 20
0000020 77 69 74 68 20 74 68 65 20 68 65 6c 70 20 6f 66
0000030 20 0a 23 20 74 68 65 20 53 69 65 76 65 20 6f 66
0000040 20 45 72 61 74 6f 73 74 68 65 6e 65 73 20 61 6c
0000050 67 6f 72 69 74 68 6d 0a 64 65 66 20 70 72 69 6d
0000060 65 53 69 65 76 65 28 73 69 65 76 65 53 69 7a 65
0000070 29 3a 0a 0a 23 20 52 65 74 75 72 6e 73 20 61 20
0000080 6c 69 73 74 20 6f 66 20 70 72 69 6d 65 20 6e 75
0000090 6d 62 65 72 73 20 0a 09 73 69 65 76 65 20 3d 20
00000a0 5b 54 72 75 65 5d 20 2a 20 73 69 65 76 65 53 69
00000b0 7a 65 0a 09 73 69 65 76 65 5b 30 5d 20 3d 20 73
00000c0 69 65 76 65 5b 31 5d 20 3d 20 46 61 6c 73 65 0a
00000d0 0a 09 23 20 63 72 65 61 74 65 20 74 68 65 20 73
00000e0 69 65 76 65 0a 09 66 6f 72 20 69 20 69 6e 20 72
00000f0 61 6e 67 65 28 32 2c 20 69 6e 74 28 6d 61 74 68
0000100 2e 73 71 72 74 28 73 69 65 76 65 53 69 7a 65 29
0000110 29 20 2b 20 31 29 3a 0a 09 09 6d 75 6c 74 69 70
0000120 6c 69 65 72 20 3d 20 32 0a 09 09 77 68 69 6c 65
0000130 20 69 20 2a 20 6d 75 6c 74 69 70 6c 69 65 72 20
0000140 3c 20 73 69 65 76 65 53 69 7a 65 3a 0a 09 09 09
0000150 73 69 65 76 65 5b 69 20 2a 20 6d 75 6c 74 69 70
0000160 6c 69 65 72 5d 20 3d 20 46 61 6c 73 65 0a 09 09
0000170 09 6d 75 6c 74 69 70 6c 69 65 72 20 2b 3d 20 31
0000180 0a 0a 09 23 20 63 72 65 61 74 65 20 6c 69 73 74
0000190 0a 09 70 72 69 6d 65 6c 69 73 74 20 3d 20 5b 5d
00001a0 0a 09 66 6f 72 20 69 20 69 6e 20 72 61 6e 67 65
00001b0 28 73 69 65 76 65 53 69 7a 65 29 3a 0a 09 09 69
00001c0 66 20 73 69 65 76 65 5b 69 5d 20 3d 3d 20 54 72
00001d0 75 65 3a 0a 09 09 09 70 72 69 6d 65 6c 69 73 74
00001e0 2e 61 70 70 65 6e 64 28 69 29 0a 0a 09 72 65 74
00001f0 75 72 6e 20 70 72 69 6d 65 6c 69 73 74 0a 09 09
0000200 09 09 09                                       
0000203
  • 加密后的文件

In [28]:
!hexdump encrypted.py


0000000 27 b6 5e aa 24 fb 3b 22 bb 62 bd fb db c0 a8 20
0000010 4f 0c 3d 41 46 d3 a9 b1 35 83 b6 5b 12 4e 2f c2
0000020 9b be 50 3c 63 e7 55 1b 15 1d 79 97 07 46 c5 e7
0000030 c7 58 b0 87 ab 07 ff 87 20 8b 03 6d a8 a2 a1 13
0000040 2d ed 82 87 3a ba 2f 56 1a ff 00 42 41 8e 79 9a
0000050 dd e7 9b b4 2e 26 47 46 dd ac 4b 61 6f 7a 55 6d
0000060 cd 37 5a de 32 61 9e 92 56 97 75 d7 d5 7e 5e a0
0000070 21 78 33 3a b0 b0 c8 85 cb 4e bf 4d 9c 23 72 07
0000080 64 3a 25 f5 4e 0b 61 30 58 1e f7 9a 8b 42 04 9a
0000090 90 76 09 1a 44 64 32 78 37 b5 7f a2 bd 47 c7 76
00000a0 9b 09 8f 30 5a a8 7d 45 1a e1 91 e5 02 78 4c 6e
00000b0 29 55 59 a6 e9 e5 f1 ad 4f d2 61 8c 8e c1 e1 26
00000c0 10 3d eb 30 a7 13 3d 84 15 25 65 bb 9d 43 d7 29
00000d0 2e dd 88 40 8f 81 c6 be a3 19 89 d2 04 ba 95 5c
00000e0 84 00 3e 38 12 42 bf 64 60 8d 3d 7b d6 50 c6 81
00000f0 b1 2f 29 a8 00 ab d6 db e6 02 1b 06 c8 bf af 4d
0000100 74 41 18 f5 90 a8 e7 ee 87 49 3b ea 61 59 df 79
0000110 5c ad 82 dd ae 61 26 f8 29 db 91 67 c9 92 4d e0
0000120 6e cc 6b ba 66 ab cf 7c 28 23 b7 8d 2f c3 fe 29
0000130 97 3f c2 4a 3f de df d8 51 ef 91 15 94 1a 90 b8
0000140 dd 57 a8 70 8c 7f 16 b2 ec 6b 31 06 eb c9 1f 40
0000150 f3 ea de 60 2f fe 6d 47 39 68 8b 66 d0 7b 3d ef
0000160 b8 35 88 73 6c 6c c4 fb c2 b5 4c 11 09 17 32 3a
0000170 5a 99 e1 09 7e 95 b4 2b e0 18 11 8f 72 0d 54 a7
0000180 40 2f 3c 2e 24 81 f6 81 e0 3c 97 0f d6 89 7c 80
0000190 8d c0 79 ed f2 57 73 f1 59 8f 34 c6 0a db 14 29
00001a0 da 99 56 e3 9e ea 7b 90 da 00 b5 08 f1 a3 d2 78
00001b0 98 34 9e 65 74 75 54 24 75 c1 ad 2f 81 8a 7c ac
00001c0 3c 04 0d 03 61 ce b2 5d 27 9f e5 ac cd 64 c2 51
00001d0 c3 3f 74 5f 30 48 13 5c 6f 33 d0 07 0a a0 85 e4
00001e0 ba ac 9c c0 5b f0 d3 16 8f 52 f3 1b 19 51 11 54
00001f0 c0 b6 fb d6 77 a8 a2 66 df b2 a9 07 6e ac 80 99
0000200 c8 6f 65 0d c6 8f ea 51 00 00 00 00 00 00 00 05
0000210
  • 解密后的文件

In [29]:
!hexdump decrypted.py


0000000 0a 69 6d 70 6f 72 74 20 6d 61 74 68 09 0a 23 20
0000010 44 65 74 65 72 6d 69 6e 65 20 50 72 69 6d 65 20
0000020 77 69 74 68 20 74 68 65 20 68 65 6c 70 20 6f 66
0000030 20 0a 23 20 74 68 65 20 53 69 65 76 65 20 6f 66
0000040 20 45 72 61 74 6f 73 74 68 65 6e 65 73 20 61 6c
0000050 67 6f 72 69 74 68 6d 0a 64 65 66 20 70 72 69 6d
0000060 65 53 69 65 76 65 28 73 69 65 76 65 53 69 7a 65
0000070 29 3a 0a 0a 23 20 52 65 74 75 72 6e 73 20 61 20
0000080 6c 69 73 74 20 6f 66 20 70 72 69 6d 65 20 6e 75
0000090 6d 62 65 72 73 20 0a 09 73 69 65 76 65 20 3d 20
00000a0 5b 54 72 75 65 5d 20 2a 20 73 69 65 76 65 53 69
00000b0 7a 65 0a 09 73 69 65 76 65 5b 30 5d 20 3d 20 73
00000c0 69 65 76 65 5b 31 5d 20 3d 20 46 61 6c 73 65 0a
00000d0 0a 09 23 20 63 72 65 61 74 65 20 74 68 65 20 73
00000e0 69 65 76 65 0a 09 66 6f 72 20 69 20 69 6e 20 72
00000f0 61 6e 67 65 28 32 2c 20 69 6e 74 28 6d 61 74 68
0000100 2e 73 71 72 74 28 73 69 65 76 65 53 69 7a 65 29
0000110 29 20 2b 20 31 29 3a 0a 09 09 6d 75 6c 74 69 70
0000120 6c 69 65 72 20 3d 20 32 0a 09 09 77 68 69 6c 65
0000130 20 69 20 2a 20 6d 75 6c 74 69 70 6c 69 65 72 20
0000140 3c 20 73 69 65 76 65 53 69 7a 65 3a 0a 09 09 09
0000150 73 69 65 76 65 5b 69 20 2a 20 6d 75 6c 74 69 70
0000160 6c 69 65 72 5d 20 3d 20 46 61 6c 73 65 0a 09 09
0000170 09 6d 75 6c 74 69 70 6c 69 65 72 20 2b 3d 20 31
0000180 0a 0a 09 23 20 63 72 65 61 74 65 20 6c 69 73 74
0000190 0a 09 70 72 69 6d 65 6c 69 73 74 20 3d 20 5b 5d
00001a0 0a 09 66 6f 72 20 69 20 69 6e 20 72 61 6e 67 65
00001b0 28 73 69 65 76 65 53 69 7a 65 29 3a 0a 09 09 69
00001c0 66 20 73 69 65 76 65 5b 69 5d 20 3d 3d 20 54 72
00001d0 75 65 3a 0a 09 09 09 70 72 69 6d 65 6c 69 73 74
00001e0 2e 61 70 70 65 6e 64 28 69 29 0a 0a 09 72 65 74
00001f0 75 72 6e 20 70 72 69 6d 65 6c 69 73 74 0a 09 09
0000200 09 09 09                                       
0000203
  • 比较差异

In [30]:
!diff classicPrime.py decrypted.py

diff 程序没有任何输出,古人云:

No news is good news. (没有消息就是好消息)

说明我们的加解密正确。

  • 随机性检测

运行提供的随机性检测程序,没有得到*,检测合格。 程序输出见附录。