需求
假设一款Armv8-A架构的芯片,由于没有硬件密码算法引擎,如何实现对SHA256算法加速。
方案
Arm Neon 技术,一种⾼级 SIMD(Single Instruction Multiple Data,一条指令操作多个数据)架构扩展,Armv8‑A 和 Armv8-R 架构均支持 Neon 技术扩展。使用 Neon 技术有多种方法:
- 支持 Neon 的开源库,例如 Arm Compute Library
- 编译器的自动矢量化特性可以利用 Neon 技术自动优化你的代码
- Neon intrinsics内建函数,编译器用相应的 Neon 指令进行了封装
- 对于经验丰富的程序员来说,为了获得极佳的性能,手动编写 Neon 汇编也是一种方法
加密扩展CE(Cryptographic Extension)是 Neon 其中的一种指令集,用于实现AES加解密和SHA哈希的加速运算。加速SHA256算法的Neon指令包括如下四种:
指令 | 功能 |
---|---|
SHA256H | SHA256 hash update (part 1) |
SHA256H2 | SHA256 hash update (part 2) |
SHA256SU0 | SHA256 schedule update 0 |
SHA256SU1 | SHA256 schedule update 1 |
这里我们采用 Neon intrinsics 实现 SHA256 算法的加速,SHA256 Intrinsics 指令如下:
本方案需要对SHA256算法原理,Neon技术等有一定的了解,详细可参考本公众号/博客的其他文章。
实现
sha256.h
SHA256算法需要实现分片哈希和一次性哈希的功能,包括如下函数:
函数 | 功能 |
---|---|
sha256_starts | SHA256开始,创建上下文 |
sha256_update | SHA256更新,输入消息数据 |
sha256_finish | SHA256结束,得到摘要值 |
sha256_compute | 计算一块数据的SHA256摘要值 |
上面前三个函数是分片计算哈希,最后一个函数是一次性计算哈希。新建一个sha256.h
文件,代码如下:
#ifndef _SHA256_H_
#define _SHA256_H_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"{
#endif
struct sha256_ctx
{
uint32_t total[2]; /* 处理过的数据长度 */
uint32_t state[8]; /* 中间摘要状态 */
uint8_t buffer[64]; /* 正在处理的数据块 */
};
typedef struct sha256_ctx sha256_ctx_t;
/**
***********************************************************************************************************************
* @brief starts a sha256 context
*
* @param[in] ctx: the context
*
* @return 0: success, other: fail
***********************************************************************************************************************
*/
int sha256_starts(sha256_ctx_t *ctx);
/**
***********************************************************************************************************************
* @brief this function feeds an input buffer into an ongoing sha256 context
*
* @param[in] ctx: the context
* @param[in] input: the buffer holding the input data
* @param[in] ilen: the length of the input data
*
* @return 0: success, other: fail
***********************************************************************************************************************
*/
int sha256_update(sha256_ctx_t *ctx, const uint8_t *input, size_t ilen);
/**
***********************************************************************************************************************
* @brief the finalization function
*
* @param[in] ctx: the context
* @param[out] output: the buffer holding the output data
*
* @return 0: success, other: fail
***********************************************************************************************************************
*/
int sha256_finish(sha256_ctx_t *ctx, uint8_t output[32]);
/**
***********************************************************************************************************************
* @brief compute the sha256 checksum of a buffer
*
* @param[in] input: the buffer holding the input data
* @param[in] ilen: the length of the input data
* @param[out] output: the buffer holding the output data
*
* @return 0: success, other: fail
***********************************************************************************************************************
*/
int sha256_compute(const uint8_t *input, size_t ilen, uint8_t output[32]);
#ifdef __cplusplus
}
#endif
#endif /* _SHA256_H_ */
其中定义了一个sha256上下文结构体,用于分片哈希计算,参数如下:
- total:记录处理过的数据长度
- state:记录中间摘要状态值
- buffer:存放正在处理的数据块
sha256.c
新建一个sha256.c
文件,需要实现sha256.h
中定义的四个函数。
sha256_starts
sha256_starts
函数主要是创建上下文,并设置初始的哈希值。
int sha256_starts(sha256_ctx_t *ctx)
{
memset(ctx, 0, sizeof(sha256_ctx_t));
/* 清零 */
ctx->total[0] = 0;
ctx->total[1] = 0;
/* 初始哈希值 */
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
return 0;
}
sha256_update
sha256_update
函数主要是进行哈希更新计算,由于sha256内部每次处理512比特(64字节),因此需要保证每次哈希更新计算的长度为512比特的倍数。
int sha256_update(sha256_ctx_t *ctx, const uint8_t *input, size_t ilen)
{
size_t fill;
uint32_t left;
size_t block_num;
size_t block_len;
if (ilen == 0) {
return 0;
}
/* 上次剩余的消息长度 */
left = ctx->total[0] & 0x3F;
/* 需要填充fill长度消息,sha256每次处理512比特(即64字节) */
fill = 64 - left;
/* 更新已处理的消息长度 */
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
/* 产生进位,total[1]+1 */
if (ctx->total[0] < (uint32_t)ilen) {
ctx->total[1]++;
}
/* 先处理上一次剩余消息 */
if (left && ilen >= fill) {
/* 拷贝fill长度消息,使其为64字节 */
memcpy((void *)(ctx->buffer + left), input, fill);
sha256_internal_process(ctx->state, ctx->buffer, 1);
input += fill;
ilen -= fill;
left = 0;
}
/* 再处理输入消息 */
block_num = ilen >> 6;
block_len = block_num << 6;
if (block_num) {
sha256_internal_process(ctx->state, input, block_num);
input += block_len;
ilen -= block_len;
}
/* 剩余消息小于64字节,暂存 */
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
return 0;
}
- 首先进行消息长度处理,与本次输入的消息长度相加
- 如果上次有消息数据未处理,先要拷贝填充至64字节,进行哈希计算
- 然后对本次的剩余消息按块计算哈希
- 最后如果本次最后一个块不足64字节,需要进行数据暂存
其中sha256_internal_process
是sha256的核心部分,实现内部的哈希调度计算,实现见下一节。
sha256_finish
sha256_finish
函数是完成哈希计算,得到消息的摘要值。
/* 双字->4字节,大端模式 */
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE( n, data, offset ) \
{ \
( data )[( offset ) ] = (uint8_t) ( (n) >> 24 ); \
( data )[( offset ) + 1] = (uint8_t) ( (n) >> 16 ); \
( data )[( offset ) + 2] = (uint8_t) ( (n) >> 8 ); \
( data )[( offset ) + 3] = (uint8_t) ( (n) ); \
}
#endif
int sha256_finish(sha256_ctx_t *ctx, uint8_t output[32])
{
uint32_t used;
uint32_t high, low;
/* 剩余未处理消息长度 */
used = ctx->total[0] & 0x3F;
/* 填充比特“1” */
ctx->buffer[used++] = 0x80;
/* 当前块有空间追加填充和长度,最后8字节填充长度L*/
if (used <= 56) {
/* 填充K */
memset(ctx->buffer + used, 0, 56 - used);
} else {
/* 还需一个块 */
memset(ctx->buffer + used, 0, 64 - used);
/* 先哈希第一个块 */
sha256_internal_process(ctx->state, ctx->buffer, 1);
memset(ctx->buffer, 0, 56);
}
/* 追加消息长度 */
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, ctx->buffer, 56);
PUT_UINT32_BE(low, ctx->buffer, 60);
/* 哈希填充后的块 */
sha256_internal_process(ctx->state, ctx->buffer, 1);
PUT_UINT32_BE(ctx->state[0], output, 0);
PUT_UINT32_BE(ctx->state[1], output, 4);
PUT_UINT32_BE(ctx->state[2], output, 8);
PUT_UINT32_BE(ctx->state[3], output, 12);
PUT_UINT32_BE(ctx->state[4], output, 16);
PUT_UINT32_BE(ctx->state[5], output, 20);
PUT_UINT32_BE(ctx->state[6], output, 24);
PUT_UINT32_BE(ctx->state[7], output, 28);
return 0;
}
- PUT_UINT32_BE:定义一个宏,将一个双字数据存放到字节数组中,大端模式
- 首先进行消息填充,根据算法原理,需要填充一个比特1,K个比特0,以及消息长度L的二进制表示
- 如果最后一次暂存的数据没有空间追加填充,还需要再计算一个块
- 计算完成最后一个填充的块,输出的中间摘要值即为最终的消息哈希值
sha256_compute
有了上面三个分片哈希的函数,就可以简单实现一次性哈希计算的函数。
int sha256_compute(const uint8_t *input, size_t ilen, uint8_t output[32])
{
int ret;
sha256_ctx_t ctx;
if ((ret = sha256_starts(&ctx)) != 0) {
goto exit;
}
if ((ret = sha256_update(&ctx, input, ilen)) != 0) {
goto exit;
}
if ((ret = sha256_finish(&ctx, output)) != 0) {
goto exit;
}
exit:
memset(&ctx, 0, sizeof(sha256_ctx_t));
return ret;
}
sha256.c
完整的代码如下:
#include <string.h>
#include <sha256.h>
#include <sha256_core.h>
/* 双字->4字节,大端模式 */
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE( n, data, offset ) \
{ \
( data )[( offset ) ] = (uint8_t) ( (n) >> 24 ); \
( data )[( offset ) + 1] = (uint8_t) ( (n) >> 16 ); \
( data )[( offset ) + 2] = (uint8_t) ( (n) >> 8 ); \
( data )[( offset ) + 3] = (uint8_t) ( (n) ); \
}
#endif
int sha256_starts(sha256_ctx_t *ctx)
{
memset(ctx, 0, sizeof(sha256_ctx_t));
/* 清零 */
ctx->total[0] = 0;
ctx->total[1] = 0;
/* 初始哈希值 */
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
return 0;
}
int sha256_update(sha256_ctx_t *ctx, const uint8_t *input, size_t ilen)
{
size_t fill;
uint32_t left;
size_t block_num;
size_t block_len;
if (ilen == 0) {
return 0;
}
/* 上次剩余的消息长度 */
left = ctx->total[0] & 0x3F;
/* 需要填充fill长度消息,sha256每次处理512比特(即64字节) */
fill = 64 - left;
/* 更新已处理的消息长度 */
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
/* 产生进位,total[1]+1 */
if (ctx->total[0] < (uint32_t)ilen) {
ctx->total[1]++;
}
/* 先处理上一次剩余消息 */
if (left && ilen >= fill) {
/* 拷贝fill长度消息,使其为64字节 */
memcpy((void *)(ctx->buffer + left), input, fill);
sha256_internal_process(ctx->state, ctx->buffer, 1);
input += fill;
ilen -= fill;
left = 0;
}
/* 再处理输入消息 */
block_num = ilen >> 6;
block_len = block_num << 6;
if (block_num) {
sha256_internal_process(ctx->state, input, block_num);
input += block_len;
ilen -= block_len;
}
/* 剩余消息小于64字节,暂存 */
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
return 0;
}
int sha256_finish(sha256_ctx_t *ctx, uint8_t output[32])
{
uint32_t used;
uint32_t high, low;
/* 剩余未处理消息长度 */
used = ctx->total[0] & 0x3F;
/* 填充比特“1” */
ctx->buffer[used++] = 0x80;
/* 当前块有空间追加填充和长度,最后8字节填充长度L*/
if (used <= 56) {
/* 填充K */
memset(ctx->buffer + used, 0, 56 - used);
} else {
/* 还需一个块 */
memset(ctx->buffer + used, 0, 64 - used);
/* 先哈希第一个块 */
sha256_internal_process(ctx->state, ctx->buffer, 1);
memset(ctx->buffer, 0, 56);
}
/* 追加消息长度 */
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, ctx->buffer, 56);
PUT_UINT32_BE(low, ctx->buffer, 60);
/* 哈希填充后的块 */
sha256_internal_process(ctx->state, ctx->buffer, 1);
PUT_UINT32_BE(ctx->state[0], output, 0);
PUT_UINT32_BE(ctx->state[1], output, 4);
PUT_UINT32_BE(ctx->state[2], output, 8);
PUT_UINT32_BE(ctx->state[3], output, 12);
PUT_UINT32_BE(ctx->state[4], output, 16);
PUT_UINT32_BE(ctx->state[5], output, 20);
PUT_UINT32_BE(ctx->state[6], output, 24);
PUT_UINT32_BE(ctx->state[7], output, 28);
return 0;
}
int sha256_compute(const uint8_t *input, size_t ilen, uint8_t output[32])
{
int ret;
sha256_ctx_t ctx;
if ((ret = sha256_starts(&ctx)) != 0) {
goto exit;
}
if ((ret = sha256_update(&ctx, input, ilen)) != 0) {
goto exit;
}
if ((ret = sha256_finish(&ctx, output)) != 0) {
goto exit;
}
exit:
memset(&ctx, 0, sizeof(sha256_ctx_t));
return ret;
}
sha256_core.c
sha256_internal_process
函数是sha256算法的哈希计算部分,使用加密扩展CE(Cryptographic Extension)进行了加速。
#include "arm_neon.h"
#include <sha256_core.h>
#define SHA256_BLOCK_SIZE 64
static const uint32_t K[] =
{
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
};
void sha256_internal_process(uint32_t state[8], const uint8_t *msg, size_t blocks)
{
uint32x4_t abcd = vld1q_u32(&state[0]);
uint32x4_t efgh = vld1q_u32(&state[4]);
for (size_t i = 0; i < blocks; i++, msg += SHA256_BLOCK_SIZE) {
uint32x4_t tmp, abcd_prev;
uint32x4_t abcd_orig = abcd;
uint32x4_t efgh_orig = efgh;
uint32x4_t sched0 = vreinterpretq_u32_u8(vld1q_u8(msg + 16 * 0));
uint32x4_t sched1 = vreinterpretq_u32_u8(vld1q_u8(msg + 16 * 1));
uint32x4_t sched2 = vreinterpretq_u32_u8(vld1q_u8(msg + 16 * 2));
uint32x4_t sched3 = vreinterpretq_u32_u8(vld1q_u8(msg + 16 * 3));
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* Will be true if not defined */
/* Untested on BE */
sched0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched0)));
sched1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched1)));
sched2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched2)));
sched3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched3)));
#endif
/* Rounds 0 to 3 */
tmp = vaddq_u32(sched0, vld1q_u32(&K[0]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
/* Rounds 4 to 7 */
tmp = vaddq_u32(sched1, vld1q_u32(&K[4]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
/* Rounds 8 to 11 */
tmp = vaddq_u32(sched2, vld1q_u32(&K[8]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
/* Rounds 12 to 15 */
tmp = vaddq_u32(sched3, vld1q_u32(&K[12]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
for (int t = 16; t < 64; t += 16) {
/* Rounds t to t + 3 */
sched0 = vsha256su1q_u32(vsha256su0q_u32(sched0, sched1), sched2, sched3);
tmp = vaddq_u32(sched0, vld1q_u32(&K[t]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
/* Rounds t + 4 to t + 7 */
sched1 = vsha256su1q_u32(vsha256su0q_u32(sched1, sched2), sched3, sched0);
tmp = vaddq_u32(sched1, vld1q_u32(&K[t + 4]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
/* Rounds t + 8 to t + 11 */
sched2 = vsha256su1q_u32(vsha256su0q_u32(sched2, sched3), sched0, sched1);
tmp = vaddq_u32(sched2, vld1q_u32(&K[t + 8]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
/* Rounds t + 12 to t + 15 */
sched3 = vsha256su1q_u32(vsha256su0q_u32(sched3, sched0), sched1, sched2);
tmp = vaddq_u32(sched3, vld1q_u32(&K[t + 12]));
abcd_prev = abcd;
abcd = vsha256hq_u32(abcd_prev, efgh, tmp);
efgh = vsha256h2q_u32(efgh, abcd_prev, tmp);
}
abcd = vaddq_u32(abcd, abcd_orig);
efgh = vaddq_u32(efgh, efgh_orig);
}
vst1q_u32(&state[0], abcd);
vst1q_u32(&state[4], efgh);
}
- 由于我们使用 Neon intrinsics,因此需要包含
arm_neon.h
头文件,其封装了Neon汇编指令,当用户在C程序中调用Neon intrinsics接口时,编译器会自动生成相关的Neon指令。 - 使用
vld1q_u32
加载中间摘要值到工作寄存器中a,b,c,d,e,f,g,h。abcd和efgh变量都是包含4个32位的数组,由于Neon寄存器是128位,因此只需要两个寄存器就可以存储所有工作寄存器。 - 接着使用
vld1q_u8
加载一个块的消息数据Wt进行调度,由于算法每次处理64字节数据,因此可以用4个变量进行存储即sched0,sched1,sched2,sched3。 - 由于算法规范在表达32位数据是采用大端的形式,如果平台是小端的话,需要先使用
vrev32q_u8
进行大小端转换。 - 接下来是64轮的消息调度,更新工作寄存器。当0≤t≤15时,使用原始输入的消息块,使用
vaddq_u32
将消息调度W和常量K相加,然后使用vsha256hq_u32
和vsha256h2q_u32
,分别计算工作寄存器abcd和efgh。 - 当16≤t≤63时,需要先计算消息调度W,即使用
vsha256su0q_u32
和vsha256su1q_u32
计算实现,之后计算工作寄存器abcd和efgh与上一步一样。 - 然后使用
vaddq_u32
将本次工作寄存器abcd和efgh与上一次相加,得到中间哈希值。 - 继续执行下一个消息数据块的处理,直至完成所有的块,使用
vst1q_u32
存储中间摘要值。
sha256_core.h
新建一个sha256_core.h
,代码如下:
#ifndef __SHA256_CORE_H_
#define __SHA256_CORE_H_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"{
#endif
void sha256_internal_process(uint32_t state[8], const uint8_t *msg, size_t blocks);
#ifdef __cplusplus
}
#endif
#endif /* __SHA256_CORE_H_ */
测试验证
新建一个sha256_test_functional.c
,测试一下sha256算法实现的正确性。
功能验证
#include <string.h>
#include <stdio.h>
#include <sha256.h>
static const uint8_t msg1[] = {
0x61, 0x62, 0x63
};
static const uint8_t msg2[] = {
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64,
};
static const uint8_t dgst1[] = {
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
};
static const uint8_t dgst2[] = {
0x04, 0x16, 0xff, 0xb1, 0xd9, 0xb7, 0xbf, 0xfb,
0xf0, 0x05, 0x08, 0x4e, 0xa7, 0xef, 0xeb, 0x05,
0x34, 0x7a, 0x40, 0x31, 0x70, 0x85, 0x66, 0xea,
0x37, 0xf6, 0x4f, 0x44, 0x84, 0x42, 0x77, 0xdf
};
int main(void)
{
uint8_t buf[32];
sha256_ctx_t ctx;
int ret;
const uint8_t *p = msg2;
printf("\n--------SHA256 FUNCTIONAL TEST BEGAIN--------\n\n");
printf("[1.1]. execute sha256 compute\n");
ret = sha256_compute(msg1, sizeof(msg1), buf);
printf("[1.2]. compare result with the refrence dgst\n");
if (ret != 0 || memcmp(buf, dgst1, sizeof(dgst1)) != 0) {
goto exit;
}
printf("[2.1]. execute sha256 starts\n");
ret = sha256_starts(&ctx);
if (ret != 0) {
goto exit;
}
printf("[2.2]. execute sha256 update\n");
ret = sha256_update(&ctx, p, 16);
if (ret != 0) {
goto exit;
}
p += 16;
printf("[2.3]. execute sha256 update\n");
ret = sha256_update(&ctx, p, 20);
if (ret != 0) {
goto exit;
}
p += 20;
printf("[2.4]. execute sha256 update\n");
ret = sha256_update(&ctx, p, 12);
if (ret != 0) {
goto exit;
}
p += 12;
printf("[2.5]. execute sha256 update\n");
ret = sha256_update(&ctx, p, 24);
if (ret != 0) {
goto exit;
}
printf("[2.6]. execute sha256 finish\n");
ret = sha256_finish(&ctx, buf);
printf("[2.7]. compare result with the refrence dgst\n");
if (ret != 0 || memcmp(buf, dgst2, sizeof(dgst2)) != 0) {
goto exit;
}
printf("\n--------SHA256 FUNCTIONAL TEST PASS----------\n\n");
return 0;
exit:
printf("\n--------SHA256 FUNCTIONAL TEST FAIL----------\n\n");
return -1;
}
编译
为了启用Neon intrinsics,编译时需要启用ARM的加密扩展CE(Cryptographic Extension),对于AArch32和AArch64,编译选项有所不同。
- AArch32:-mfpu=crypto-neon-fp-armv8
- AArch64:-march=armv8-a+simd+crypto
以AArch64为例,编译sha256代码:
aarch64-linux-gnu-gcc -march=armv8-a+simd+crypto sha256_test_functional.c sha256.c sha256_core.c -I. -o sha256_test_functional
在支持Armv8-A架构的硬件平台上验证通过:
# ./sha256_test_functional
--------SHA256 FUNCTIONAL TEST BEGAIN--------
[1.1]. execute sha256 compute
[1.2]. compare result with the refrence dgst
[2.1]. execute sha256 starts
[2.2]. execute sha256 update
[2.3]. execute sha256 update
[2.4]. execute sha256 update
[2.5]. execute sha256 update
[2.6]. execute sha256 finish
[2.7]. compare result with the refrence dgst
--------SHA256 FUNCTIONAL TEST PASS----------