@romangol
2017-08-26T11:01:32.000000Z
字数 40653
阅读 11311
未分类
Sodium是一个现代易于使用的软件库,它可以用来做加密、解密、签名、口令Hash以及更多的事情。
它是一个可移植的、可交叉编译的、可安装的基于NaCl开发的加密库,具有兼容的API以及扩展的API,来进一步提高可用性。
它的目标是提供构建更高级加密工具所需的所有核心操作。
Sodium支持各种编译器和操作系统,包括Windows(MinGW或Visual Studio,x86和x86_64)、iOS和Android。
这个设计选择强调安全性,“魔术常量”(magic constants)有明确的理论基础。
尽管强调了高安全性,但是与NIST标准的大多数实现相比,原语的速度要快得多。
1.0.11版本于2016年7月31日发布。
邮件列表可用于讨论libsodium。
为了加入,只需发送一个随机邮件到sodium-subscribe
{at} pureftpd
{dot} org
。
您可以下载ePUB(适用于iPad、iPhone及Mac)格式,MOBI(适用于Kindle)格式和PDF格式的文档:https://www.gitbook.com/book/jedisct1/libsodium/details
有关详细信息,请参阅LICENSE
文件。
Sodium是一个共享库,具有机器独立的头文件集合,因此它方便被第三方项目使用。
这个库是使用自动工具构建的,这让它易于打包。
安装很简单,能利用多核进行编译和测试。
下载libsodium压缩包,接着按照以下步骤:
$ ./configure
$ make && make check
# make install
通常不需要在Windows上编译,因为MinGW和Visual Studio的预构建库可用(见下文)。
但是,如果您想要自己亲手编译,首先从Git中克隆稳定分支库。
Visual Studio解决方案可以在builds/msvc
目录中找到。
为了用MingW编译,在Win32或x64平台上运行./dist-build/msys2-win32.sh
或./dist-build/msys2-win64.sh
。
Visual Studio 2010,2012,2013和2015的预构建x86和x86_64库可用, MinGW32和MinGW64的预构建库也可用。
它们包括头文件,以及用于所有受支持的编译器版本的静态(.LIB
)和共享(.DLL
)库。
想要静态链接Sodium的项目必须定义一个名为SODIUM_STATIC
的宏,这将阻止符号定义引用__dllexport
。
完全支持交叉编译。这是使用ARM嵌入式处理器的GNU工具对ARM进行交叉编译的一个例子:
$ export PATH=/path/to/gcc-arm-none-eabi/bin:$PATH
$ export LDFLAGS='--specs=nosys.specs'
$ export CFLAGS='-Os'
$ ./configure --host=arm-none-eabi --prefix=/install/path
$ make install
make check
还可以构建测试应用程序,但这些应用程序必须在本地平台上运行。
注意:--specs = nosys.specs
仅用于ARM编译工具链。
可以使用CompCert编译器来编译发行版本。但在使用CompCert时,即使在小端系统上,Autoconf脚本可能会错误地将字节排序检测为大端序列。
您可能需要手动预定义ac_cv_c_bigendian=no
,以解决此问题。
使用CompCert在一个小端系统上编译Sodium的典型命令行是:
env CC=ccomp ac_cv_c_bigendian=no \
CFLAGS="-O2 -fstruct-passing" ./configure \
--disable-shared --enable-static && \
make check && make install
我们建议在克隆libsodium git仓库时使用分布式压缩包,特别是当压缩包不需要libtool或autotools这样的依赖。
然而,当克隆Git存储库更方便的时候,稳定分支总是包含以下内容:
稳定分支中的代码还包括生成的文件,因此不需要使用自动化工具(libtool,autoconf,automake)来生成文件。
可以使用Minisign和以下的Ed25519密钥来验证配置文件:
RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3
或者使用GnuPG和以下的RSA密钥:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1 (OpenBSD)
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
=Hkmu
-----END PGP PUBLIC KEY BLOCK-----
#使用libsodium的项目
有一些应用程序使用Libsodium。发送一个pull请求将你的请求添加到列表中。
有一些库和框架使用Libsodium。发送一个pull请求将你的请求添加到列表中。
有一些企业可能在非开源的产品中使用Libsodium。发送一个pull请求将你的请求添加到列表中。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
...
}
sodium.h
是唯一需要被包含的头文件。
在Linux系统中,使用-lsodium
来链接sodium
库,在可用系统上使用pgk-config
可以获得正确的编译器/链接器标识。
CFLAGS=$(pkg-config --cflags libsodium)
LDFLAGS=$(pkg-config --libs libsodium)
对于静态链接,Visual Studio的用户应该定义SODIUM_STATIC=1
和SODIUM_EXPORT=
。而在其他的平台上这不是必须的。
sodium_init()
初始化库并且应该在任何由Sodium提供的函数前被调用。这个函数可以被调用不止一次,并且可以被从版本1.0.11开始的多线程同时调用。
在这个函数返回后,有Sodium提供的其他所有的函数都将是线程安全的。
sodium_init()
不进行任何的内存分配。但是,在Unix系统中,它将打开/dev/urandom
并且保持描述符打开,这样设备在一个chroot()
调用后仍保持可访问。对sodium_init()
的多次调用不会导致额外的描述符被打开。
sodium_init()
成功则返回0
,失败返回-1
,当库已经被初始化则返回1
。
int sodium_memcmp(const void * const b1_, const void * const b2_, size_t len);
当一个比较涉及私密数据(例如:密钥,认证标签)的时候,使用一个常量时间比较函数来减轻侧信道攻击是至关重要的。
sodium_memcmp()
函数可以用于此目的。
当b1_
指向的len
字节与b2_
指向的len
字节相匹配的时候,该函数返回0
,否则返回-1
。
注意:sodium_memcmp()
不是一个字典比较器,它并不是memcmp()
的一个通用替代品。
char *sodium_bin2hex(char * const hex, const size_t hex_maxlen,
const unsigned char * const bin, const size_t bin_len);
sodium_bin2hex()
函数将bin
中存储的bin_len
字节转换成十六进制字符串。
字符串存储在hex
并且包含了一个空字节(\0
)终止符。
hex_maxlen
是从十六制开始允许写入该函数的最大字节数。它应该至少有bin_len * 2 + 1
。
该函数成功返回hex,溢出返回NULL。它在给定大小的常量时间内进行评估。
int sodium_hex2bin(unsigned char * const bin, const size_t bin_maxlen,
const char * const hex, const size_t hex_len,
const char * const ignore, size_t * const bin_len,
const char ** const hex_end);
sodium_hex2bin()
函数解析十六进制字符串hex
并将其转换为字节序列。
hex
不必空终止,因为要解析的字符数是通过hex_len
参数提供的。
ignore
是一串要跳过的字符。例如,字符串": "
允许列和空格出现在十六进制字符串的任何位置。这些字符将被忽略。因此,"69:FC"
,"69 FC"
,"69 : FC"
和 "69FC"
将被有效输入并产生相同的输出。
ignore
可以被设置为NULL
来禁止任何非十六进制字符。
bin_maxlen
是要放入bin
的最大的字节数。
当一个非十六进制,不可忽略的字符被找到或者当bin_maxlen
字节已被写入时,解析器就停止。
如果需要多于bin_maxlen
字节来存储解析字符串,该函数将返回-1
。它成功时返回0
,并将hex_end
(如果不为NULL
)设置为指向最后一个解析字符后的字符指针。
在常量时间内,对给定的长度和格式进行评估。
void sodium_increment(unsigned char *n, const size_t nlen);
sodium_increment()
函数将指针指向任意长度的无符号数字,并将其递增。
它以常量时间运行给定的长度,并将数字视为小端格式进行编码。
sodium_increment()
可以用于在常量时间内递增瞬时值。
这个函数在libsodium 1.0.4中引入。
void sodium_add(unsigned char *a, const unsigned char *b, const size_t len);
sodium_ad()
函数接收两个指向无符号数字的指针,将这些指针以小端的格式进行编码,a
和b
的大小都是len
字节。
它在给定的长度上以常量时间计算(a + b) mod 2^(8*len)
,并用结果覆盖它。
这个函数在libsodium 1.0.7被引入。
int sodium_compare(const void * const b1_, const void * const b2_, size_t len);
给定b1_
和b2_
,以小端格式编码两个len
字节数字,该函数返回:
- -1
如果b1_
比b2_
小
- 0
如果b1_
等于b2_
- 1
如果b1_
比b2_
大
比较在给定长度的常量时间内完成。
该函数可以与瞬时值一起使用,以防止重放攻击。
这个函数在libsodium 1.0.6被引入。
int sodium_is_zero(const unsigned char *n, const size_t nlen);
如果n
指向nlen
个字节的向量只包含零,此函数返回1
。如果找到非零的比特,就返回0
.
其执行时间对于给定长度是恒定的。
这个函数在libsodium 1.0.7中被引入。
void sodium_memzero(void * const pnt, const size_t len);
使用结束后,敏感数据应该被覆盖,但是memset()
和手写的代码会被优化编译器或链接器悄悄地删除。
即使代码会被优化,sodium_memzero()
这个函数尝试从pnt
开始有效地清空len
个字节。
int sodium_mlock(void * const addr, const size_t len);
sodium_mlock()
这个函数锁定从addr
开始的至少len
个字节的内存。这将有助于避免将敏感数据交换到硬盘上。
另外,建议在处理敏感数据的机器上完全禁用交换分区,或者作为第二选择,使用加密的交换分区。
出于类似的原因,在Unix系统上,当您在开发环境之外运行密码相关的代码时,您应该禁用核心转储。这可以通过使用内置的shell(例如ulimit
)或者使用setrlimit(RLIMIT_CORE, &(struct rlimit) {0, 0})
。在能够实现此功能的操作系统上,还应禁用内核崩溃转储。
sodium_mlock()
包装了mlock()
和VirtualLock()
。注意:许多系统对进程可能锁定的内存量进行限制。注意在必要时应提高这些限制(例如Unix ulimits)。当任何限制达到的时候,sodium_lock()
将返回-1
。
int sodium_munlock(void * const addr, const size_t len);
在锁定的内存不再被使用之后,应该调用sodium_munlock()
函数。在将页面重新标记为可交换之前,它会从addr
开始清空len
个字节。因此不需要在使用sodium_munlock()
函数之前调用sodium_memzero()
函数。
在支持它的系统上,sodium_mlock()
也包装了madvise()
,并建议内核不要将锁定的内存包含在核心转储中。 sodium_unlock()
也会撤消这个额外的保护。
Sodium提供用于存储敏感数据的堆分配函数,这些函数不是一般的堆分配函数。 特别地,它们比malloc()
慢,并且需要3或4个额外的虚拟内存页。
在使用任何保护堆分配函数之前,必须先调用sodium_init()
函数。
void *sodium_malloc(size_t size);
sodium_malloc()
函数返回一个指针,这个指针可以访问大小连续的内存字节。
分配的区域放置在页边界的末尾,紧随其后的是保护页。 因此,访问该区域结尾处的内存将立即终止应用程序。
一个canary也放置在返回的指针之前。尝试使用sodium_free()
释放分配的区域时,如果检测到这个canary的修改,会导致应用程序立即终止。
在这个canary之前放置一个附加的保护页面,以便当读取到超过无关区域的末尾时,敏感数据不太可能被访问。
分配的区域填充0xd0
个字节,以帮助捕获由于初始化数据引起的错误。
此外,在该区域调用了sodium_mlock()
以帮助避免将其交换到磁盘。在支持MAP_NOCORE
或MADV_DONTDUMP
的操作系统上,以这种方式分配的内存也不会成为核心转储的一部分。
如果分配的大小不是所需对齐字节的倍数,返回的地址将不会对齐。
因此,为了确保正确地对齐,sodium_malloc()
函数不应该和包装或可变长度的结构一起使用,除非给与sodium_malloc()
的大小被舍入。
libsodium使用的所有结构都可以安全地使用sodium_malloc()
进行分配,唯一需要额外注意的是crypto_generichash_state
,其大小需要舍入到64字节的倍数。
分配0
个字节是一个有效的操作,并返回一个可以成功传递给sodium_free()
的指针。
void *sodium_allocarray(size_t count, size_t size);
sodium_allocarray()
函数返回一个指针,这个指针可以访问size
字节大小的count
对象。
它提供与sodium_malloc()
相同的保证,当count * size
超过SIZE_MAX
时可以防止算术溢出。
void sodium_free(void *ptr);
sodium_free()
函数解锁并释放使用sodium_malloc()
或sodium_allocarray()
分配的内存。
在此之前,检查canary以检测可能的缓冲区下溢,并在需要时终止该过程。
在释放之前,sodium_free()
也会用零填充内存区域。
即使您以前使用sodium_mprotect_readonly()
保护过该区域,也可以调用此函数; 保护将根据需要自动更改。
ptr
可以为NULL
,在这种情况下不执行任何操作。
int sodium_mprotect_noaccess(void *ptr);
sodium_mprotect_noaccess()
函数使得使用sodium_malloc()
或sodium_allocarray()
分配的区域无法访问。 它不能被读取或写入,但数据被保留。
此功能可用于使机密数据无法访问,除了特定操作实际需要之外。
int sodium_mprotect_readonly(void *ptr);
sodium_mprotect_readonly()
函数将使用sodium_malloc()
或sodium_allocarray()
分配的区域标记为只读。
尝试修改数据将导致进程终止。
int sodium_mprotect_readwrite(void *ptr);
在使用sodium_mprotect_readonly()
或sodium_mprotect_noaccess()
将区域置于保护的状态之后,sodium_mprotect_readwrite()
函数将使用sodium_malloc()
或sodium_allocarray()
分配的区域标记为可读写。
这个函数库提供了一组函数来生成不可预知的数据,这适用于创建密钥。
- 在Windows系统上,使用RtlGenRandom()
函数
- 在OpenBSD和Bitrig上,使用arc4random()
函数
- 在较新的Linux内核中,使用getrandom
系统调用(从Sodium 1.0.3版本开始)
- 在其它类Linux系统上,使用/dev/urandom
设备
- 如果上述选项都不能被安全地使用,我们也提供自定义实现。
uint32_t randombytes_random(void);
这个randombytes_random()
函数返回0
到0xffffffff
之间(闭区间)的一个不可预测的值。
uint32_t randombytes_uniform(const uint32_t upper_bound);
randombytes_uniform()
函数返回0
和upper_bound
之间(开区间)的一个不可预测的值。与randombytes_random() % upper_bound
不同,即使upper_bound
不是2的幂次方,它也尽可能保证可能的输出值是均匀分布的。
void randombytes_buf(void * const buf, const size_t size);
randombytes_buf()
函数用不可预测的字节序列填充从buf
开始的size
个字节。
int randombytes_close(void);
这个函数释放了伪随机数生成器使用的全局资源。更具体地说,当/dev/urandom
设备在被使用时,它会关闭描述符。显然调用这个函数是几乎不需要的。
void randombytes_stir(void);
如果它支持这种操作的话,randombytes_stir()
函数会重新设置伪随机生成器的种子。即使在调用了fork()
后,调用这个函数也不需要使用默认的生成器,除非您使用了randombytes_close()
函数来关闭/dev/urandom
的描述符。
如果您使用了非默认实现(请参阅randombytes_set_implementation()
函数),
在调用了fork()
后,必须再调用randombytes_stir()
函数。
如果这在VM中的一个应用程序中使用,并且VM恢复了快照,那么上述的功能可能会产生相同的输出。
(这部分的文档正在编写中。欢迎贡献一个很好的关于密钥加密的介绍!)
#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 forged! */
}
我们希望这个操作满足以下两个条件:
- 使用密钥和瞬时值机密消息,以保持消息的机密性。
- 计算一个身份验证标记。这个标记用于确保消息在解密之前不会被篡改。
一个密钥既用于加密/签名,也用于验证/解密消息。因此,保密这个密钥至关重要。
这个瞬时值并不一定要保密,但它永远不能用相同的密钥重复使用。生成瞬时值最简单的方法是使用randombytes_buf()
函数。
在组合模式中,认证标签和加密消息被一同存储。这通常就是你想要的。
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()
函数使用一个密钥k
和一个瞬时值n
来加密一个长度为mlen
字节的消息m
。
k
应该是crypto_secretbox_KEYBYTES
字节,而n应该是crypto_secretbox_NONCEBYTES
字节。
c
应该是至少crypto_secretbox_MACBYTES + mlen
字节长。
此函数在c
中写入长度为crypto_secretbox_MACBYTES
字节的认证标签,紧接着的是长度与明文长度mlen
相同的加密消息。
c
和m
可以重叠,从而使得就地加密成为可能。但是,不要忘记crypto_secretbox_MACBYTES
额外的字节需要添加标签。
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()
生成的指向认证标签 + 加密消息组合的指针。clen
是这个认证标签 + 加密消息组合的长度。换句话说,clen
是由crypto_secretbox_easy()
写入的字节数,这是crypto_secretbox_MACBYTES
+ 消息的长度。
瞬时值n
和密钥k
必须匹配用于加密和认证消息。
如果验证失败,该函数返回-1
,成功则返回0
.成功后,解密消息被存储在m
中。
m
和c
可以重叠,是就地解密成为可能。
一些应用可能需要在不同的位置存储认证标签和加密消息。
对于这种具体的例子,可以使用上述函数的“分离”变体。
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);
这个函数使用密钥k
和瞬时值n
来加密mlen
长的消息m
,并且将其加密的消息放入c
中。正确的mlen
字节将被放入c
,这个函数不会添加认证标签。标签大小为crypto_secretbox_MACBYTES
字节将被放入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
的加密消息c
。clen
不包括标签,所以这个长度与明文的长度相同。
通过给定的瞬时值n
和密钥k
,验证mac
是该密文的有效认证标签之后,明文被放入m
中。
如果验证失败,这个函数返回-1
,成功则返回0
。
crypto_secretbox_KEYBYTES
crypto_secretbox_MACBYTES
crypto_secretbox_NONCEBYTES
原始的NaCl crypto_secretbox
API也是支持的,虽然不推荐使用。
crypto_secretbox()
在消息之前接收32个字节的指针,并将密文存储在目标指针后的16个字节,前16个字节被零覆盖。crypto_secretbox_open()
在密文接收16字节的指针,将消息存储在目标指针后的32个字节。前32个字节被零覆盖。
_easy
和 _detached APIs
更快,并且不需要填充、复制或者复杂的指针运算,可以提高可用性。
#define MESSAGE (const unsigned char *) "test"
#define MESSAGE_LEN 4
unsigned char key[crypto_auth_KEYBYTES];
unsigned char mac[crypto_auth_BYTES];
randombytes_buf(key, sizeof key);
crypto_auth(mac, MESSAGE, MESSAGE_LEN, key);
if (crypto_auth_verify(mac, MESSAGE, MESSAGE_LEN, key) != 0) {
/* message forged! */
}
该操作为消息和密钥计算了认证标签,并提供一种验证给定标签对于给定消息和密钥是否有效的一种方式。
该函数计算了标签的有效性:相同的(消息,密钥)元组将始终产生相同的输出。
然而,即使消息是公开的,为了能够计算出一个有效的标签,需要知道密钥。因此,密钥应该保密,但标签可以公开。
一个典型的用例是:
- A
准备了一条消息,添加一个认证的标签,将它发送给B
- A
不存储这条消息
- 稍后,B
将消息和认证标签发送给A
- A
使用认证标签来确认它是否创建了这条消息
这个操作不加密消息。它只计算和验证认证标签。
int crypto_auth(unsigned char *out, const unsigned char *in,
unsigned long long inlen, const unsigned char *k);
crypto_auth()
函数为消息in
计算了一个标签,标签的长度为inlen
个字节,密钥为k
。k
的长度应该是crypto_auth_KEYBYTES
个字节。这个函数将标签放入out
中。这个标签的长度为crypto_auth_BYTES
个字节。
int crypto_auth_verify(const unsigned char *h, const unsigned char *in,
unsigned long long inlen, const unsigned char *k);
对于长度为inlen
个字节的消息in
和密钥k
来说,crypto_auth_verify()
函数验证存储在h
中的标签是一个合法的标签。
如果验证失败,函数返回-1
,否则返回0
。
crypto_auth_BYTES
crypto_auth_KEYBYTES
我们希望这个操作满足以下两个条件:
- 使用一个密钥和一个瞬时值来加密消息以保证消息的机密性。
- 计算一个认证标签。这个标签用来确保消息和可选的非机密(未加密)数据尚未被篡改。
附加数据的一个典型用例是存储关于消息的协议特定的元数据,比如消息的长度和编码。
Libsodium支持两种流行的结构:AES256-GCM和ChaCha20-Poly1305。
目前这种结构的实现是硬件加速的,它需要英特尔SSSE3扩展,以及aesni
和pclmul
指令。
英特尔Westmere处理器(2010年推出)以及更新的处理器满足这个要求。
没有计划支持AES-GCM的非硬件加速实现。
如果不考虑可移植性,AES256-GCM是最快的选择。
虽然AES在专用硬件上的速度非常快,但在缺乏此类硬件的平台上的性能要低得多。另一个问题是,许多AES的软件实现都容易受到缓存-碰撞时间攻击的影响。
ChaCha20在软件上的实现上比AES快得多,在缺乏专用AES硬件的平台上,ChaCha20的速度是AES速度的三倍。ChaCha20对定时攻击也不敏感。
Poly1305是一个速度很快的消息认证码。
ChaCha20流密码与Poly1305认证器的组合于2014年1月提出,它是一种更快速的替代方法,用来替代研究良好的Salsa20-Poly1305结构。
不久后,ChaCha20-Poly1305在主流操作系统、Web浏览器以及加密库中得以实现。它最终于2015年5月成为IETF的官方标准。
在所有支持的体系结构中,Libsodium的ChaCha20-Poly1305实现是可移植的,并且是大多数应用程序的推荐选择。
我们希望这个操作满足以下两个条件:
- 使用密钥和瞬时值加密消息来保证其机密性
- 计算一个认证标签。该标签用于确保消息和可选且非机密(非加密)数据不能被篡改。
附加数据的一个典型的用例就是存储有关消息的协议特定元数据,例如其长度和编码。
选择的构造使用encrypt-then-MAC,并且在验证之前,不会被解密甚至部分解密。
Libsodium 实行两个版本的ChaCha20-Poly1305构造:
- 原始结构可以用相同的密钥安全地加密最多2^64条消息,对消息的大小没有任何实际限制(最多2^70字节)。
- IETF变体会有些慢。它可以安全地加密一个实际上无限数量的消息(2^96),但是单个消息不能超过 64*(1^32)-64 字节(大约256GB)。
两者都能与其他的密码库协作,共享相同的安全属性,可以通过类似的API访问。
crypto_aead_chacha20poly1305_*()
函数集实现原始构造,而crypto_aead_chacha20poly1305_ietf_*()
函数实现IETF版本。这些常数除了瞬时值的大小,都是相同的。
原始的ChaCha20-Poly1305结构可以使用相同的密钥安全地加密最多2^64条消息,对消息的大小没有任何实际的限制(最多2^70个字节)。
#define MESSAGE (const unsigned char *) "test"
#define MESSAGE_LEN 4
#define ADDITIONAL_DATA (const unsigned char *) "123456"
#define ADDITIONAL_DATA_LEN 6
unsigned char nonce[crypto_aead_chacha20poly1305_NPUBBYTES];
unsigned char key[crypto_aead_chacha20poly1305_KEYBYTES];
unsigned char ciphertext[MESSAGE_LEN + crypto_aead_chacha20poly1305_ABYTES];
unsigned long long ciphertext_len;
randombytes_buf(key, sizeof key);
randombytes_buf(nonce, sizeof nonce);
crypto_aead_chacha20poly1305_encrypt(ciphertext, &ciphertext_len,
MESSAGE, MESSAGE_LEN,
ADDITIONAL_DATA, ADDITIONAL_DATA_LEN,
NULL, nonce, key);
unsigned char decrypted[MESSAGE_LEN];
unsigned long long decrypted_len;
if (crypto_aead_chacha20poly1305_decrypt(decrypted, &decrypted_len,
NULL,
ciphertext, ciphertext_len,
ADDITIONAL_DATA,
ADDITIONAL_DATA_LEN,
nonce, key) != 0) {
/* message forged! */
}
在组合模式下,认证标签和加密的消息一同存储。这通常就是你想要的。
int crypto_aead_chacha20poly1305_encrypt(unsigned char *c,
unsigned long long *clen,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_encrypt()
函数使用一个密钥k(crypto_aead_chacha20poly1305_KEYBYTES
字节)和公共瞬时值npub
(crypto_aead_chacha20poly1305_NPUBBYTES
字节)加密一个长度为mlen
字节的消息m
。
将加密消息和认证机密消息m
与adlen
字节长的非机密消息ad
的标签一起放入c
。
如果不需要附加数据,ad
可以是一个NULL
指针,alen
等于0
。
至多mlen + crypto_aead_chacha20poly1305_ABYTES
字节放入c
,并且实际的字节数存储在clen
,除非clen
是一个NULL
指针。
在这个特定的结构中不使用nsec
,而应该始终为NULL
。
公共瞬时值npub
永远不应该与相同的密钥一起使用。我们推荐使用randombytes_buf()
函数来为第一条消息生成瞬时值,并使用相同的密钥为后续的消息增加瞬时值。
int crypto_aead_chacha20poly1305_decrypt(unsigned char *m,
unsigned long long *mlen,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_decrypt()
函数验证密文c(由crypto_aead_chacha20poly1305_encrypt()
生成)包含使用密钥k
、公共瞬时值npub
和附加数据ad
(adlen
字节)生成的有效标签。
如果不需要附加数据,ad
可以是一个NULL
指针,alen
等于0
。
这个特定的结构不使用nsec
,并且其应该始终为NULL
。
当验证失败的时候这个函数返回-1
。
如果验证成功,函数返回0
。如果mlen
不是一个NULL
指针,将解密后的消息放入m
,并且存储其实际的字节数到mlen
中。
至多有clen - crypto_aead_chacha20poly1305_ABYTES
字节将被放入m
中。
一些应用可能需要在不同的位置存储认证标签和加密消息。
对于这种具体的用例,可以使用上述函数的“分离”变体。
int crypto_aead_chacha20poly1305_encrypt_detached(unsigned char *c,
unsigned char *mac,
unsigned long long *maclen_p,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_encrypt_detached()
函数使用一个密钥k
和一个瞬时值npub
加密一个消息m
。它将生成长度等于消息的密文放入c
中。
它还计算一个标签,用于认证密文和长adlen
的可选的附加数据ad
。该标签将被放入mac
,并且它的长度是crypto_aead_chacha20poly1305_ABYTES
字节。
这个特定结构不使用nsec
并且始终应该为NULL
。
int crypto_aead_chacha20poly1305_decrypt_detached(unsigned char *m,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *mac,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_decrypt_detached()
函数验证认证标签mac
对长度为clen
字节的密文c
、瞬时值npub
和长度为adlen
字节的可选附加数据ad
是有效的。
如果标签无效,函数返回-1
并且不进行下一步操作。
如果标签有效,密文被解密并且明文将放入m
。长度等于密文的长度。
这个特定结构不使用nsec
并且始终应该为NULL
。
crypto_aead_chacha20poly1305_KEYBYTES
crypto_aead_chacha20poly1305_NPUBBYTES
crypto_aead_chacha20poly1305_ABYTES
为了防止瞬时值重用,如果一个密钥被重用了,建议增加之前的瞬时值,而不是为每条消息都生成一个随机的瞬时值。
为了防止瞬时值在C/S模型中被重用,要么在每个方向的通信中使用不同的密钥,要么确保在一个比特位在一个方向中被屏蔽,然后再另一个方向上设置。
API符合CAESAR竞赛提出的API。
直到CAESAR竞赛结束,一个高级别的crypto_aead_*()
API才被定义。
ChaCha20-Poly1305 结构的IETF变体可以安全加密一个几乎无限数量的消息(2^96),但单个消息不能超过 64*(1^32)-64 字节(大约256GB)。
#define MESSAGE (const unsigned char *) "test"
#define MESSAGE_LEN 4
#define ADDITIONAL_DATA (const unsigned char *) "123456"
#define ADDITIONAL_DATA_LEN 6
unsigned char nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES];
unsigned char key[crypto_aead_chacha20poly1305_IETF_KEYBYTES];
unsigned char ciphertext[MESSAGE_LEN + crypto_aead_chacha20poly1305_IETF_ABYTES];
unsigned long long ciphertext_len;
randombytes_buf(key, sizeof key);
randombytes_buf(nonce, sizeof nonce);
crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext, &ciphertext_len,
MESSAGE, MESSAGE_LEN,
ADDITIONAL_DATA, ADDITIONAL_DATA_LEN,
NULL, nonce, key);
unsigned char decrypted[MESSAGE_LEN];
unsigned long long decrypted_len;
if (crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, &decrypted_len,
NULL,
ciphertext, ciphertext_len,
ADDITIONAL_DATA,
ADDITIONAL_DATA_LEN,
nonce, key) != 0) {
/* message forged! */
}
在组合模式的情况下,认证标签和加密的消息一起存储。这通常是你想要的。
int crypto_aead_chacha20poly1305_ietf_encrypt(unsigned char *c,
unsigned long long *clen,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_ietf_encrypt()
函数使用密钥k(crypto_aead_chacha20poly1305_IETF_KEYBYTES
个字节长)和一个公共的瞬时值npub
(crypto_aead_chacha20poly1305_IETF_NPUBBYTES
个字节长)来加密mlen
个字节长的消息m
。
加密后的消息以及一个标签都放入c
中,这个标签认证了机密消息m
和adlen
个字节长的非机密数据ad
。
如果不需要附加的数据,ad
可以是一个adlen
为0
的NULL
指针。
最多mlen + crypto_aead_chacha20poly1305_IETF_ABYTES
个字节被放入到c
中,如果clen
不是一个空指针,那么c
确切的字节数存放在clen
中。
在这个特定的结构中,nsec
没有被使用,它的值应该始终为NULL
。
公共的瞬时值npub
永远不能和相同的密钥一起重复使用。建议使用randombytes_buf()
函数来为第一条消息生成瞬时值,并使用相同的密钥为每个后续消息增加这个瞬时值。
int crypto_aead_chacha20poly1305_ietf_decrypt(unsigned char *m,
unsigned long long *mlen,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_ietf_decrypt()
函数使用一个密钥k
、一个公共的瞬时值npub
以及一个附加数据ad
(adlen
个字节长)来验证密文c
(由crypto_aead_chacha20poly1305_ietf_encrypt()
函数生成)包含一个有效的标签。
如果不需要附加的数据,ad
可以是一个adlen
为0
的NULL
指针。
在这个特定的结构中,nsec
没有被使用,它的值应该始终为NULL
。
当验证失败的时候函数返回-1
。
当验证成功的时候,函数返回0
,将解密后的数据放入m
,如果clen
不是一个空指针,那么m
确切的字节数存放在clen
中。
最多clen - crypto_aead_chacha20poly1305_IETF_ABYTES
个字节被放入m
。
一些应用可能需要将认证标签和加密后的消息存储在不同的位置。
对于这种具体用例,可以使用上述函数的“分离”变体。
int crypto_aead_chacha20poly1305_ietf_encrypt_detached(unsigned char *c,
unsigned char *mac,
unsigned long long *maclen_p,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_ietf_encrypt_detached()
函数使用密钥k
和瞬时值npub
来加密消息。它将得到的密文放入c
中,其中密文的长度和消息的长度相等。
它还计算标签,这个标签用来认证密文以及可选的adlen
长度的附加数据ad
。这个标签放在mac
中,标签的长度为crypto_aead_chacha20poly1305_IETF_ABYTES
个字节。
在这个特定的结构中,nsec
没有被使用,它的值应该始终为NULL
。
int crypto_aead_chacha20poly1305_ietf_decrypt_detached(unsigned char *m,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *mac,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_chacha20poly1305_ietf_decrypt_detached()
函数验证了认证标签mac
对于clen
个字节长的密文c
、密钥k
、瞬时值npub
以及可选的adlen
个字节长的附加数据ad
来说是有效的。
如果标签无效,函数返回-1
并且不再进行任何进一步的处理。
如果标签有效,密文被解密并且明文被放入m
中。明文的长度与密文的长度相等。
在这个特定的结构中,nsec
没有被使用,它的值应该始终为NULL
。
crypto_aead_chacha20poly1305_IETF_ABYTES
从Sodium 1.0.9开始:
- crypto_aead_chacha20poly1305_IETF_KEYBYTES
- crypto_aead_chacha20poly1305_IETF_NPUBBYTES
在早期的版本中,使用crypto_aead_chacha20poly1305_KEYBYTES
和crypto_aead_chacha20poly1305_NPUBBYTES
。在原始变体和IETF变体之间,瞬时值的大小是唯一一个不同的常量。
为了防止瞬时值重用,如果一个密钥被重用了,建议增加之前的瞬时值,而不是为每条消息都生成一个随机的瞬时值。
为了防止瞬时值在C/S模型中被重用,要么在每个方向的通信中使用不同的密钥,要么确保在一个比特位在一个方向中被屏蔽,然后再另一个方向上设置。
该API符合CAESAR竞赛中提出的API。
直到CAESAR竞赛结束时,一个高水平的API crypto_aead_*()
才有意被定义。
用于IETF协议的ChaCha20和Poly1305 - IETF变体的规范
#include <sodium.h>
#define MESSAGE (const unsigned char *) "test"
#define MESSAGE_LEN 4
#define ADDITIONAL_DATA (const unsigned char *) "123456"
#define ADDITIONAL_DATA_LEN 6
unsigned char nonce[crypto_aead_aes256gcm_NPUBBYTES];
unsigned char key[crypto_aead_aes256gcm_KEYBYTES];
unsigned char ciphertext[MESSAGE_LEN + crypto_aead_aes256gcm_ABYTES];
unsigned long long ciphertext_len;
sodium_init();
if (crypto_aead_aes256gcm_is_available() == 0) {
abort(); /* Not available on this CPU */
}
randombytes_buf(key, sizeof key);
randombytes_buf(nonce, sizeof nonce);
crypto_aead_aes256gcm_encrypt(ciphertext, &ciphertext_len,
MESSAGE, MESSAGE_LEN,
ADDITIONAL_DATA, ADDITIONAL_DATA_LEN,
NULL, nonce, key);
unsigned char decrypted[MESSAGE_LEN];
unsigned long long decrypted_len;
if (ciphertext_len < crypto_aead_aes256gcm_ABYTES ||
crypto_aead_aes256gcm_decrypt(decrypted, &decrypted_len,
NULL,
ciphertext, ciphertext_len,
ADDITIONAL_DATA,
ADDITIONAL_DATA_LEN,
nonce, key) != 0) {
/* message forged! */
}
我们希望这个操作满足以下两个条件:
- 使用一个密钥和一个瞬时值来加密消息以保证消息的机密性。
- 计算一个认证标签。这个标签用来确保消息和可选的非机密(未加密)数据尚未被篡改。
附加数据的一个典型用例是存储关于消息的协议特定的元数据,比如消息的长度和编码。
它也能被用作带有空消息的MAC。
在消息被验证之前,即使是部分解密也永远不会被执行。
当CPU支持时,AES-GCM是该库中最快的AEAD算法。
目前这种结构的实现是硬件加速的,它需要英特尔SSSE3扩展,以及aesni
和pclmul
指令。
英特尔Westmere处理器(2010年推出)以及更新的处理器满足这个要求。
没有计划支持AES-GCM的非硬件加速实现。如果考虑移植性,请改用ChaCha20-Poly1305算法。
在使用以下的函数之前,先可以通过以下方式检查AES的硬件支持:
int crypto_aead_aes256gcm_is_available(void);
如果当前的CPU支持AES256-GCM实现,函数返回1
,否则函数返回0
。
在调用这个函数前,必须使用sodium_init()
初始化库。
在组合模式的情况下,认证标签和加密的消息一起存储。这通常是你想要的。
int crypto_aead_aes256gcm_encrypt(unsigned char *c,
unsigned long long *clen,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_aes256gcm_encrypt()
函数使用密钥k(crypto_aead_aes256gcm_KEYBYTES
个字节长)和一个公共的瞬时值npub
(crypto_aead_aes256gcm_NPUBBYTES
个字节长)来加密mlen
个字节长的消息m
。
加密后的消息以及一个标签都放入c
中,这个标签认证了机密消息m
和adlen
个字节长的非机密数据ad
。
如果不需要附加数据,ad
可以是一个adlen
为0
的NULL
指针。
最多mlen + crypto_aead_aes256gcm_ABYTES
个字节被放入到c
中,如果clen
不是一个空指针,那么c
确切的字节数存放在clen
中。
在这个特定的结构中,nsec
没有被使用,它的值应该始终为NULL
。
这个函数总是返回0
。
公共的瞬时值npub
永远不能和相同的密钥一起重复使用。建议使用randombytes_buf()
函数来为第一条消息生成瞬时值,并使用相同的密钥为每个后续消息增加这个瞬时值。
int crypto_aead_aes256gcm_decrypt(unsigned char *m,
unsigned long long *mlen_p,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_aes256gcm_decrypt()
函数使用一个密钥k
、一个公共的瞬时值npub
以及一个附加数据ad
(adlen
个字节长)来验证密文c
(由crypto_aead_aes256gcm_encrypt()
函数生成)包含一个有效的标签。clen
是认证者的密文字节长度,因此它必须至少为 aead_aes256gcm_ABYTES
。
如果不需要附加数据,ad
可以是一个NULL
指针。
这个特定结构不使用nsec
,并且应该始终为NULL
。
如果验证失败函数返回-1
。
如果验证成功,函数返回0
。将解密消息放入m
,如果mlen
表示一个NULL
指针,则将实际字节数存储到mlen
。
最多有clen - crypto_aead_aes256gcm_ABYTES
字节被放入m
。
一些应用可能需要在不同的位置存储认证标签以及加密消息。
对于这种具体用例,可以使用上述函数的“分离”变体。
int crypto_aead_aes256gcm_encrypt_detached(unsigned char *c,
unsigned char *mac,
unsigned long long *maclen_p,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const unsigned char *k);
crypto_aead_aes256gcm_encrypt_detached()
函数使用一个密钥k
(crypto_aead_aes256gcm_KEYBYTES
字节)和一个公共瞬时值npub
(crypto_aead_aes256gcm_NPUBBYTES
字节)加密一个长度为mlen
字节的消息m
。
加密消息放入c
。认证机密消息m
和adlen
字节的非机密数据ad
的标签被放入mac
。
如果不需要附加数据,ad
也可以是NULL
指针。
将crypto_aead_aes256gcm_ABYTES
字节放入mac
,验证所需的实际字节数存储到maclen_p
,除非maclen_p
是NULL
指针。
这个特定结构不使用nsec
,并且应该始终为NULL
。
函数总是返回0
。
int crypto_aead_aes256gcm_decrypt_detached(unsigned char *m,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *mac,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k);
函数crypto_aead_aes256gcm_decrypt_detached()
使用密钥k
、公共瞬时值npub
和附加数据ad
(adlen
字节)来验证标签mac
对于密文c是否有效。
clen
是以字节为单位的密文长度。
如果不需要附加数据,ad
可以是NULL
指针。
这个特定结构不使用nsec
,并且应该始终为NULL
。
如果验证失败函数返回-1
。
如果验证成功,函数返回0
。将解密消息放入m
,其长度与密文的长度一致。
crypto_aead_aes256gcm_KEYBYTES
crypto_aead_aes256gcm_NPUBBYTES
crypto_aead_aes256gcm_ABYTES
为了防止瞬时值重用,如果一个密钥被重用了,建议增加之前的瞬时值,而不是为每条消息都生成一个随机的瞬时值。为了防止瞬时值在C/S模型中被重用,要么在每个方向的通信中使用不同的密钥,要么确保在一个比特位在一个方向中被屏蔽,然后再另一个方向上设置。
建议将大于2Gb的消息拆分成更小的块。
在Libsodium 1.0.4引入了对AES256-GCM的支持。
Libsodium 1.0.9中引入了分离API。
使用相同密钥加密多条消息的应用程序可以通过预计算接口仅仅扩展一次AES密钥,从而获得一点加速。
int crypto_aead_aes256gcm_beforenm(crypto_aead_aes256gcm_state *ctx_,
const unsigned char *k);
crypto_aead_aes256gcm_beforenm()
函数通过扩展密钥k
来初始化上下文ctx
,并始终返回0
。
ctx
的地址需要16字节对齐。可以使用sizeof(crypto_aead_aes256gcm_state)
或者crypto_aead_aes256gcm_statebytes()
来获取这个值的大小。
int crypto_aead_aes256gcm_encrypt_afternm(unsigned char *c,
unsigned long long *clen_p,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const crypto_aead_aes256gcm_state *ctx_);
int crypto_aead_aes256gcm_decrypt_afternm(unsigned char *m,
unsigned long long *mlen_p,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const crypto_aead_aes256gcm_state *ctx_);
crypto_aead_aes256gcm_encrypt_afternm()
函数、crypto_aead_aes256gcm_decrypt_afternm()
函数与crypto_aead_aes256gcm_encrypt()
函数、crypto_aead_aes256gcm_decrypt()
函数相同,但是接收的是之前初始化的上下文ctx
而不是密钥。
int crypto_aead_aes256gcm_encrypt_detached_afternm(unsigned char *c,
unsigned char *mac,
unsigned long long *maclen_p,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *nsec,
const unsigned char *npub,
const crypto_aead_aes256gcm_state *ctx_);
int crypto_aead_aes256gcm_decrypt_detached_afternm(unsigned char *m,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *mac,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const crypto_aead_aes256gcm_state *ctx_)
crypto_aead_aes256gcm_encrypt_detached_afternm()
函数、crypto_aead_aes256gcm_decrypt_detached_afternm()
函数和crypto_aead_aes256gcm_encrypt_detached()
函数、crypto_aead_aes256gcm_decrypt_detached()
函数相同,但是接收的是之前初始化的上下文ctx
而不是密钥。
crypto_aead_aes256gcm_KEYBYTES
crypto_aead_aes256gcm_NPUBBYTES
crypto_aead_aes256gcm_ABYTES
crypto_aead_aes256gcm_state
瞬时值为96比特长。为了防止瞬时值重用,如果一个密钥被重用了,建议增加之前的瞬时值,而不是为每条消息都生成一个随机的瞬时值。为了防止瞬时值在C/S模型中被重用,要么在每个方向的通信中使用不同的密钥,要么确保在一个比特位在一个方向中被屏蔽,然后再另一个方向上设置。
建议将大于2Gb的消息拆分成更小的块,在某个密钥加密的数据量达到64比特前换一个新的密钥。
在Libsodium 1.0.4中引入了对AES256-GCM的支持。
在Libsodium 1.0.9中引入了分离API。