蓝牙Classic加密算法设计和实现,SAFER+,E0,E1,E2,E3(python)

概述

之前用python给大家实现了所有LE相关加密工具算法。bobwenstudy/BluetoothCryptographicToolbox: LE SMP加密算法设计和实现(python) (github.com),最近重温了下Classic加密,顺便将Classic所有加密算法给实现了一遍。

在蓝牙Classic Spec中,有一个很重要的概念就是加密,Secure Connection部分没怎么研究,目前就不讲了,后续学到了再补充。相比于BLE,Classic将认证和加密都放在了Controller实现,通过一系列LMP交互来完成秘钥生成和数据加密。为了解决中间人攻击,监听,安全的问题,Spec定义的一堆加密函数及其使用方法。

由于历史原因,Classic配对分为Legacy配对(基于对称加密SAFER+的E21和E22算法)和Secure Simple Pairing配对(基于非对称加密的SHA-256, HMAC-SHA-256和P-192椭圆曲线算法)。

当然后续又升级到了P256加密,就不在本文展开了。大体是:BR/EDR物理传输通道上的安全连接将安全简单配对中的算法升级为P-256椭圆曲线并将设备鉴权中的算法升级为FIPS批准的算法(HMAC-SHA-256和AES-CTR),同时增加了消息完整性(AES-CCM加密算法)。

在芯片具体实现中,经常会听同事说一些是需要硬件支持,那为什么一定要硬件支持呢,软件难道不能做吗,软件做的局限性在哪?那要确定这些问题,那就必须了解各个加密算法实现原理,才能进一步分析清楚软硬件之间的差异。

为了研究这一问题,最简单的办法就是将所有相关算法实现一遍,并了解各个算法的作用范围。为更好的分析其算法实现,本文采用python作为开发语言,对各个加密算法原理和其具体实现具体进行说明。

这里有一些很好的文章,感兴趣可以看看:Guide to Bluetooth Security (nist.gov),蓝牙安全概述-CSDN博客,Kitsos_j01.pdf (eap.gr)

项目测试代码地址:BluetoothCryptographicToolboxClassic

加密算法实现

算法分布

Classic加密分为三个版本,分别是Legacy、Secure Simple Pairing和Secure Connection。本文暂时不对Secure Connection进行说明。

这里提到的加密算法只有P-192 ECDH是非对称加密,关于非对称加密的原理详见之前BLE算法的说明,里面讲得很清楚了。

本文重点对E0、SAFER+还有HMAC-SHA-256算法进行说明。

image-20240522215646414

E0算法

原理概述

在Core Spec v5.4 P965中有定义Classic加密所使用的算法为E0,网上关于E0的资料比较少,还好spec已经讲得比较细了,再参考一些文章,基本就能理解E0如何实现的了。当然一些细节得看一些资料才清楚,附上一些好的参考链接。edderick/E0_Python: A Python implementation of the E0 stream cipher used by Bluetooth. (github.com),Galois’ Theorem and Polynomial Arithmetic,Binary polynomials | SpringerLink,蓝牙链路层加密算法—EO加密算法 - 夏冰加密软件技术博客 (jiamisoft.com)。

从Spec这个图片可以看出,E0加密的输入有BD_ADD_C、CLK和K_enc,输出一个Kcipher,和数据进行XOR操作。因为输入需要加入CLK信息,所以就算是同一笔数据,在重传的时候,数据内容也不相同,软件去做,时间也基本很难保证,所以这里基本都是由硬件实现的。

image-20240523082446728

EO加密算法是蓝牙链路层的加密算法,属于流加密方式,即将数据流与密钥比特流进行异或运算。对每一分组的有效载荷的加密是单独进行的,它发生在循环冗余校验之后,前向纤错编码之前。主要原理是利用线性反馈移位寄存器产生伪随机序列,从而形成可用于加密的密钥流,然后将密钥流与要加密的数据流进行异或,实现加密。解密时把密文与同样的密钥流再异或一次就可得到明文。其核心算法如下图所示。

image-20240523083504577

EO加密算法主要有:线性反馈移位寄存器组(Linear Feedback Shift Registcr,LFSR),(LFSR1~4)、组合逻辑(Summation Combiner Logic)和复合器(Blend)这三部分组成,其中Blend中T1和T2为线性变换网络,Z-1为延迟网络。

LFSR

LFSRs的长度分别为25、31、33、39。采用多个LFSR是为了增加生成的伪随机序列的长度和随机性。

image-20240523094314725

LFSR的代码实现也比较简单。设定好抽头后,每次运行一次即可,注意其有记忆性。其原理感兴趣可以看看【Verilog编程】线性反馈移位寄存器(LFSR)原理及Verilog代码实现 - 知乎 (zhihu.com)的说明。

def LFSR_process_step(input, last, poly_list, xor_en=True):
    xor = input & 0x01
    input = input >> 1
    if xor_en:
        for i in poly_list:
            if (last & (1 << (i - 1))) != 0:
                xor ^= 1
    last = last << 1
    last |= xor
    last &= (1 << poly_list[0]) - 1
    return input, last

input_LFSR_1, last_LFSR_1 = LFSR_process_step(input_LFSR_1, last_LFSR_1, [25,20,12,8], xor_en=index >= 25)
input_LFSR_2, last_LFSR_2 = LFSR_process_step(input_LFSR_2, last_LFSR_2, [31,24,16,12], xor_en=index >= 31)
input_LFSR_3, last_LFSR_3 = LFSR_process_step(input_LFSR_3, last_LFSR_3, [33,28,24,4], xor_en=index >= 33)
input_LFSR_4, last_LFSR_4 = LFSR_process_step(input_LFSR_4, last_LFSR_4, [39,36,28,4], xor_en=index >= 39)

yt

其实就是4个LFSR的输出。由于X取值为0-1,所以yt的取值为0-3。

image-20240523094400398

LFSR的输出就是spec定义的从LFSR中抽取相应bit,而后求和即可。

X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

y = X1 + X2 + X3 + X4

zt,st+1,ct+1

计算也比较简单。

image-20240523094457903

代码实现也很简单。

St_1 = (Ct + y) // 2

Ct_1 = T1 ^ T2 ^ St_1

Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)

T1,T2

看spec比较麻烦,其实就是输入输出变换。

image-20240523094616182

代码实现也非常简单。

T1_values = [0b00, 0b01, 0b10, 0b11]
T2_values = [0b00, 0b11, 0b01, 0b10]

T1 = T1_values[Ct]
T2 = T2_values[Ct_neg_1]
LSFR初始化
Step1: K_session生成

在E0算法中, 加密密钥K_enc被修改为实际加密密钥K_seesionK_enc的有效长度为L,通过Binary polynomials运算得到一个新的128bit的K_seesion

image-20240523090052162

其中g1g2的的值如下。

image-20240523090321844

这里用到了Binary polynomials运算,说起来麻烦,代码实现也简单,也就是下面的bitpol_multbitpol_mod。具体算法就是调用classic_k_session操作。

def bitpol_mult(a, b):
    t = 0
    while(b != 0):
        if(b & 0x01 != 0):
            t ^= a
        b >>= 1
        a <<= 1
    return t

def bitpol_mod(a, b):
    while(a.bit_length() >= b.bit_length()):
        a ^= b << (a.bit_length() - b.bit_length())
    return a

g1_arr = [
    0x0000000000000000000000000000011d,
    0x0000000000000000000000000001003f,
    0x000000000000000000000000010000db,
    0x000000000000000000000001000000af,
    0x00000000000000000000010000000039,
    0x00000000000000000001000000000291,
    0x00000000000000000100000000000095,
    0x0000000000000001000000000000001b,
    0x00000000000001000000000000000609,
    0x00000000000100000000000000000215,
    0x0000000001000000000000000000013b,
    0x000000010000000000000000000000dd,
    0x0000010000000000000000000000049d,
    0x0001000000000000000000000000014f,
    0x010000000000000000000000000000e7,
    0x100000000000000000000000000000000,
]
g2_arr = [
    0x00e275a0abd218d4cf928b9bbf6cb08f,
    0x0001e3f63d7659b37f18c258cff6efef,
    0x000001bef66c6c3ab1030a5a1919808b,
    0x000000016ab89969de17467fd3736ad9,
    0x000000000163063291da50ec55715247,
    0x0000000000002c9352aa6cc054468311,
    0x00000000000000b3f7fffce279f3a073,
    0x0000000000000000a1ab815bc7ec8025,
    0x00000000000000000002c98011d8b04d,
    0x00000000000000000000058e24f9a4bb,
    0x00000000000000000000000ca76024d7,
    0x0000000000000000000000001c9c26b9,
    0x0000000000000000000000000026d9e3,
    0x00000000000000000000000000004377,
    0x00000000000000000000000000000089,
    0x00000000000000000000000000000001,
]

def classic_k_session(k_enc, L):
    g1 = g1_arr[L - 1]
    g2 = g2_arr[L - 1]
    return bitpol_mult(g2, bitpol_mod(k_enc, g1))
Step2: 39次轮转

K_seesionBD ADDR_C、26位蓝牙时钟CLK以及常数 111001 共208位移入线性反馈移位寄存器。 如下图所示,各输入端的信号分配如图。

  1. 打开所有开关(也就是不进行反馈,只进行移位)。设置所有线性反馈移位寄存器内容为0,令t=0。

  2. 开始往LFSRi中移位,每行最右边的一位最先移入相应的LFSR

  3. 当第i行的第一位到达LFSRi的最右边时,关闭相应LFSR的开关(也就是启动反馈)。

  4. 在t=39时 (此时 LFSR4 的开关已关闭), 令 c39 =c39-1 =0, 现在 c39c39-1的内容已没关系。但是,它们的内容现在将是
    用于计算输出序列。

  5. 从现在开始产生输出符号。剩余的输入位被连续移入相应的移位寄存器。最后一位移入后,移位寄存器的时钟输入为0。

image-20240523083821947

代码也就是这一段,这里直接将步骤3放进来了。最终输出的output_Z就是所需的初始化序列。

CLL = CL[0] & (0x0f)
CLU = (CL[0] >> 4) & (0x0f)
CL24 = (CL[24 // 8] >> (24 % 8)) & 0x01
CL25 = (CL[25 // 8] >> (25 % 8)) & 0x01

LFSR_1 = []
LFSR_1.append(BD_ADDR_C[2])
LFSR_1.append(CL[1])
LFSR_1.append(K_session[12])
LFSR_1.append(K_session[8])
LFSR_1.append(K_session[4])
LFSR_1.append(K_session[0])
LFSR_1.append(CL24 << 7)
LFSR_1 = int(bytes(LFSR_1).hex(), 16)
# print(LFSR_1.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
LFSR_1 = LFSR_1 >> 7
# print(LFSR_1.to_bytes(length=7,byteorder='big',signed=False).hex(' '))


LFSR_2 = []
LFSR_2.append(BD_ADDR_C[3])
LFSR_2.append(BD_ADDR_C[0])
LFSR_2.append(K_session[13])
LFSR_2.append(K_session[9])
LFSR_2.append(K_session[5])
LFSR_2.append(K_session[1])
LFSR_2.append((CLL << 4) | (0b001) << 1)
LFSR_2 = int(bytes(LFSR_2).hex(), 16)
# print(LFSR_2.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
LFSR_2 = LFSR_2 >> 1
# print(LFSR_2.to_bytes(length=7,byteorder='big',signed=False).hex(' '))

LFSR_3 = []
LFSR_3.append(BD_ADDR_C[4])
LFSR_3.append(CL[2])
LFSR_3.append(K_session[14])
LFSR_3.append(K_session[10])
LFSR_3.append(K_session[6])
LFSR_3.append(K_session[2])
LFSR_3.append(CL25 << 7)
LFSR_3 = int(bytes(LFSR_3).hex(), 16)
# print(LFSR_3.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
LFSR_3 = LFSR_3 >> 7
# print(LFSR_3.to_bytes(length=7,byteorder='big',signed=False).hex(' '))

LFSR_4 = []
LFSR_4.append(BD_ADDR_C[5])
LFSR_4.append(BD_ADDR_C[1])
LFSR_4.append(K_session[15])
LFSR_4.append(K_session[11])
LFSR_4.append(K_session[7])
LFSR_4.append(K_session[3])
LFSR_4.append((CLU << 4) | (0b111) << 1)
LFSR_4 = int(bytes(LFSR_4).hex(), 16)
# print(LFSR_4.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
LFSR_4 = LFSR_4 >> 1
# print(LFSR_4.to_bytes(length=7,byteorder='big',signed=False).hex(' '))

input_LFSR_1 = LFSR_1
input_LFSR_2 = LFSR_2
input_LFSR_3 = LFSR_3
input_LFSR_4 = LFSR_4

last_LFSR_1 = 0
last_LFSR_2 = 0
last_LFSR_3 = 0
last_LFSR_4 = 0

Ct_neg_1 = 0
Ct = 0
Ct_1 = 0

T1_values = [0b00, 0b01, 0b10, 0b11]
T2_values = [0b00, 0b11, 0b01, 0b10]

output_Z = 0
for index in range(200 + 39):
    if index > 0:
        # next round
        Ct_neg_1 = Ct
        Ct = Ct_1

    t = index + 1
    # At t = 39 (when the switch of LFSR4 is closed), reset both blend registers
    if t == 39:
        Ct = 0
        Ct_neg_1 = 0

    input_LFSR_1, last_LFSR_1 = LFSR_process_step(input_LFSR_1, last_LFSR_1, [25,20,12,8], xor_en=index >= 25)
    input_LFSR_2, last_LFSR_2 = LFSR_process_step(input_LFSR_2, last_LFSR_2, [31,24,16,12], xor_en=index >= 31)
    input_LFSR_3, last_LFSR_3 = LFSR_process_step(input_LFSR_3, last_LFSR_3, [33,28,24,4], xor_en=index >= 33)
    input_LFSR_4, last_LFSR_4 = LFSR_process_step(input_LFSR_4, last_LFSR_4, [39,36,28,4], xor_en=index >= 39)

    X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
    X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
    X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
    X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

    y = X1 + X2 + X3 + X4

    St_1 = (Ct + y) // 2

    T1 = T1_values[Ct]
    T2 = T2_values[Ct_neg_1]

    Ct_1 = T1 ^ T2 ^ St_1

    Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)

    #Last 128 bits generated
    start_output_pos = 200 + 39 - 128
    if t > start_output_pos:
        pos = t - start_output_pos - 1
        output_Z |= Z << pos

    if debug: print("%3d %12s%1s %12s%1s %12s%1s %12s%1s    %2s %2s %2s %2s   %2s  %6s %6s %6s" % 
        (t
            , last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 25)
            , last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 31)
            , last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 33)
            , last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 39)
            , X1, X2, X3, X4
            , Z
            , '{:02b}'.format(Ct_1), '{:02b}'.format(Ct), '{:02b}'.format(Ct_neg_1)
            )
        )

if debug: print("output_Z: %s" % (output_Z.to_bytes(length=16,byteorder='big',signed=False).hex().upper()))
Step3:混合初始数据

为了混合初始数据,继续计时,直到200个符号所有开关闭合时产生(t = 239);

Step4:生成移位寄存器初始序列Z

保持混合寄存器ctct-1,并将最后128位输出Z,按照下图放入LFSR中。

image-20240523093536980

转化为代码就是下面的操作。

# Reload this pattern into the LFSRs
# Hold content of Summation Combiner regs and calculate new C[t+1] and Z values
outputZ_arr = output_Z.to_bytes(length=16,byteorder='little',signed=False)

last_LFSR_1 = []
last_LFSR_1.append((outputZ_arr[12] & 0x01))
last_LFSR_1.append(outputZ_arr[8])
last_LFSR_1.append(outputZ_arr[4])
last_LFSR_1.append(outputZ_arr[0])
last_LFSR_1 = int(bytes(last_LFSR_1).hex(), 16)
# if debug: print(last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper())

last_LFSR_2 = []
last_LFSR_2.append((outputZ_arr[12] & 0xfe) >> 1)
last_LFSR_2.append(outputZ_arr[9])
last_LFSR_2.append(outputZ_arr[5])
last_LFSR_2.append(outputZ_arr[1])
last_LFSR_2 = int(bytes(last_LFSR_2).hex(), 16)
# if debug: print(last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper())

last_LFSR_3 = []
last_LFSR_3.append((outputZ_arr[15] & 0x01))
last_LFSR_3.append(outputZ_arr[13])
last_LFSR_3.append(outputZ_arr[10])
last_LFSR_3.append(outputZ_arr[6])
last_LFSR_3.append(outputZ_arr[2])
last_LFSR_3 = int(bytes(last_LFSR_3).hex(), 16)
# if debug: print(last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper())

last_LFSR_4 = []
last_LFSR_4.append((outputZ_arr[15] & 0xfe) >> 1)
last_LFSR_4.append(outputZ_arr[14])
last_LFSR_4.append(outputZ_arr[11])
last_LFSR_4.append(outputZ_arr[7])
last_LFSR_4.append(outputZ_arr[3])
last_LFSR_4 = int(bytes(last_LFSR_4).hex(), 16)
# if debug: print(last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper())

if debug: print("LFSR1  <= %s" % (last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()))
if debug: print("LFSR2  <= %s" % (last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()))
if debug: print("LFSR3  <= %s" % (last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()))
if debug: print("LFSR4  <= %s" % (last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()))
if debug: print("C[t+1] <= %s" % ('{:02b}'.format(Ct_1)))
Final:按照数据进行加密

从此时起,在每一时钟到来时发生器产生加/解密序列, 与发送(接收)的有效载荷数据进行XOR。也就是生成序列Z与每个要发送数据bit进行XOR。

也就是这个图,Init就是前面Step4中放入LFSRs的初始序列Z。

image-20240523094710546

如下所示,如果数据是125bit,则进行125次翻转,需要注意第一个bit为初始值。将生成的output_keystream和要加密的数据XOR即可,当然实际实现是每发送1个bit都进行一次E0运算,将输出的Z和数据bit进行XOR。

#Generate keystream
input_LFSR_1 = 0
input_LFSR_2 = 0
input_LFSR_3 = 0
input_LFSR_4 = 0

key_length = 125
start_pos = 200 + 39

# Get first keystream bit.
X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)
output_keystream = Z

t = start_pos + 1
if debug: print("%3d %12s%1s %12s%1s %12s%1s %12s%1s    %2s %2s %2s %2s   %2s  %6s %6s %6s" % 
        (t
        , last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
        , get_switch_on_flag(index >= 25)
        , last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
        , get_switch_on_flag(index >= 31)
        , last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
        , get_switch_on_flag(index >= 33)
        , last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
        , get_switch_on_flag(index >= 39)
        , X1, X2, X3, X4
        , Z
        , '{:02b}'.format(Ct_1), '{:02b}'.format(Ct), '{:02b}'.format(Ct_neg_1)
        )
    )

for index in range(start_pos + 1, start_pos + key_length):
    t = index + 1

    # next round
    Ct_neg_1 = Ct
    Ct = Ct_1

    input_LFSR_1, last_LFSR_1 = LFSR_process_step(input_LFSR_1, last_LFSR_1, [25,20,12,8])
    input_LFSR_2, last_LFSR_2 = LFSR_process_step(input_LFSR_2, last_LFSR_2, [31,24,16,12])
    input_LFSR_3, last_LFSR_3 = LFSR_process_step(input_LFSR_3, last_LFSR_3, [33,28,24,4])
    input_LFSR_4, last_LFSR_4 = LFSR_process_step(input_LFSR_4, last_LFSR_4, [39,36,28,4])

    X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
    X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
    X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
    X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

    y = X1 + X2 + X3 + X4

    St_1 = (Ct + y) // 2

    T1 = T1_values[Ct]
    T2 = T2_values[Ct_neg_1]

    Ct_1 = T1 ^ T2 ^ St_1

    Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)

    #keystram generated, spec not give the sample.
    pos = index - start_pos
    output_keystream |= Z << pos

    if debug: print("%3d %12s%1s %12s%1s %12s%1s %12s%1s    %2s %2s %2s %2s   %2s  %6s %6s %6s" % 
        (t
            , last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 25)
            , last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 31)
            , last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 33)
            , last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 39)
            , X1, X2, X3, X4
            , Z
            , '{:02b}'.format(Ct_1), '{:02b}'.format(Ct), '{:02b}'.format(Ct_neg_1)
            )
        )

算法实现

知道了的原理后,实现也比较简单,上面Spec分为两部分给了Sample Data,分别说明。

K_session算法实现

按照Binary polynomials运算,设计并实现classic_k_session()函数,输入秘钥K_enc和秘钥长度L,输出128bit的K_session值。

参考Core Spec v5.4 P811里面的例程,设计小的测试代码来验证其功能,也就是classic_E0_k_session_test函数。

def bitpol_mult(a, b):
    t = 0
    while(b != 0):
        if(b & 0x01 != 0):
            t ^= a
        b >>= 1
        a <<= 1
    return t

def bitpol_mod(a, b):
    while(a.bit_length() >= b.bit_length()):
        a ^= b << (a.bit_length() - b.bit_length())
    return a

g1_arr = [
    0x0000000000000000000000000000011d,
    0x0000000000000000000000000001003f,
    0x000000000000000000000000010000db,
    0x000000000000000000000001000000af,
    0x00000000000000000000010000000039,
    0x00000000000000000001000000000291,
    0x00000000000000000100000000000095,
    0x0000000000000001000000000000001b,
    0x00000000000001000000000000000609,
    0x00000000000100000000000000000215,
    0x0000000001000000000000000000013b,
    0x000000010000000000000000000000dd,
    0x0000010000000000000000000000049d,
    0x0001000000000000000000000000014f,
    0x010000000000000000000000000000e7,
    0x100000000000000000000000000000000,
]
g2_arr = [
    0x00e275a0abd218d4cf928b9bbf6cb08f,
    0x0001e3f63d7659b37f18c258cff6efef,
    0x000001bef66c6c3ab1030a5a1919808b,
    0x000000016ab89969de17467fd3736ad9,
    0x000000000163063291da50ec55715247,
    0x0000000000002c9352aa6cc054468311,
    0x00000000000000b3f7fffce279f3a073,
    0x0000000000000000a1ab815bc7ec8025,
    0x00000000000000000002c98011d8b04d,
    0x00000000000000000000058e24f9a4bb,
    0x00000000000000000000000ca76024d7,
    0x0000000000000000000000001c9c26b9,
    0x0000000000000000000000000026d9e3,
    0x00000000000000000000000000004377,
    0x00000000000000000000000000000089,
    0x00000000000000000000000000000001,
]
def classic_k_session(k_enc, L):
    g1 = g1_arr[L - 1]
    g2 = g2_arr[L - 1]
    return bitpol_mult(g2, bitpol_mod(k_enc, g1))


def classic_E0_k_session_test():
    print_header("classic_E0_k_session_test")
    print_header("1.1.1 Generating K_session from K_enc")
    
    L = 1
    print_header("L = %d" % L)
    K_enc = int("a2b230a4 93f281bb 61a85b82 a9d4a30e".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("7aa16f39 59836ba3 22049a7b 87f1d8a5".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 2
    print_header("L = %d" % L)
    K_enc = int("64e7df78 bb7ccaa4 61433123 5b3222ad".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("142057bb 0bceac4c 58bd142e 1e710a50".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 3
    print_header("L = %d" % L)
    K_enc = int("575e5156 ba685dc6 112124ac edb2c179".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("d56d0adb 8216cb39 7fe3c591 1ff95618".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 4
    print_header("L = %d" % L)
    K_enc = int("8917b4fc 403b6db2 1596b86d 1cb8adab".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("91910128 b0e2f5ed a132a03e af3d8cda".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 5
    print_header("L = %d" % L)
    K_enc = int("785c915b dd25b9c6 0102ab00 b6cd2a68".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("6fb5651c cb80c8d7 ea1ee56d f1ec5d02".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 6
    print_header("L = %d" % L)
    K_enc = int("5e77d19f 55ccd7d5 798f9a32 3b83e5d8".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("16096bcb afcf8def 1d226a1b 4d3f9a3d".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 7
    print_header("L = %d" % L)
    K_enc = int("05454e03 8ddcfbe3 ed024b2d 92b7f54c".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("50f9c0d4 e3178da9 4a09fe0d 34f67b0e".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 8
    print_header("L = %d" % L)
    K_enc = int("7ce149fc f4b38ad7 2a5d8a41 eb15ba31".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("532c36d4 5d0954e0 922989b6 826f78dc".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 9
    print_header("L = %d" % L)
    K_enc = int("5eeff7ca 84fc2782 9c051726 3df6f36e".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("016313f6 0d3771cf 7f8e4bb9 4aa6827d".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 10
    print_header("L = %d" % L)
    K_enc = int("7b13846e 88beb4de 34e7160a fd44dc65".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("023bc1ec 34a0029e f798dcfb 618ba58d".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 11
    print_header("L = %d" % L)
    K_enc = int("bda6de6c 6e7d757e 8dfe2d49 9a181193".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("022e08a9 3aa51d8d 2f93fa78 85cc1f87".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 12
    print_header("L = %d" % L)
    K_enc = int("e6483b1c 2cdb1040 9a658f97 c4efd90d".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("030d752b 216fe29b b880275c d7e6f6f9".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 13
    print_header("L = %d" % L)
    K_enc = int("d79d281d a2266847 6b223c46 dc0ab9ee".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("03f11138 9cebf919 00b93808 4ac158aa".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 14
    print_header("L = %d" % L)
    K_enc = int("cad9a65b 9fca1c1d a2320fcf 7c4ae48e".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("284840fd f1305f3c 529f5703 76adf7cf".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 15
    print_header("L = %d" % L)
    K_enc = int("21f0cc31 049b7163 d375e9e1 06029809".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("7f10b53b 6df84b94 f22e566a 3754a37e".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)



    L = 16
    print_header("L = %d" % L)
    K_enc = int("35ec8fc3 d50ccd32 5f2fd907 bde206de".replace(" ", ""), 16)
    K_session = classic_k_session(K_enc, L)

    K_session_exp = int("35ec8fc3 d50ccd32 5f2fd907 bde206de".replace(" ", ""), 16)
    print_result_with_exp(K_session == K_session_exp)

输出结果如下,测试结果和Spec提供一致,测试通过。

###################################################################################
#                            classic_E0_k_session_test                            #
###################################################################################
###################################################################################
#                      1.1.1 Generating K_session from K_enc                      #
###################################################################################
###################################################################################
#                                      L = 1                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 2                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 3                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 4                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 5                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 6                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 7                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 8                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      L = 9                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 10                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 11                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 12                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 13                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 14                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 15                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     L = 16                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
E0算法实现

按照之前的步骤,设计并实现classic_E0()函数,输入秘钥K_enc和秘钥长度L,输出128bit的K_session值。

参考Core Spec v5.4 P814里面的例程,设计小的测试代码来验证其功能,也就是classic_E0_process_test函数。由于Spec没给加密/解密数据实例,这里就完全按照Spec输出数据顺序,依次将中间过程打印出来。

注意,这里一些中间数据Ct/Ct-1/Ct+1和Spec不一致,但是结果Z却是一致的,具体过程我就不研究了,估计spec写错了?


def get_switch_on_flag(flag):
    if not flag:
        return '*'
    return ''

def LFSR_process_step(input, last, poly_list, xor_en=True):
    xor = input & 0x01
    input = input >> 1
    if xor_en:
        for i in poly_list:
            if (last & (1 << (i - 1))) != 0:
                xor ^= 1
    last = last << 1
    last |= xor
    last &= (1 << poly_list[0]) - 1
    return input, last

def classic_E0(K_session, BD_ADDR_C, CL, debug=True):
    CLL = CL[0] & (0x0f)
    CLU = (CL[0] >> 4) & (0x0f)
    CL24 = (CL[24 // 8] >> (24 % 8)) & 0x01
    CL25 = (CL[25 // 8] >> (25 % 8)) & 0x01

    LFSR_1 = []
    LFSR_1.append(BD_ADDR_C[2])
    LFSR_1.append(CL[1])
    LFSR_1.append(K_session[12])
    LFSR_1.append(K_session[8])
    LFSR_1.append(K_session[4])
    LFSR_1.append(K_session[0])
    LFSR_1.append(CL24 << 7)
    LFSR_1 = int(bytes(LFSR_1).hex(), 16)
    # print(LFSR_1.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
    LFSR_1 = LFSR_1 >> 7
    # print(LFSR_1.to_bytes(length=7,byteorder='big',signed=False).hex(' '))


    LFSR_2 = []
    LFSR_2.append(BD_ADDR_C[3])
    LFSR_2.append(BD_ADDR_C[0])
    LFSR_2.append(K_session[13])
    LFSR_2.append(K_session[9])
    LFSR_2.append(K_session[5])
    LFSR_2.append(K_session[1])
    LFSR_2.append((CLL << 4) | (0b001) << 1)
    LFSR_2 = int(bytes(LFSR_2).hex(), 16)
    # print(LFSR_2.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
    LFSR_2 = LFSR_2 >> 1
    # print(LFSR_2.to_bytes(length=7,byteorder='big',signed=False).hex(' '))

    LFSR_3 = []
    LFSR_3.append(BD_ADDR_C[4])
    LFSR_3.append(CL[2])
    LFSR_3.append(K_session[14])
    LFSR_3.append(K_session[10])
    LFSR_3.append(K_session[6])
    LFSR_3.append(K_session[2])
    LFSR_3.append(CL25 << 7)
    LFSR_3 = int(bytes(LFSR_3).hex(), 16)
    # print(LFSR_3.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
    LFSR_3 = LFSR_3 >> 7
    # print(LFSR_3.to_bytes(length=7,byteorder='big',signed=False).hex(' '))

    LFSR_4 = []
    LFSR_4.append(BD_ADDR_C[5])
    LFSR_4.append(BD_ADDR_C[1])
    LFSR_4.append(K_session[15])
    LFSR_4.append(K_session[11])
    LFSR_4.append(K_session[7])
    LFSR_4.append(K_session[3])
    LFSR_4.append((CLU << 4) | (0b111) << 1)
    LFSR_4 = int(bytes(LFSR_4).hex(), 16)
    # print(LFSR_4.to_bytes(length=7,byteorder='big',signed=False).hex(' '))
    LFSR_4 = LFSR_4 >> 1
    # print(LFSR_4.to_bytes(length=7,byteorder='big',signed=False).hex(' '))

    input_LFSR_1 = LFSR_1
    input_LFSR_2 = LFSR_2
    input_LFSR_3 = LFSR_3
    input_LFSR_4 = LFSR_4

    last_LFSR_1 = 0
    last_LFSR_2 = 0
    last_LFSR_3 = 0
    last_LFSR_4 = 0

    Ct_neg_1 = 0
    Ct = 0
    Ct_1 = 0

    T1_values = [0b00, 0b01, 0b10, 0b11]
    T2_values = [0b00, 0b11, 0b01, 0b10]

    output_Z = 0
    for index in range(200 + 39):
        if index > 0:
            # next round
            Ct_neg_1 = Ct
            Ct = Ct_1

        t = index + 1
        # At t = 39 (when the switch of LFSR4 is closed), reset both blend registers
        if t == 39:
            Ct = 0
            Ct_neg_1 = 0

        input_LFSR_1, last_LFSR_1 = LFSR_process_step(input_LFSR_1, last_LFSR_1, [25,20,12,8], xor_en=index >= 25)
        input_LFSR_2, last_LFSR_2 = LFSR_process_step(input_LFSR_2, last_LFSR_2, [31,24,16,12], xor_en=index >= 31)
        input_LFSR_3, last_LFSR_3 = LFSR_process_step(input_LFSR_3, last_LFSR_3, [33,28,24,4], xor_en=index >= 33)
        input_LFSR_4, last_LFSR_4 = LFSR_process_step(input_LFSR_4, last_LFSR_4, [39,36,28,4], xor_en=index >= 39)

        X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
        X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
        X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
        X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

        y = X1 + X2 + X3 + X4

        St_1 = (Ct + y) // 2

        T1 = T1_values[Ct]
        T2 = T2_values[Ct_neg_1]

        Ct_1 = T1 ^ T2 ^ St_1

        Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)

        #Last 128 bits generated
        start_output_pos = 200 + 39 - 128
        if t > start_output_pos:
            pos = t - start_output_pos - 1
            output_Z |= Z << pos

        if debug: print("%3d %12s%1s %12s%1s %12s%1s %12s%1s    %2s %2s %2s %2s   %2s  %6s %6s %6s" % 
            (t
                , last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 25)
                , last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 31)
                , last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 33)
                , last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 39)
                , X1, X2, X3, X4
                , Z
                , '{:02b}'.format(Ct_1), '{:02b}'.format(Ct), '{:02b}'.format(Ct_neg_1)
                )
            )

    if debug: print("output_Z: %s" % (output_Z.to_bytes(length=16,byteorder='big',signed=False).hex().upper()))

    # Reload this pattern into the LFSRs
    # Hold content of Summation Combiner regs and calculate new C[t+1] and Z values
    outputZ_arr = output_Z.to_bytes(length=16,byteorder='little',signed=False)

    last_LFSR_1 = []
    last_LFSR_1.append((outputZ_arr[12] & 0x01))
    last_LFSR_1.append(outputZ_arr[8])
    last_LFSR_1.append(outputZ_arr[4])
    last_LFSR_1.append(outputZ_arr[0])
    last_LFSR_1 = int(bytes(last_LFSR_1).hex(), 16)
    # if debug: print(last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper())

    last_LFSR_2 = []
    last_LFSR_2.append((outputZ_arr[12] & 0xfe) >> 1)
    last_LFSR_2.append(outputZ_arr[9])
    last_LFSR_2.append(outputZ_arr[5])
    last_LFSR_2.append(outputZ_arr[1])
    last_LFSR_2 = int(bytes(last_LFSR_2).hex(), 16)
    # if debug: print(last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper())

    last_LFSR_3 = []
    last_LFSR_3.append((outputZ_arr[15] & 0x01))
    last_LFSR_3.append(outputZ_arr[13])
    last_LFSR_3.append(outputZ_arr[10])
    last_LFSR_3.append(outputZ_arr[6])
    last_LFSR_3.append(outputZ_arr[2])
    last_LFSR_3 = int(bytes(last_LFSR_3).hex(), 16)
    # if debug: print(last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper())

    last_LFSR_4 = []
    last_LFSR_4.append((outputZ_arr[15] & 0xfe) >> 1)
    last_LFSR_4.append(outputZ_arr[14])
    last_LFSR_4.append(outputZ_arr[11])
    last_LFSR_4.append(outputZ_arr[7])
    last_LFSR_4.append(outputZ_arr[3])
    last_LFSR_4 = int(bytes(last_LFSR_4).hex(), 16)
    # if debug: print(last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper())

    if debug: print("LFSR1  <= %s" % (last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()))
    if debug: print("LFSR2  <= %s" % (last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()))
    if debug: print("LFSR3  <= %s" % (last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()))
    if debug: print("LFSR4  <= %s" % (last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()))
    if debug: print("C[t+1] <= %s" % ('{:02b}'.format(Ct_1)))


    #Generate keystream
    input_LFSR_1 = 0
    input_LFSR_2 = 0
    input_LFSR_3 = 0
    input_LFSR_4 = 0

    key_length = 125
    start_pos = 200 + 39

    # Get first keystream bit.
    X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
    X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
    X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
    X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

    Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)
    output_keystream = Z

    t = start_pos + 1
    if debug: print("%3d %12s%1s %12s%1s %12s%1s %12s%1s    %2s %2s %2s %2s   %2s  %6s %6s %6s" % 
            (t
            , last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 25)
            , last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 31)
            , last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 33)
            , last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
            , get_switch_on_flag(index >= 39)
            , X1, X2, X3, X4
            , Z
            , '{:02b}'.format(Ct_1), '{:02b}'.format(Ct), '{:02b}'.format(Ct_neg_1)
            )
        )
    
    for index in range(start_pos + 1, start_pos + key_length):
        t = index + 1

        # next round
        Ct_neg_1 = Ct
        Ct = Ct_1

        input_LFSR_1, last_LFSR_1 = LFSR_process_step(input_LFSR_1, last_LFSR_1, [25,20,12,8])
        input_LFSR_2, last_LFSR_2 = LFSR_process_step(input_LFSR_2, last_LFSR_2, [31,24,16,12])
        input_LFSR_3, last_LFSR_3 = LFSR_process_step(input_LFSR_3, last_LFSR_3, [33,28,24,4])
        input_LFSR_4, last_LFSR_4 = LFSR_process_step(input_LFSR_4, last_LFSR_4, [39,36,28,4])

        X1 = (last_LFSR_1 >> (24 - 1)) & 0x01
        X2 = (last_LFSR_2 >> (24 - 1)) & 0x01
        X3 = (last_LFSR_3 >> (32 - 1)) & 0x01
        X4 = (last_LFSR_4 >> (32 - 1)) & 0x01

        y = X1 + X2 + X3 + X4

        St_1 = (Ct + y) // 2

        T1 = T1_values[Ct]
        T2 = T2_values[Ct_neg_1]

        Ct_1 = T1 ^ T2 ^ St_1

        Z = X1 ^ X2 ^ X3 ^ X4 ^ (Ct & 0x01)

        #keystram generated, spec not give the sample.
        pos = index - start_pos
        output_keystream |= Z << pos

        if debug: print("%3d %12s%1s %12s%1s %12s%1s %12s%1s    %2s %2s %2s %2s   %2s  %6s %6s %6s" % 
            (t
                , last_LFSR_1.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 25)
                , last_LFSR_2.to_bytes(length=4,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 31)
                , last_LFSR_3.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 33)
                , last_LFSR_4.to_bytes(length=5,byteorder='big',signed=False).hex().upper()
                , get_switch_on_flag(index >= 39)
                , X1, X2, X3, X4
                , Z
                , '{:02b}'.format(Ct_1), '{:02b}'.format(Ct), '{:02b}'.format(Ct_neg_1)
                )
            )

def classic_E0_process_test():
    print_header("classic_E0_process_test")

    print_header("1.1.2 First set of sample data")
    K_session = get_bytes_from_big_eddian_string('00000000 00000000 00000000 00000000')
    BD_ADDR_C = get_bytes_from_big_eddian_string('00 00 00 00 00 00')
    CL = get_bytes_from_big_eddian_string('00 00 00 00')
    # no process expected, just check the output print
    classic_E0(K_session, BD_ADDR_C, CL)


    # TODO: some Ct-1, Ct, Ct+1 value is not correct, need to check the spec again, but the output Z is correct.
    print_header("1.1.3 Second set of sample data")
    K_session = get_bytes_from_little_eddian_string('00000000 00000000 00000000 00000000')
    BD_ADDR_C = get_bytes_from_little_eddian_string('00 00 00 00 00 00')
    CL = get_bytes_from_little_eddian_string('00 00 00 03')
    # no process expected, just check the output print
    classic_E0(K_session, BD_ADDR_C, CL)

    # TODO: some Ct-1, Ct, Ct+1 value is not correct, need to check the spec again, but the output Z is correct.
    print_header("1.1.4 Third set of samples")
    K_session = get_bytes_from_little_eddian_string('ffffffff ffffffff ffffffff ffffffff')
    BD_ADDR_C = get_bytes_from_little_eddian_string('ff ff ff ff ff ff')
    CL = get_bytes_from_little_eddian_string('ff ff ff 03')
    # no process expected, just check the output print
    classic_E0(K_session, BD_ADDR_C, CL)


    # TODO: some Ct-1, Ct, Ct+1 value is not correct, need to check the spec again, but the output Z is correct.
    print_header("1.1.5 Fourth set of samples")
    K_session = get_bytes_from_little_eddian_string('2187f04a ba9031d0 780d4c53 e0153a63')
    BD_ADDR_C = get_bytes_from_little_eddian_string('2c 7f 94 56 0f 1b')
    CL = get_bytes_from_little_eddian_string('5f 1a 00 02')
    # no process expected, just check the output print
    classic_E0(K_session, BD_ADDR_C, CL)

输出结果如下,测试结果和Spec提供一致,测试通过。

###################################################################################
#                             classic_E0_process_test                             #
###################################################################################
###################################################################################
#                         1.1.2 First set of sample data                          #
###################################################################################
  1     00000000*     00000001*   0000000000*   0000000001*     0  0  0  0    0      00     00     00
  2     00000000*     00000002*   0000000000*   0000000003*     0  0  0  0    0      00     00     00
  3     00000000*     00000004*   0000000000*   0000000007*     0  0  0  0    0      00     00     00
  4     00000000*     00000008*   0000000000*   000000000E*     0  0  0  0    0      00     00     00
  5     00000000*     00000010*   0000000000*   000000001C*     0  0  0  0    0      00     00     00
  6     00000000*     00000020*   0000000000*   0000000038*     0  0  0  0    0      00     00     00
  7     00000000*     00000040*   0000000000*   0000000070*     0  0  0  0    0      00     00     00
  8     00000000*     00000080*   0000000000*   00000000E0*     0  0  0  0    0      00     00     00
  9     00000000*     00000100*   0000000000*   00000001C0*     0  0  0  0    0      00     00     00
 10     00000000*     00000200*   0000000000*   0000000380*     0  0  0  0    0      00     00     00
 11     00000000*     00000400*   0000000000*   0000000700*     0  0  0  0    0      00     00     00
 12     00000000*     00000800*   0000000000*   0000000E00*     0  0  0  0    0      00     00     00
 13     00000000*     00001000*   0000000000*   0000001C00*     0  0  0  0    0      00     00     00
 14     00000000*     00002000*   0000000000*   0000003800*     0  0  0  0    0      00     00     00
 15     00000000*     00004000*   0000000000*   0000007000*     0  0  0  0    0      00     00     00
 16     00000000*     00008000*   0000000000*   000000E000*     0  0  0  0    0      00     00     00
 17     00000000*     00010000*   0000000000*   000001C000*     0  0  0  0    0      00     00     00
 18     00000000*     00020000*   0000000000*   0000038000*     0  0  0  0    0      00     00     00
 19     00000000*     00040000*   0000000000*   0000070000*     0  0  0  0    0      00     00     00
 20     00000000*     00080000*   0000000000*   00000E0000*     0  0  0  0    0      00     00     00
 21     00000000*     00100000*   0000000000*   00001C0000*     0  0  0  0    0      00     00     00
 22     00000000*     00200000*   0000000000*   0000380000*     0  0  0  0    0      00     00     00
 23     00000000*     00400000*   0000000000*   0000700000*     0  0  0  0    0      00     00     00
 24     00000000*     00800000*   0000000000*   0000E00000*     0  1  0  0    1      00     00     00
 25     00000000*     01000000*   0000000000*   0001C00000*     0  0  0  0    0      00     00     00
 26     00000000      02000000*   0000000000*   0003800000*     0  0  0  0    0      00     00     00
 27     00000000      04000000*   0000000000*   0007000000*     0  0  0  0    0      00     00     00
 28     00000000      08000000*   0000000000*   000E000000*     0  0  0  0    0      00     00     00
 29     00000000      10000000*   0000000000*   001C000000*     0  0  0  0    0      00     00     00
 30     00000000      20000000*   0000000000*   0038000000*     0  0  0  0    0      00     00     00
 31     00000000      40000000*   0000000000*   0070000000*     0  0  0  0    0      00     00     00
 32     00000000      00000001    0000000000*   00E0000000*     0  0  0  1    1      00     00     00
 33     00000000      00000002    0000000000*   01C0000000*     0  0  0  1    1      00     00     00
 34     00000000      00000004    0000000000    0380000000*     0  0  0  1    1      00     00     00
 35     00000000      00000008    0000000000    0700000000*     0  0  0  0    0      00     00     00
 36     00000000      00000010    0000000000    0E00000000*     0  0  0  0    0      00     00     00
 37     00000000      00000020    0000000000    1C00000000*     0  0  0  0    0      00     00     00
 38     00000000      00000040    0000000000    3800000000*     0  0  0  0    0      00     00     00
 39     00000000      00000080    0000000000    7000000000*     0  0  0  0    0      00     00     00
 40     00000000      00000100    0000000000    6000000001      0  0  0  0    0      00     00     00
 41     00000000      00000200    0000000000    4000000003      0  0  0  0    0      00     00     00
 42     00000000      00000400    0000000000    0000000007      0  0  0  0    0      00     00     00
 43     00000000      00000800    0000000000    000000000E      0  0  0  0    0      00     00     00
 44     00000000      00001001    0000000000    000000001D      0  0  0  0    0      00     00     00
 45     00000000      00002002    0000000000    000000003B      0  0  0  0    0      00     00     00
 46     00000000      00004004    0000000000    0000000077      0  0  0  0    0      00     00     00
 47     00000000      00008008    0000000000    00000000EE      0  0  0  0    0      00     00     00
 48     00000000      00010011    0000000000    00000001DD      0  0  0  0    0      00     00     00
 49     00000000      00020022    0000000000    00000003BB      0  0  0  0    0      00     00     00
 50     00000000      00040044    0000000000    0000000777      0  0  0  0    0      00     00     00
 51     00000000      00080088    0000000000    0000000EEE      0  0  0  0    0      00     00     00
 52     00000000      00100110    0000000000    0000001DDD      0  0  0  0    0      00     00     00
 53     00000000      00200220    0000000000    0000003BBB      0  0  0  0    0      00     00     00
 54     00000000      00400440    0000000000    0000007777      0  0  0  0    0      00     00     00
 55     00000000      00800880    0000000000    000000EEEE      0  1  0  0    1      00     00     00
 56     00000000      01001100    0000000000    000001DDDD      0  0  0  0    0      00     00     00
 57     00000000      02002200    0000000000    000003BBBB      0  0  0  0    0      00     00     00
 58     00000000      04004400    0000000000    0000077777      0  0  0  0    0      00     00     00
 59     00000000      08008800    0000000000    00000EEEEE      0  0  0  0    0      00     00     00
 60     00000000      10011000    0000000000    00001DDDDD      0  0  0  0    0      00     00     00
 61     00000000      20022000    0000000000    00003BBBBB      0  0  0  0    0      00     00     00
 62     00000000      40044000    0000000000    0000777777      0  0  0  0    0      00     00     00
 63     00000000      00088001    0000000000    0000EEEEEE      0  0  0  0    0      00     00     00
 64     00000000      00110003    0000000000    0001DDDDDD      0  0  0  0    0      00     00     00
 65     00000000      00220006    0000000000    0003BBBBBB      0  0  0  0    0      00     00     00
 66     00000000      0044000C    0000000000    0007777777      0  0  0  0    0      00     00     00
 67     00000000      00880018    0000000000    000EEEEEEE      0  1  0  0    1      00     00     00
 68     00000000      01100031    0000000000    001DDDDDDC      0  0  0  0    0      00     00     00
 69     00000000      02200062    0000000000    003BBBBBB8      0  0  0  0    0      00     00     00
 70     00000000      044000C4    0000000000    0077777770      0  0  0  0    0      00     00     00
 71     00000000      08800188    0000000000    00EEEEEEE0      0  1  0  1    0      01     00     00
 72     00000000      11000311    0000000000    01DDDDDDC1      0  0  0  1    0      00     01     00
 73     00000000      22000622    0000000000    03BBBBBB83      0  0  0  1    1      11     00     01
 74     00000000      44000C44    0000000000    0777777707      0  0  0  0    1      10     11     00
 75     00000000      08001888    0000000000    0EEEEEEE0E      0  0  0  1    1      01     10     11
 76     00000000      10003111    0000000000    1DDDDDDC1D      0  0  0  1    0      01     01     10
 77     00000000      20006222    0000000000    3BBBBBB83B      0  0  0  1    0      11     01     01
 78     00000000      4000C444    0000000000    7777777077      0  0  0  0    1      01     11     01
 79     00000000      00018888    0000000000    6EEEEEE0EF      0  0  0  1    0      10     01     11
 80     00000000      00031110    0000000000    5DDDDDC1DE      0  0  0  1    1      00     10     01
 81     00000000      00062220    0000000000    3BBBBB83BC      0  0  0  1    1      01     00     10
 82     00000000      000C4440    0000000000    7777770779      0  0  0  0    1      01     01     00
 83     00000000      00188880    0000000000    6EEEEE0EF2      0  0  0  1    0      11     01     01
 84     00000000      00311100    0000000000    5DDDDC1DE5      0  0  0  1    0      10     11     01
 85     00000000      00622200    0000000000    3BBBB83BCB      0  0  0  1    1      01     10     11
 86     00000000      00C44400    0000000000    7777707797      0  1  0  0    0      01     01     10
 87     00000000      01888801    0000000000    6EEEE0EF2F      0  1  0  1    1      11     01     01
 88     00000000      03111003    0000000000    5DDDC1DE5E      0  0  0  1    0      10     11     01
 89     00000000      06222006    0000000000    3BBB83BCBC      0  0  0  1    1      01     10     11
 90     00000000      0C44400C    0000000000    7777077979      0  0  0  0    1      00     01     10
 91     00000000      18888018    0000000000    6EEE0EF2F2      0  1  0  1    0      10     00     01
 92     00000000      31110030    0000000000    5DDC1DE5E5      0  0  0  1    1      11     10     00
 93     00000000      62220060    0000000000    3BB83BCBCB      0  0  0  1    0      00     11     10
 94     00000000      444400C1    0000000000    7770779797      0  0  0  0    0      10     00     11
 95     00000000      08880183    0000000000    6EE0EF2F2F      0  1  0  1    0      00     10     00
 96     00000000      11100307    0000000000    5DC1DE5E5F      0  0  0  1    1      01     00     10
 97     00000000      2220060E    0000000000    3B83BCBCBF      0  0  0  1    0      00     01     00
 98     00000000      44400C1C    0000000000    770779797E      0  0  0  0    0      11     00     01
 99     00000000      08801838    0000000000    6E0EF2F2FC      0  1  0  0    0      01     11     00
100     00000000      11003070    0000000000    5C1DE5E5F8      0  0  0  0    1      11     01     11
101     00000000      220060E0    0000000000    383BCBCBF0      0  0  0  0    1      01     11     01
102     00000000      4400C1C0    0000000000    70779797E0      0  0  0  0    1      11     01     11
103     00000000      08018380    0000000000    60EF2F2FC1      0  0  0  1    0      10     11     01
104     00000000      10030701    0000000000    41DE5E5F82      0  0  0  1    1      01     10     11
105     00000000      20060E02    0000000000    03BCBCBF04      0  0  0  1    0      01     01     10
106     00000000      400C1C05    0000000000    0779797E09      0  0  0  0    1      10     01     01
107     00000000      0018380A    0000000000    0EF2F2FC12      0  0  0  1    1      00     10     01
108     00000000      00307015    0000000000    1DE5E5F825      0  0  0  1    1      01     00     10
109     00000000      0060E02A    0000000000    3BCBCBF04B      0  0  0  1    0      00     01     00
110     00000000      00C1C055    0000000000    779797E097      0  1  0  1    0      10     00     01
111     00000000      018380AA    0000000000    6F2F2FC12F      0  1  0  0    1      11     10     00
112     00000000      03070154    0000000000    5E5E5F825E      0  0  0  0    1      11     11     10
113     00000000      060E02A8    0000000000    3CBCBF04BC      0  0  0  1    0      11     11     11
114     00000000      0C1C0550    0000000000    79797E0979      0  0  0  0    1      00     11     11
115     00000000      18380AA0    0000000000    72F2FC12F2      0  0  0  1    1      10     00     11
116     00000000      30701541    0000000000    65E5F825E5      0  0  0  1    1      11     10     00
117     00000000      60E02A82    0000000000    4BCBF04BCB      0  1  0  1    1      00     11     10
118     00000000      41C05505    0000000000    1797E09796      0  1  0  1    0      11     00     11
119     00000000      0380AA0A    0000000000    2F2FC12F2C      0  1  0  0    0      01     11     00
120     00000000      07015415    0000000000    5E5F825E59      0  0  0  0    1      11     01     11
121     00000000      0E02A82A    0000000000    3CBF04BCB2      0  0  0  1    0      10     11     01
122     00000000      1C055054    0000000000    797E097964      0  0  0  0    0      01     10     11
123     00000000      380AA0A8    0000000000    72FC12F2C9      0  0  0  1    0      01     01     10
124     00000000      70154151    0000000000    65F825E593      0  0  0  1    0      11     01     01
125     00000000      602A82A3    0000000000    4BF04BCB26      0  0  0  1    0      10     11     01
126     00000000      40550546    0000000000    17E097964C      0  0  0  1    1      01     10     11
127     00000000      00AA0A8D    0000000000    2FC12F2C99      0  1  0  1    1      01     01     10
128     00000000      0154151A    0000000000    5F825E5932      0  0  0  1    0      11     01     01
129     00000000      02A82A34    0000000000    3F04BCB264      0  1  0  0    0      10     11     01
130     00000000      05505468    0000000000    7E097964C9      0  0  0  0    0      01     10     11
131     00000000      0AA0A8D0    0000000000    7C12F2C992      0  1  0  0    0      01     01     10
132     00000000      154151A1    0000000000    7825E59324      0  0  0  0    1      10     01     01
133     00000000      2A82A342    0000000000    704BCB2648      0  1  0  0    1      00     10     01
134     00000000      55054684    0000000000    6097964C91      0  0  0  1    1      01     00     10
135     00000000      2A0A8D09    0000000000    412F2C9923      0  0  0  0    1      01     01     00
136     00000000      54151A12    0000000000    025E593246      0  0  0  0    1      10     01     01
137     00000000      282A3424    0000000000    04BCB2648D      0  0  0  1    1      00     10     01
138     00000000      50546848    0000000000    097964C91A      0  0  0  0    0      01     00     10
139     00000000      20A8D090    0000000000    12F2C99235      0  1  0  1    1      00     01     00
140     00000000      4151A120    0000000000    25E593246A      0  0  0  1    1      11     00     01
141     00000000      02A34240    0000000000    4BCB2648D5      0  1  0  1    1      01     11     00
142     00000000      05468481    0000000000    17964C91AB      0  0  0  1    0      10     01     11
143     00000000      0A8D0903    0000000000    2F2C992357      0  1  0  0    1      00     10     01
144     00000000      151A1206    0000000000    5E593246AE      0  0  0  0    0      01     00     10
145     00000000      2A34240C    0000000000    3CB2648D5C      0  0  0  1    0      00     01     00
146     00000000      54684818    0000000000    7964C91AB8      0  0  0  0    0      11     00     01
147     00000000      28D09030    0000000000    72C9923571      0  1  0  1    1      01     11     00
148     00000000      51A12060    0000000000    6593246AE2      0  1  0  1    1      10     01     11
149     00000000      234240C0    0000000000    4B2648D5C5      0  0  0  0    0      00     10     01
150     00000000      46848180    0000000000    164C91AB8A      0  1  0  0    1      01     00     10
151     00000000      0D090301    0000000000    2C99235714      0  0  0  1    0      00     01     00
152     00000000      1A120602    0000000000    593246AE28      0  0  0  0    0      11     00     01
153     00000000      34240C04    0000000000    32648D5C51      0  0  0  0    1      10     11     00
154     00000000      68481809    0000000000    64C91AB8A2      0  0  0  1    1      01     10     11
155     00000000      50903012    0000000000    4992357144      0  1  0  1    1      01     01     10
156     00000000      21206024    0000000000    13246AE288      0  0  0  0    1      10     01     01
157     00000000      4240C048    0000000000    2648D5C511      0  0  0  0    0      00     10     01
158     00000000      04818090    0000000000    4C91AB8A23      0  1  0  1    0      00     00     10
159     00000000      09030120    0000000000    1923571446      0  0  0  0    0      00     00     00
160     00000000      12060240    0000000000    3246AE288D      0  0  0  0    0      00     00     00
161     00000000      240C0480    0000000000    648D5C511B      0  0  0  1    1      00     00     00
162     00000000      48180900    0000000000    491AB8A237      0  0  0  0    0      00     00     00
163     00000000      10301200    0000000000    123571446F      0  0  0  0    0      00     00     00
164     00000000      20602400    0000000000    246AE288DF      0  0  0  0    0      00     00     00
165     00000000      40C04800    0000000000    48D5C511BE      0  1  0  1    0      01     00     00
166     00000000      01809001    0000000000    11AB8A237D      0  1  0  1    1      00     01     00
167     00000000      03012002    0000000000    23571446FA      0  0  0  0    0      11     00     01
168     00000000      06024004    0000000000    46AE288DF5      0  0  0  1    0      01     11     00
169     00000000      0C048008    0000000000    0D5C511BEA      0  0  0  0    1      11     01     11
170     00000000      18090011    0000000000    1AB8A237D5      0  0  0  1    0      10     11     01
171     00000000      30120022    0000000000    3571446FAA      0  0  0  0    0      01     10     11
172     00000000      60240044    0000000000    6AE288DF55      0  0  0  1    0      01     01     10
173     00000000      40480089    0000000000    55C511BEAA      0  0  0  1    0      11     01     01
174     00000000      00900113    0000000000    2B8A237D54      0  1  0  1    1      10     11     01
175     00000000      01200227    0000000000    571446FAA8      0  0  0  0    0      01     10     11
176     00000000      0240044E    0000000000    2E288DF550      0  0  0  0    1      00     01     10
177     00000000      0480089C    0000000000    5C511BEAA0      0  1  0  0    1      11     00     01
178     00000000      09001138    0000000000    38A237D540      0  0  0  1    0      01     11     00
179     00000000      12002270    0000000000    71446FAA81      0  0  0  0    1      11     01     11
180     00000000      240044E0    0000000000    6288DF5503      0  0  0  1    0      10     11     01
181     00000000      480089C0    0000000000    4511BEAA06      0  0  0  0    0      01     10     11
182     00000000      10011381    0000000000    0A237D540D      0  0  0  0    1      00     01     10
183     00000000      20022702    0000000000    1446FAA81A      0  0  0  0    0      11     00     01
184     00000000      40044E04    0000000000    288DF55035      0  0  0  1    0      01     11     00
185     00000000      00089C08    0000000000    511BEAA06A      0  0  0  0    1      11     01     11
186     00000000      00113810    0000000000    2237D540D5      0  0  0  0    1      01     11     01
187     00000000      00227021    0000000000    446FAA81AA      0  0  0  0    1      11     01     11
188     00000000      0044E042    0000000000    08DF550355      0  0  0  1    0      10     11     01
189     00000000      0089C085    0000000000    11BEAA06AA      0  1  0  1    0      10     10     11
190     00000000      0113810A    0000000000    237D540D54      0  0  0  0    0      10     10     10
191     00000000      02270215    0000000000    46FAA81AA9      0  0  0  1    1      10     10     10
192     00000000      044E042A    0000000000    0DF5503553      0  0  0  1    1      10     10     10
193     00000000      089C0854    0000000000    1BEAA06AA7      0  1  0  1    0      01     10     10
194     00000000      113810A8    0000000000    37D540D54E      0  0  0  1    0      01     01     10
195     00000000      22702150    0000000000    6FAA81AA9D      0  0  0  1    0      11     01     01
196     00000000      44E042A0    0000000000    5F5503553A      0  1  0  0    0      10     11     01
197     00000000      09C08540    0000000000    3EAA06AA75      0  1  0  1    0      10     10     11
198     00000000      13810A80    0000000000    7D540D54EA      0  1  0  0    1      10     10     10
199     00000000      27021500    0000000000    7AA81AA9D5      0  0  0  1    1      10     10     10
200     00000000      4E042A00    0000000000    75503553AB      0  0  0  0    0      10     10     10
201     00000000      1C085400    0000000000    6AA06AA756      0  0  0  1    1      10     10     10
202     00000000      3810A800    0000000000    5540D54EAC      0  0  0  0    0      10     10     10
203     00000000      70215000    0000000000    2A81AA9D58      0  0  0  1    1      10     10     10
204     00000000      6042A001    0000000000    5503553AB0      0  0  0  0    0      10     10     10
205     00000000      40854002    0000000000    2A06AA7561      0  1  0  0    1      10     10     10
206     00000000      010A8004    0000000000    540D54EAC3      0  0  0  0    0      10     10     10
207     00000000      02150009    0000000000    281AA9D586      0  0  0  0    0      10     10     10
208     00000000      042A0012    0000000000    503553AB0C      0  0  0  0    0      10     10     10
209     00000000      08540024    0000000000    206AA75618      0  0  0  0    0      10     10     10
210     00000000      10A80048    0000000000    40D54EAC30      0  1  0  1    0      01     10     10
211     00000000      21500091    0000000000    01AA9D5861      0  0  0  1    0      01     01     10
212     00000000      42A00122    0000000000    03553AB0C3      0  1  0  0    0      11     01     01
213     00000000      05400244    0000000000    06AA756186      0  0  0  1    0      10     11     01
214     00000000      0A800488    0000000000    0D54EAC30D      0  1  0  0    1      01     10     11
215     00000000      15000911    0000000000    1AA9D5861A      0  0  0  1    0      01     01     10
216     00000000      2A001223    0000000000    3553AB0C35      0  0  0  0    1      10     01     01
217     00000000      54002446    0000000000    6AA756186A      0  0  0  1    1      00     10     01
218     00000000      2800488D    0000000000    554EAC30D5      0  0  0  0    0      01     00     10
219     00000000      5000911B    0000000000    2A9D5861AA      0  0  0  1    0      00     01     00
220     00000000      20012236    0000000000    553AB0C355      0  0  0  0    0      11     00     01
221     00000000      4002446C    0000000000    2A756186AA      0  0  0  0    1      10     11     00
222     00000000      000488D9    0000000000    54EAC30D54      0  0  0  1    1      01     10     11
223     00000000      000911B2    0000000000    29D5861AA8      0  0  0  1    0      01     01     10
224     00000000      00122364    0000000000    53AB0C3550      0  0  0  1    0      11     01     01
225     00000000      002446C8    0000000000    2756186AA0      0  0  0  0    1      01     11     01
226     00000000      00488D90    0000000000    4EAC30D540      0  0  0  1    0      10     01     11
227     00000000      00911B20    0000000000    1D5861AA81      0  1  0  0    1      00     10     01
228     00000000      01223640    0000000000    3AB0C35502      0  0  0  1    1      01     00     10
229     00000000      02446C80    0000000000    756186AA05      0  0  0  0    1      01     01     00
230     00000000      0488D901    0000000000    6AC30D540B      0  1  0  1    1      11     01     01
231     00000000      0911B203    0000000000    55861AA817      0  0  0  1    0      10     11     01
232     00000000      12236407    0000000000    2B0C35502F      0  0  0  0    0      01     10     11
233     00000000      2446C80E    0000000000    56186AA05F      0  0  0  0    1      00     01     10
234     00000000      488D901C    0000000000    2C30D540BF      0  1  0  0    1      11     00     01
235     00000000      111B2039    0000000000    5861AA817E      0  0  0  0    1      10     11     00
236     00000000      22364072    0000000000    30C35502FD      0  0  0  1    1      01     10     11
237     00000000      446C80E4    0000000000    6186AA05FB      0  0  0  1    0      01     01     10
238     00000000      08D901C8    0000000000    430D540BF6      0  1  0  0    0      11     01     01
239     00000000      11B20391    0000000000    061AA817EC      0  1  0  0    0      10     11     01
output_Z: 1E7A63402AC18E4B42421E58BBF0C13D
LFSR1  <= 004B583D
LFSR2  <= 208E1EC1
LFSR3  <= 0063C142F0
LFSR4  <= 0F7A2A42BB
C[t+1] <= 10
240     004B583D      208E1EC1    0063C142F0    0F7A2A42BB      0  1  0  0    0      10     11     01
241     0096B07A      411C3D82    00C78285E1    1EF4548577      1  0  1  1    1      10     10     11
242     012D60F4      02387B04    018F050BC3    3DE8A90AEF      0  0  1  1    0      01     10     10
243     005AC1E9      0470F609    011E0A1786    7BD15215DF      0  0  0  1    0      01     01     10
244     00B583D2      08E1EC13    003C142F0C    77A2A42BBF      1  1  0  1    0      00     01     01
245     016B07A5      11C3D827    0078285E18    6F4548577E      0  1  0  0    1      11     00     01
246     00D60F4B      2387B04F    00F050BC30    5E8A90AEFD      1  1  1  1    1      00     11     00
247     01AC1E97      470F609E    01E0A17860    3D15215DFA      1  0  1  0    0      11     00     11
248     01583D2E      0E1EC13D    01C142F0C0    7A2A42BBF4      0  0  1  0    0      01     11     00
249     00B07A5D      1C3D827B    018285E181    74548577E9      1  0  1  0    1      10     01     11
250     0160F4BB      387B04F7    01050BC302    68A90AEFD2      0  0  0  1    1      00     10     01
251     00C1E976      70F609EE    000A178605    515215DFA5      1  1  0  0    0      00     00     10
252     0183D2ED      61EC13DD    00142F0C0B    22A42BBF4B      1  1  0  1    1      01     00     00
253     0107A5DA      43D827BA    00285E1817    4548577E97      0  1  0  0    0      00     01     00
254     000F4BB4      07B04F74    0050BC302F    0A90AEFD2E      0  1  0  1    0      10     00     01
255     001E9769      0F609EE8    00A178605E    15215DFA5C      0  0  1  0    1      11     10     00
256     003D2ED3      1EC13DD0    0142F0C0BD    2A42BBF4B9      0  1  0  0    0      00     11     10
257     007A5DA7      3D827BA0    0085E1817B    548577E972      0  1  1  1    1      11     00     11
258     00F4BB4F      7B04F740    010BC302F6    290AEFD2E5      1  0  0  0    0      01     11     00
259     01E9769F      7609EE80    00178605ED    5215DFA5CA      1  0  0  0    0      10     01     11
260     01D2ED3F      6C13DD01    002F0C0BDA    242BBF4B94      1  0  0  0    1      00     10     01
261     01A5DA7E      5827BA03    005E1817B4    48577E9729      1  0  0  0    1      01     00     10
262     014BB4FC      304F7407    00BC302F69    10AEFD2E53      0  0  1  1    1      00     01     00
263     009769F9      609EE80E    0178605ED2    215DFA5CA7      1  1  0  0    0      10     00     01
264     012ED3F2      413DD01C    00F0C0BDA4    42BBF4B94F      0  0  1  1    0      00     10     00
265     005DA7E5      027BA038    01E1817B49    0577E9729F      0  0  1  0    1      01     00     10
266     00BB4FCA      04F74071    01C302F693    0AEFD2E53F      1  1  1  1    1      11     01     00
267     01769F95      09EE80E3    018605ED27    15DFA5CA7F      0  1  1  1    0      11     11     01
268     00ED3F2B      13DD01C6    010C0BDA4F    2BBF4B94FE      1  1  0  1    0      10     11     11
269     01DA7E56      27BA038D    001817B49F    577E9729FD      1  1  0  0    0      10     10     11
270     01B4FCAD      4F74071B    00302F693E    2EFD2E53FB      1  0  0  1    0      01     10     10
271     0169F95B      1EE80E37    00605ED27D    5DFA5CA7F7      0  1  0  1    1      01     01     10
272     00D3F2B7      3DD01C6E    00C0BDA4FB    3BF4B94FEF      1  1  1  1    1      00     01     01
273     01A7E56F      7BA038DC    01817B49F6    77E9729FDE      1  1  1  1    0      01     00     01
274     014FCADF      774071B9    0102F693ED    6FD2E53FBD      0  0  0  1    0      00     01     00
275     009F95BE      6E80E373    0005ED27DB    5FA5CA7F7B      1  1  0  1    1      10     00     01
276     013F2B7C      5D01C6E7    000BDA4FB6    3F4B94FEF7      0  0  0  0    0      11     10     00
277     007E56F9      3A038DCE    0017B49F6C    7E9729FDEE      0  0  0  1    0      00     11     10
278     00FCADF2      74071B9C    002F693ED8    7D2E53FBDD      1  0  0  0    1      10     00     11
279     01F95BE5      680E3738    005ED27DB0    7A5CA7F7BA      1  0  0  0    1      11     10     00
280     01F2B7CA      501C6E71    00BDA4FB60    74B94FEF74      1  0  1  1    0      01     11     10
281     01E56F94      2038DCE2    017B49F6C0    69729FDEE8      1  0  0  0    0      10     01     11
282     01CADF29      4071B9C4    00F693ED80    52E53FBDD1      1  0  1  1    1      11     10     01
283     0195BE53      00E37389    01ED27DB01    25CA7F7BA3      1  1  1  1    1      01     11     10
284     012B7CA6      01C6E713    01DA4FB602    4B94FEF747      0  1  1  1    0      01     01     11
285     0056F94C      038DCE26    01B49F6C04    1729FDEE8E      0  1  1  0    1      11     01     01
286     00ADF299      071B9C4D    01693ED808    2E53FBDD1C      1  0  0  0    0      10     11     01
287     015BE532      0E37389A    00D27DB011    5CA7F7BA38      0  0  1  1    0      10     10     11
288     00B7CA64      1C6E7135    01A4FB6022    394FEF7471      1  0  1  0    0      01     10     10
289     016F94C9      38DCE26A    0149F6C044    729FDEE8E2      0  1  0  1    1      01     01     10
290     00DF2993      71B9C4D4    0093ED8089    653FBDD1C4      1  1  1  0    0      00     01     01
291     01BE5327      637389A9    0127DB0112    4A7F7BA388      1  0  0  0    1      11     00     01
292     017CA64E      46E71353    004FB60224    14FEF74710      0  1  0  1    1      01     11     00
293     00F94C9C      0DCE26A6    009F6C0448    29FDEE8E21      1  1  1  1    1      01     01     11
294     01F29939      1B9C4D4D    013ED80890    53FBDD1C42      1  1  0  1    0      00     01     01
295     01E53272      37389A9A    007DB01121    27F7BA3884      1  0  0  1    0      10     00     01
296     01CA64E5      6E713534    00FB602242    4FEF747108      1  0  1  1    1      00     10     00
297     0194C9CB      5CE26A69    01F6C04485    1FDEE8E210      1  1  1  1    0      11     00     10
298     01299397      39C4D4D3    01ED80890A    3FBDD1C420      0  1  1  1    0      00     11     00
299     0053272F      7389A9A6    01DB011214    7F7BA38840      0  1  1  0    0      11     00     11
300     00A64E5E      6713534C    01B6022428    7EF7471081      1  0  1  1    0      00     11     00
301     014C9CBD      4E26A699    016C044850    7DEE8E2102      0  0  0  1    1      10     00     11
302     0099397A      1C4D4D32    00D80890A0    7BDD1C4205      1  0  1  1    1      00     10     00
303     013272F4      389A9A65    01B0112141    77BA38840B      0  1  1  1    1      00     00     10
304     0064E5E8      713534CB    0160224283    6F74710817      0  0  0  0    0      00     00     00
305     00C9CBD1      626A6997    00C0448507    5EE8E2102E      1  0  1  1    1      01     00     00
306     019397A3      44D4D32E    0180890A0E    3DD1C4205C      1  1  1  1    1      11     01     00
307     01272F46      09A9A65D    010112141D    7BA38840B8      0  1  0  1    1      10     11     01
308     004E5E8C      13534CBA    000224283A    7747108171      0  0  0  0    0      01     10     11
309     009CBD19      26A69975    0004485075    6E8E2102E3      1  1  0  1    0      10     01     10
310     01397A32      4D4D32EB    000890A0EA    5D1C4205C7      0  0  0  0    0      00     10     01
311     0072F465      1A9A65D7    00112141D5    3A38840B8F      0  1  0  0    1      01     00     10
312     00E5E8CA      3534CBAF    00224283AA    747108171F      1  0  0  0    0      00     01     00
313     01CBD194      6A69975E    0044850755    68E2102E3E      1  0  0  1    0      10     00     01
314     0197A329      54D32EBC    00890A0EAB    51C4205C7D      1  1  1  1    0      01     10     00
315     012F4653      29A65D79    0112141D56    238840B8FA      0  1  0  1    1      01     01     10
316     005E8CA6      534CBAF2    0024283AAD    47108171F4      0  0  0  0    1      10     01     01
317     00BD194D      269975E5    004850755B    0E2102E3E9      1  1  0  0    0      11     10     01
318     017A329A      4D32EBCB    0090A0EAB6    1C4205C7D2      0  0  1  0    0      00     11     10
319     00F46535      1A65D797    012141D56D    38840B8FA5      1  0  0  1    0      11     00     11
320     01E8CA6A      34CBAF2F    004283AADA    7108171F4B      1  1  0  0    1      01     11     00
321     01D194D5      69975E5F    00850755B4    62102E3E97      1  1  1  0    0      01     01     11
322     01A329AA      532EBCBF    010A0EAB68    44205C7D2F      1  0  0  0    0      11     01     01
323     01465355      265D797F    00141D56D1    0840B8FA5E      0  0  0  0    1      01     11     01
324     008CA6AB      4CBAF2FF    00283AADA2    108171F4BC      1  1  0  1    0      01     01     11
325     01194D56      1975E5FF    0050755B45    2102E3E979      0  0  0  0    1      10     01     01
326     00329AAD      32EBCBFF    00A0EAB68A    4205C7D2F3      0  1  1  0    0      11     10     01
327     0065355A      65D797FF    0141D56D14    040B8FA5E7      0  1  0  0    0      00     11     10
328     00CA6AB4      4BAF2FFF    0083AADA28    08171F4BCF      1  1  1  0    1      11     00     11
329     0194D569      175E5FFF    010755B450    102E3E979E      1  0  0  0    0      01     11     00
330     0129AAD3      2EBCBFFF    000EAB68A1    205C7D2F3C      0  1  0  0    0      10     01     11
331     005355A6      5D797FFF    001D56D142    40B8FA5E78      0  0  0  1    1      00     10     01
332     00A6AB4D      3AF2FFFE    003AADA285    0171F4BCF1      1  1  0  0    0      00     00     10
333     014D569B      75E5FFFD    00755B450A    02E3E979E2      0  1  0  1    0      01     00     00
334     009AAD37      6BCBFFFA    00EAB68A15    05C7D2F3C4      1  1  1  1    1      11     01     00
335     01355A6E      5797FFF4    01D56D142A    0B8FA5E788      0  1  1  1    0      11     11     01
336     006AB4DC      2F2FFFE8    01AADA2854    171F4BCF11      0  0  1  0    0      11     11     11
337     00D569B8      5E5FFFD0    0155B450A9    2E3E979E23      1  0  0  0    0      11     11     11
338     01AAD370      3CBFFFA1    00AB68A153    5C7D2F3C46      1  1  1  0    0      10     11     11
339     0155A6E0      797FFF43    0156D142A7    38FA5E788D      0  0  0  1    1      01     10     11
340     00AB4DC0      72FFFE87    00ADA2854E    71F4BCF11B      1  1  1  1    1      10     01     10
341     01569B81      65FFFD0E    015B450A9D    63E979E236      0  1  0  1    0      11     10     01
342     00AD3703      4BFFFA1C    00B68A153B    47D2F3C46C      1  1  1  1    1      01     11     10
343     015A6E07      17FFF438    016D142A76    0FA5E788D8      0  1  0  1    1      10     01     11
344     00B4DC0F      2FFFE870    00DA2854EC    1F4BCF11B0      1  1  1  0    1      11     10     01
345     0169B81F      5FFFD0E1    01B450A9D8    3E979E2360      0  1  1  1    0      01     11     10
346     00D3703F      3FFFA1C3    0168A153B0    7D2F3C46C1      1  1  0  0    1      10     01     11
347     01A6E07E      7FFF4386    00D142A761    7A5E788D83      1  1  1  0    1      11     10     01
348     014DC0FD      7FFE870C    01A2854EC2    74BCF11B07      0  1  1  1    0      01     11     10
349     009B81FB      7FFD0E19    01450A9D84    6979E2360E      1  1  0  0    1      10     01     11
350     013703F6      7FFA1C33    008A153B09    52F3C46C1C      0  1  1  1    1      11     10     01
351     006E07EC      7FF43867    01142A7612    25E788D838      0  1  0  1    1      00     11     10
352     00DC0FD8      7FE870CF    002854EC25    4BCF11B071      1  1  0  1    1      11     00     11
353     01B81FB1      7FD0E19E    0050A9D84B    179E2360E3      1  1  0  1    0      00     11     00
354     01703F62      7FA1C33D    00A153B096    2F3C46C1C7      0  1  1  0    0      11     00     11
355     00E07EC4      7F43867B    0142A7612C    5E788D838E      1  0  0  0    0      01     11     00
356     01C0FD88      7E870CF6    00854EC259    3CF11B071C      1  1  1  1    1      01     01     11
357     0181FB11      7D0E19ED    010A9D84B3    79E2360E38      1  0  0  1    1      11     01     01
358     0103F622      7A1C33DA    00153B0967    73C46C1C71      0  0  0  1    0      10     11     01
359     0007EC45      743867B5    002A7612CE    6788D838E3      0  0  0  1    1      01     10     11
360     000FD88B      6870CF6B    0054EC259C    4F11B071C6      0  0  0  0    1      00     01     10
361     001FB117      50E19ED7    00A9D84B38    1E2360E38C      0  1  1  0    0      10     00     01
362     003F622F      21C33DAE    0153B09671    3C46C1C718      0  1  0  0    1      11     10     00
363     007EC45F      43867B5C    00A7612CE2    788D838E30      0  1  1  1    0      01     11     10
364     00FD88BF      070CF6B9    014EC259C4    711B071C61      1  0  0  0    0      10     01     11

SAFER+算法、Ar和A’r

原理概述

在Core Spec v5.4 P978中有定义Classic所使用的算法SAFER+,**SAFER+**是标准算法,网上有不少资料,但是感觉用的人不多,没什么现场的库可以用,并且蓝牙还定义了一个特殊实现A’r。所以这里就按照规范一步步实现吧。

Spec将算法写得比较详细,再参考一些文章,基本就能理解**SAFER+**如何实现的了。当然一些细节得看一些资料才清楚,附上一些好的参考链接。Nomination of SAFER+ as Candidate Algorithm for AES (princeton.edu),1160386 (dergipark.org.tr),应用密码学Safer K系列加密学习笔记_safer k-64的特点-CSDN博客。

Ar与**SAFER+**相同。它由一套8层组成(每层被称为一轮)和用于生成子密钥的并行机制,子密钥Kp[j],p=1,2,...,17是在每一轮中使用的轮密钥。

这函数将从128位随机输入字符串和128位密钥中产生128位结果。除了Ar功能之外,还有一个稍加修改的版本,称为A’r`,其中第一轮的输入被添加到第三轮的输入。这是这样做是为了使修改后的版本不可逆(因为加入了明文信息,解密的时候没有明文信息,就无法解出原本的明文)。

轮计算

每一轮中的计算都是用轮密钥加密、替换、用下一轮密钥加密以及最后的**Pseudo Hadamard Transform (PHT)**的组合。最后一轮再和K17进行运算。

操作如下图所示。

image-20240523103223541

替换框“e”和“l”

在上图中,显示了两个方框,标记为“e”和“l”。这些框实现了与**SAFER+**中使用的相同的替换;与SAFER+算法一样,它们的作用是引入非线性。即它们实现了如下变化。

image-20240523192340106

因为exp和log运算耗时较多,目前代码是通过查表来实现的。

注意:为什么选45,个人觉得其刚好能讲0-255非线性映射到0-255上,而且能一一对应。

a0_exp45_tab_const = [
  1,  45, 226, 147, 190,  69,  21, 174, 120,   3, 135, 164, 184,  56, 207,  63, 
  8, 103,   9, 148, 235,  38, 168, 107, 189,  24,  52,  27, 187, 191, 114, 247, 
 64,  53,  72, 156,  81,  47,  59,  85, 227, 192, 159, 216, 211, 243, 141, 177, 
255, 167,  62, 220, 134, 119, 215, 166,  17, 251, 244, 186, 146, 145, 100, 131, 
241,  51, 239, 218,  44, 181, 178,  43, 136, 209, 153, 203, 140, 132,  29,  20, 
129, 151, 113, 202,  95, 163, 139,  87,  60, 130, 196,  82,  92,  28, 232, 160, 
  4, 180, 133,  74, 246,  19,  84, 182, 223,  12,  26, 142, 222, 224,  57, 252, 
 32, 155,  36,  78, 169, 152, 158, 171, 242,  96, 208, 108, 234, 250, 199, 217, 
  0, 212,  31, 110,  67, 188, 236,  83, 137, 254, 122,  93,  73, 201,  50, 194, 
249, 154, 248, 109,  22, 219,  89, 150,  68, 233, 205, 230,  70,  66, 143,  10, 
193, 204, 185, 101, 176, 210, 198, 172,  30,  65,  98,  41,  46,  14, 116,  80, 
  2,  90, 195,  37, 123, 138,  42,  91, 240,   6,  13,  71, 111, 112, 157, 126, 
 16, 206,  18,  39, 213,  76,  79, 214, 121,  48, 104,  54, 117, 125, 228, 237, 
128, 106, 144,  55, 162,  94, 118, 170, 197, 127,  61, 175, 165, 229,  25,  97, 
253,  77, 124, 183,  11, 238, 173,  75,  34, 245, 231, 115,  35,  33, 200,   5,
225, 102, 221, 179,  88, 105,  99,  86,  15, 161,  49, 149,  23,   7,  58,  40,
]



a0_log45_tab_const = [
128,   0, 176,   9,  96, 239, 185, 253,  16,  18, 159, 228, 105, 186, 173, 248,
192,  56, 194, 101,  79,   6, 148, 252,  25, 222, 106,  27,  93,  78, 168, 130,
112, 237, 232, 236, 114, 179,  21, 195, 255, 171, 182,  71,  68,   1, 172,  37,
201, 250, 142,  65,  26,  33, 203, 211,  13, 110, 254,  38,  88, 218,  50,  15,
 32, 169, 157, 132, 152,   5, 156, 187,  34, 140,  99, 231, 197, 225, 115, 198,
175,  36,  91, 135, 102,  39, 247,  87, 244, 150, 177, 183,  92, 139, 213,  84,
121, 223, 170, 246,  62, 163, 241,  17, 202, 245, 209,  23, 123, 147, 131, 188,
189,  82,  30, 235, 174, 204, 214,  53,   8, 200, 138, 180, 226, 205, 191, 217,
208,  80,  89,  63,  77,  98,  52,  10,  72, 136, 181,  86,  76,  46, 107, 158,
210,  61,  60,   3,  19, 251, 151,  81, 117,  74, 145, 113,  35, 190, 118,  42,
 95, 249, 212,  85,  11, 220,  55,  49,  22, 116, 215, 119, 167, 230,   7, 219,
164,  47,  70, 243,  97,  69, 103, 227,  12, 162,  59,  28, 133,  24,   4,  29,
 41, 160, 143, 178,  90, 216, 166, 126, 238, 141,  83,  75, 161, 154, 193,  14,
122,  73, 165,  44, 129, 196, 199,  54,  43, 127,  67, 149,  51, 242, 108, 104,
109, 240,   2,  40, 206, 221, 155, 234,  94, 153, 124,  20, 134, 207, 229,  66,
184,  64, 120,  45,  58, 233, 100,  31, 146, 144, 125,  57, 111, 224, 137,  48, 
]

密钥扩展算法

原本输入的秘钥只有16字节,但是实际使用需要17组16字节的秘钥,这里需要通过秘钥扩展算法生成所需的秘钥序列。

其算法为:

image-20240523192525777

步骤就是下图操作。

image-20240523192544437

按照图片要求,进行编码,其实就是一个for循环搞定。

key_16 = 0
for i in range(16):
    key_16 ^= key[i]

round_tmp_keys.append(key_16)

round_keys = [] # 17 round keys

for key_index in range(1, 18):
    if key_index == 1:
        round_keys.append(bytes(round_tmp_keys[0:16]))
    else:
        temp = []
        for i in range(16):
            bias_vector = a0_exp45_tab_const[a0_exp45_tab_const[(17 * key_index + i + 1) & 0xff]]
            temp_index = (key_index - 1) + i
            if temp_index >= 17:
                temp_index -= 17
            # print("temp_index: %d" % (temp_index))
            temp.append((round_tmp_keys[temp_index] + bias_vector) & 0xFF)
        round_keys.append(bytes(temp))

    if debug: print("K%d: %s" % (key_index, print_hex_little(round_keys[key_index-1])))

    # key schedule
    for i in range(17):
        round_tmp_keys[i] = (((round_tmp_keys[i] << 3) & 0xFF) | ((round_tmp_keys[i] >> 5) & 0xFF))

算法实现

按照原理进行代码实现,设计并实现encrypt_Ar()函数,其中输入is_bt_spec为指定是否执行A'r。输入秘钥key和明文plain_text,输出128bit的cipher_text值。

安装**SAFER+**的要求,进行逆运算,就是解密,实现了decrypt_Ar()函数。

Spec并没有给出具体的验证Sample,这里直接拿E1的测试数据,先进行加解密运算,之后在E0运算中对算法行为进行二次验证。这里提供的验证函数为saferplus_Ar_test函数。


a0_exp45_tab_const = [
  1,  45, 226, 147, 190,  69,  21, 174, 120,   3, 135, 164, 184,  56, 207,  63, 
  8, 103,   9, 148, 235,  38, 168, 107, 189,  24,  52,  27, 187, 191, 114, 247, 
 64,  53,  72, 156,  81,  47,  59,  85, 227, 192, 159, 216, 211, 243, 141, 177, 
255, 167,  62, 220, 134, 119, 215, 166,  17, 251, 244, 186, 146, 145, 100, 131, 
241,  51, 239, 218,  44, 181, 178,  43, 136, 209, 153, 203, 140, 132,  29,  20, 
129, 151, 113, 202,  95, 163, 139,  87,  60, 130, 196,  82,  92,  28, 232, 160, 
  4, 180, 133,  74, 246,  19,  84, 182, 223,  12,  26, 142, 222, 224,  57, 252, 
 32, 155,  36,  78, 169, 152, 158, 171, 242,  96, 208, 108, 234, 250, 199, 217, 
  0, 212,  31, 110,  67, 188, 236,  83, 137, 254, 122,  93,  73, 201,  50, 194, 
249, 154, 248, 109,  22, 219,  89, 150,  68, 233, 205, 230,  70,  66, 143,  10, 
193, 204, 185, 101, 176, 210, 198, 172,  30,  65,  98,  41,  46,  14, 116,  80, 
  2,  90, 195,  37, 123, 138,  42,  91, 240,   6,  13,  71, 111, 112, 157, 126, 
 16, 206,  18,  39, 213,  76,  79, 214, 121,  48, 104,  54, 117, 125, 228, 237, 
128, 106, 144,  55, 162,  94, 118, 170, 197, 127,  61, 175, 165, 229,  25,  97, 
253,  77, 124, 183,  11, 238, 173,  75,  34, 245, 231, 115,  35,  33, 200,   5,
225, 102, 221, 179,  88, 105,  99,  86,  15, 161,  49, 149,  23,   7,  58,  40,
]



a0_log45_tab_const = [
128,   0, 176,   9,  96, 239, 185, 253,  16,  18, 159, 228, 105, 186, 173, 248,
192,  56, 194, 101,  79,   6, 148, 252,  25, 222, 106,  27,  93,  78, 168, 130,
112, 237, 232, 236, 114, 179,  21, 195, 255, 171, 182,  71,  68,   1, 172,  37,
201, 250, 142,  65,  26,  33, 203, 211,  13, 110, 254,  38,  88, 218,  50,  15,
 32, 169, 157, 132, 152,   5, 156, 187,  34, 140,  99, 231, 197, 225, 115, 198,
175,  36,  91, 135, 102,  39, 247,  87, 244, 150, 177, 183,  92, 139, 213,  84,
121, 223, 170, 246,  62, 163, 241,  17, 202, 245, 209,  23, 123, 147, 131, 188,
189,  82,  30, 235, 174, 204, 214,  53,   8, 200, 138, 180, 226, 205, 191, 217,
208,  80,  89,  63,  77,  98,  52,  10,  72, 136, 181,  86,  76,  46, 107, 158,
210,  61,  60,   3,  19, 251, 151,  81, 117,  74, 145, 113,  35, 190, 118,  42,
 95, 249, 212,  85,  11, 220,  55,  49,  22, 116, 215, 119, 167, 230,   7, 219,
164,  47,  70, 243,  97,  69, 103, 227,  12, 162,  59,  28, 133,  24,   4,  29,
 41, 160, 143, 178,  90, 216, 166, 126, 238, 141,  83,  75, 161, 154, 193,  14,
122,  73, 165,  44, 129, 196, 199,  54,  43, 127,  67, 149,  51, 242, 108, 104,
109, 240,   2,  40, 206, 221, 155, 234,  94, 153, 124,  20, 134, 207, 229,  66,
184,  64, 120,  45,  58, 233, 100,  31, 146, 144, 125,  57, 111, 224, 137,  48, 
]

def saffer_bytewise_xor(x, y):
    return (x ^ y) & 0xff

def saffer_bytewise_add(x, y):
    return (x + y) & 0xff

def saffer_bytewise_xor_decrypt(x, y):
    return saffer_bytewise_xor(x, y)

def saffer_bytewise_add_decrypt(x, y):
    return (x - y) & 0xff

def saffer_bytewise_xor_arr(x, y):
    z = []
    for i in range(len(x)):
        z.append(saffer_bytewise_xor(x[i], y[i]))
    return bytes(z)

def saffer_bytewise_add_arr(x, y):
    z = []
    for i in range(len(x)):
        z.append(saffer_bytewise_add(x[i], y[i]))
    return bytes(z)

def saffer_pht(x, y):
    return (2 * x + y) & 0xFF, (x + y) & 0xFF

# SAFFER+
def encrypt_Ar(key, plain_text, is_bt_spec=False, debug=True):
    if debug: print("key: %s" % (print_hex_little(key)))
    if debug: print("plain_text: %s" % (print_hex_little(plain_text)))

    ############################
    # generate round keys
    round_tmp_keys = []
    round_tmp_keys += key

    key_16 = 0
    for i in range(16):
        key_16 ^= key[i]
    
    round_tmp_keys.append(key_16)

    round_keys = [] # 17 round keys

    for key_index in range(1, 18):
        if key_index == 1:
            round_keys.append(bytes(round_tmp_keys[0:16]))
        else:
            temp = []
            for i in range(16):
                bias_vector = a0_exp45_tab_const[a0_exp45_tab_const[(17 * key_index + i + 1) & 0xff]]
                temp_index = (key_index - 1) + i
                if temp_index >= 17:
                    temp_index -= 17
                # print("temp_index: %d" % (temp_index))
                temp.append((round_tmp_keys[temp_index] + bias_vector) & 0xFF)
            round_keys.append(bytes(temp))

        if debug: print("K%d: %s" % (key_index, print_hex_little(round_keys[key_index-1])))
        
        # key schedule
        for i in range(17):
            round_tmp_keys[i] = (((round_tmp_keys[i] << 3) & 0xFF) | ((round_tmp_keys[i] >> 5) & 0xFF))


    ############################
    # encryption
    tmp_data = bytearray(plain_text)

    for round in range(1, 9):
        if debug: print("round%3d: %s" % (round, print_hex_little(tmp_data)))
        # bt spec
        if is_bt_spec and round == 3:
            select_keys = plain_text

            tmp_data[0] = saffer_bytewise_xor(tmp_data[0], select_keys[0])
            tmp_data[1] = saffer_bytewise_add(tmp_data[1], select_keys[1])
            tmp_data[2] = saffer_bytewise_add(tmp_data[2], select_keys[2])
            tmp_data[3] = saffer_bytewise_xor(tmp_data[3], select_keys[3])
            tmp_data[4] = saffer_bytewise_xor(tmp_data[4], select_keys[4])
            tmp_data[5] = saffer_bytewise_add(tmp_data[5], select_keys[5])
            tmp_data[6] = saffer_bytewise_add(tmp_data[6], select_keys[6])
            tmp_data[7] = saffer_bytewise_xor(tmp_data[7], select_keys[7])
            tmp_data[8] = saffer_bytewise_xor(tmp_data[8], select_keys[8])
            tmp_data[9] = saffer_bytewise_add(tmp_data[9], select_keys[9])
            tmp_data[10] = saffer_bytewise_add(tmp_data[10], select_keys[10])
            tmp_data[11] = saffer_bytewise_xor(tmp_data[11], select_keys[11])
            tmp_data[12] = saffer_bytewise_xor(tmp_data[12], select_keys[12])
            tmp_data[13] = saffer_bytewise_add(tmp_data[13], select_keys[13])
            tmp_data[14] = saffer_bytewise_add(tmp_data[14], select_keys[14])
            tmp_data[15] = saffer_bytewise_xor(tmp_data[15], select_keys[15])
            if debug: print("added ->: %s" % (print_hex_little(tmp_data)))

        # first add.
        select_keys = round_keys[(2 * round - 1) - 1]

        # if debug: print("index %d, select_keys: %s" % ((2 * round - 1), print_hex_little(select_keys)))

        # if debug: print("origin, tmp_data: %s" % (print_hex_little(tmp_data)))
        tmp_data[0] = saffer_bytewise_xor(tmp_data[0], select_keys[0])
        tmp_data[1] = saffer_bytewise_add(tmp_data[1], select_keys[1])
        tmp_data[2] = saffer_bytewise_add(tmp_data[2], select_keys[2])
        tmp_data[3] = saffer_bytewise_xor(tmp_data[3], select_keys[3])
        tmp_data[4] = saffer_bytewise_xor(tmp_data[4], select_keys[4])
        tmp_data[5] = saffer_bytewise_add(tmp_data[5], select_keys[5])
        tmp_data[6] = saffer_bytewise_add(tmp_data[6], select_keys[6])
        tmp_data[7] = saffer_bytewise_xor(tmp_data[7], select_keys[7])
        tmp_data[8] = saffer_bytewise_xor(tmp_data[8], select_keys[8])
        tmp_data[9] = saffer_bytewise_add(tmp_data[9], select_keys[9])
        tmp_data[10] = saffer_bytewise_add(tmp_data[10], select_keys[10])
        tmp_data[11] = saffer_bytewise_xor(tmp_data[11], select_keys[11])
        tmp_data[12] = saffer_bytewise_xor(tmp_data[12], select_keys[12])
        tmp_data[13] = saffer_bytewise_add(tmp_data[13], select_keys[13])
        tmp_data[14] = saffer_bytewise_add(tmp_data[14], select_keys[14])
        tmp_data[15] = saffer_bytewise_xor(tmp_data[15], select_keys[15])
        # if debug: print("first, tmp_data: %s" % (print_hex_little(tmp_data)))
        
        
        tmp_data[0] = a0_exp45_tab_const[tmp_data[0]]
        tmp_data[1] = a0_log45_tab_const[tmp_data[1]]
        tmp_data[2] = a0_log45_tab_const[tmp_data[2]]
        tmp_data[3] = a0_exp45_tab_const[tmp_data[3]]
        tmp_data[4] = a0_exp45_tab_const[tmp_data[4]]
        tmp_data[5] = a0_log45_tab_const[tmp_data[5]]
        tmp_data[6] = a0_log45_tab_const[tmp_data[6]]
        tmp_data[7] = a0_exp45_tab_const[tmp_data[7]]
        tmp_data[8] = a0_exp45_tab_const[tmp_data[8]]
        tmp_data[9] = a0_log45_tab_const[tmp_data[9]]
        tmp_data[10] = a0_log45_tab_const[tmp_data[10]]
        tmp_data[11] = a0_exp45_tab_const[tmp_data[11]]
        tmp_data[12] = a0_exp45_tab_const[tmp_data[12]]
        tmp_data[13] = a0_log45_tab_const[tmp_data[13]]
        tmp_data[14] = a0_log45_tab_const[tmp_data[14]]
        tmp_data[15] = a0_exp45_tab_const[tmp_data[15]]
        # if debug: print("second, tmp_data: %s" % (print_hex_little(tmp_data)))

        select_keys = round_keys[(2 * round) - 1]
        # if debug: print("index %d, select_keys: %s" % ((2 * round), print_hex_little(select_keys)))

        tmp_data[0] = saffer_bytewise_add(tmp_data[0], select_keys[0])
        tmp_data[1] = saffer_bytewise_xor(tmp_data[1], select_keys[1])
        tmp_data[2] = saffer_bytewise_xor(tmp_data[2], select_keys[2])
        tmp_data[3] = saffer_bytewise_add(tmp_data[3], select_keys[3])
        tmp_data[4] = saffer_bytewise_add(tmp_data[4], select_keys[4])
        tmp_data[5] = saffer_bytewise_xor(tmp_data[5], select_keys[5])
        tmp_data[6] = saffer_bytewise_xor(tmp_data[6], select_keys[6])
        tmp_data[7] = saffer_bytewise_add(tmp_data[7], select_keys[7])
        tmp_data[8] = saffer_bytewise_add(tmp_data[8], select_keys[8])
        tmp_data[9] = saffer_bytewise_xor(tmp_data[9], select_keys[9])
        tmp_data[10] = saffer_bytewise_xor(tmp_data[10], select_keys[10])
        tmp_data[11] = saffer_bytewise_add(tmp_data[11], select_keys[11])
        tmp_data[12] = saffer_bytewise_add(tmp_data[12], select_keys[12])
        tmp_data[13] = saffer_bytewise_xor(tmp_data[13], select_keys[13])
        tmp_data[14] = saffer_bytewise_xor(tmp_data[14], select_keys[14])
        tmp_data[15] = saffer_bytewise_add(tmp_data[15], select_keys[15])
        # if debug: print("last, tmp_data: %s" % (print_hex_little(tmp_data)))

        # if debug: print("before pht, tmp_data: %s" % (print_hex_little(tmp_data)))
        # PHT switch
        for i in range(int(16/2)):
            tmp_x = tmp_data[i * 2 + 0]
            tmp_y = tmp_data[i * 2 + 1]
            tmp_data[i * 2 + 0] = (2 * tmp_x + tmp_y) & 0xFF
            tmp_data[i * 2 + 1] = (tmp_x + tmp_y) & 0xFF
            
        # if debug: print("after pht, tmp_data: %s" % (print_hex_little(tmp_data, ' ')))
        
        for sub_time in range(3):
            # PREMUTE
            tmp_data_1 = []
            for i in range(16):
                tmp_data_1.append(tmp_data[i])
            # 8 11 12 15 2 1 6 5 10 9 14 13 0 7 4 3
            tmp_data[0] = tmp_data_1[8]
            tmp_data[1] = tmp_data_1[11]
            tmp_data[2] = tmp_data_1[12]
            tmp_data[3] = tmp_data_1[15]
            tmp_data[4] = tmp_data_1[2]
            tmp_data[5] = tmp_data_1[1]
            tmp_data[6] = tmp_data_1[6]
            tmp_data[7] = tmp_data_1[5]
            tmp_data[8] = tmp_data_1[10]
            tmp_data[9] = tmp_data_1[9]
            tmp_data[10] = tmp_data_1[14]
            tmp_data[11] = tmp_data_1[13]
            tmp_data[12] = tmp_data_1[0]
            tmp_data[13] = tmp_data_1[7]
            tmp_data[14] = tmp_data_1[4]
            tmp_data[15] = tmp_data_1[3]

            # if debug: print("after PREMUTE, tmp_data: %s" % (print_hex_little(tmp_data, ' ')))
            
            # if debug: print("before pht, tmp_data: %s" % (print_hex_little(tmp_data)))
            # PHT switch
            for i in range(int(16/2)):
                tmp_x = tmp_data[i * 2 + 0]
                tmp_y = tmp_data[i * 2 + 1]
                tmp_data[i * 2 + 0] = (2 * tmp_x + tmp_y) & 0xFF
                tmp_data[i * 2 + 1] = (tmp_x + tmp_y) & 0xFF
                
            # if debug: print("after pht, tmp_data: %s" % (print_hex_little(tmp_data, ' ')))
        
        # if debug: print("round%3d: %s" % (round + 1, print_hex_little(tmp_data)))
        




    ############################
    # last add.
    select_keys = round_keys[(17) - 1]

    # if debug: print("index %d, select_keys: %s" % ((2 * round - 1), print_hex_little(select_keys)))

    # if debug: print("before, tmp_data: %s" % (print_hex_little(tmp_data)))
    tmp_data[0] = saffer_bytewise_xor(tmp_data[0], select_keys[0])
    tmp_data[1] = saffer_bytewise_add(tmp_data[1], select_keys[1])
    tmp_data[2] = saffer_bytewise_add(tmp_data[2], select_keys[2])
    tmp_data[3] = saffer_bytewise_xor(tmp_data[3], select_keys[3])
    tmp_data[4] = saffer_bytewise_xor(tmp_data[4], select_keys[4])
    tmp_data[5] = saffer_bytewise_add(tmp_data[5], select_keys[5])
    tmp_data[6] = saffer_bytewise_add(tmp_data[6], select_keys[6])
    tmp_data[7] = saffer_bytewise_xor(tmp_data[7], select_keys[7])
    tmp_data[8] = saffer_bytewise_xor(tmp_data[8], select_keys[8])
    tmp_data[9] = saffer_bytewise_add(tmp_data[9], select_keys[9])
    tmp_data[10] = saffer_bytewise_add(tmp_data[10], select_keys[10])
    tmp_data[11] = saffer_bytewise_xor(tmp_data[11], select_keys[11])
    tmp_data[12] = saffer_bytewise_xor(tmp_data[12], select_keys[12])
    tmp_data[13] = saffer_bytewise_add(tmp_data[13], select_keys[13])
    tmp_data[14] = saffer_bytewise_add(tmp_data[14], select_keys[14])
    tmp_data[15] = saffer_bytewise_xor(tmp_data[15], select_keys[15])
    # if debug: print("after, tmp_data: %s" % (print_hex_little(tmp_data)))

    return bytes(tmp_data)

# SAFFER+ decrpyt
def decrypt_Ar(key, cipher_text, debug=True):
    if debug: print("key: %s" % (print_hex_little(key)))
    if debug: print("cipher_text: %s" % (print_hex_little(cipher_text)))

    ############################
    # generate round keys
    round_tmp_keys = []
    round_tmp_keys += key

    key_16 = 0
    for i in range(16):
        key_16 ^= key[i]
    
    round_tmp_keys.append(key_16)

    round_keys = [] # 17 round keys

    for key_index in range(1, 18):
        if key_index == 1:
            round_keys.append(bytes(round_tmp_keys[0:16]))
        else:
            temp = []
            for i in range(16):
                bias_vector = a0_exp45_tab_const[a0_exp45_tab_const[(17 * key_index + i + 1) & 0xff]]
                temp_index = (key_index - 1) + i
                if temp_index >= 17:
                    temp_index -= 17
                # print("temp_index: %d" % (temp_index))
                temp.append((round_tmp_keys[temp_index] + bias_vector) & 0xFF)
            round_keys.append(bytes(temp))

        if debug: print("K%d: %s" % (key_index, print_hex_little(round_keys[key_index-1])))
        
        # key schedule
        for i in range(17):
            round_tmp_keys[i] = (((round_tmp_keys[i] << 3) & 0xFF) | ((round_tmp_keys[i] >> 5) & 0xFF))


    tmp_data = bytearray(cipher_text)

    ############################
    # last add.
    select_keys = round_keys[(17) - 1]

    # if debug: print("index %d, select_keys: %s" % ((2 * round - 1), print_hex_little(select_keys)))

    # if debug: print("before, tmp_data: %s" % (print_hex_little(tmp_data)))
    tmp_data[0] = saffer_bytewise_xor_decrypt(tmp_data[0], select_keys[0])
    tmp_data[1] = saffer_bytewise_add_decrypt(tmp_data[1], select_keys[1])
    tmp_data[2] = saffer_bytewise_add_decrypt(tmp_data[2], select_keys[2])
    tmp_data[3] = saffer_bytewise_xor_decrypt(tmp_data[3], select_keys[3])
    tmp_data[4] = saffer_bytewise_xor_decrypt(tmp_data[4], select_keys[4])
    tmp_data[5] = saffer_bytewise_add_decrypt(tmp_data[5], select_keys[5])
    tmp_data[6] = saffer_bytewise_add_decrypt(tmp_data[6], select_keys[6])
    tmp_data[7] = saffer_bytewise_xor_decrypt(tmp_data[7], select_keys[7])
    tmp_data[8] = saffer_bytewise_xor_decrypt(tmp_data[8], select_keys[8])
    tmp_data[9] = saffer_bytewise_add_decrypt(tmp_data[9], select_keys[9])
    tmp_data[10] = saffer_bytewise_add_decrypt(tmp_data[10], select_keys[10])
    tmp_data[11] = saffer_bytewise_xor_decrypt(tmp_data[11], select_keys[11])
    tmp_data[12] = saffer_bytewise_xor_decrypt(tmp_data[12], select_keys[12])
    tmp_data[13] = saffer_bytewise_add_decrypt(tmp_data[13], select_keys[13])
    tmp_data[14] = saffer_bytewise_add_decrypt(tmp_data[14], select_keys[14])
    tmp_data[15] = saffer_bytewise_xor_decrypt(tmp_data[15], select_keys[15])
    # if debug: print("after, tmp_data: %s" % (print_hex_little(tmp_data)))

    
    ############################
    # decrpytion
    for index in range(1, 9):
        round = 9 - index

        # if debug: print("before pht, tmp_data: %s" % (print_hex_little(tmp_data)))
        # PHT switch - decrypt
        for i in range(int(16/2)):
            tmp_A = tmp_data[i * 2 + 0]
            tmp_B = tmp_data[i * 2 + 1]
            tmp_data[i * 2 + 0] = (tmp_A - tmp_B) & 0xFF
            tmp_data[i * 2 + 1] = (tmp_B - tmp_data[i * 2 + 0]) & 0xFF
        # if debug: print("after pht, tmp_data: %s" % (print_hex_little(tmp_data, ' ')))
        
        for sub_time in range(3):
            # PREMUTE
            tmp_data_1 = []
            for i in range(16):
                tmp_data_1.append(tmp_data[i])
            # 8 11 12 15 2 1 6 5 10 9 14 13 0 7 4 3
            tmp_data[8] = tmp_data_1[0]
            tmp_data[11] = tmp_data_1[1]
            tmp_data[12] = tmp_data_1[2]
            tmp_data[15] = tmp_data_1[3]
            tmp_data[2] = tmp_data_1[4]
            tmp_data[1] = tmp_data_1[5]
            tmp_data[6] = tmp_data_1[6]
            tmp_data[5] = tmp_data_1[7]
            tmp_data[10] = tmp_data_1[8]
            tmp_data[9] = tmp_data_1[9]
            tmp_data[14] = tmp_data_1[10]
            tmp_data[13] = tmp_data_1[11]
            tmp_data[0] = tmp_data_1[12]
            tmp_data[7] = tmp_data_1[13]
            tmp_data[4] = tmp_data_1[14]
            tmp_data[3] = tmp_data_1[15]
            # if debug: print("after PREMUTE, tmp_data: %s" % (print_hex_little(tmp_data, ' ')))
            
            # if debug: print("before pht, tmp_data: %s" % (print_hex_little(tmp_data)))
            # PHT switch - decrypt
            for i in range(int(16/2)):
                tmp_A = tmp_data[i * 2 + 0]
                tmp_B = tmp_data[i * 2 + 1]
                tmp_data[i * 2 + 0] = (tmp_A - tmp_B) & 0xFF
                tmp_data[i * 2 + 1] = (tmp_B - tmp_data[i * 2 + 0]) & 0xFF
            # if debug: print("after pht, tmp_data: %s" % (print_hex_little(tmp_data, ' ')))

        select_keys = round_keys[(2 * round) - 1]
        # if debug: print("index %d, select_keys: %s" % ((2 * round), print_hex_little(select_keys)))

        tmp_data[0] = saffer_bytewise_add_decrypt(tmp_data[0], select_keys[0])
        tmp_data[1] = saffer_bytewise_xor_decrypt(tmp_data[1], select_keys[1])
        tmp_data[2] = saffer_bytewise_xor_decrypt(tmp_data[2], select_keys[2])
        tmp_data[3] = saffer_bytewise_add_decrypt(tmp_data[3], select_keys[3])
        tmp_data[4] = saffer_bytewise_add_decrypt(tmp_data[4], select_keys[4])
        tmp_data[5] = saffer_bytewise_xor_decrypt(tmp_data[5], select_keys[5])
        tmp_data[6] = saffer_bytewise_xor_decrypt(tmp_data[6], select_keys[6])
        tmp_data[7] = saffer_bytewise_add_decrypt(tmp_data[7], select_keys[7])
        tmp_data[8] = saffer_bytewise_add_decrypt(tmp_data[8], select_keys[8])
        tmp_data[9] = saffer_bytewise_xor_decrypt(tmp_data[9], select_keys[9])
        tmp_data[10] = saffer_bytewise_xor_decrypt(tmp_data[10], select_keys[10])
        tmp_data[11] = saffer_bytewise_add_decrypt(tmp_data[11], select_keys[11])
        tmp_data[12] = saffer_bytewise_add_decrypt(tmp_data[12], select_keys[12])
        tmp_data[13] = saffer_bytewise_xor_decrypt(tmp_data[13], select_keys[13])
        tmp_data[14] = saffer_bytewise_xor_decrypt(tmp_data[14], select_keys[14])
        tmp_data[15] = saffer_bytewise_add_decrypt(tmp_data[15], select_keys[15])
        # if debug: print("last, tmp_data: %s" % (print_hex_little(tmp_data)))

        
        tmp_data[0] = a0_log45_tab_const[tmp_data[0]]
        tmp_data[1] = a0_exp45_tab_const[tmp_data[1]]
        tmp_data[2] = a0_exp45_tab_const[tmp_data[2]]
        tmp_data[3] = a0_log45_tab_const[tmp_data[3]]
        tmp_data[4] = a0_log45_tab_const[tmp_data[4]]
        tmp_data[5] = a0_exp45_tab_const[tmp_data[5]]
        tmp_data[6] = a0_exp45_tab_const[tmp_data[6]]
        tmp_data[7] = a0_log45_tab_const[tmp_data[7]]
        tmp_data[8] = a0_log45_tab_const[tmp_data[8]]
        tmp_data[9] = a0_exp45_tab_const[tmp_data[9]]
        tmp_data[10] = a0_exp45_tab_const[tmp_data[10]]
        tmp_data[11] = a0_log45_tab_const[tmp_data[11]]
        tmp_data[12] = a0_log45_tab_const[tmp_data[12]]
        tmp_data[13] = a0_exp45_tab_const[tmp_data[13]]
        tmp_data[14] = a0_exp45_tab_const[tmp_data[14]]
        tmp_data[15] = a0_log45_tab_const[tmp_data[15]]
        # if debug: print("second, tmp_data: %s" % (print_hex_little(tmp_data)))

        # first add.
        select_keys = round_keys[(2 * round - 1) - 1]

        # if debug: print("index %d, select_keys: %s" % ((2 * round - 1), print_hex_little(select_keys)))

        # if debug: print("origin, tmp_data: %s" % (print_hex_little(tmp_data)))
        tmp_data[0] = saffer_bytewise_xor_decrypt(tmp_data[0], select_keys[0])
        tmp_data[1] = saffer_bytewise_add_decrypt(tmp_data[1], select_keys[1])
        tmp_data[2] = saffer_bytewise_add_decrypt(tmp_data[2], select_keys[2])
        tmp_data[3] = saffer_bytewise_xor_decrypt(tmp_data[3], select_keys[3])
        tmp_data[4] = saffer_bytewise_xor_decrypt(tmp_data[4], select_keys[4])
        tmp_data[5] = saffer_bytewise_add_decrypt(tmp_data[5], select_keys[5])
        tmp_data[6] = saffer_bytewise_add_decrypt(tmp_data[6], select_keys[6])
        tmp_data[7] = saffer_bytewise_xor_decrypt(tmp_data[7], select_keys[7])
        tmp_data[8] = saffer_bytewise_xor_decrypt(tmp_data[8], select_keys[8])
        tmp_data[9] = saffer_bytewise_add_decrypt(tmp_data[9], select_keys[9])
        tmp_data[10] = saffer_bytewise_add_decrypt(tmp_data[10], select_keys[10])
        tmp_data[11] = saffer_bytewise_xor_decrypt(tmp_data[11], select_keys[11])
        tmp_data[12] = saffer_bytewise_xor_decrypt(tmp_data[12], select_keys[12])
        tmp_data[13] = saffer_bytewise_add_decrypt(tmp_data[13], select_keys[13])
        tmp_data[14] = saffer_bytewise_add_decrypt(tmp_data[14], select_keys[14])
        tmp_data[15] = saffer_bytewise_xor_decrypt(tmp_data[15], select_keys[15])
        # if debug: print("first, tmp_data: %s" % (print_hex_little(tmp_data)))
        
        if debug: print("round%3d: %s" % (round, print_hex_little(tmp_data)))

    return bytes(tmp_data)


def saferplus_Ar_test():
    print_header("saferplus_Ar_test")

    # from 10.1 FOUR TESTS OF E1
    key = bytes.fromhex("00000000000000000000000000000000")
    plain_text = bytes.fromhex("00000000000000000000000000000000")

    e = encrypt_Ar(key, plain_text)  # encrpyt
    d = decrypt_Ar(key, e)  # decrpyt
    
    print_result_with_exp(d == plain_text)

    
    key = bytes.fromhex("159dd9f43fc3d328efba0cd8a861fa57")
    plain_text = bytes.fromhex("bc3f30689647c8d7c5a03ca80a91eceb")

    e = encrypt_Ar(key, plain_text)  # encrpyt
    d = decrypt_Ar(key, e)  # decrpyt
    
    print_result_with_exp(d == plain_text)

测试结果如下,测试加密的中间输出和Spec P921一致。并且进行逆运算能够还原出原本的明文。

###################################################################################
#                                saferplus_Ar_test                                #
###################################################################################
key: 00000000000000000000000000000000
plain_text: 00000000000000000000000000000000
K1: 00000000000000000000000000000000
K2: 4697b1baa3b7100ac537b3c95a28ac64
K3: ecabaac66795580df89af66e66dc053d
K4: 8ac3d8896ae9364943bfebd4969b68a0
K5: 5d57921fd5715cbb22c1be7bbc996394
K6: 2a61b8343219fdfb1740e6511d41448f
K7: dd0480dee731d67f01a2f739da6f23ca
K8: 3ad01cd1303e12a1cd0fe0a8af82592c
K9: 7dadb2efc287ce75061302904f2e7233
K10: c08dcfa981e2c4272f6c7a9f52e11538
K11: fc2042c708e409555e8c147660ffdfd7
K12: fa0b21001af9a6b9e89e624cd99150d2
K13: 18b40784ea5ba4c80ecb48694b4e9c35
K14: 454d54e5253c0c4a8b3fcca7db6baef4
K15: 2df37c6d9db52674f29353b0f011ed83
K16: b60316733b1e8e70bd861b477e2456f1
K17: 884697b1baa3b7100ac537b3c95a28ac
round  1: 00000000000000000000000000000000
round  2: 78d19f9307d2476a523ec7a8a026042a
round  3: 600265247668dda0e81c07bbb30ed503
round  4: d7552ef7cc9dbde568d80c2215bc4277
round  5: fb06bef32b52ab8f2a4f2b6ef7f6d0cd
round  6: b46b711ebb3cf69e847a75f0ab884bdd
round  7: c585f308ff19404294f06b292e978994
round  8: 2665fadb13acf952bf74b4ab12264b9f
key: 00000000000000000000000000000000
cipher_text: 158ffe43352085e8a5ec7a88e1ff2ba8
K1: 00000000000000000000000000000000
K2: 4697b1baa3b7100ac537b3c95a28ac64
K3: ecabaac66795580df89af66e66dc053d
K4: 8ac3d8896ae9364943bfebd4969b68a0
K5: 5d57921fd5715cbb22c1be7bbc996394
K6: 2a61b8343219fdfb1740e6511d41448f
K7: dd0480dee731d67f01a2f739da6f23ca
K8: 3ad01cd1303e12a1cd0fe0a8af82592c
K9: 7dadb2efc287ce75061302904f2e7233
K10: c08dcfa981e2c4272f6c7a9f52e11538
K11: fc2042c708e409555e8c147660ffdfd7
K12: fa0b21001af9a6b9e89e624cd99150d2
K13: 18b40784ea5ba4c80ecb48694b4e9c35
K14: 454d54e5253c0c4a8b3fcca7db6baef4
K15: 2df37c6d9db52674f29353b0f011ed83
K16: b60316733b1e8e70bd861b477e2456f1
K17: 884697b1baa3b7100ac537b3c95a28ac
round  8: 2665fadb13acf952bf74b4ab12264b9f
round  7: c585f308ff19404294f06b292e978994
round  6: b46b711ebb3cf69e847a75f0ab884bdd
round  5: fb06bef32b52ab8f2a4f2b6ef7f6d0cd
round  4: d7552ef7cc9dbde568d80c2215bc4277
round  3: 600265247668dda0e81c07bbb30ed503
round  2: 78d19f9307d2476a523ec7a8a026042a
round  1: 00000000000000000000000000000000
>>>>>>>>>> Pass <<<<<<<<<<
key: 159dd9f43fc3d328efba0cd8a861fa57
plain_text: bc3f30689647c8d7c5a03ca80a91eceb
K1: 159dd9f43fc3d328efba0cd8a861fa57
K2: 326558b3c15551899a97790e65ff669e
K3: 62e879b65b9f53bbfbd020c624b1d682
K4: 73415f30bac8ab61f410adc9442992db
K5: 5093cfa1d31c1c48acd76df030ea3c31
K6: 0b4acc2b8f1f694fc7bd91f4a70f3009
K7: 2ca43fc817947804ecff148d50d6f6c6
K8: 3fcd73524b533e00b7f7825bea2040a4
K9: 6c67bec76ae8c8cc4d289f69436d3506
K10: 95ed95ee8cb97e61d75848464bffb379
K11: ff566c1fc6b9da9ac502514550f3e9d2
K12: ab5ce3f5c887d0f49b87e0d380e12f47
K13: a2cab6f95eac7d655dbe84a6cd4c47f5
K14: f5caff88af0af8c42a20b5bbd2c8b460
K15: 185099c1131cf97001e2f36fda415025
K16: a0ebb82676bc75e8378b189eff3f6b1d
K17: cf5b348aaee27ae332b4f1bfa10289a6
round  1: bc3f30689647c8d7c5a03ca80a91eceb
round  2: 3e950edf197615638cc19c09f8fedc9b
round  3: 6a7640791cb536678936c5ecd4ae5a73
round  4: fca2c022a577e2ffb2aa007589693ec7
round  5: e97f8ea4ed1a6f4a36ffc179dc6bb563
round  6: 38b07261d7340d028749de1773a415c7
round  7: 58241f1aed7c1c3e047d724331a0b774
round  8: 3d1aaeff53c0910de63b9788b13c490f
key: 159dd9f43fc3d328efba0cd8a861fa57
cipher_text: 0e9c9630c8bae88227c1e704206c5723
K1: 159dd9f43fc3d328efba0cd8a861fa57
K2: 326558b3c15551899a97790e65ff669e
K3: 62e879b65b9f53bbfbd020c624b1d682
K4: 73415f30bac8ab61f410adc9442992db
K5: 5093cfa1d31c1c48acd76df030ea3c31
K6: 0b4acc2b8f1f694fc7bd91f4a70f3009
K7: 2ca43fc817947804ecff148d50d6f6c6
K8: 3fcd73524b533e00b7f7825bea2040a4
K9: 6c67bec76ae8c8cc4d289f69436d3506
K10: 95ed95ee8cb97e61d75848464bffb379
K11: ff566c1fc6b9da9ac502514550f3e9d2
K12: ab5ce3f5c887d0f49b87e0d380e12f47
K13: a2cab6f95eac7d655dbe84a6cd4c47f5
K14: f5caff88af0af8c42a20b5bbd2c8b460
K15: 185099c1131cf97001e2f36fda415025
K16: a0ebb82676bc75e8378b189eff3f6b1d
K17: cf5b348aaee27ae332b4f1bfa10289a6
round  8: 3d1aaeff53c0910de63b9788b13c490f
round  7: 58241f1aed7c1c3e047d724331a0b774
round  6: 38b07261d7340d028749de1773a415c7
round  5: e97f8ea4ed1a6f4a36ffc179dc6bb563
round  4: fca2c022a577e2ffb2aa007589693ec7
round  3: 6a7640791cb536678936c5ecd4ae5a73
round  2: 3e950edf197615638cc19c09f8fedc9b
round  1: bc3f30689647c8d7c5a03ca80a91eceb
>>>>>>>>>> Pass <<<<<<<<<<

HMAC-SHA256算法

原理概述

这个SHA256的算法资料很多,而且python也提供了库实现,在本文就不再赘述了。

算法提供了sha256_hmac接口,输入keymessage,可以得到256bytes的hash值。sha256_hash接口,输入message,可以得到256bytes的数值。

import hmac
import hashlib
def sha256_hmac(key, message):
    hmac_digest = hmac.new(key, message, hashlib.sha256)
    digest = hmac_digest.digest()
    
    return digest

def sha256_hash(message):
    sha256 = hashlib.sha256()

    sha256.update(message)

    hash_value = sha256.digest()

    return hash_value

Elliptic Curve加密算法

这个在bobwenstudy/BluetoothCryptographicToolbox: LE SMP加密算法设计和实现(python) (github.com)有说明和实现,这里就不再展开了。

Security Specification实现

在Core Spec v5.4 P948中定义了一系列Classic用于配对的算法库。按照Legacy和Secure Simple Pairing所使用的函数各不相同。

  • E0用于链路层加解密数据流,上一章有其具体算法实现。
  • E3用于加密的秘钥生成。

下列加密函数被用于认证阶段,用于完成对Link Key的确认。

  • E1用于Authentication。Legacy Pairing和Secure Simple Pairing都用这个算法。

  • h4h5用于Authentication。Secure Connection用这个算法。

下列加密函数被定义为支持Legacy Pairing过程。

  • E2用于Authentication的秘钥生成,也就是生成Link Key。

以下是为支持Secure Simple Pairing过程而定义的加密函数连接的配对过程(包含Secure Connections)。

  • f1用于生成128位确认值Ca和Cb。
  • g用于计算6位数的数字校验值。
  • f2用于根据DHKey和随机数来计算链路密钥和可能的其他密钥。
  • f3用于在认证阶段2中计算检查值Ea和Eb。
  • h3用于AES加密密钥生成。
  • h4用于设备认证密钥生成。
  • h5用于设备认证确认功能。

加密相关函数

在Legacy和Simple Pairing模式下,是使用E0加密。但是E0加密所需的秘钥由E3算法生成。

E0函数已经在之前介绍了,这些只介绍E3函数。

image-20240523213024154

E3函数实现
概述

算法操作流程如下图所示:

image-20240523213414999

其中秘钥K的offset运算处理如下。

image-20240523205929881

其中的E3运算处理如下,Hash运算,Hash运算实现在E1算法实现说明。

image-20240523213443879

算法实现

如下所示,输入K/RAND/COF,L取值为12,输出128bit的K_enc

def classic_E3(K, RAND, COF, debug=True):
    # print_header("classic_E3")
    return classic_Hash(K, RAND, COF, L=12)
测试

参考Core Spec v5.4 P941中写的测试用例进行测试。

def classic_E3_test():
    print_header("classic_E3_test")

    print_header("10.5 FOUR TESTS OF E3")
    print_header("case 1")
    RAND = bytes.fromhex("00000000000000000000000000000000")
    aco = bytes.fromhex("48afcdd4bd40fef76693b113")
    key = bytes.fromhex("00000000000000000000000000000000")

    Kenc = classic_E3(key, RAND, aco)

    Kenc_exp = bytes.fromhex('cc802aecc7312285912e90af6a1e1154')
    print_result_with_exp(Kenc == Kenc_exp)



    print_header("case 2")
    RAND = bytes.fromhex("950e604e655ea3800fe3eb4a28918087")
    aco = bytes.fromhex("68f4f472b5586ac5850f5f74")
    key = bytes.fromhex("34e86915d20c485090a6977931f96df5")

    Kenc = classic_E3(key, RAND, aco)

    Kenc_exp = bytes.fromhex('c1beafea6e747e304cf0bd7734b0a9e2')
    print_result_with_exp(Kenc == Kenc_exp)



    print_header("case 3")
    RAND = bytes.fromhex("6a8ebcf5e6e471505be68d5eb8a3200c")
    aco = bytes.fromhex("658d791a9554b77c0b2f7b9f")
    key = bytes.fromhex("35cf77b333c294671d426fa79993a133")

    Kenc = classic_E3(key, RAND, aco)

    Kenc_exp = bytes.fromhex('a3032b4df1cceba8adc1a04427224299')
    print_result_with_exp(Kenc == Kenc_exp)



    print_header("case 4")
    RAND = bytes.fromhex("5ecd6d75db322c75b6afbd799cb18668")
    aco = bytes.fromhex("63f701c7013238bbf88714ee")
    key = bytes.fromhex("b9f90c53206792b1826838b435b87d4d")

    Kenc = classic_E3(key, RAND, aco)

    Kenc_exp = bytes.fromhex('ea520cfc546b00eb7c3a6cea3ecb39ed')
    print_result_with_exp(Kenc == Kenc_exp)

测试结果如下:

###################################################################################
#                                 classic_E1_test                                 #
###################################################################################
###################################################################################
#                              10.1 FOUR TESTS OF E1                              #
###################################################################################
###################################################################################
#                                     case 1                                      #
###################################################################################
key: 00000000000000000000000000000000
plain_text: 00000000000000000000000000000000
K1: 00000000000000000000000000000000
K2: 4697b1baa3b7100ac537b3c95a28ac64
K3: ecabaac66795580df89af66e66dc053d
K4: 8ac3d8896ae9364943bfebd4969b68a0
K5: 5d57921fd5715cbb22c1be7bbc996394
K6: 2a61b8343219fdfb1740e6511d41448f
K7: dd0480dee731d67f01a2f739da6f23ca
K8: 3ad01cd1303e12a1cd0fe0a8af82592c
K9: 7dadb2efc287ce75061302904f2e7233
K10: c08dcfa981e2c4272f6c7a9f52e11538
K11: fc2042c708e409555e8c147660ffdfd7
K12: fa0b21001af9a6b9e89e624cd99150d2
K13: 18b40784ea5ba4c80ecb48694b4e9c35
K14: 454d54e5253c0c4a8b3fcca7db6baef4
K15: 2df37c6d9db52674f29353b0f011ed83
K16: b60316733b1e8e70bd861b477e2456f1
K17: 884697b1baa3b7100ac537b3c95a28ac
round  1: 00000000000000000000000000000000
round  2: 78d19f9307d2476a523ec7a8a026042a
round  3: 600265247668dda0e81c07bbb30ed503
round  4: d7552ef7cc9dbde568d80c2215bc4277
round  5: fb06bef32b52ab8f2a4f2b6ef7f6d0cd
round  6: b46b711ebb3cf69e847a75f0ab884bdd
round  7: c585f308ff19404294f06b292e978994
round  8: 2665fadb13acf952bf74b4ab12264b9f
key: e9e5dfc1b3a79583e9e5dfc1b3a79583
plain_text: 158ffe43352085e8a5ec7a88e1ff2ba8
K1: e9e5dfc1b3a79583e9e5dfc1b3a79583
K2: 7595bf57e0632c59f435c16697d4c864
K3: e31b96afcc75d286ef0ae257cbbc05b7
K4: 0d2a27b471bc0108c6263aff9d9b3b6b
K5: 98d1eb5773cf59d75d3b17b3bc37c191
K6: fd2b79282408ddd4ea0aa7511133336f
K7: 331227756638a41d57b0f7e071ee2a98
K8: aa0dd8cc68b406533d0f1d64aabacf20
K9: 669291b0752e63f806fce76f10e119c8
K10: ef8bdd46be8ee0277e9b78adef1ec154
K11: f3902eb06dc409cfd78384624964bf51
K12: 7d72702b21f97984a721c99b0498239d
K13: 532e60bceaf902c52a06c2c283ecfa32
K14: 181715e5192efb2a64129668cf5d9dd4
K15: 83017c1434342d4290e961578790f451
K16: 2603532f365604646ff65803795ccce5
K17: 882f7c907b565ea58dae1c928a0dcf41
round  1: 158ffe43352085e8a5ec7a88e1ff2ba8
round  2: 0b5cc75febcdf7827ca29ec0901b6b5b
round  3: e4278526c8429211f7f2f0016220aef4
added ->: f1b68365fd6217f952de6a89831fd95c
round  4: d0304ad18337f86040145d27aa5c8153
round  5: 84db909d213bb0172b8b6aaf71bf1472
round  6: f835f52921e903dfa762f1df5abd7f95
round  7: ae6c0b4bb09f25c6a5d9788a31b605d1
round  8: 744a6235b86cc0b853cc9f74f6b65311
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 2                                      #
###################################################################################
key: 159dd9f43fc3d328efba0cd8a861fa57
plain_text: bc3f30689647c8d7c5a03ca80a91eceb
K1: 159dd9f43fc3d328efba0cd8a861fa57
K2: 326558b3c15551899a97790e65ff669e
K3: 62e879b65b9f53bbfbd020c624b1d682
K4: 73415f30bac8ab61f410adc9442992db
K5: 5093cfa1d31c1c48acd76df030ea3c31
K6: 0b4acc2b8f1f694fc7bd91f4a70f3009
K7: 2ca43fc817947804ecff148d50d6f6c6
K8: 3fcd73524b533e00b7f7825bea2040a4
K9: 6c67bec76ae8c8cc4d289f69436d3506
K10: 95ed95ee8cb97e61d75848464bffb379
K11: ff566c1fc6b9da9ac502514550f3e9d2
K12: ab5ce3f5c887d0f49b87e0d380e12f47
K13: a2cab6f95eac7d655dbe84a6cd4c47f5
K14: f5caff88af0af8c42a20b5bbd2c8b460
K15: 185099c1131cf97001e2f36fda415025
K16: a0ebb82676bc75e8378b189eff3f6b1d
K17: cf5b348aaee27ae332b4f1bfa10289a6
round  1: bc3f30689647c8d7c5a03ca80a91eceb
round  2: 3e950edf197615638cc19c09f8fedc9b
round  3: 6a7640791cb536678936c5ecd4ae5a73
round  4: fca2c022a577e2ffb2aa007589693ec7
round  5: e97f8ea4ed1a6f4a36ffc179dc6bb563
round  6: 38b07261d7340d028749de1773a415c7
round  7: 58241f1aed7c1c3e047d724331a0b774
round  8: 3d1aaeff53c0910de63b9788b13c490f
key: fe78b835f26468ab069fd3991b086fda
plain_text: 2e4b417b9a2a9cfd7d8417d9a6a556eb
K1: fe78b835f26468ab069fd3991b086fda
K2: 095c5a51c6fa6d3ac1d57fa19aa382bd
K3: 1af866df817fd9f4ec00bc704192cffc
K4: f4a8a059c1f575f076f5fbb24bf16590
K5: 8c9d18d9356a9954d341b4286e88ea1f
K6: 5c958d370102c9881bf753e69c7da029
K7: 7eb2985c3697429fbe0da334bb51f795
K8: af900f4b63a1138e2874bfb7c628b7b8
K9: 834c8588dd8f3d4f31117a488420d69b
K10: bc2b9b81c15d9a80262f3f48e9045895
K11: f08608c9e39ad3147cba61327919c958
K12: 2d4131decf4fa3a959084714a9e85c11
K13: c934fd319c4a2b5361fa8eef05ae9572
K14: 4904c17aa47868e40471007cde3a97c0
K15: ea5e28687e97fa3f833401c86e6053ef
K16: 1168f58252c4ecfccafbdb3af857b9f2
K17: b3440f69ef951b78b5cbd6866275301b
round  1: 2e4b417b9a2a9cfd7d8417d9a6a556eb
round  2: b8bca81d6bb45af9d92beadd9300f5ed
round  3: 351aa16dec2c3a4787080249ed323eae
added ->: 1b65e2167656d6bafa8c19904bd79445
round  4: 2ce8fef47dda6a5bee74372e33e478a2
round  5: 572787f563e1643c1c862b7555637fb4
round  6: 16b4968c5d02853c3a43aa4cdb5f26ac
round  7: 10e4120c7cccef9dd4ba4e6da8571b01
round  8: f9081772498fed41b6ffd72b71fcf6c6
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 3                                      #
###################################################################################
key: 45298d06e46bac21421ddfbed94c032b
plain_text: 0891caee063f5da1809577ff94ccdcfb
K1: 45298d06e46bac21421ddfbed94c032b
K2: 8f03e1e1fe1c191cad35a897bc400597
K3: 4f2ce3a092dde854ef496c8126a69e8e
K4: 968caee2ac6d7008c07283daec67f2f2
K5: ab0d5c31f94259a6bf85ee2d22edf56c
K6: dfb74855c0085ce73dc17b84bfd50a92
K7: 8f888952662b3db00d4e904e7ea53b5d
K8: 5e18bfcc07799b0132db88cd6042f599
K9: bfca91ad9bd3d1a06c582b1d5512dddf
K10: a88bc477e3fa1d5a59b5e6cf793c7a41
K11: f3cfb8dac8aea2a6a8ef95af3a2a2767
K12: 77beb90670c5300b03aa2b2232d3d40c
K13: b578373650af36a06e19fe335d726d32
K14: 6bcee918c7d0d24dfdf42237fcf99d53
K15: 399f158241eb3e079f45d7b96490e7ea
K16: 1bcfbe98ecde2add52aa63ea79fb917a
K17: ee8bc03ec08722bc2b075492873374af
round  1: 0891caee063f5da1809577ff94ccdcfb
round  2: 1c6ca013480a685c1b28e0317f7167e1
round  3: 06b4915f5fcc1fc551a52048f0af8a26
round  4: 077a92b040acc86e6e0a877db197a167
round  5: 7204881fb300914825fdc863e8ceadf3
round  6: 27031131d86cea2d747deb4f756143aa
round  7: fc8c13d49149b1ce8d86f96e44a00065
round  8: 04ef5f5a7ddf846cda0a07782fc23866
key: 2ecc6cc797cc41a2ab02007f6af396ae
plain_text: d989d7a40cde7032d17b52f8117b69d5
K1: 2ecc6cc797cc41a2ab02007f6af396ae
K2: acfaef7609c12567d537ae1cf9dc2198
K3: 079c8ff9b73d428df879906a0b87a6c8
K4: 19f2710baf403a494193d201f3a8c439
K5: d623a6498f915cb2c8002765247b2f5a
K6: 900109093319bc30108b3d9434a77a72
K7: e28e2ee6e72e7f4e5b5c11f10d204228
K8: 8e455cd11f8b9073a2dfa5413c7a4bc5
K9: 28afb26e2c7a64238c41cefc16c53e74
K10: d08dcafc2096395ba0d2dddd0e471f4d
K11: fcffdcc3ad8faae091a7055b934f87c1
K12: f8df082d77060252c02d91e55bd6a7d6
K13: bef3706e523d708e8a44147d7508bc35
K14: 3e98ab283ca2422d56a56cf8b06caeb3
K15: 87ad9625d06645d22598dd5ef811ea2c
K16: 8bd3db0cc8168009e5da90877e13a36f
K17: 0e74631d813a8351ac7039b348c41b42
round  1: d989d7a40cde7032d17b52f8117b69d5
round  2: 8e76eb9a29b2ad5eea790db97aee37c1
round  3: 346bb7c35b2539676375aafe3af69089
added ->: edf48e675703a955b2f0fc062b71f95c
round  4: fafb6c1f3ebbd2477be2da49dd923f69
round  5: 7c72230df588060a3cf920f9b0a08f06
round  6: 55991df991db26ff00073a12baa3031d
round  7: 70ec682ff864375f63701fa4f6be5377
round  8: 172f12ec933da85504b4ea5c90f8f0ea
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 4                                      #
###################################################################################
key: 35949a914225fabad91995d226de1d92
plain_text: 0ecd61782b4128480c05dc45542b1b8c
K1: 35949a914225fabad91995d226de1d92
K2: ea6b3dcccc8ee5d88de349fa5010404f
K3: 920f3a0f2543ce535d4e7f25ad80648a
K4: ad47227edf9c6874e80ba80ebb95d2c9
K5: 81a941ca7202b5e884ae8fa493ecac3d
K6: bcde1520bee3660e86ce2f0fb78b9157
K7: c8eee7423d7c6efa75ecec0d2cd969d3
K8: 910b3f838a02ed441fbe863a02b4a1d0
K9: 56c647c1e865eb078348962ae070972d
K10: 883965da77ca5812d8104e2b640aec0d
K11: 61d4cb7e4f8868a283327806a9bd8d4d
K12: 9f57de3a3ff310e21dc1e696ce060304
K13: 7aa1d8adc1aeed7127ef9a18f6eb2d8e
K14: b4db9da3bf865912acd14904c7f7785d
K15: a13d7141ef1f6c7d867e3d175467381b
K16: 08b2bc058e50d6141cdd566a307e1acc
K17: 057b2b4b4be5dc0ac49e50489b8006c9
round  1: 0ecd61782b4128480c05dc45542b1b8c
round  2: 8935e2e263fbc4b9302cabdfc06bce3e
round  3: b4c8b878675f184a0c72f3dab51f8f05
round  4: 77ced9f2fc42bdd5c6312b87fc2377c5
round  5: fe28e8056f3004d60bb207e628b39cf2
round  6: 1f2ba92259d9e88101518f145a33840f
round  7: cc9b5d0218d29037e88475152ebebb2f
round  8: b04d352bedc02682e4a7f59d7cda1dba
key: 1e717950f5828f3930fe4a9395858815
plain_text: 5cfacc773bae995cd7f1b81e7c9ec7df
K1: 1e717950f5828f3930fe4a9395858815
K2: d1623369b733d98bbc894f75866c544c
K3: 4abf27664ae364cc8a7e5bcf88214cc4
K4: 2aaedda8dc4933dd6aeaf6e5c0d5a482
K5: bc7f8ab2d86000f47b1946cc8d7a7a2b
K6: 6b28544cb13ec6c5d98470df2cf900b7
K7: 1be840d9107f2c9523f66bb19f5464a1
K8: 61d6fb1aa2f0c2b26fb2a3d6de8c177c
K9: adabfc82570c568a233173099f23f4c2
K10: b7df6b55ad266c0f1ff7452101f59101
K11: 8e04a7282a2950dcbaea28f300e22de3
K12: 21362c114433e29bda3e4d51f803b0cf
K13: 710c8fd5bb3cbb5f132a7061de518bd9
K14: 0791de7334f4c87285809343f3ead3bd
K15: 4f47f0e5629a674bfcd13770eb3a3bd9
K16: 58a6d9a16a284cc0aead2126c79608a1
K17: a564082a0a98399f43f535fd5cefad34
round  1: 5cfacc773bae995cd7f1b81e7c9ec7df
round  2: d571ffa21d9daa797b1a0a3c962fc64c
round  3: e17c8e498a00f125bf654c938c23f36d
added ->: bd765a3eb1ae8a796856048df0c1bab2
round  4: a9727c26f2f06bd9920e83c8605dcd76
round  5: aeff751f146eab7e4626b2e2c9e2fb39
round  6: cf412b95f454d5185e67ca671892e5bd
round  7: 16165722fe4e07ef88f8056b17d89567
round  8: 28854cd6ad4a3c572b15490d4b81bc3f
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<

认证相关函数

在一方有加密时,当需要进行加密时,其会进行认证流程,去判断对方是否拥有相同的Link Key。

Legacy和Secure Simple Pairing认证

首先Verifier端发送RAND值,如果Claimant端有Link Key,会回复计算后的SRES,Verifier也会计算SRES,确保接收的SRES和计算的一致,则认为两个设备拥有相同的Link Key,无需进行配对,直接进行加密。并记录中间生成的ACO,用于后续加密。

image-20240523203547365

LMP的交互流程如下。

image-20240523203904672

Secure Connection的认证

两边都会发送RAND,然后两边会分别进行h4和h5算法,计算得到SRES_C和SRES_P。Central端发送SRES_C并验证收到的SRES_P;Peripheral端发送SRES_P并验证收到的SRES_C。两边都匹配,则认为两个设备拥有相同的Link Key,无需进行配对,直接进行加密。并记录中间生成的ACO,用于后续加密。

image-20240523204744400

LMP的交互流程如下。

image-20240523205639298

E1函数实现
概述

算法操作流程如下图所示:

image-20240523205818385

其中秘钥K的offset运算处理如下。

image-20240523205929881

其中的E运算处理如下,其实就是将一个有限长度的数据扩展为16字节。

image-20240523210230106

在Spec中也将E1运算表述为Hash运算,公式中的I1RANDI2Address。计算得到的Hash值为128bit,其中前4个字节用于SRES,剩余的12字节作为ACO

image-20240523210435286

算法实现

如下所示,输入K/RAND/address,L取值为6,前4字节为SRES,后12字节为ACO。

def classic_E1_sub_fun_E(X, L):
    '''
        E1 sub-function E
        expansion of the octet word into a 128-bit word
        X: input L bytes
        L: length of the input
        return: 16 bytes
    '''
    out = []
    for i in range(16):
        out.append(X[i % L])
    return bytes(out)

def classic_Hash(K, I1, I2, L, debug=True):
    # print_header("classic_Hash")

    tmp_Ar_out = encrypt_Ar(K, I1)
    # if debug: print("tmp_Ar_out: %s" % (print_hex_little(tmp_Ar_out)))
    tmp_Ar_out = saffer_bytewise_xor_arr(tmp_Ar_out, I1)
    # if debug: print("tmp_Ar_out: %s" % (print_hex_little(tmp_Ar_out)))
    tmp_E_out = classic_E1_sub_fun_E(I2, L)
    # if debug: print("tmp_E_out: %s" % (print_hex_little(tmp_E_out)))
    tmp_Ar_out = saffer_bytewise_add_arr(tmp_Ar_out, tmp_E_out)
    # if debug: print("tmp_Ar_out: %s" % (print_hex_little(tmp_Ar_out)))

    ############################
    # generate bt spec round keys
    K_offset = []
    K_offset.append(saffer_bytewise_add(K[0], 233))
    K_offset.append(saffer_bytewise_xor(K[1], 229))
    K_offset.append(saffer_bytewise_add(K[2], 223))
    K_offset.append(saffer_bytewise_xor(K[3], 193))
    K_offset.append(saffer_bytewise_add(K[4], 179))
    K_offset.append(saffer_bytewise_xor(K[5], 167))
    K_offset.append(saffer_bytewise_add(K[6], 149))
    K_offset.append(saffer_bytewise_xor(K[7], 131))
    K_offset.append(saffer_bytewise_xor(K[8], 233))
    K_offset.append(saffer_bytewise_add(K[9], 229))
    K_offset.append(saffer_bytewise_xor(K[10], 223))
    K_offset.append(saffer_bytewise_add(K[11], 193))
    K_offset.append(saffer_bytewise_xor(K[12], 179))
    K_offset.append(saffer_bytewise_add(K[13], 167))
    K_offset.append(saffer_bytewise_xor(K[14], 149))
    K_offset.append(saffer_bytewise_add(K[15], 131))
    K_offset = bytes(K_offset)
    
    return encrypt_Ar(K_offset, tmp_Ar_out, is_bt_spec=True)

def classic_E1(K, RAND, address, debug=True):
    # print_header("classic_E1")
    hash = classic_Hash(K, RAND, address, L=6)
    # if debug: print("hash: %s" % (print_hex_little(hash)))
    return hash[0:4], hash[4:16]
测试

参考Core Spec v5.4 P921中写的测试用例进行测试。注意测试用例中的参数是大端的。

def classic_E1_test():
    print_header("classic_E1_test")

    print_header("10.1 FOUR TESTS OF E1")
    print_header("case 1")
    RAND = bytes.fromhex("00000000000000000000000000000000")
    address = bytes.fromhex("000000000000")
    K = bytes.fromhex("00000000000000000000000000000000")

    sres, aco = classic_E1(K, RAND, address)

    sres_exp = bytes.fromhex('056c0fe6')
    print_result_with_exp(sres == sres_exp)
    aco_exp = bytes.fromhex('48afcdd4bd40fef76693b113')
    print_result_with_exp(aco == aco_exp)



    print_header("case 2")
    RAND = bytes.fromhex("bc3f30689647c8d7c5a03ca80a91eceb")
    address = bytes.fromhex("7ca89b233c2d")
    K = bytes.fromhex("159dd9f43fc3d328efba0cd8a861fa57")

    sres, aco = classic_E1(K, RAND, address)

    sres_exp = bytes.fromhex('8d5205c5')
    print_result_with_exp(sres == sres_exp)
    aco_exp = bytes.fromhex('3ed75df4abd9af638d144e94')
    print_result_with_exp(aco == aco_exp)



    print_header("case 3")
    RAND = bytes.fromhex("0891caee063f5da1809577ff94ccdcfb")
    address = bytes.fromhex("c62f19f6ce98")
    K = bytes.fromhex("45298d06e46bac21421ddfbed94c032b")

    sres, aco = classic_E1(K, RAND, address)

    sres_exp = bytes.fromhex('00507e5f')
    print_result_with_exp(sres == sres_exp)
    aco_exp = bytes.fromhex('2a5f19fbf60907e69f39ca9f')
    print_result_with_exp(aco == aco_exp)



    print_header("case 4")
    RAND = bytes.fromhex("0ecd61782b4128480c05dc45542b1b8c")
    address = bytes.fromhex("f428f0e624b3")
    K = bytes.fromhex("35949a914225fabad91995d226de1d92")

    sres, aco = classic_E1(K, RAND, address)

    sres_exp = bytes.fromhex('80e5629c')
    print_result_with_exp(sres == sres_exp)
    aco_exp = bytes.fromhex('a6fe4dcde3924611d3cc6ba1')
    print_result_with_exp(aco == aco_exp)

测试结果如下:

###################################################################################
#                                 classic_E1_test                                 #
###################################################################################
###################################################################################
#                              10.1 FOUR TESTS OF E1                              #
###################################################################################
###################################################################################
#                                     case 1                                      #
###################################################################################
key: 00000000000000000000000000000000
plain_text: 00000000000000000000000000000000
K1: 00000000000000000000000000000000
K2: 4697b1baa3b7100ac537b3c95a28ac64
K3: ecabaac66795580df89af66e66dc053d
K4: 8ac3d8896ae9364943bfebd4969b68a0
K5: 5d57921fd5715cbb22c1be7bbc996394
K6: 2a61b8343219fdfb1740e6511d41448f
K7: dd0480dee731d67f01a2f739da6f23ca
K8: 3ad01cd1303e12a1cd0fe0a8af82592c
K9: 7dadb2efc287ce75061302904f2e7233
K10: c08dcfa981e2c4272f6c7a9f52e11538
K11: fc2042c708e409555e8c147660ffdfd7
K12: fa0b21001af9a6b9e89e624cd99150d2
K13: 18b40784ea5ba4c80ecb48694b4e9c35
K14: 454d54e5253c0c4a8b3fcca7db6baef4
K15: 2df37c6d9db52674f29353b0f011ed83
K16: b60316733b1e8e70bd861b477e2456f1
K17: 884697b1baa3b7100ac537b3c95a28ac
round  1: 00000000000000000000000000000000
round  2: 78d19f9307d2476a523ec7a8a026042a
round  3: 600265247668dda0e81c07bbb30ed503
round  4: d7552ef7cc9dbde568d80c2215bc4277
round  5: fb06bef32b52ab8f2a4f2b6ef7f6d0cd
round  6: b46b711ebb3cf69e847a75f0ab884bdd
round  7: c585f308ff19404294f06b292e978994
round  8: 2665fadb13acf952bf74b4ab12264b9f
key: e9e5dfc1b3a79583e9e5dfc1b3a79583
plain_text: 158ffe43352085e8a5ec7a88e1ff2ba8
K1: e9e5dfc1b3a79583e9e5dfc1b3a79583
K2: 7595bf57e0632c59f435c16697d4c864
K3: e31b96afcc75d286ef0ae257cbbc05b7
K4: 0d2a27b471bc0108c6263aff9d9b3b6b
K5: 98d1eb5773cf59d75d3b17b3bc37c191
K6: fd2b79282408ddd4ea0aa7511133336f
K7: 331227756638a41d57b0f7e071ee2a98
K8: aa0dd8cc68b406533d0f1d64aabacf20
K9: 669291b0752e63f806fce76f10e119c8
K10: ef8bdd46be8ee0277e9b78adef1ec154
K11: f3902eb06dc409cfd78384624964bf51
K12: 7d72702b21f97984a721c99b0498239d
K13: 532e60bceaf902c52a06c2c283ecfa32
K14: 181715e5192efb2a64129668cf5d9dd4
K15: 83017c1434342d4290e961578790f451
K16: 2603532f365604646ff65803795ccce5
K17: 882f7c907b565ea58dae1c928a0dcf41
round  1: 158ffe43352085e8a5ec7a88e1ff2ba8
round  2: 0b5cc75febcdf7827ca29ec0901b6b5b
round  3: e4278526c8429211f7f2f0016220aef4
added ->: f1b68365fd6217f952de6a89831fd95c
round  4: d0304ad18337f86040145d27aa5c8153
round  5: 84db909d213bb0172b8b6aaf71bf1472
round  6: f835f52921e903dfa762f1df5abd7f95
round  7: ae6c0b4bb09f25c6a5d9788a31b605d1
round  8: 744a6235b86cc0b853cc9f74f6b65311
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 2                                      #
###################################################################################
key: 159dd9f43fc3d328efba0cd8a861fa57
plain_text: bc3f30689647c8d7c5a03ca80a91eceb
K1: 159dd9f43fc3d328efba0cd8a861fa57
K2: 326558b3c15551899a97790e65ff669e
K3: 62e879b65b9f53bbfbd020c624b1d682
K4: 73415f30bac8ab61f410adc9442992db
K5: 5093cfa1d31c1c48acd76df030ea3c31
K6: 0b4acc2b8f1f694fc7bd91f4a70f3009
K7: 2ca43fc817947804ecff148d50d6f6c6
K8: 3fcd73524b533e00b7f7825bea2040a4
K9: 6c67bec76ae8c8cc4d289f69436d3506
K10: 95ed95ee8cb97e61d75848464bffb379
K11: ff566c1fc6b9da9ac502514550f3e9d2
K12: ab5ce3f5c887d0f49b87e0d380e12f47
K13: a2cab6f95eac7d655dbe84a6cd4c47f5
K14: f5caff88af0af8c42a20b5bbd2c8b460
K15: 185099c1131cf97001e2f36fda415025
K16: a0ebb82676bc75e8378b189eff3f6b1d
K17: cf5b348aaee27ae332b4f1bfa10289a6
round  1: bc3f30689647c8d7c5a03ca80a91eceb
round  2: 3e950edf197615638cc19c09f8fedc9b
round  3: 6a7640791cb536678936c5ecd4ae5a73
round  4: fca2c022a577e2ffb2aa007589693ec7
round  5: e97f8ea4ed1a6f4a36ffc179dc6bb563
round  6: 38b07261d7340d028749de1773a415c7
round  7: 58241f1aed7c1c3e047d724331a0b774
round  8: 3d1aaeff53c0910de63b9788b13c490f
key: fe78b835f26468ab069fd3991b086fda
plain_text: 2e4b417b9a2a9cfd7d8417d9a6a556eb
K1: fe78b835f26468ab069fd3991b086fda
K2: 095c5a51c6fa6d3ac1d57fa19aa382bd
K3: 1af866df817fd9f4ec00bc704192cffc
K4: f4a8a059c1f575f076f5fbb24bf16590
K5: 8c9d18d9356a9954d341b4286e88ea1f
K6: 5c958d370102c9881bf753e69c7da029
K7: 7eb2985c3697429fbe0da334bb51f795
K8: af900f4b63a1138e2874bfb7c628b7b8
K9: 834c8588dd8f3d4f31117a488420d69b
K10: bc2b9b81c15d9a80262f3f48e9045895
K11: f08608c9e39ad3147cba61327919c958
K12: 2d4131decf4fa3a959084714a9e85c11
K13: c934fd319c4a2b5361fa8eef05ae9572
K14: 4904c17aa47868e40471007cde3a97c0
K15: ea5e28687e97fa3f833401c86e6053ef
K16: 1168f58252c4ecfccafbdb3af857b9f2
K17: b3440f69ef951b78b5cbd6866275301b
round  1: 2e4b417b9a2a9cfd7d8417d9a6a556eb
round  2: b8bca81d6bb45af9d92beadd9300f5ed
round  3: 351aa16dec2c3a4787080249ed323eae
added ->: 1b65e2167656d6bafa8c19904bd79445
round  4: 2ce8fef47dda6a5bee74372e33e478a2
round  5: 572787f563e1643c1c862b7555637fb4
round  6: 16b4968c5d02853c3a43aa4cdb5f26ac
round  7: 10e4120c7cccef9dd4ba4e6da8571b01
round  8: f9081772498fed41b6ffd72b71fcf6c6
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 3                                      #
###################################################################################
key: 45298d06e46bac21421ddfbed94c032b
plain_text: 0891caee063f5da1809577ff94ccdcfb
K1: 45298d06e46bac21421ddfbed94c032b
K2: 8f03e1e1fe1c191cad35a897bc400597
K3: 4f2ce3a092dde854ef496c8126a69e8e
K4: 968caee2ac6d7008c07283daec67f2f2
K5: ab0d5c31f94259a6bf85ee2d22edf56c
K6: dfb74855c0085ce73dc17b84bfd50a92
K7: 8f888952662b3db00d4e904e7ea53b5d
K8: 5e18bfcc07799b0132db88cd6042f599
K9: bfca91ad9bd3d1a06c582b1d5512dddf
K10: a88bc477e3fa1d5a59b5e6cf793c7a41
K11: f3cfb8dac8aea2a6a8ef95af3a2a2767
K12: 77beb90670c5300b03aa2b2232d3d40c
K13: b578373650af36a06e19fe335d726d32
K14: 6bcee918c7d0d24dfdf42237fcf99d53
K15: 399f158241eb3e079f45d7b96490e7ea
K16: 1bcfbe98ecde2add52aa63ea79fb917a
K17: ee8bc03ec08722bc2b075492873374af
round  1: 0891caee063f5da1809577ff94ccdcfb
round  2: 1c6ca013480a685c1b28e0317f7167e1
round  3: 06b4915f5fcc1fc551a52048f0af8a26
round  4: 077a92b040acc86e6e0a877db197a167
round  5: 7204881fb300914825fdc863e8ceadf3
round  6: 27031131d86cea2d747deb4f756143aa
round  7: fc8c13d49149b1ce8d86f96e44a00065
round  8: 04ef5f5a7ddf846cda0a07782fc23866
key: 2ecc6cc797cc41a2ab02007f6af396ae
plain_text: d989d7a40cde7032d17b52f8117b69d5
K1: 2ecc6cc797cc41a2ab02007f6af396ae
K2: acfaef7609c12567d537ae1cf9dc2198
K3: 079c8ff9b73d428df879906a0b87a6c8
K4: 19f2710baf403a494193d201f3a8c439
K5: d623a6498f915cb2c8002765247b2f5a
K6: 900109093319bc30108b3d9434a77a72
K7: e28e2ee6e72e7f4e5b5c11f10d204228
K8: 8e455cd11f8b9073a2dfa5413c7a4bc5
K9: 28afb26e2c7a64238c41cefc16c53e74
K10: d08dcafc2096395ba0d2dddd0e471f4d
K11: fcffdcc3ad8faae091a7055b934f87c1
K12: f8df082d77060252c02d91e55bd6a7d6
K13: bef3706e523d708e8a44147d7508bc35
K14: 3e98ab283ca2422d56a56cf8b06caeb3
K15: 87ad9625d06645d22598dd5ef811ea2c
K16: 8bd3db0cc8168009e5da90877e13a36f
K17: 0e74631d813a8351ac7039b348c41b42
round  1: d989d7a40cde7032d17b52f8117b69d5
round  2: 8e76eb9a29b2ad5eea790db97aee37c1
round  3: 346bb7c35b2539676375aafe3af69089
added ->: edf48e675703a955b2f0fc062b71f95c
round  4: fafb6c1f3ebbd2477be2da49dd923f69
round  5: 7c72230df588060a3cf920f9b0a08f06
round  6: 55991df991db26ff00073a12baa3031d
round  7: 70ec682ff864375f63701fa4f6be5377
round  8: 172f12ec933da85504b4ea5c90f8f0ea
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 4                                      #
###################################################################################
key: 35949a914225fabad91995d226de1d92
plain_text: 0ecd61782b4128480c05dc45542b1b8c
K1: 35949a914225fabad91995d226de1d92
K2: ea6b3dcccc8ee5d88de349fa5010404f
K3: 920f3a0f2543ce535d4e7f25ad80648a
K4: ad47227edf9c6874e80ba80ebb95d2c9
K5: 81a941ca7202b5e884ae8fa493ecac3d
K6: bcde1520bee3660e86ce2f0fb78b9157
K7: c8eee7423d7c6efa75ecec0d2cd969d3
K8: 910b3f838a02ed441fbe863a02b4a1d0
K9: 56c647c1e865eb078348962ae070972d
K10: 883965da77ca5812d8104e2b640aec0d
K11: 61d4cb7e4f8868a283327806a9bd8d4d
K12: 9f57de3a3ff310e21dc1e696ce060304
K13: 7aa1d8adc1aeed7127ef9a18f6eb2d8e
K14: b4db9da3bf865912acd14904c7f7785d
K15: a13d7141ef1f6c7d867e3d175467381b
K16: 08b2bc058e50d6141cdd566a307e1acc
K17: 057b2b4b4be5dc0ac49e50489b8006c9
round  1: 0ecd61782b4128480c05dc45542b1b8c
round  2: 8935e2e263fbc4b9302cabdfc06bce3e
round  3: b4c8b878675f184a0c72f3dab51f8f05
round  4: 77ced9f2fc42bdd5c6312b87fc2377c5
round  5: fe28e8056f3004d60bb207e628b39cf2
round  6: 1f2ba92259d9e88101518f145a33840f
round  7: cc9b5d0218d29037e88475152ebebb2f
round  8: b04d352bedc02682e4a7f59d7cda1dba
key: 1e717950f5828f3930fe4a9395858815
plain_text: 5cfacc773bae995cd7f1b81e7c9ec7df
K1: 1e717950f5828f3930fe4a9395858815
K2: d1623369b733d98bbc894f75866c544c
K3: 4abf27664ae364cc8a7e5bcf88214cc4
K4: 2aaedda8dc4933dd6aeaf6e5c0d5a482
K5: bc7f8ab2d86000f47b1946cc8d7a7a2b
K6: 6b28544cb13ec6c5d98470df2cf900b7
K7: 1be840d9107f2c9523f66bb19f5464a1
K8: 61d6fb1aa2f0c2b26fb2a3d6de8c177c
K9: adabfc82570c568a233173099f23f4c2
K10: b7df6b55ad266c0f1ff7452101f59101
K11: 8e04a7282a2950dcbaea28f300e22de3
K12: 21362c114433e29bda3e4d51f803b0cf
K13: 710c8fd5bb3cbb5f132a7061de518bd9
K14: 0791de7334f4c87285809343f3ead3bd
K15: 4f47f0e5629a674bfcd13770eb3a3bd9
K16: 58a6d9a16a284cc0aead2126c79608a1
K17: a564082a0a98399f43f535fd5cefad34
round  1: 5cfacc773bae995cd7f1b81e7c9ec7df
round  2: d571ffa21d9daa797b1a0a3c962fc64c
round  3: e17c8e498a00f125bf654c938c23f36d
added ->: bd765a3eb1ae8a796856048df0c1bab2
round  4: a9727c26f2f06bd9920e83c8605dcd76
round  5: aeff751f146eab7e4626b2e2c9e2fb39
round  6: cf412b95f454d5185e67ca671892e5bd
round  7: 16165722fe4e07ef88f8056b17d89567
round  8: 28854cd6ad4a3c572b15490d4b81bc3f
>>>>>>>>>> Pass <<<<<<<<<<
>>>>>>>>>> Pass <<<<<<<<<<

Legacy Pairing相关函数

注意,Legacy Pairing过程用到了HMAC-SHA-256和A`r的加密函数。

Legacy Pairing只有PIN Code模式,当用户在一个或两个设备上输入完全相同的机密的PIN码时,根据配置和设备类型,两个蓝牙设备同时获得链接密钥。下图从概念上描述了PIN码输入和密钥获得过程。注意,如果PN码小于16字节,会用发起设备的地址(BD ADDR)来补足PIN值以生成初始密钥。那些E框代表了蓝牙链接密钥获得过程所使用的加密算法。

image-20240523212619621

完成链接密钥生成后,设备通过互相认证彼此来完成配对,以验证他们拥有相同的链接密钥。蓝牙配对中使用的PN码可以是1到16个字节的二进制数或更常见的字母数字字符。对于低风险应用场景,典型的4位数PN码可能是足够的;对于要求更高安全级别的设备,应当使用更长的PN码(例如,8个字符的字母数字)。

可以看到,这里加密只用到了E21和E22算法。

E21函数实现
概述

计算公式如下,就是用A'r进行计算:

image-20240523213741821

算法框图如下。

image-20240523213907998

算法实现

如下所示,输入参数后,最终输出128bit的值。

def classic_E21(RAND, address, debug=True):
    # print_header("classic_E21")
    tmp_const = 6 * pow(2, 120) # to bytes array
    tmp_const = tmp_const.to_bytes(length=16,byteorder='little',signed=False)
    # tmp_const = bytes.fromhex("00000000000000000000000000000006")
    X = saffer_bytewise_xor_arr(RAND, tmp_const)
    Y = (address + address + address)[0:16]

    if debug: print("X: %s" % (print_hex_little(X)))
    if debug: print("Y: %s" % (print_hex_little(Y)))

    return encrypt_Ar(X, Y, is_bt_spec=True)
测试

参考Core Spec v5.4 P926中写的测试用例进行测试。

def classic_E21_test():
    print_header("classic_E21_test")

    print_header("10.2 FOUR TESTS OF E21")
    print_header("case 1")
    RAND = bytes.fromhex("00000000000000000000000000000000")
    address = bytes.fromhex("000000000000")

    Ka = classic_E21(RAND, address)

    Ka_exp = bytes.fromhex('d14ca028545ec262cee700e39b5c39ee')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 2")
    RAND = bytes.fromhex("2dd9a550343191304013b2d7e1189d09")
    address = bytes.fromhex("cac4364303b6")

    Ka = classic_E21(RAND, address)

    Ka_exp = bytes.fromhex('e62f8bac609139b3999aedbc9d228042')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 3")
    RAND = bytes.fromhex("dab3cffe9d5739d1b7bf4a667ae5ee24")
    address = bytes.fromhex("02f8fd4cd661")

    Ka = classic_E21(RAND, address)

    Ka_exp = bytes.fromhex('b0376d0a9b338c2e133c32b69cb816b3')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 4")
    RAND = bytes.fromhex("13ecad08ad63c37f8a54dc56e82f4dc1")
    address = bytes.fromhex("9846c5ead4d9")

    Ka = classic_E21(RAND, address)

    Ka_exp = bytes.fromhex('5b61e83ad04d23e9d1c698851fa30447')
    print_result_with_exp(Ka == Ka_exp)

测试结果如下:

###################################################################################
#                                classic_E21_test                                 #
###################################################################################
###################################################################################
#                             10.2 FOUR TESTS OF E21                              #
###################################################################################
###################################################################################
#                                     case 1                                      #
###################################################################################
X: 00000000000000000000000000000006
Y: 00000000000000000000000000000000
key: 00000000000000000000000000000006
plain_text: 00000000000000000000000000000000
K1: 00000000000000000000000000000006
K2: 4697b1baa3b7100ac537b3c95a28dc94
K3: ecabaac66795580df89af66e665d863d
K4: 8ac3d8896ae9364943bfebd4a2a768a0
K5: 5d57921fd5715cbb22c1bedb1c996394
K6: 2a61b8343219fdfb1740e9541d41448f
K7: dd0480dee731d67f01ba0f39da6f23ca
K8: 3ad01cd1303e12a18dcfe0a8af82592c
K9: 7dadb2efc287ce7b0c1302904f2e7233
K10: c08dcfa981e2f4572f6c7a9f52e11538
K11: fc2042c708658a555e8c147660ffdfd7
K12: fa0b21002605a6b9e89e624cd99150d2
K13: 18b407e44a5ba4c80ecb48694b4e9c35
K14: 454d57e8253c0c4a8b3fcca7db6baef4
K15: 2d0b946d9db52674f29353b0f011ed83
K16: 76c316733b1e8e70bd861b477e2456f1
K17: 8e4697b1baa3b7100ac537b3c95a28ac
round  1: 00000000000000000000000000000000
round  2: 98611307ab76bbde9a86af1ce8cad412
round  3: 820999ad2e6618f4b578974beeedf9e7
added ->: 820999ad2e6618f4b578974beeedf9e7
round  4: acd6edec87581ac22dbdc64ea4ced3a2
round  5: 1c7798732f09fbfe25795a4a2fbc93c2
round  6: c05b88b56aa70e9c40c79bb81cd911bd
round  7: abacc71b481c84c798d1bdf3d62f7e20
round  8: e8204e1183ae85cf19edb2c86215b700
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 2                                      #
###################################################################################
X: 2dd9a550343191304013b2d7e1189d0f
Y: cac4364303b6cac4364303b6cac43643
key: 2dd9a550343191304013b2d7e1189d0f
plain_text: cac4364303b6cac4364303b6cac43643
K1: 2dd9a550343191304013b2d7e1189d0f
K2: 14c4335b2c43910c5dcc71d81a14242b
K3: 55bfb712cba168d1a48f6e74cd9f4388
K4: 2a2b3aacca695caef2821b0fb48cc253
K5: a06aab22d9a287384042976b4b6b00ee
K6: c229d054bb72e8eb230e6dcdb32d16b7
K7: 23c4812ab1905ddf77dedaed4105649a
K8: 40d87e272a7a1554ae2e85e3638cdf52
K9: bdc064c6a39f6b84fe40db359f62a3c4
K10: 58228db841ce3cee983aa721f36aa1b9
K11: a815bacd6fa747a0d4f52883ac63ebe7
K12: a9ce513b38ea006c333ecaaefcf1d0f8
K13: 3635e074792d4122130e5b824e52cd60
K14: 511bdb61bb28de72a5d794bffbf407df
K15: a32f5f21044b6744b6d913b13cdb4c0a
K16: 9722bbaeef281496ef8c23a9d41e92f4
K17: 807370560ad7e8a13a054a65a03b4049
round  1: cac4364303b6cac4364303b6cac43643
round  2: e169f788aad45a9011f11db5270b1277
round  3: 540f9c76652e92c44987c617035037bf
added ->: 9ed3d23566e45c007fcac9a1c9146dfc
round  4: 83659a41675f7171ea57909dc5a79ab4
round  5: 0b9382d0ed4f2fccdbb69d0db7b130a4
round  6: c6ebda0f8f489792f09c189568226c1f
round  7: 75a8aba07e69c9065bcd831c40115116
round  8: 57a6e279dcb764cf7dd6a749dd60c735
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 3                                      #
###################################################################################
X: dab3cffe9d5739d1b7bf4a667ae5ee22
Y: 02f8fd4cd66102f8fd4cd66102f8fd4c
key: dab3cffe9d5739d1b7bf4a667ae5ee22
plain_text: 02f8fd4cd66102f8fd4cd66102f8fd4c
K1: dab3cffe9d5739d1b7bf4a667ae5ee22
K2: e315a8a65d809ec7c289e69c899fbdcc
K3: df6a119bb50945fc8a3394e7216448f3
K4: 87fe86fb0d58b5dd0fb3b6b1dab51d07
K5: 36cc253c506c0021c91fac9d8c469e90
K6: d5fda00f113e303809b7f7d78a1a2b0e
K7: c14b5edc10cabf16bc2a2ba4a8ae1e40
K8: 74c6131afc8dce7e11b03b1ea8610c16
K9: 346cfc553c6cbc9713edb55f4dcbc96c
K10: bddf027cb059d58f0509f8963e9bdec6
K11: 8eb9e040c36c4c0b4a7fd3dd354d53c4
K12: c6ffecdd5e135b20879b9dfa4b34bf51
K13: bf12f5a6ba08dfc4fda4bdfc68c997d9
K14: 37c4656b9215f3c959ea688fb64ad327
K15: e87bb0d86bf421ea4f779a8eee3a866c
K16: faa471e934fd415ae4c0113ec7f0a5ad
K17: 95204a80b8400e49db7cf6fd2fd40d9a
round  1: 02f8fd4cd66102f8fd4cd66102f8fd4c
round  2: ef85ff081b8709405e19f3e275cec7dc
round  3: aa25c21bf577d92dd97381e3e9edcc54
added ->: a81dbf5723d8dbd524bf5782ebe5c918
round  4: 9e69ce9b53caec3990894d2baed41e0d
round  5: a5460fa8cedca48a14fd02209e01f02e
round  6: 92b33f11eadcacc5a43dd05f13d334dd
round  7: fb0541aa5e5df1a61c51aef606eb5a41
round  8: f0bbd2b94ae374346730581fc77a9c98
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 4                                      #
###################################################################################
X: 13ecad08ad63c37f8a54dc56e82f4dc7
Y: 9846c5ead4d99846c5ead4d99846c5ea
key: 13ecad08ad63c37f8a54dc56e82f4dc7
plain_text: 9846c5ead4d99846c5ead4d99846c5ea
K1: 13ecad08ad63c37f8a54dc56e82f4dc7
K2: ad04f127bed50b5e671d6510d392eaed
K3: 57ad159e5774fa222f2f3039b9cd5101
K4: 9a1e9e1068fede02ef90496e25fd8e79
K5: 378dce167db62920b0b392f7cfca316e
K6: db4277795c87286faee6c9e9a6b71a93
K7: ec01aa2f5a8a793b36c1bb858d254380
K8: 2921a66cfa5bf74ac535424564830e98
K9: 07018e45aab61b3c3726ee3d57dbd5f6
K10: 627381f0fa4c02b0c7d3e7dfbffc3333
K11: 33b57c925bd5551999f716e138efbe79
K12: a6dc7f9aa95bcc9243aebd12608f657a
K13: a6a6db00fd8c72a28ea57ea542f6e102
K14: dcf3377daeb2e24e61f0ad6620951c1f
K15: 621240b9506b462a7fa250da41844626
K16: ae297810f01f43dc35756cd119ee73d6
K17: b959835ec2501ad3894f8b8f1f4257f9
round  1: 9846c5ead4d99846c5ead4d99846c5ea
round  2: 97374e18cdd0a6f7a5aa49d1ac875c84
round  3: 9dd3260373edd9d5f4e774826b88fd2d
added ->: 0519ebe9a7c6719331d1485bf3cec2c7
round  4: 40ec6563450299ac4e120d88672504d6
round  5: 57287bbb041bd6a56c2bd931ed410cd4
round  6: 66affa66a8dcd36e36bf6c3f1c6a276e
round  7: 450e65184fd8c72c578d5cdecb286743
round  8: e5eb180b519a4e673f21b7c4f4573f3d
>>>>>>>>>> Pass <<<<<<<<<<
E22函数实现
概述

计算公式如下,需要对PIN进行扩展。然后就是用A'r进行计算:

image-20240523214115658

算法框图如下。

image-20240523214144746

算法实现

如下所示,输入参数后,最终输出128bit的值。

def classic_E22(RAND, PIN, address, L=16, debug=True):
    # print_header("classic_E22")
    L_sub = min(16, L + 6)
    PIN_sub = (PIN + address)[:L_sub]
    tmp_const = L_sub * pow(2, 120) # to bytes array
    tmp_const = tmp_const.to_bytes(length=16,byteorder='little',signed=False)
    X = (PIN_sub + PIN_sub + PIN_sub)[0:16]
    Y = saffer_bytewise_xor_arr(RAND, tmp_const)

    if debug: print("X: %s" % (print_hex_little(X)))
    if debug: print("Y: %s" % (print_hex_little(Y)))

    return encrypt_Ar(X, Y, is_bt_spec=True)
测试

参考Core Spec v5.4 P929中写的测试用例进行测试。

def classic_E22_test():
    print_header("classic_E22_test")

    print_header("10.3 THREE TESTS OF E22")
    print_header("case 1")
    RAND = bytes.fromhex("001de169248850245a5f7cc7f0d6d633")
    PIN = bytes.fromhex("d5a51083a04a1971f18649ea8b79311a")
    address = bytes.fromhex("000000000000")

    Ka = classic_E22(RAND, PIN, address)

    Ka_exp = bytes.fromhex('539e4f2732e5ae2de1e0401f0813bd0d')
    print_result_with_exp(Ka == Ka_exp)



    print_header("case 2")
    RAND = bytes.fromhex("67ed56bfcf99825f0c6b349369da30ab")
    PIN = bytes.fromhex("7885b515e84b1f082cc499976f1725ce")
    address = bytes.fromhex("000000000000")

    Ka = classic_E22(RAND, PIN, address)

    Ka_exp = bytes.fromhex('04435771e03a9daceb8bb1a493ee9bd8')
    print_result_with_exp(Ka == Ka_exp)



    print_header("case 3")
    RAND = bytes.fromhex("40a94509238664f244ff8e3d13b119d3")
    PIN = bytes.fromhex("1ce44839badde30396d03c4c36f23006")
    address = bytes.fromhex("000000000000")

    Ka = classic_E22(RAND, PIN, address)

    Ka_exp = bytes.fromhex('9cde4b60f9b5861ed9df80858bac6f7f')
    print_result_with_exp(Ka == Ka_exp)






    print_header("10.4 TESTS OF E22 WITH PIN AUGMENTING")
    print_header("case 1")
    RAND = bytes.fromhex("24b101fd56117d42c0545a4247357048")
    PIN = bytes.fromhex("fd397c7f5c1f937cdf82d8816cc377e2")
    address = bytes.fromhex("000000000000")

    Ka = classic_E22(RAND, PIN, address, L=len(PIN))

    Ka_exp = bytes.fromhex('a5f2adf328e4e6a2b42f19c8b74ba884')
    print_result_with_exp(Ka == Ka_exp)



    print_header("case 2")
    RAND = bytes.fromhex("321964061ac49a436f9fb9824ac63f8b")
    PIN = bytes.fromhex("ad955d58b6b8857820ac1262d617a6")
    address = bytes.fromhex("0314c0642543")

    Ka = classic_E22(RAND, PIN, address, L=len(PIN))

    Ka_exp = bytes.fromhex('c0ec1a5694e2b48d54297911e6c98b8f')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 3")
    RAND = bytes.fromhex("d4ae20c80094547d7051931b5cc2a8d6")
    PIN = bytes.fromhex("e1232e2c5f3b833b3309088a87b6")
    address = bytes.fromhex("fabecc58e609")

    Ka = classic_E22(RAND, PIN, address, L=len(PIN))

    Ka_exp = bytes.fromhex('d7b39be13e3692c65b4a9e17a9c55e17')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 4")
    RAND = bytes.fromhex("272b73a2e40db52a6a61c6520549794a")
    PIN = bytes.fromhex("549f2694f353f5145772d8ae1e")
    address = bytes.fromhex("20487681eb9f")

    Ka = classic_E22(RAND, PIN, address, L=len(PIN))

    Ka_exp = bytes.fromhex('9ac64309a37c25c3b4a584fc002a1618')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 5")
    RAND = bytes.fromhex("7edb65f01a2f45a2bc9b24fb3390667e")
    PIN = bytes.fromhex("2e5a42797958557b23447ca8")
    address = bytes.fromhex("04f0d2737f02")

    Ka = classic_E22(RAND, PIN, address, L=len(PIN))

    Ka_exp = bytes.fromhex('d3af4c81e3f482f062999dee7882a73b')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 6")
    RAND = bytes.fromhex("26a92358294dce97b1d79ec32a67e81a")
    PIN = bytes.fromhex("05fbad03f52fa9324f7732")
    address = bytes.fromhex("b9ac071f9d70")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('be87b44d079d45a08a71d15208c5cb50')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 7")
    RAND = bytes.fromhex("0edef05327eab5262430f21fc91ce682")
    PIN = bytes.fromhex("8210e47390f3f48c32b3")
    address = bytes.fromhex("7a3cdfe377d1")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('bf0706d76ec3b11cce724b311bf71ff5')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 8")
    RAND = bytes.fromhex("86290e2892f278ff6c3fb917b020576a")
    PIN = bytes.fromhex("3dcdffcfd086802107")
    address = bytes.fromhex("791a6a2c5cc3")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('cdb0cc68f6f6fbd70b46652de3ef3ffb')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 9")
    RAND = bytes.fromhex("3ab52a65bb3b24a08eb6cd284b4b9d4b")
    PIN = bytes.fromhex("d0fb9b6838d464d8")
    address = bytes.fromhex("25a868db91ab")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('983218718ca9aa97892e312d86dd9516')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 10")
    RAND = bytes.fromhex("a6dc447ff08d4b366ff96e6cf207e179")
    PIN = bytes.fromhex("9c57e10b4766cc")
    address = bytes.fromhex("54ebd9328cb6")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('9cd6650ead86323e87cafb1ff516d1e0')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 11")
    RAND = bytes.fromhex("3348470a7ea6cc6eb81b40472133262c")
    PIN = bytes.fromhex("fcad169d7295")
    address = bytes.fromhex("430d572f8842")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('98f1543ab4d87bd5ef5296fb5e3d3a21')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 12")
    RAND = bytes.fromhex("0f5bb150b4371ae4e5785293d22b7b0c")
    PIN = bytes.fromhex("b10d068bca")
    address = bytes.fromhex("b44775199f29")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('c55070b72bc982adb972ed05d1a74ddb')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 13")
    RAND = bytes.fromhex("148662a4baa73cfadb55489159e476e1")
    PIN = bytes.fromhex("fb20f177")
    address = bytes.fromhex("a683bd0b1896")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('7ec864df2f1637c7e81f2319ae8f4671')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 14")
    RAND = bytes.fromhex("193a1b84376c88882c8d3b4ee93ba8d5")
    PIN = bytes.fromhex("a123b9")
    address = bytes.fromhex("4459a44610f6")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('ac0daabf17732f632e34ef193658bf5d')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 15")
    RAND = bytes.fromhex("1453db4d057654e8eb62d7d62ec3608c")
    PIN = bytes.fromhex("3eaf")
    address = bytes.fromhex("411fbbb51d1e")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('1674f9dc2063cc2b83d3ef8ba692ebef')
    print_result_with_exp(Ka == Ka_exp)


    print_header("case 16")
    RAND = bytes.fromhex("1313f7115a9db842fcedc4b10088b48d")
    PIN = bytes.fromhex("6d")
    address = bytes.fromhex("008aa9be62d5")

    Ka = classic_E22(RAND, PIN, address, len(PIN))

    Ka_exp = bytes.fromhex('38ec0258134ec3f08461ae5c328968a1')
    print_result_with_exp(Ka == Ka_exp)

测试结果如下:

###################################################################################
#                                classic_E22_test                                 #
###################################################################################
###################################################################################
#                             10.3 THREE TESTS OF E22                             #
###################################################################################
###################################################################################
#                                     case 1                                      #
###################################################################################
X: d5a51083a04a1971f18649ea8b79311a
Y: 001de169248850245a5f7cc7f0d6d623
key: d5a51083a04a1971f18649ea8b79311a
plain_text: 001de169248850245a5f7cc7f0d6d623
K1: d5a51083a04a1971f18649ea8b79311a
K2: 7317cdbff57f9b99f9810a2525b17cc7
K3: f08bd258adf1d4ae4a54d8ccb26220b2
K4: 91046cbb4ccc43db18d6dd36ca7313eb
K5: 67fb2336f4d9f069da58d11c82f6bd95
K6: 4fed702c75bd72c0d3d8f38707134c50
K7: 41c947f80cdc0464c50aa89070af314c
K8: 680eecfa8daf41c7109c9a5cb1f26d75
K9: 6e33fbd94d00ff8f72e8a7a0d2cebc4c
K10: f4d726054c6b948add99fabb5733ddc3
K11: 4eda2425546a24cac790f49ef2453b53
K12: cf2213624ed1510408a5a3e00b7333df
K13: d04b1a25b0b8fec946d5ecfa626d04c9
K14: 01e5611b0f0e140bdb64585fd3ae5269
K15: f15b2dc433f534f61bf718770a3698b1
K16: f990d0273d8ea2b9e0b45917a781c720
K17: f41b3cc13d4301297bb6bdfcb3e5a1dd
round  1: 001de169248850245a5f7cc7f0d6d623
round  2: 5f05c143347b59acae3cb002db23830f
round  3: c8f3e3300541a25b6ac5a80c3105f3c4
added ->: c810c45921c9f27f302424cbc1dbc9e7
round  4: bd5e0c3a97fa55b91a3bbbf306ebb978
round  5: 21c1a762c3cc33e75ce8976a73983087
round  6: 56d0df484345582f6b574a449ba155eb
round  7: 120cf9963fe9ff22993f7fdf9600d9b8
round  8: a6337400ad8cb47fefb91332f5cb2713
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 2                                      #
###################################################################################
X: 7885b515e84b1f082cc499976f1725ce
Y: 67ed56bfcf99825f0c6b349369da30bb
key: 7885b515e84b1f082cc499976f1725ce
plain_text: 67ed56bfcf99825f0c6b349369da30bb
K1: 7885b515e84b1f082cc499976f1725ce
K2: 72445901fdaf506beb036f4412512248
K3: 59f0e4982e97633e5e7fd133af8f2c5b
K4: b4946ec77a41bf7c729d191e33d458ab
K5: eb0b839f97bdf534183210678520bbef
K6: cff0bc4a94e5c8b2a2d24d9f59031e19
K7: 592430f14d8f93db95dd691af045776d
K8: 3b55b404222bf445a6a2ef5865247695
K9: a9714b86319ef343a28b87456416bd52
K10: e6598b24390b3a0bf2982747993b0d78
K11: 62051d8c51973073bff959b032c6e1e2
K12: 29e94f4ab73296c453c833e217a1a85b
K13: 0e255970b3e2fc235f59fc5acb10e8ce
K14: d0dfbb3361fee6d4ffe45babf1cd7abf
K15: c12eee4eb38b7a171f0f736003774b40
K16: 8f962523f1c0abd9a087a0dfb11643d3
K17: 24be1c66cf8b022f12f1fb4c60c93fd1
round  1: 67ed56bfcf99825f0c6b349369da30bb
round  2: 6b160b66a1f6c26c1f3432f463ef5aa1
round  3: 3f22046c964c3e5ca2a26ec9a76a9f67
added ->: 580f5ad359e5c003ae0da25ace44cfdc
round  4: 87aa61fc0ff88e744c195249b9a33632
round  5: 83dcf592a854226c4dcd94e1ecf1bc75
round  6: dee0d13a52e96bcf7c72045a21609fc6
round  7: 08488005761e6c7c4dbb203ae453fe3a
round  8: 0d81e89bddde7a7065316c47574feb8f
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 3                                      #
###################################################################################
X: 1ce44839badde30396d03c4c36f23006
Y: 40a94509238664f244ff8e3d13b119c3
key: 1ce44839badde30396d03c4c36f23006
plain_text: 40a94509238664f244ff8e3d13b119c3
K1: 1ce44839badde30396d03c4c36f23006
K2: 6dd97a8f91d628be4b18157af1a9dcba
K3: fef9583d5f55fd4107ad832a725db744
K4: fc3893507016d7c1db2bd034a230a069
K5: 0834d04f3e7e1f7f85f0c1db685ab118
K6: 1852397f9a3723169058e9b62bb3682b
K7: 6c10da21d762ae4ac1ba22a96d9007b4
K8: 9aa23658b90470a78d686344b8a9b0e7
K9: 137dee3bf879fe7bd02fe6d888e84f16
K10: 466e315a1863f47d0f93bc6827cf3450
K11: 0b33cf831465bb5c979e6224d7f79f7c
K12: 92770660268ede827810d707a0977d73
K13: 7be30ae4961cf24ca17625a77bb7a9f8
K14: be65574a33ae30e6e82dbd2826d3cc1a
K15: ed0ba7dd30d60a5e69225f0a33011e5b
K16: 765c990f4445e52b39e6ed6105ad1c4f
K17: 52627bf9f35d94f30d5b07ef15901adc
round  1: 40a94509238664f244ff8e3d13b119c3
round  2: 0eac5288057d9947a24eabc1744c4582
round  3: 60b424f1082b0cc3bd61be7b4c0155f0
added ->: 205d69f82bb17031f9604c465fb26e33
round  4: 2c6b65a49d66af6566675afdd6fa7d7d
round  5: a2c537899665113a42f1ac24773bdc31
round  6: e26982980d79b21ed3e20f8c3e71ba96
round  7: e7b063c4e2e3110b89b7e1631c762dd5
round  8: 7a963e37b2c2e76b489cfe40a2cf00e5
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                      10.4 TESTS OF E22 WITH PIN AUGMENTING                      #
###################################################################################
###################################################################################
#                                     case 1                                      #
###################################################################################
X: fd397c7f5c1f937cdf82d8816cc377e2
Y: 24b101fd56117d42c0545a4247357058
key: fd397c7f5c1f937cdf82d8816cc377e2
plain_text: 24b101fd56117d42c0545a4247357058
K1: fd397c7f5c1f937cdf82d8816cc377e2
K2: 0f7aac9c9b53f308d9fdbf2c78e3c30e
K3: 0b8ac18d4bb44fad2efa115e43945abc
K4: 887b16b062a83bfa469772c25b456312
K5: 2248cbe6d299e9d3e8fd35a91178f65b
K6: b92af6237385bd31f8fb57fb1bdd824e
K7: 2bf5ffe84a37878ede2d4c30be60203b
K8: c9cb6cec60cb8a8f29b99fcf3e71e40f
K9: 5c2f8a702e4a45575b103b0cce8a91c6
K10: d453db0c9f9ddbd11e355d9a34d9b11b
K11: 32805db7e59c5ed4acabf38d27e3fece
K12: fde3a8eedfa3a12be09c1a8a00890fd7
K13: def07eb23f3a378f059039a2124bc4c2
K14: 2608c58f23d84a09b9ce95e5caac1ab4
K15: 0a7ed16481a623e56ee1442ffa74f334
K16: 12add59aca0d19532f1516979954e369
K17: dd43d02d39ffd6a386a4b98b4ac6eb23
round  1: 24b101fd56117d42c0545a4247357058
round  2: 838edfe1226266953ccba8379d873107
round  3: 8cd0c9283120aba89a7f9d635dd4fe3f
added ->: a881cad5673128ea5ad3f7211a096e67
round  4: 2648d9c618a622b10ef80c4dbaf68b99
round  5: b5a7d9e96f68b14ccebf361de3914d0f
round  6: 632a091e7eefe1336857ddafd1ff3265
round  7: 048531e9fd3efa95910540150f8b137b
round  8: 461814ec7439d412d0732f0a6f799a6a
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 2                                      #
###################################################################################
X: ad955d58b6b8857820ac1262d617a603
Y: 321964061ac49a436f9fb9824ac63f9b
key: ad955d58b6b8857820ac1262d617a603
plain_text: 321964061ac49a436f9fb9824ac63f9b
K1: ad955d58b6b8857820ac1262d617a603
K2: f281736f68e3d30b2ac7c67f125dc416
K3: 43c157f4c8b360387c32ab330f9c9aa8
K4: 3a3049945a298f6d076c19219c47c3cb
K5: c8e2eaa6d73b7de18f3228ab2173bc69
K6: 8623f44488222e66a293677cf30bf2bb
K7: f3e500902fba31db9bae50ef30e484a4
K8: 49d4b1137c18f4752dd9955a5a8d2f43
K9: 9d59c451989e74785cc097eda7e42ab8
K10: 251de25f3917dcd99c18646107a641fb
K11: 80b8f78cb1a49ec0c3e32a238e60fddf
K12: beb84f4d20a501e4a24ecfbde481902b
K13: 852571b44f35fd9d9336d3c1d2506656
K14: d0a0d510fb06ba76e69b8ee3ebc1b725
K15: c7ffd523f32a874ed4a93430a25976de
K16: 16cdcb25e62964876d951fdcc07030d3
K17: def32c0e12596f9582e5e3c52b303f52
round  1: 321964061ac49a436f9fb9824ac63f9b
round  2: 7c4a4ece1398681f4bafd309328b7770
round  3: 9672b00738bdfaf9bd92a855bc6f3afb
added ->: a48b1401228194bad23161d7f6357960
round  4: 9b30247aad3bf133712d034b46d21c68
round  5: 4492c25fda08083a768b4b5588966b23
round  6: 21ae346635714d2623041f269978c0ee
round  7: 9b56a3d0f8932f20c6a77a229514fb00
round  8: 6cd8492b2fd31a86978bcdf644eb08a8
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 3                                      #
###################################################################################
X: e1232e2c5f3b833b3309088a87b6fabe
Y: d4ae20c80094547d7051931b5cc2a8c6
key: e1232e2c5f3b833b3309088a87b6fabe
plain_text: d4ae20c80094547d7051931b5cc2a8c6
K1: e1232e2c5f3b833b3309088a87b6fabe
K2: 5f0812b47cd3e9a30d7707050fffa1f2
K3: 77b681944763244ffa3cd71b248b79b5
K4: e2814e90e04f485958ce58c9133e2be6
K5: 520acad20801dc639a2c6d66d9b79576
K6: c72255cdb61d42be72bd45390dd25ba5
K7: ebf04c02075bf459ec9c3ec06627d347
K8: a1363dd2812ee800a4491c0c74074493
K9: b0b6ba79493dc833d7f425be7b8dadb6
K10: 08cd23e536b9b9b53e85eb004cba3111
K11: fec22374c6937dcd26171f4d2edfada3
K12: 0f1a8ef5979c69ff44f620c2e007b6e4
K13: 901fb66f0779d6aad0c0fba1fe812cb5
K14: a0cab3cd15cd23603adc8d4474efb239
K15: 18edc3f4296dd6f1dea13f7c143117a1
K16: 8d3d52d700a379d72ded81687f7546c7
K17: 5927badfe602f29345f840bb53e1dea6
round  1: d4ae20c80094547d7051931b5cc2a8c6
round  2: 1f45f16be89794bef33e4547c9c0916a
round  3: b10d2f4ac941035263cee3552d774d2f
added ->: 65bb4f82c9d5572f131f764e7139f5e9
round  4: ead4dc34207b6ea721c62166e155aaad
round  5: f507944f3018e20586d81d7f326aae9d
round  6: fff450f4302a2b3571e8405e148346da
round  7: de558779589897f3402a90ee78c3f921
round  8: b2df0aa0c9f07fbbaa02f510a29cf540
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 4                                      #
###################################################################################
X: 549f2694f353f5145772d8ae1e204876
Y: 272b73a2e40db52a6a61c6520549795a
key: 549f2694f353f5145772d8ae1e204876
plain_text: 272b73a2e40db52a6a61c6520549795a
K1: 549f2694f353f5145772d8ae1e204876
K2: 42c855593d66b0c458fd28b95b6a5fbf
K3: 75d0a69ae49a2da92e457d767879df52
K4: b3aa7e7492971afaa0fb2b64827110df
K5: 9c8cf1604a98e9a503c342e272de5cf6
K6: d35bc2df6b85540a27642106471057d9
K7: b454dda74aeb4eff227ba48a58077599
K8: bcba6aec050116aa9b7c6a9b7314d796
K9: d41f8a9de0a716eb7167a1b6e321c528
K10: 5353449982247782d168ab43f17bc4d8
K11: 32cbc9cf1a81e36a45153972347ce4ac
K12: 5747619006cf4ef834c749f2c4b9feb6
K13: f9b68beba0a09d2a570a7dc88cc3c3c2
K14: 55718f9a4f0b1f9484e8c6b186a41a4b
K15: 4ecc29be1b4d78433f6aa30db974a7fb
K16: 8470a066ffb00cda7b08059599f919f5
K17: f39a36d74e960a051e1ca98b777848f4
round  1: 272b73a2e40db52a6a61c6520549795a
round  2: d7276dc8073f7677c31f855bde9501e2
round  3: 71aae503831133d19bc452da4d0e409b
added ->: 56d558a1671ee8fbf12518884857b9c1
round  4: f41a709c89ea80481aa3d2b9b2a9f8ca
round  5: 20fdda20f4a26b1bd38eb7f355a7be87
round  6: a70e316997eeed49a5a9ef9ba5e913b5
round  7: e66f2317a825f589f76b47b6aa6e73fb
round  8: 5f68f940440a9798e074776019804ada
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 5                                      #
###################################################################################
X: 2e5a42797958557b23447ca804f0d273
Y: 7edb65f01a2f45a2bc9b24fb3390666e
key: 2e5a42797958557b23447ca804f0d273
plain_text: 7edb65f01a2f45a2bc9b24fb3390666e
K1: 2e5a42797958557b23447ca804f0d273
K2: 18a97c856561eb23e71af8e9e1be4799
K3: 7c0908dcbc73201e17c4f7aa1ab8aec8
K4: 7cb58833602fbe4194c7cc797ce8c454
K5: f4dce7d607b5234562d0ebb2267b08b8
K6: 560b75c5545751fd8fa99fa4346e654b
K7: 32f10cefd8d3e6424c6f91f1437808af
K8: a934a46045be30fb3be3a5f3f7b18837
K9: a0f12e97c677a0e8ac415cd2c8a7ca88
K10: e27014c908785f5ca03e8c6a1da3bf13
K11: 1b4a4303bcc0b2e0f41c72d47654bd9f
K12: 4b1302a50046026d6c9054fc8387965a
K13: 58c334bb543d49eca562cdbe0280e0fc
K14: bdb60d383c692d06476b76646c8dec48
K15: 78c0162506be0b5953e8403c01028f93
K16: 24d7dbbe834dbd7b67f57fcf0d39d60f
K17: 2e74f1f3331c0f6585e87b2f715e187e
round  1: 7edb65f01a2f45a2bc9b24fb3390666e
round  2: 3436e12db8ffdc1265cb5a86da2fed0b
round  3: caed6af4226f67e4ad1914620803ef2a
added ->: b4c8cf04389eac4611b438993b935544
round  4: ee67c87d6f74bb75db98f68bff0192c1
round  5: 792398dcbeb8d10bdb07ae3c819e943c
round  6: e778b6e0c3e8e7edf90861c7916d97a8
round  7: 1fafddc7efa5f04c1dec1869d3f2d9bb
round  8: 3d7c326d074bd6aa222ea050f04a3c7f
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 6                                      #
###################################################################################
X: 05fbad03f52fa9324f7732b9ac071f9d
Y: 26a92358294dce97b1d79ec32a67e80a
key: 05fbad03f52fa9324f7732b9ac071f9d
plain_text: 26a92358294dce97b1d79ec32a67e80a
K1: 05fbad03f52fa9324f7732b9ac071f9d
K2: 2504c9691c04a18480c8802e922098c0
K3: 576b2791d1212bea8408212f2d43e77e
K4: 90ae36dcce8724adb618f912d1b27297
K5: bc492c42c9e87f56ec31af5474e9226e
K6: c135d1dbed32d9519acfb4169f3e1a10
K7: 83ccbdbbaf17889b7d18254dc9252fa1
K8: 80b90a1767d3f2848080802764e21711
K9: cc24e4a86e8eed129118fd3d5223a1dc
K10: 7b1e9c0eb9dab083574be7b7015a62c9
K11: 888e6d88cf4beb965cf7d4f32b696baa
K12: 6d642f3e5510b0b043a44daa2cf5eec0
K13: e224f85da2ab63a23e2a3a036e421358
K14: c8dc22aaa739e2cb85d6a0c08226c7d0
K15: a969aa818c6b324bae391bedcdd9d335
K16: 6974b6f2f07e4c55f2cc0435c45bebd1
K17: 134b925ebd98e6b93c14aee582062fcb
round  1: 26a92358294dce97b1d79ec32a67e80a
round  2: 0be20e3d76888e57b6bf77f97a8714fb
round  3: 1969667060764453257d906b7e58bd5b
added ->: 3f12892849c312c494542ea854bfa551
round  4: ac404205118fe771e54aa6f392da1153
round  5: 41795e89ae9a0cf776ffece76f47fd7a
round  6: 29ca9e2f87ca00370ef1633505bfba4b
round  7: 81fc891c3c6fd99acc00028a387e2366
round  8: e30b537e7a000e3d2424a9c0f04c4042
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 7                                      #
###################################################################################
X: 8210e47390f3f48c32b37a3cdfe377d1
Y: 0edef05327eab5262430f21fc91ce692
key: 8210e47390f3f48c32b37a3cdfe377d1
plain_text: 0edef05327eab5262430f21fc91ce692
K1: 8210e47390f3f48c32b37a3cdfe377d1
K2: c6be4c3e425e749b620a94c779e33a7e
K3: 2587cec2a4b8e4f996a9ed664350d5dd
K4: 70e4bf72834d9d3dbb7eb2c239216dc0
K5: 6696e1e7f8ac037e1fff3598f0c164e2
K6: 23dbfe4d0b561bea08fbcef25e49b648
K7: b03648acd021550edee904431a02f00c
K8: cb169220b7398e8f077730aa4bf06baa
K9: af602c2ba16a454649951274c2be6527
K10: 5d60b0a7a09d524143eca13ad680bc9c
K11: 9a2f39bfe558d9f562c5f09a5c3c0263
K12: 72cae8eebd7fabd9b1848333c2aab439
K13: 15f27ea11e83a51645d487b81371d7dc
K14: 36083c8666447e03d33846edf444eb12
K15: 0a3a8977dd48f3b6c1668578befadd02
K16: f06b6675d78ca0ee5b1761bdcdab516d
K17: cbc8a7952d33aa0496f7ea2d05390b23
round  1: 0edef05327eab5262430f21fc91ce692
round  2: 07ca3c7a7a6bcbc31d79a856d9cffc0e
round  3: 792ad2ac4e4559d1463714d2f161b6f4
added ->: 7708c2ff692f0ef7626706cd387d9c66
round  4: 7d8c71a9d7fbdcbd851bdf074550b100
round  5: b6fcaa45064ffd557e4b7b30cfbb83e0
round  6: b3416d391a0c26c558843debd0601e9e
round  7: abe4b498d9c36ea97b8fd27d7f813913
round  8: 8032104338a945ba044d102eabda3b22
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 8                                      #
###################################################################################
X: 3dcdffcfd086802107791a6a2c5cc33d
Y: 86290e2892f278ff6c3fb917b0205765
key: 3dcdffcfd086802107791a6a2c5cc33d
plain_text: 86290e2892f278ff6c3fb917b0205765
K1: 3dcdffcfd086802107791a6a2c5cc33d
K2: b4962f40d7bb19429007062a3c469521
K3: eb9ede6787dd196b7e340185562bf28c
K4: 2964e58aacf7287d1717a35b100ae23b
K5: 6abf9a314508fd61e486fa4e376c3f93
K6: 6da148b7ee2632114521842cbb274376
K7: df889cc34fda86f01096d52d116e620d
K8: 5eb04b147dc39d1974058761ae7b73fc
K9: 8426cc59eee391b2bd50cf8f1efef8b3
K10: 8b5d220a6300ade418da791dd8151941
K11: 82ba4ddef833f6a4d18b07aa011f2798
K12: ce63d98794682054e73d0359dad35ec4
K13: da794357652e80c70ad8b0715dbe33d6
K14: 732ef2c0c3220b31f3820c375e27bb29
K15: 3ce75a61d4b465b70c95d7ccd5799633
K16: 5df9bd2c3a17a840cdaafb76c171db7c
K17: 3f8364b089733d902bccb0cd3386846f
round  1: 86290e2892f278ff6c3fb917b0205765
round  2: 1ec59ffd3065f19991872a7863b0ef02
round  3: f817406f1423fc2fe33e25152679eaaf
added ->: 7e404e47861574d08f7dde02969941ca
round  4: e9c2a8fac22b8c7cf0c619e2b3f890ed
round  5: 444a8aac0efee1c02f8d38f8274b7b28
round  6: 9185f983db150b1bccab1e5c12eb63a1
round  7: 5eded2668f5916dfd036c09e87902886
round  8: 88a5291b4acbba009a85b7dd6a834b3b
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 9                                      #
###################################################################################
X: d0fb9b6838d464d825a868db91abd0fb
Y: 3ab52a65bb3b24a08eb6cd284b4b9d45
key: d0fb9b6838d464d825a868db91abd0fb
plain_text: 3ab52a65bb3b24a08eb6cd284b4b9d45
K1: d0fb9b6838d464d825a868db91abd0fb
K2: 2573f47b49dad6330a7a9155b7ae8ba1
K3: d2c5b8fb80cba13712905a589adaee71
K4: 5a3381511b338719fae242758dea0997
K5: e0a4d8ac27fbe2783b7bcb3a36a6224d
K6: 949324c6864deac3eca8e324853e11c3
K7: 6e67148088a01c2d4491957cc9ddc4aa
K8: 557431deab7087bb4c03fa27228f60c6
K9: a2551aca53329e70ade3fd2bb7664697
K10: 05d0ad35de68a364b54b56e2138738fe
K11: 1616a6b13ce2f2895c722e8495181520
K12: b12e78a1114847b01f6ed2f5a1429a23
K13: 316e144364686381944e95afd8a026bb
K14: 1ab551b88d39d97ea7a9fe136dbfe2e1
K15: 70e21ab08c23c7544524b64492b25cc9
K16: 35f730f2ae2b950a49a1bf5c8b9f8866
K17: 2f16924c22db8b74e2eadf1ba4ebd37c
round  1: 3ab52a65bb3b24a08eb6cd284b4b9d45
round  2: ad2ffdff408fcfab44941016a9199251
round  3: 2ddc17e570d7931a2b1d13f6ace928a5
added ->: 17914180cb12b7baa5d3e0dee734c5e0
round  4: 62c1db5cf31590d331ec40ad692e8df5
round  5: 9c8933bc361f4bde4d1bda2b5f8bb235
round  6: 9156db34136aa06655bf28a05be0596a
round  7: 84dcc292ed836c1c2d523f2a899a2ad5
round  8: 87bdcac878d777877f4eccf042cfee5e
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 10                                     #
###################################################################################
X: 9c57e10b4766cc54ebd9328cb69c57e1
Y: a6dc447ff08d4b366ff96e6cf207e174
key: 9c57e10b4766cc54ebd9328cb69c57e1
plain_text: a6dc447ff08d4b366ff96e6cf207e174
K1: 9c57e10b4766cc54ebd9328cb69c57e1
K2: 00a609f4d61db26993c8177e3ee2bba8
K3: 646d7b5f9aaa528384bda3953b542764
K4: a051a42212c0e9ad5c2c248259aca14e
K5: d1bd5e64930e7f838d8a33994462d8b2
K6: 5dc7e2291e32435665ebd6956bec3414
K7: 10552f45af63b0f15e2919ab37f64fe7
K8: c44d5717c114a58b09207392ebe341f8
K9: 6886e47b782325568eaf59715a75d8ff
K10: 8e1e335e659cd36b132689f78c147bda
K11: 8843efeedd5c2b7c3304d647f932f4d1
K12: 13785aaedd0adf67abb4f01872392785
K13: 837d7ca2722419e6be3fae35900c3958
K14: 93f8442973e7fccf2e7232d1d057c73a
K15: 8a7a9edffa3c52918bc6a45f57d91f5d
K16: f214a95d777f763c56109882c4b52c84
K17: 10e2ee92c5ea1ddc5eb010e55510c403
round  1: a6dc447ff08d4b366ff96e6cf207e174
round  2: 1ed26b96a306d7014f4e5c9ee523b73d
round  3: a53f526db18e3d7d53edbfc9711041ed
added ->: 031b9612411b884b3ce62da583172299
round  4: 9438be308ec83f35c560e2796f4e0559
round  5: b79a7b14386066d339f799c40479cb3d
round  6: ef232462228aa166438d10c34e17424b
round  7: 02d133fe40d15f1073673b36bba35abd
round  8: 275506a3d08c84e94cc58ed60054505e
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 11                                     #
###################################################################################
X: fcad169d7295430d572f8842fcad169d
Y: 3348470a7ea6cc6eb81b404721332620
key: fcad169d7295430d572f8842fcad169d
plain_text: 3348470a7ea6cc6eb81b404721332620
K1: fcad169d7295430d572f8842fcad169d
K2: b3479d4d4fd178c43e7bc5b0c7d8983c
K3: 7112462b37d82dd81a2a35d9eb43cb7c
K4: c5a7030f8497945ac7b84600d1d161fb
K5: 84b0c6ef4a63e4dff19b1f546d683df5
K6: f4023edfc95d1e79ed4bb4de9b174f5d
K7: ea38dd9a093ac9355918632c90c79993
K8: dbba01e278ddc76380727f5d7135a7de
K9: d4dc3a31be34e412210fafa6eca00776
K10: 39d1e190ee92b0ff16d92a8be58d2fa0
K11: 1eb081328d4bcf94c9117b12c5cf22ac
K12: 7e047c2c552f9f1414d946775fabfe30
K13: e78e685d9b2a7e29e7f2a19d1bc38ebd
K14: 1b582272a3121718c4096d2d8602f215
K15: 8569e860530d9c3d48a0870dac33f676
K16: 6966b528fdd1dc222527052c8f6cf5a6
K17: a34244c757154c53171c663b0b56d5c2
round  1: 3348470a7ea6cc6eb81b404721332620
round  2: af976da9225066d563e10ab955e6fc32
round  3: d08f826ebd55a0bd7591c19a89ed9bde
added ->: e3d7c964c3fb6cd3cdac01dda820c1fe
round  4: 6cd952785630dfc7cf81eea625e42c5c
round  5: 93573b2971515495978264b88f330f7f
round  6: b3f01d5e7fe1ce6da7b46d8c389baf47
round  7: 0b833bff6106d5bae033b4ce5af5a924
round  8: 23de0bbdc70850a7803f4f10c63b2c0f
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 12                                     #
###################################################################################
X: b10d068bcab44775199f29b10d068bca
Y: 0f5bb150b4371ae4e5785293d22b7b07
key: b10d068bcab44775199f29b10d068bca
plain_text: 0f5bb150b4371ae4e5785293d22b7b07
K1: b10d068bcab44775199f29b10d068bca
K2: aec70d1048f1bbd2c18040318a8402ad
K3: 6d8d5cf338f29ef4420639ef488e4fa9
K4: a1584117541b759ba6d9f7eb2bedcbba
K5: 09a20676666aeed6f22176274eb433f4
K6: 840472c001add5811a054be5f5c74754
K7: fad9e45c8bf70a972fcd9bff0e8751f5
K8: e8f30ff666dfd212263416496ff3b2c2
K9: 964cdba0cf8d593f2fc40f96daf8267a
K10: bcd65c11b13e1a70bcd4aafba8864fe3
K11: 468c8548ea9653c1a10df6288dd03c1d
K12: 5d252d17af4b09d3f4b5f7b5677b8211
K13: e814bf307c767428c67793dda2df95c7
K14: 4812b979fdc20f0ff0996f61673a42cc
K15: 5b1e2033d1cd549fc4b028146eb5b3b7
K16: 0f284c14fb8fe706a5343e3aa35af7b1
K17: b1f7a4b7456d6b577fded6dc7a672e37
round  1: 0f5bb150b4371ae4e5785293d22b7b07
round  2: 342d2b79d7fb7cd110379742b9842c79
round  3: 9407e8e3e810603921bf81cfda62770a
added ->: 9b6299b35c477addc437d35c088df20d
round  4: 9a3ba953225a7862c0a842ed3d0b2679
round  5: 2c573b6480852e875df34b28a5c44509
round  6: 21b0cc49e880c5811d24dee0194e6e9e
round  7: e6d6bdcd63e1d37d9883543ba86392fd
round  8: e3dde7ce6bd7d8a34599aa04d6a760ab
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 13                                     #
###################################################################################
X: fb20f177a683bd0b1896fb20f177a683
Y: 148662a4baa73cfadb55489159e476eb
key: fb20f177a683bd0b1896fb20f177a683
plain_text: 148662a4baa73cfadb55489159e476eb
K1: fb20f177a683bd0b1896fb20f177a683
K2: 47266cefbfa468ca7916b458155dc825
K3: 688853a6d6575eb2f6a2724b0fbc133b
K4: 7810df048019634083a2d9219d0b5fe0
K5: c78f6dcf56da1bbd413828b33f5865b3
K6: eb3f3d407d160df3d293a76d1a513c4a
K7: d330e038d6b19d5c9bb0d7285a360064
K8: 9bd3ee50347c00753d165faced702d9c
K9: 9543ad0fb3fe74f83e0e2281c6d4f5f0
K10: 746cd0383c17e0e80e6d095a87fd0290
K11: fa28bea4b1c417536608f11f406ea1dd
K12: 3aee0f4d21699df9cb8caf5354a780ff
K13: 372b71bc6d1aa6e785358044fbcf05f4
K14: 00a01501224c0405de00aa2ce7b6ab04
K15: c7015c5c1d7c030e00897f104a006d4a
K16: 260a9577790c62e074e71e19fd2894df
K17: c041b7a231493acd15ddcdaee94b9f52
round  1: 148662a4baa73cfadb55489159e476eb
round  2: 3a942eb6271c3f4e433838a5d3fcbd27
round  3: 9c835b98a063701c0887943596780769
added ->: 8809bd3c1a0aace6d3dcdca4cf5c7d82
round  4: 7e68c4bafa020a4a59b5a1968105bab5
round  5: 227bad0cf0838bdb15b3b3872c24f592
round  6: e026e98c71121a0cb739ef6f59e14d26
round  7: cd6a6d8137d55140046f8991da1fa40a
round  8: 52cd7257fe8d0c782c259bcb6c9f5942
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 14                                     #
###################################################################################
X: a123b94459a44610f6a123b94459a446
Y: 193a1b84376c88882c8d3b4ee93ba8dc
key: a123b94459a44610f6a123b94459a446
plain_text: 193a1b84376c88882c8d3b4ee93ba8dc
K1: a123b94459a44610f6a123b94459a446
K2: 5f64d384c8e990c1d25080eb244dde9b
K3: 5abc00eff8991575c00807c48f6dbea5
K4: 127521158ad6798fb6479d1d2268abe6
K5: f2a1f620448b8e56665608df2ab3952f
K6: 7c84c0af02aad91dc39209c4edd220b1
K7: f6445b647317e7e493bb92bf6655342f
K8: 3cae503567c63d3595eb140ce60a84c0
K9: 734ed5a806e072bbecb4254993871679
K10: cda69ccb4b07f65e3c8547c11c0647b8
K11: c48e531d3175c2bd26fa25cc8990e394
K12: 6d93d349a6c6e9ff5b26149565b13d15
K13: 5c4951e85875d663526092cd4cbdb667
K14: f19f7758f5cde86c3791efaf563b3fd0
K15: bf0c17f3299b37d984ac938b769dd394
K16: 7edf4ad772a6b9048588f97be25bde1c
K17: 6ee7ba6afefc5b561abbd8d6829e8150
round  1: 193a1b84376c88882c8d3b4ee93ba8dc
round  2: 3badbd58f100831d781ddd3ccedefd3f
round  3: 0b53075a49c6bf2df2421c655fdedf68
added ->: 128d22de7e3247a5decf572bb61987b4
round  4: 793f4484fb592e7a78756fd4662f990d
round  5: 9e46a8df925916a342f299a8306220a0
round  6: 6bf9cd82c9e1be13fc58eae0b936c75a
round  7: e96a9871471240f198811d4b8311e9a6
round  8: e94ca67d3721d5fb08ec069191801a46
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 15                                     #
###################################################################################
X: 3eaf411fbbb51d1e3eaf411fbbb51d1e
Y: 1453db4d057654e8eb62d7d62ec36084
key: 3eaf411fbbb51d1e3eaf411fbbb51d1e
plain_text: 1453db4d057654e8eb62d7d62ec36084
K1: 3eaf411fbbb51d1e3eaf411fbbb51d1e
K2: c3a1a997509f00fb4241aba607109c64
K3: 3c729833ae1ce7f84861e4dbad6305cc
K4: c83a43c3a66595cb8136560ed29be4ff
K5: 18b26300b86b70acdd1c8f5cbc7c5da8
K6: 04efc75309b98cd8f1cef5513c18e41e
K7: 517c789cecadc455751af73198749fb8
K8: fd9711f913b5c844900fa79dd765d0e2
K9: bb5cf30e7d3ceb930651b1d16ee92750
K10: 3d97c7862ecab42720e984972f8efd28
K11: 4ce730344f6b09e449dcdb64cd466666
K12: 38828c3a56f922186adcd9b713cdcc31
K13: d30fd865ea3e9edcff86a33a2c319649
K14: 1fdb63e54413acd968195ab6fa424e83
K15: a16b7c655bbaa262c807cba8ae166971
K16: 7903dd68630105266049e23ca607cda7
K17: 888446f2d95e6c2d2803e6f4e815ddc9
round  1: 1453db4d057654e8eb62d7d62ec36084
round  2: 0b78276c1ebc65707d38c9c5fa1372bd
round  3: 23f3f0f6441563d4c202cee0e5cb2335
added ->: 3746cbbb418bb73c2964a536cb8e83b1
round  4: c61afa90d3c14bdf588320e857afdc00
round  5: a8a0e02ceb556af8bfa321789801183a
round  6: 0b58e922438d224db34b68fca9a5ea12
round  7: b90664c4ac29a8b4bb26debec9ffc5f2
round  8: 6934de3067817cefd811abc5736c163b
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     case 16                                     #
###################################################################################
X: 6d008aa9be62d56d008aa9be62d56d00
Y: 1313f7115a9db842fcedc4b10088b48a
key: 6d008aa9be62d56d008aa9be62d56d00
plain_text: 1313f7115a9db842fcedc4b10088b48a
K1: 6d008aa9be62d56d008aa9be62d56d00
K2: 46ebfeafb6657b0a1984a8dc0893accf
K3: 8e15595edcf058af62498ee3c1dc6098
K4: dd409c3444e94b9cc08396ae967542a0
K5: 487deff5d519f6a6481e947b926f633c
K6: 5b4b6e3477ed5c2c01f6e607d3418963
K7: 34b980088d2b5fd6b6a2aceeda99c9c4
K8: e7d06d06078acc4ecdbc8da800b73078
K9: 7d375bad245c3b757380021af8ecd408
K10: 14dac4bc2f4dc4929a6cceec47f4c3a3
K11: 66cfda3c63e464b05e2e7e25f8743ad7
K12: 77cfccda1ad380b9fdf1df10846b50e7
K13: 3e11dd84c031a470a8b66ec6214e44cf
K14: 2f03549bdb3c511eea70b65ddbb08253
K15: e2f331229ddfcc6e7bea08b01ab7e70c
K16: b6b0c3738c5365bc77331b98b3fba2ab
K17: f5b3973b636119e577c5c15c87bcfd19
round  1: 1313f7115a9db842fcedc4b10088b48a
round  2: 839b23b83b5701ab095bafd162ec0ac7
round  3: c0a2010cc44f2139427f093f4f97ae68
added ->: d3b5f81d9eecd97bbe6ccd8e4f1f62e2
round  4: 1a5517a0efad3575931d8ea3bee8bd07
round  5: d3ce1fdfe716d72c1075ff37a8a2093f
round  6: 47e90cb55be6e8dd0f583623c2f2257b
round  7: f866ae6624f7abd4a4f5bd24b04b6d43
round  8: 02e8e17cf8be4837c9c40706b613dfa8
>>>>>>>>>> Pass <<<<<<<<<<

Secure Simple Pairing相关函数

注意,Secure Simple Pairing过程只用到了HMAC-SHA256函数,DHKey的生成用到了ECC P192/P256(椭圆曲线加密)。

以Numeric Comparison为例对相关算法使用步骤进行说明。

  • Step1:是交互Public Key,然后使用ECC P192/256(椭圆曲线加密)对DHKey进行计算。

image-20240523200919222

  • Step2,显示在Numeric Comparison模式下,认证阶段1中f1g函数使用的位置和传入的参数。

image-20240523201058253

  • Step3,显示在在认证阶段2中f3函数使用的位置和传入的参数。

image-20240523201204678

  • Step4,用f2函数进行LK的转换。

image-20240523201230054

f1函数实现
概述

输入参数为:

image-20240523195743157

函数缩写如下,其实就是HMAC-SHA-256运算后,保留高128bit。Key是X。

image-20240523195814125

不同协议的输入如下:

image-20240523195915741

算法实现

如下所示,输入U/V/X/Z,截取输出的高128位作为输出。

def classic_f1(U, V, X, Z):
    key = X
    message = (U + V + Z)

    ret = sha256_hmac(key, message)

    return ret[0:16]
测试

参考Core Spec v5.4 P1644中写的测试用例进行测试。注意测试用例中的参数是大端的。

def classic_f1_test():
    print_header("classic_f1_test")

    print_header("7.2.1 f1()")
    print_header("7.2.1.1 f1() with P-192 inputs")
    print_header("Set 1a")
    U = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    V = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("00")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('1bdc955a9d542ffc9f9e670cdf665010')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 1b")
    U = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    V = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("80")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('611325ebcb6e5269b868113306095fa6')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 1c")
    U = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    V = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("81")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('b68df39fd8a406b06a6c517d3666cf91')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 2a (swapped U and V inputs compared with set 1)")
    U = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    V = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("00")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('f4e1ec4b88f305e81477627b1643a927')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 2b (swapped U and V inputs compared with set 1)")
    U = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    V = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("80")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('ac6aa7cfa96ae99dd3a74225adb068ae')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 2c (swapped U and V inputs compared with set 1)")
    U = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    V = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("81")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('5ad4721258aa1fa06082edad980d0cc5')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 3a (U and V set to same value as U in set 1)")
    U = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    V = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("00")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('49125fc1e8cdc615826c15e5d23ede41')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 3b (U and V set to same value as V in set 1)")
    U = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    V = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("80")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('159f204c520565175c2b9c523acad2eb')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 3c (U and V set to same value as V in set 1)")
    U = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    V = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("81")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('9a162ff9a8235e5b12539ba0ff9179da')
    print_result_with_exp(out == out_exp)
    



    print_header("7.2.1.2 f1() with P-256 inputs")
    print_header("Set 1a")
    U = bytes.fromhex("20b003d2f297be2c5e2c83a7e9f9a5b9eff49111acf4fddbcc0301480e359de6")
    V = bytes.fromhex("55188b3d32f6bb9a900afcfbeed4e72a59cb9ac2f19d7cfb6b4fdd49f47fc5fd")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("00")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('D301CE92CC7B9E3F51D2924B8B33FACA')
    print_result_with_exp(out == out_exp)
    


    print_header("Set 1b")
    U = bytes.fromhex("20b003d2f297be2c5e2c83a7e9f9a5b9eff49111acf4fddbcc0301480e359de6")
    V = bytes.fromhex("55188b3d32f6bb9a900afcfbeed4e72a59cb9ac2f19d7cfb6b4fdd49f47fc5fd")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Z = bytes.fromhex("80")

    out = classic_f1(U, V, X, Z)
    
    out_exp = bytes.fromhex('7E431112C10DE8A3984C8AC8149FF6EC')
    print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_f1_test                                 #
###################################################################################
###################################################################################
#                                   7.2.1 f1()                                    #
###################################################################################
###################################################################################
#                         7.2.1.1 f1() with P-192 inputs                          #
###################################################################################
###################################################################################
#                                     Set 1a                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     Set 1b                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     Set 1c                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#               Set 2a (swapped U and V inputs compared with set 1)               #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#               Set 2b (swapped U and V inputs compared with set 1)               #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#               Set 2c (swapped U and V inputs compared with set 1)               #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                Set 3a (U and V set to same value as U in set 1)                 #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                Set 3b (U and V set to same value as V in set 1)                 #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                Set 3c (U and V set to same value as V in set 1)                 #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                         7.2.1.2 f1() with P-256 inputs                          #
###################################################################################
###################################################################################
#                                     Set 1a                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     Set 1b                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
g函数实现
概述

用于Secure Simple Pairing中的Numerical计算,输入参数为:

image-20240523201431477

image-20240523201439618

函数缩写如下,使用的是SHA-256计算,并取结果的低32bit:

image-20240523201457205

最终比较值为6位的10进制数:

image-20240523201551788

算法实现

如下所示,输入参数后,最终输出32bits的Compare Value。


def classic_g(U, V, X, Y):
    message = (U + V + X + Y)

    ret = sha256_hash(message)

    return ret[-4:]
测试

参考Core Spec v5.4 P908中写的测试用例进行测试。第二个测试结果和SPEC不同,但是应该是SPEC有问题吧,先不管。

def classic_g_test():
    print_header("classic_g_test")

    print_header("7.2.2 g()")
    print_header("7.2.2.1 g() with P-192 inputs")
    print_header("Set 1")
    U = bytes.fromhex("15207009984421a6586f9fc3fe7e4329d2809ea51125f8ed")
    V = bytes.fromhex("356b31938421fbbf2fb331c89fd588a69367e9a833f56812")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Y = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")

    out = classic_g(U, V, X, Y)
    
    out_exp = bytes.fromhex('52146a1e')
    print_result_with_exp(out == out_exp)



    print_header("7.2.2.2 g() with P-256 inputs")
    print_header("Set 1")
    U = bytes.fromhex("20b003d2f297be2c5e2c83a7e9f9a5b9eff49111acf4fddbcc0301480e359de6")
    V = bytes.fromhex("55188b3d32f6bb9a900afcfbeed4e72a59cb9ac2f19d7cfb6b4fdd49f47fc5fd")
    X = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    Y = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")

    out = classic_g(U, V, X, Y)
    
    # something error, maybe is the spec error? just not compare here.
    # out_exp = bytes.fromhex('000240E0')
    # print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_g_test                                  #
###################################################################################
###################################################################################
#                                    7.2.2 g()                                    #
###################################################################################
###################################################################################
#                          7.2.2.1 g() with P-192 inputs                          #
###################################################################################
###################################################################################
#                                      Set 1                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                          7.2.2.2 g() with P-256 inputs                          #
###################################################################################
###################################################################################
#                                      Set 1                                      #
###################################################################################
f2函数实现
概述

用于Secure Simple Pairing中的秘钥生成,输入参数为:

image-20240523201928754

函数缩写如下,其实就是进行HMAC-SHA-256运算,取高128bits的值。

image-20240523201945729

输入的参数变化成蓝牙的交互内容为:

image-20240523202041719

算法实现

如下所示,输入参数后,最终输出128bits的LK。

def classic_f2(W, N1, N2, keyID, A1, A2):
    key = W
    message = (N1 + N2 + keyID + A1 + A2)

    ret = sha256_hmac(key, message)

    return ret[0:16]
测试

参考Core Spec v5.4 P908中写的测试用例进行测试。

def classic_f2_test():
    print_header("classic_f2_test")

    print_header("7.2.3 f2()")
    print_header("7.2.3.1 f2() with P-192 inputs")
    print_header("Set 1")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    keyID = bytes.fromhex("62746c6b")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f2(W, N1, N2, keyID, A1, A2)
    
    out_exp = bytes.fromhex('c234c1198f3b520186ab92a2f874934e')
    print_result_with_exp(out == out_exp)



    print_header("7.2.3.2 f2() with P-256 inputs")
    print_header("Set 1")
    W = bytes.fromhex("ec0234a357c8ad05341010a60a397d9b99796b13b4f866f1868d34f373bfa698")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    keyID = bytes.fromhex("62746c6b")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f2(W, N1, N2, keyID, A1, A2)
    
    out_exp = bytes.fromhex('47300bb95c7404129450674b1741104d')
    print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_f2_test                                 #
###################################################################################
###################################################################################
#                                   7.2.3 f2()                                    #
###################################################################################
###################################################################################
#                         7.2.3.1 f2() with P-192 inputs                          #
###################################################################################
###################################################################################
#                                      Set 1                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                         7.2.3.2 f2() with P-256 inputs                          #
###################################################################################
###################################################################################
#                                      Set 1                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
f3函数实现
概述

用于Secure Simple Pairing中的确认值计算,输入参数为:

image-20240523202253045

其实就是进行HMAC-SHA-256运算,取高128bits的值。

image-20240523202324156

不同协议的输入如下:

image-20240523202401662

算法实现

如下所示,输入参数后,最终输出128bits的校验值。

def classic_f3(W, N1, N2, R, IOcap, A1, A2):
    key = W
    message = (N1 + N2 + R + IOcap + A1 + A2)

    ret = sha256_hmac(key, message)

    return ret[0:16]
测试

参考Core Spec v5.4 P909中写的测试用例进行测试。

def classic_f3_test():
    print_header("classic_f3_test")

    print_header("7.2.4 f3()")
    print_header("7.2.4.1 f3() with P-192 inputs")
    print_header("Set 1")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000000")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('5e6a346b8add7ee80e7ec0c2461b1509')
    print_result_with_exp(out == out_exp)




    print_header("Set 2")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000001")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('7840e5445a13e3ce6e48a2decbe51482')
    print_result_with_exp(out == out_exp)




    print_header("Set 3")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000002")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('da9afb5c6c9dbe0af4722b532520c4b3')
    print_result_with_exp(out == out_exp)




    print_header("Set 4")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000003")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('2c0f220c50075285852e01bcee4b5f90')
    print_result_with_exp(out == out_exp)




    print_header("Set 5")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000100")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('0a096af0fa61dce0933987febe95fc7d')
    print_result_with_exp(out == out_exp)




    print_header("Set 6")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000101")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('49b8d74007888e770e1a49d6810069b9')
    print_result_with_exp(out == out_exp)




    print_header("Set 7")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000102")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('309cd0327dec2514894a0c88b101a436')
    print_result_with_exp(out == out_exp)




    print_header("Set 8")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000103")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('4512274ba875b156c2187e2061b90434')
    print_result_with_exp(out == out_exp)




    print_header("Set 9 (same as set 1 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000000")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('8d56dc59e70855f563b5e85e42d5964e')
    print_result_with_exp(out == out_exp)




    print_header("Set 10 (same as set 2 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000001")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('c92fdacbf0ce931e9c4087a9dfb7bc0b')
    print_result_with_exp(out == out_exp)




    print_header("Set 11 (same as set 3 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000002")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('52ac910200dc34285bbbf2144883c498')
    print_result_with_exp(out == out_exp)




    print_header("Set 12 (same as set 4 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000003")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('c419d677e0d426e6bb36de5fa54c5041')
    print_result_with_exp(out == out_exp)




    print_header("Set 13 (same as set 5 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000100")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('fb0e1f9f7c623c1bf2675fcff1551137')
    print_result_with_exp(out == out_exp)




    print_header("Set 14 (same as set 6 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000101")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('16c7be68184f1170fbbb2bef5a9c515d')
    print_result_with_exp(out == out_exp)




    print_header("Set 15 (same as set 7 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000102")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('24849f33d3ac05fef9034c18d9adb310')
    print_result_with_exp(out == out_exp)




    print_header("Set 16 (same as set 8 with N1 and N2 swapped and A1 and A2 swapped)")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    N2 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000103")
    A1 = bytes.fromhex("a713702dcfc1")
    A2 = bytes.fromhex("56123737bfce")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('e0f484bb0b071483285903e85094046b')
    print_result_with_exp(out == out_exp)




    print_header("Set 17")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("010000")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('4bf22677415ed90aceb21873c71c1884')
    print_result_with_exp(out == out_exp)




    print_header("Set 18")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("010001")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('0d4b97992eb570efb369cfe45e1681b5')
    print_result_with_exp(out == out_exp)




    print_header("Set 19")
    W = bytes.fromhex("fb3ba2012c7e62466e486e229290175b4afebc13fdccee46")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000001")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('7840e5445a13e3ce6e48a2decbe51482')
    print_result_with_exp(out == out_exp)

    # skip other.

    print_header("7.2.4.2 f3() with P-256 inputs")
    print_header("Set 1")
    W = bytes.fromhex("ec0234a357c8ad05341010a60a397d9b99796b13b4f866f1868d34f373bfa698")
    N1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    N2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    R = bytes.fromhex("12a3343bb453bb5408da42d20c2d0fc8")
    IOcap = bytes.fromhex("000000")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")

    out = classic_f3(W, N1, N2, R, IOcap, A1, A2)
    
    out_exp = bytes.fromhex('5634c83c9996a86b53473fe25979ec90')
    print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_f3_test                                 #
###################################################################################
###################################################################################
#                                   7.2.4 f3()                                    #
###################################################################################
###################################################################################
#                         7.2.4.1 f3() with P-192 inputs                          #
###################################################################################
###################################################################################
#                                      Set 1                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 2                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 3                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 4                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 5                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 6                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 7                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                      Set 8                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 9 (same as set 1 with N1 and N2 swapped and A1 and A2 swapped)        #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 10 (same as set 2 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 11 (same as set 3 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 12 (same as set 4 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 13 (same as set 5 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 14 (same as set 6 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 15 (same as set 7 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#       Set 16 (same as set 8 with N1 and N2 swapped and A1 and A2 swapped)       #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     Set 17                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     Set 18                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                                     Set 19                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
###################################################################################
#                         7.2.4.2 f3() with P-256 inputs                          #
###################################################################################
###################################################################################
#                                      Set 1                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
h3函数实现
概述

用于Secure Connection中的AES秘钥生成,输入参数为:

image-20240523202753186

函数缩写如下,其实就是进行HMAC-SHA-256运算,取高128bits的值。

image-20240523202808586

算法实现

如下所示,输入参数后,最终输出128bits的LK。

def classic_h3(T, keyID, A1, A2, ACO):
    key = T
    message = (keyID + A1 + A2 + ACO)

    ret = sha256_hmac(key, message)

    return ret[0:16]
测试

参考Core Spec v5.4 P916中写的测试用例进行测试。

def classic_h3_test():
    print_header("classic_h3_test")

    print_header("7.2.8 h3()")
    print_header("Set 1a")
    keyID = bytes.fromhex("6274616b")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")
    ACO = bytes.fromhex("c683b97d9d421f91")
    W = bytes.fromhex("c234c1198f3b520186ab92a2f874934e")

    out = classic_h3(W, keyID, A1, A2, ACO)
    
    out_exp = bytes.fromhex('677b377f74a5d501121c46492d4cb489')
    print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_h3_test                                 #
###################################################################################
###################################################################################
#                                   7.2.8 h3()                                    #
###################################################################################
###################################################################################
#                                     Set 1a                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
h4函数实现
概述

用于Secure Connection中的Authentication秘钥生成,输入参数为:

image-20240523203112664

函数缩写如下,其实就是进行HMAC-SHA-256运算,取高128bits的值。

image-20240523203146050

算法实现

如下所示,输入参数后,最终输出128bits的LK。

def classic_h4(T, keyID, A1, A2):
    key = T
    message = (keyID + A1 + A2)

    ret = sha256_hmac(key, message)

    return ret[0:16]
测试

参考Core Spec v5.4 P916中写的测试用例进行测试。

def classic_h4_test():
    print_header("classic_h4_test")

    print_header("7.2.6 h4()")
    print_header("Set 1a")
    keyID = bytes.fromhex("6274646b")
    A1 = bytes.fromhex("56123737bfce")
    A2 = bytes.fromhex("a713702dcfc1")
    W = bytes.fromhex("c234c1198f3b520186ab92a2f874934e")

    out = classic_h4(W, keyID, A1, A2)
    
    out_exp = bytes.fromhex('b089c4e39d7c192c3aba3c2109d24c0d')
    print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_h4_test                                 #
###################################################################################
###################################################################################
#                                   7.2.6 h4()                                    #
###################################################################################
###################################################################################
#                                     Set 1a                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<
h5函数实现
概述

用于Secure Connection中的Authentication确认值生成,输入参数为:

image-20240523203237436

函数缩写如下,其实就是进行HMAC-SHA-256运算,取高128bits的值。

image-20240523203258739

算法实现

如下所示,输入参数后,最终输出128bits的LK。

def classic_h5(S, R1, R2):
    key = S
    message = (R1 + R2)

    ret = sha256_hmac(key, message)

    return ret
测试

参考Core Spec v5.4 P916中写的测试用例进行测试。

def classic_h5_test():
    print_header("classic_h5_test")

    print_header("7.2.7 h5()")
    print_header("Set 1a")
    R1 = bytes.fromhex("d5cb8454d177733effffb2ec712baeab")
    R2 = bytes.fromhex("a6e8e7cc25a75f6e216583f7ff3dc4cf")
    W = bytes.fromhex("b089c4e39d7c192c3aba3c2109d24c0d")

    out = classic_h5(W, R1, R2)
    
    out_exp = bytes.fromhex('746af87e1eeb1137c683b97d9d421f911f3ddf100403871b362958c458976d65')
    print_result_with_exp(out == out_exp)

测试结果如下:

###################################################################################
#                                 classic_h5_test                                 #
###################################################################################
###################################################################################
#                                   7.2.7 h5()                                    #
###################################################################################
###################################################################################
#                                     Set 1a                                      #
###################################################################################
>>>>>>>>>> Pass <<<<<<<<<<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/633343.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32项目】基于stm32智能鱼缸控制系统的设计与实现(完整工程资料源码)

实物演示效果 基于stm32智能鱼缸控制系统的设计与实现 目录&#xff1a; 实物演示效果 目录&#xff1a; 一、 绪论 1.1 项目研究目的及意义 1.1.1 选题目的 1.1.2 选题意义 1.2 国内外研究现状 1.2.1 国外发展现状 1.2.2 国内发展现状 1.3 项目研究内容 二、智能鱼缸系统总体设…

IPA酒精清洁笔:打印机打印头、相机镜头等光学设备清洁的好帮手!

打印机打印头、相机镜头等光学设备在长期使用的过程中&#xff0c;会产生油脂、污渍和灰尘等污染物杂质&#xff0c;从而影响到打印质量、镜头清晰度等。通过IPA酒精清洁笔可以简便快捷、安全有效的清除这些污染物杂质&#xff0c;保持设备表面的清洁度&#xff0c;从而确保设备…

列举几个淘宝商品详情API接口测试示例

API名&#xff1a;item_get 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cacheString否[yes…

MyBatis入门——MyBatis XML配置文件(3)

目录 一、配置连接字符串和MyBatis 二、写持久层代码 1、添加 mapper 接口 2、添加 USerInfoXmlMapper.xml 3、测试类代码 三、增删改查操作 1、增&#xff08;Insert&#xff09; 返回自增 id 2、删&#xff08;Delete&#xff09; 3、改&#xff08;update&#xf…

Java中Spring MVC 来如何接收表单数据

目录 一、Java语言介绍 二、Spring MVC 框架介绍 三、什么是表单 四、Spring MVC 来如何接收表单数据 一、Java语言介绍 Java是一种广泛使用的面向对象的编程语言&#xff0c;由Sun Microsystems公司的James Gosling等人开发。它最初于1995年发布&#xff0c;被设计为具有…

Android Studio 与 Gradle 及插件版本兼容性

Android Studio 开始新项目时&#xff0c;会自动创建其中部分文件&#xff0c;并为其填充合理的默认值。 项目文件结构布局&#xff1a; 一、Android Gradle 及插件作用&#xff1a; Android Studio 构建系统以 Gradle 为基础&#xff0c;并且 Android Gradle 插件 (AGP) 添加…

利用远控工具横向

一.横向移动介绍和方式 1.介绍 内网渗透的横向移动是指攻击者在成功进入内网后&#xff0c;通过利用内部系统的漏洞或者获取的合法访问权限&#xff0c;从一个受感染的系统向其他系统扩散或移动。这种横向移动的目的通常是为了获取更多的敏感信息、提升权限、扩大攻击面或者更…

记录踩坑事件 分页查询order by出现重复数据bug

MySQL排序小坑_mysql order by name相同导致排序混乱-CSDN博客 1、问题描述 列表页分页查询出现重复数据。 2、问题排查 排查最终执行sql日志。 select * from tableA where (start_time>2024-04-17 00:00:00) AND (start_time<2024-05-18 00:00:00) ORDER BY sta…

rocketmq 学习二 基本概念

教程&#xff1a;基本概念 | RocketMQ 视频教程 https://www.bilibili.com/video/BV1d5411y7UW?vd_sourcef1bd3b5218c30adf0a002c8c937e0a27 版本&#xff1a;5.0 一 基本概念 1.1 生产者/Producer 1.1.1 定义 消息发布者。是构建并传输消息到服务端的运行实体。…

mac远程桌面连接工具:Microsoft Remote Desktop正式版

Microsoft Remote Desktop 是一款由微软开发的远程桌面控制软件。它允许用户通过互联网连接到远程计算机&#xff0c;从而可以在本地计算机上访问和控制远程计算机的桌面、文件和应用程序。 下载地址&#xff1a;https://www.macz.com/mac/1004.html?idOTI2NjQ5Jl8mMjcuMTg2Lj…

ACM实训

【碎碎念】继续搞习题学习&#xff0c;今天完成第四套的ABCD&#xff0c;为下一周挤出时间复习&#xff0c;加油 Digit Counting 问题 法希姆喜欢解决数学问题。但有时解决所有的数学问题对他来说是一个挑战。所以有时候他会为了解决数学难题而生气。他拿起一支粉笔&#xff…

通过管理系统完成商品属性维护

文章目录 1.数据库表设计1.商品属性表 2.renren-generator生成CRUD1.基本配置检查1.generator.properties2.application.yml 2.启动RenrenGeneratorApplication.java生成CRUD1.启动后访问localhost:812.生成商品属性表的crud 3.将crud代码集成到项目中1.解压&#xff0c;找到ma…

Java日志冲突问题

在今天的 Maven 构建过程中&#xff0c;我遇到了 SLF4J 和 Logback 之间的依赖冲突问题。以下是对这些问题的总结以及相应的解决方案。 问题描述: Maven 构建中 SLF4J 和 Logback 依赖冲突问题总结 日志警告&#xff1a; SLF4J: Class path contains multiple SLF4J binding…

flannel详细介绍

一、前言 Flannel 是一个简单、高效的容器网络解决方案&#xff0c;适用于需要在多个主机上运行容器的场景。它通过虚拟网络技术和 IP 地址管理来实现容器之间的通信和跨主机连接&#xff0c;为容器平台提供了可靠的网络基础设施&#xff0c;flannel有三种模式&#xff0c;分别…

sudo pip3 install rpi_ws281x error: externally-managed-environment

报错 error: externally-managed-environment piraspberrypi:~ $ sudo pip3 install rpi_ws281x error: externally-managed-environment This environment is externally managed ╰─> To install Python packages system-wide, try apt installpython3-xyz, where xyz i…

如何自建谷歌站点?

自建谷歌站点其实是一个相对简单的过程&#xff0c;主要是指创建一个能够被谷歌搜索引擎索引和搜索的网站 首先要做的自然就是选择一个域名&#xff0c;域名基本就相当于你的门牌号了&#xff0c;你得想一个既好记又能代表你网站内容的名字&#xff0c;注册域名可以去很多网站…

【Qt】之【Bug】C2001 常量中有换行符

分析 参考&#xff1a;Qt记录&#xff1a;Qt编程遇C2001错误&#xff0c;提示“常量中有换行符”_qt 常量中有换行符-CSDN博客 原因 字符串中有中文字符 &#xff1a;使用了中文标点符号&#xff01; 解决 中文感叹号改为英文的

基于transformers框架实践Bert系列4-文本相似度

本系列用于Bert模型实践实际场景&#xff0c;分别包括分类器、命名实体识别、选择题、文本摘要等等。&#xff08;关于Bert的结构和详细这里就不做讲解&#xff0c;但了解Bert的基本结构是做实践的基础&#xff0c;因此看本系列之前&#xff0c;最好了解一下transformers和Bert…

RisingWave 用户定义函数 (一) :概览

&#xff5c;作者&#xff1a;王润基 RisingWave Labs 内核开发工程师 用户定义函数&#xff08;User Defined Function&#xff0c;以下简称 UDF&#xff09;是数据系统中的常见功能。它允许用户使用各种语言定义自己的函数&#xff0c;作为内置函数的补充&#xff0c;以实现…

【前端】使用 Canvas 实现贪吃蛇小游戏

使用 Canvas 实现贪吃蛇小游戏 在这篇博客中&#xff0c;我们将介绍如何使用 HTML5 Canvas 和 JavaScript 实现一个简单的贪吃蛇&#xff08;Snake&#xff09;小游戏。这个项目是一个基础的游戏开发练习&#xff0c;它可以帮助你理解如何在 Canvas 上绘图、如何处理用户输入以…