PY32F002A 试用 ChaCha20 流密码加密 FlashStore 存储数据

ChaCha20 是什么

ChaCha20 是一种流密码(stream cipher),RFC 8439 标准。它是 TLS 1.3、QUIC、WireGuard 等新一代协议的默认加密算法之一,Google 在移动端全面用它替代 AES。和 AES 不同,它不需要硬件加速器,纯软件就能在 MCU 上高效运行。

核心特性:

在 PY32F002A 上的用法

结合之前移植的 FlashStore A/B 分区存储,把 ChaCha20 作为加密层叠加在上面:

保存: 明文 → ChaCha20 加密 → FlashStore_Save → 写入 Flash
加载: FlashStore_Load → 密文 → ChaCha20 解密 → 明文

核心代码三行:

chacha20_crypt(data, size, key, nonce);   // 加密(或解密,同一函数)
FlashStore_Save(&cfg, data, size);        // 存到闪存
FlashStore_Load(&cfg, data, size);        // 读回
chacha20_crypt(data, size, key, nonce);   // 解密

实测:加密一句名言

用 "Stay hungry, stay foolish." 做测试,32 字节密钥 + 12 字节 nonce。

明文:

53 74 61 79 20 68 75 6e 67 72 79 2c 20 73 74 61  │Stay hungry, sta
79 20 66 6f 6f 6c 69 73 68 2e 00 00 00 00 00 00  │y foolish.......

加密后 — 完全不可读:

3a 28 1d a0 11 62 cd 14 4a 03 07 03 93 67 63 73  │:(...b..J....gcs
69 d1 59 2f a1 dd 95 06 2d 14 b5 28 29 ee 9f 0a  │i.Y/....-..()...

FlashStore 存盘再读回,解密还原 — 一字不差。

资源占用

PY32F002A(Cortex-M0+,48MHz,-Os):

模块 Flash 说明
flash_store 594 B A/B 分区核心
flash_store_port 230 B HAL 适配
chacha20 ~700 B 加密/解密
合计 ~1.5 KB 含加密的可靠闪存存储

如果只需要存储不需要加密,去掉 ChaCha20 即可,约 800 字节搞定。

完整源码

只有 100 行,零依赖,复制到任何 MCU 项目即可使用。

#include "chacha20.h"
#include <string.h>

#define QR(a, b, c, d) do {          \
    (a) += (b); (d) ^= (a);           \
    (d) = ((d) << 16) | ((d) >> 16);  \
    (c) += (d); (b) ^= (c);           \
    (b) = ((b) << 12) | ((b) >> 20);  \
    (a) += (b); (d) ^= (a);           \
    (d) = ((d) << 8)  | ((d) >> 24);  \
    (c) += (d); (b) ^= (c);           \
    (b) = ((b) << 7)  | ((b) >> 25);  \
} while (0)

static uint32_t ld32(const uint8_t *p) {
    uint32_t v;
    memcpy(&v, p, sizeof(v));
    return v;
}

static void st32(uint8_t *p, uint32_t v) {
    memcpy(p, &v, sizeof(v));
}

static void chacha20_block(uint8_t out[64], const uint32_t init[16]) {
    uint32_t x[16];
    memcpy(x, init, sizeof(x));

    for (int i = 0; i < 10; i++) {
        QR(x[0], x[4], x[ 8], x[12]);
        QR(x[1], x[5], x[ 9], x[13]);
        QR(x[2], x[6], x[10], x[14]);
        QR(x[3], x[7], x[11], x[15]);
        QR(x[0], x[5], x[10], x[15]);
        QR(x[1], x[6], x[11], x[12]);
        QR(x[2], x[7], x[ 8], x[13]);
        QR(x[3], x[4], x[ 9], x[14]);
    }

    for (int i = 0; i < 16; i++)
        st32(out + i * 4, x[i] + init[i]);
}

void chacha20_crypt(uint8_t *data, size_t size,
                    const uint8_t key[32], const uint8_t nonce[12]) {
    if (size == 0) return;

    uint32_t state[16];
    state[0] = 0x61707865u;
    state[1] = 0x3320646eu;
    state[2] = 0x79622d32u;
    state[3] = 0x6b206574u;

    for (int i = 0; i < 8; i++)
        state[4 + i] = ld32(key + i * 4);

    state[12] = 1;

    for (int i = 0; i < 3; i++)
        state[13 + i] = ld32(nonce + i * 4);

    uint8_t keystream[64];
    size_t offset = 0;

    while (offset < size) {
        chacha20_block(keystream, state);

        size_t chunk = size - offset;
        if (chunk > 64) chunk = 64;

        for (size_t i = 0; i < chunk; i++)
            data[offset + i] ^= keystream[i];

        offset += chunk;
        state[12]++;
    }
}

头文件:

#ifndef CHACHA20_H
#define CHACHA20_H

#include <stddef.h>
#include <stdint.h>

void chacha20_crypt(uint8_t *data, size_t size,
                    const uint8_t key[32], const uint8_t nonce[12]);

#endif