5.2.2. 功能描述

5.2.2.1. 密钥来源

SPI 数据加密使用 AES-128-CTR 算法,其算法原理如 AES-128-CTR 算法原理 所示。在计算过程中,以 16字节为单位对数据进行分组,并且为每个数据分组产生对应的 Counter 值,然后使用一个 128 bit 的 AES 密钥,对 Counter 值进行加密运算,产生数据分组的对应分组密钥,最后使用分组密钥与数据进行异或运算,得到密文。

产生数据分组对应的 Counter 值,需要使用一个 Nonce,在 SPI ENC使用过程中,Nonce 作为密钥数据的一部分提供。

密钥数据的来源有两个:

  • 一是 SPI 加密模块从 eFuse 中读取 AES KEY 和 Nonce;

  • 二是用户通过相应的寄存器配置 AES KEY 和 Nonce(该配置主要用于内部调试)。

eFuse 中保存的密钥数据如下:

名称

长度

说明

SPI_ENC_KEY

128 bit

加密密钥,仅 SPI_ENC 模块可见

SPI_ENC_NONCE

64 bit

用于生成 Counter,仅 SPI_ENC 模块可见

5.2.2.2. 计算分组密钥

../../../_images/aes-128-ctr-alg.png

图 5.8 AES-128-CTR 算法原理

使用 AES-128-CTR 进行加密或者解密的过程分为三个步骤:

  1. 生成数据分组的 Counter 值

  2. 生成分组密钥

  3. 使用分组密钥对数据进行异或运算,产生加密数据

Counter 值的产生方式如下:

ID = (SPIE_ADDR & 0xFFFFFFF0) / 16
Counter(ID) = (Nonce << 64) +(SPIE_TWEAK << 32) +  ID

首先使用数据地址寄存器 SPIE_ADDR 中的地址值,计算分组 ID,然后与 Nonce 和 Tweak 值组成 Counter 值。

计算分组密钥使用 AES-128-ECB 加密算法,使用 AES KEY 对 Counter 值进行加密,得到的结果即为分组密钥。

SPI ENC 一次可产生16字节的分组密钥,输出到内部 Key buffer。模块开始工作后,只要 Key buffer有空间,计算分组密钥就会继续,并且总是按地址顺序自动计算下一组密钥,直到控制寄存器 SPIE_CTL[0] 被设置为0。

注意,从图 AES-128-CTR 算法原理 可以看到,数据的加密和解密的分组密钥计算流程是完全一样的。

5.2.2.3. 输出分组密钥

SPI ENC 模块只计算 AES-128-CTR 的分组密钥,最后一步使用分组密钥对数据进行异或运算由 SPI 控制器完成。因此在启用 SPI ENC 的情况下,SPI 控制器需要为发送到总线或者从总线接收到的每一字节数据读取分组密钥。

../../../_images/spi-transfer-mode.png

图 5.9 SPI 数据传输模式

如上图所示,在与 SPI NOR/NAND 通信过程中,传输的数据可能出现明文和密文混合传输的情形。

SPI ENC 输出分组密钥的规则如下:

  • SPI 控制器每收发一个字节数据,都要读取一个字节的分组密钥

  • 明文数据位置对应的分组密钥,总是读取到0;

  • 密文数据位置对应的分组密钥,总是计算得到的分组密钥;

注意,分组密钥的计算总是以16字节为单位进行,但用户访问的地址可能并不是16字节对齐,如从 0x0000_0005 地址开始读取数据。 这种情况下,SPI ENC 计算的第一个分组密钥仍然为16字节,只是对应的数据地址为 0x0000_0000 ~ 0x0000_000F, 此时 SPI ENC 输出数据分组密钥时,应输出 0x05~0x0F 数据对应的分组密钥值。

备注

SPI ENC 模块要准确的计算和输出分组密钥,依赖于用户开始传输前配置的数据地址、长度、加密数据的起始地址和长度等信息。

5.2.2.4. 数据加解密

使用分组密钥对数据进行加密和解密的过程比较简单,直接使用分组密钥与对应的数据分组进行异或运算,即可完成数据的加密或者解密。

需要注意的是,使能 SPI ENC 时,不支持 SPI 全双工模式,原因是 SPI ENC 输出的密钥,只能应用于接收的数据,或者发送的数据,不可以同时应用于两组数据。

对于发送数据,数据会先被写入 TXFIFO,SPI 控制器在将数据发送到 SPI 总线上之前执行:

  1. 读取一个字节的分组密钥数据

  2. 将分组密钥数据与要发送的一个字节数据进行异或运算

  3. 将结果发送到 SPI 总线

对于接收数据:

  1. 从 SPI 总线读取到一个字节数据的同时,读取一个字节的分组密钥

  2. 将分组密钥数据与接收的数据进行异或运算

  3. 将解密后的数据,写入 RXFIFO