使用ARM的Neon技术实现SHA256算法加速

需求

假设一款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指令包括如下四种:

指令功能
SHA256HSHA256 hash update (part 1)
SHA256H2SHA256 hash update (part 2)
SHA256SU0SHA256 schedule update 0
SHA256SU1SHA256 schedule update 1

这里我们采用 Neon intrinsics 实现 SHA256 算法的加速,SHA256 Intrinsics 指令如下:

SHA256 Intrinsics

本方案需要对SHA256算法原理,Neon技术等有一定的了解,详细可参考本公众号/博客的其他文章。

实现

sha256.h

SHA256算法需要实现分片哈希和一次性哈希的功能,包括如下函数:

函数功能
sha256_startsSHA256开始,创建上下文
sha256_updateSHA256更新,输入消息数据
sha256_finishSHA256结束,得到摘要值
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_u32vsha256h2q_u32,分别计算工作寄存器abcd和efgh。
  • 当16≤t≤63时,需要先计算消息调度W,即使用vsha256su0q_u32vsha256su1q_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----------

参考

  1. Neon简介
  2. 哈希算法SHA256
  3. 使用Neon intrinsics优化C代码
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值