轻量级安全库:Monocypher

Crypto++虽然全面,但是太重了。如果只是想要实现安全但不需要特定的方法,则Monocypher是更好的选择。

https://monocypher.org/

Monocypher是一个轻量化的安全库,只有一个.h和.c文件,直接加入工程即可。其提供了安全方法的良好抽象,直接在业务层面使用,不需要使用者关心底层用的到底是什么实现。

一、Hash

有一个crypto_blake2b可以直接算数据的hash,使用方法很简单,比如官网例子:

uint8_t hash   [64]; /* Output hash (64 bytes)          */ 
uint8_t message[12] = "Lorem ipsum"; /* Message to hash */ 
crypto_blake2b(hash, message, 12);

Monocypher的所有代码风格都是这样后面的内容就不在这里给出了,去官网看例子即可。

二、公钥签名

当你只需要authentication和integrity,不需要encryption时,可以使用public key signature算法。这个算法分成三步
首先A使用crypto_sign_public_key生成公钥和私钥,公钥传给B。
其次A使用crypto_sign函数,对消息进行签名(需要私钥)
最后B收到A的消息和签名后,使用crypto_check对消息进行检查(需要公钥)

该算法保证没有私钥的人是不可能提供有效签名的,所以能够保证authentication和integrity。

三、认证加密

Monocypher提供的authenticated encryption即可使用对称加密也可使用非对称加密。

先看对称加密。假设A和B通过某种途径互通了秘钥,则算法分成三步:
首先A使用crypto_lock对消息进行加密。需要用到秘钥和nonce(随机字节),加密的过程中库会生成mac(message authentication code)。
然后A把密文+nonce+mac发送给B。注意nonce和mac可以直接发送不用进一步处理,但是只能用一次。
最后B收到A的消息后,使用crypto_unlock对消息进行解密(需要秘钥+nonce+mac)。

再看非对称加密。
如果加密通讯的前提是已经有一条秘密渠道,那么适用范围就太狭窄了。

Monocypher的非对称加密体现为key exchange算法。

要在非安全环境下交换秘钥,需要以下几步:
首先A在本地生成私钥AS(随机字节),然后使用crypto_key_exchange_public_key生成对应公钥AP。同理B在本地生成私钥BS(随机字节),然后使用crypto_key_exchange_public_key生成对应公钥BP。
然后A和B互相将公钥发给对方,然后使用crypto_key_exchange,基于自己的私钥和对方的公钥生成session key SSK。这个SSK就相当于对称加密中的共同秘钥。
最后A使用crypto_lock加密信息(秘钥用SSK),B使用crypto_unlock解密信息(秘钥用SSK),方法跟上述对称加密一样。

四、生成随机字节

Monocypher为了轻量化没有提供生成随机字节的方法,并建议用户使用操作系统层面的API而不是在应用层面生成随机字节。

本人经过摸索可以这样实现:
在Mac上可以

#include <stdlib.h>
void Crypto::GenRndBuffer(uint8_t* pBuf, int nSize)
{
    arc4random_buf(pBuf, nSize);
}

在Linux上可以

//https://man7.org/linux/man-pages/man2/getrandom.2.html
#include <sys/random.h>
void Crypto::GenRndBuffer(uint8_t* pBuf, int nSize)
{
    getrandom(pBuf, nSize, 0);
}

在Win上可以

//https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
#include <Windows.h>
#include <bcrypt.h>
#pragma comment(lib, "Bcrypt")
void Crypto::GenRndBuffer(uint8_t* pBuf, int nSize)
{
    BCRYPT_ALG_HANDLE hProv;
    BCryptOpenAlgorithmProvider(&hProv, BCRYPT_RNG_ALGORITHM, NULL, 0);
    BCryptGenRandom(hProv, pBuf, nSize, 0);
    BCryptCloseAlgorithmProvider(hProv, 0);
}