对称密码学 认证加密

例子

#define MESSAGE ((const unsigned char *) "test")
#define MESSAGE_LEN 4
#define CIPHERTEXT_LEN (crypto_secretbox_MACBYTES + MESSAGE_LEN)

unsigned char nonce[crypto_secretbox_NONCEBYTES];
unsigned char key[crypto_secretbox_KEYBYTES];
unsigned char ciphertext[CIPHERTEXT_LEN];

randombytes_buf(nonce, sizeof nonce);
randombytes_buf(key, sizeof key);
crypto_secretbox_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce, key);

unsigned char decrypted[MESSAGE_LEN];
if (crypto_secretbox_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, key) != 0) {
    /* message 被篡改了! */
}

目的

这个操作:

  • 用 一个 key 和 一个 nonce 加密一个消息,让消息保持保密。
  • 计算一个 认证 tag。 这个tag 用于确保消息在解密之前没有被篡改过。

一个单一的 key 同时用于 加密/签名 , 和 验证/解密 消息。 因此,保证 key 保密,是极其重要的。 nonce不需要保密,但是绝对不能在同一个key下重复使用。生成一个 nonce 的最简单办法是使用 randombytes_buf()

Combined 模式

在 combined 模式, 认证 和 消息密文 存储在一起。 一般需求都是这样的。

int crypto_secretbox_easy(unsigned char *c, const unsigned char *m,
                          unsigned long long mlen, const unsigned char *n,
                          const unsigned char *k);

crypto_secretbox_easy() 函数加密一个消息 mlen 字节长的消息m , 使用 key k 和 nonce n

k 应该是 crypto_secretbox_KEYBYTES 字节长,并且 n应该是 crypto_secretbox_NONCEBYTES 字节长.

c 应该最少是 crypto_secretbox_MACBYTES + mlen 字节长.

消息密文的长度也是 mlen 字节, 这个函数先在 c 中 写入 crypto_secretbox_MACBYTES 字节长的 认证tag , 然后追加加密后的 消息密文 。

cm 内存区间可以重叠, 因此可以做 ‘原地加密’ 。当然不要忘记 要有额外的 crypto_secretbox_MACBYTES 字节用于写入 认证 tag。

int crypto_secretbox_open_easy(unsigned char *m, const unsigned char *c,
                               unsigned long long clen, const unsigned char *n,
                               const unsigned char *k);

crypto_secretbox_open_easy() 函数 验证 并 解密 crypto_secretbox_easy() 生成的密文。

c 是个指针,指向 crypto_secretbox_easy() 生成的 认证tag + 消息密文 的组合buffer。clen 是 这个组合的buffer的长度,换句话说,就是 crypto_secretbox_MACBYTES + 明文消息 的长度。

nonce n 和 key k 必须和 加密/认证时候用的 nonce 和 相同。

这个函数返回 -1, 表示验证失败。 返回 0 ,表示成功,并且解密出来的 消息 存入 m 中。

mc 内存区间可以重叠,因此可以做原地解密。

Detached 模式

一些程序可能需要 把 认证 tag 和 消息密文分开存储。

对这种使用场景, 可以使用"detached" 系列变种函数。

int crypto_secretbox_detached(unsigned char *c, unsigned char *mac,
                              const unsigned char *m,
                              unsigned long long mlen,
                              const unsigned char *n,
                              const unsigned char *k);

这个函数加密 mlen 长度的消息 m,使用 key k 和 nonce n,并且把消息密文 存入 c,由于这个函数不会在前面写入 认证tag, 会 精确地写入 mlen 字节到 ccrypto_secretbox_MACBYTES 字节的认证tag,会写入 mac

int crypto_secretbox_open_detached(unsigned char *m,
                                   const unsigned char *c,
                                   const unsigned char *mac,
                                   unsigned long long clen,
                                   const unsigned char *n,
                                   const unsigned char *k);

crypto_secretbox_open_detached() 函数验证并解密 长度是 clen 的消息密文 cclen 不包括 认证tag,所以长度和明文相等。

在使用 key k 和 nonce n 验证确认 mac 是这个密文的合法认证tag后, 明文会写入m

这个函数 返回 -1 如果验证失败, 返回 0 表示成功。

常量

  • crypto_secretbox_KEYBYTES
  • crypto_secretbox_MACBYTES
  • crypto_secretbox_NONCEBYTES

算法细节

  • 加密算法 Encryption: XSalsa20 流加密算法
  • 认证算法 Authentication: Poly1305 MAC算法

注意

NaCl 原来的 crypto_secretbox API 也支持, 尽管不推荐。

crypto_secretbox() 接受 一个指向 消息之前32字节的指针,并且把密文写入 目的地指针之后 16字节, 并把目的地指针的指向的前16字节填充成0 。 crypto_secretbox_open() 接受一个指向密文前 16字节的指针,并把消息存入 目的地指针的 32字节后, 并把目的地指针的前32字节填充成0。

_easy_detached API 更快,并且改进了易用性,不再需要 padding 填充,拷贝,和指针计算。

results matching ""

    No results matching ""