? 1. 簡介
Libsodium 是一個開源、跨平臺、跨語言的加密庫,提供了一組簡單易用的函式,大大簡化了加密、雜湊、簽名、鑑別、解密等複雜工作。支援許多種主流的加密演算法和雜湊演算法,包括 AES256-GCM 和 ChaCha20-Poly1305 兩種 AEAD 加密方案。此外還提供了一系列方便實用的函式,可完成隨機數的生成、大數的計算、編碼和解碼等輔助性工作。
? 2. 起步
執行以下命令,完成 Libsodium 的下載、解壓、編譯和安裝。
$ yum -y groupinstall "Development Tools" # apt install -y build-essential
$ wget -N --no-check-certificate https://download.libsodium.org/libsodium/releases/libsodium-1.0.17.tar.gz
$ tar -zxf libsodium-1.0.17.tar.gz
$ cd libsodium-1.0.17
$ ./configure
$ make && make check
$ make install
Libsodium 的動態連結庫 lib*.so*
位於 /usr/local/lib
目錄中。須將此目錄設為動態庫的搜尋目錄之一,否則依賴於 Libsodium 的程式將無法執行。
$ echo "/usr/local/lib" > /etc/ld.so.conf.d/usr-local-lib.conf
$ ldconfig
在 /usr/local/lib/pkgconfig
目錄中,可以找到檔案 libsodium.pc
。為了能夠透過命令 pkg-config
獲取編譯和連結所需引數,須將此檔案複製到 pkg-config
命令的搜尋目錄中。
$ cp /usr/local/lib/pkgconfig/libsodium.pc /usr/share/pkgconfig/
透過命令 pkg-config
獲取編譯和連結所需引數。
$ pkg-config --cflags libsodium
-I/usr/local/include
$ pkg-config --libs libsodium
-L/usr/local/lib -lsodium
如果使用了 make,應當在 Makefile
檔案中使用上述兩條命令。
CFLAGS = $(pkg-config --cflags libsodium)
LDFLAGS = $(pkg-config --libs libsodium)
而在程式中,只需包含標頭檔案 sodium.h
即可。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
...
}
在使用 Libsodium 的其他函式之前,必須先呼叫函式 sodium_init()
。該函式不需要任何引數,返回 0
表示成功,返回 -1
表示失敗,返回 1
則表示已經初始化過了。
? 3. 知識儲備
3.1 兩種密碼體制
當前有兩種密碼體制:一種稱為對稱金鑰密碼體制;另一種稱為公鑰密碼體制。
在對稱金鑰密碼體制中,加密和解密使用相同的金鑰。金鑰由通訊雙方事先約定。演算法可以公開,而金鑰需要保密。
公鑰密碼體制在加密和解密過程中使用不同的金鑰。並且使用其中一個進行加密,則需要用另一個才能解密。這兩個成對的金鑰在使用時,一個金鑰作為私鑰,需要保密;另一個金鑰作為公鑰,可以公開。
3.2 對稱加密演算法
對稱加密演算法分為分組密碼(又稱塊加密)和序列密碼(又稱流密碼、流加密)兩種。
- 著名的分組密碼:DES、AES
- 常用的流密碼:Salsa20、ChaCha20
3.3 MAC 報文鑑別碼
MAC 是 Message Authentication Code 的縮寫,即報文鑑別碼。通常是經過加密的雜湊值。計算報文鑑別碼的演算法稱為 MAC 演算法。常用的 MAC 演算法有:
- GMAC
- CBC-MAC
- Poly1305
3.4 AE
AE 是 Authenticated encryption 的縮寫。顧名思義,這種加密方案不僅能提供機密性,還能提供完整性。任何偽造或篡改都會被發現。
AE 實際上是對稱加密演算法和 MAC 演算法的結合體。在加密一個報文時,需要一個金鑰和一個不重數,加密後將得到密文和一個報文鑑別碼。報文鑑別碼須隨同密文一起傳送給接收方。
接收方收到報文鑑別碼和密文後,須用相同的金鑰和不重數才能進行解密。任何對密文和報文鑑別碼的篡改都會導致解密失敗。當然,只要確保金鑰沒有洩露,其他人也無法偽造出合法的密文和相應的報文鑑別碼。
不重數,即不重複的數,通常是從 0 開始遞增的計數器,不需要保密。金鑰和不重數的結合,相當於一次一密,能有效抵禦「重放攻擊」。
3.5 AEAD
AEAD 是 Authenticated Encryption with Additional Data 的縮寫。相比於 AE,AEAD 在加、解密時還可以選擇性地給定一些沒有保密性要求的「附加資料」,例如版本號、時間戳、報文的長度和編碼方式等。這些附加資料會參與到報文鑑別碼的計算中去,但不會被加密,也不會成為密文的一部分。附加資料可以隨同密文一起傳送。
常用的 AEAD 有以下兩種:
- AES256-GCM
- ChaCha20-Poly1305
Intel 在 2008 年推出新的指令集——AES-NI,為 AES 演算法提供了硬體層面上的支援。但在其他平臺(ARM)上,針對移動網際網路最佳化的 ChaCha20 的速度大約是 AES 的三倍。
ChaCha20-Poly1305 最初在 2014 年提出,在 2015 年成為 IETF 標準,即 ChaCha20-Poly1305-IETF。後來,又透過對 ChaCha20 的改進,形成 XChaCha20-Poly1305-IETF。這一版本有望成為新的 IETF 標準,也是 Libsodium 目前首推的加密方案。
不同 AEAD 的金鑰、不重數和報文鑑別碼的長度(單位:位):
AEAD | Key 金鑰 | Nonce 不重數 | MAC 報文鑑別碼 |
---|---|---|---|
AES256-GCM | 256 | 96 | 128 |
ChaCha20-Poly1305 | 256 | 64 | 128 |
ChaCha20-Poly1305-IETF | 256 | 96 | 128 |
XChaCha20-Poly1305-IETF | 256 | 192 | 128 |
? 4. Libsodium 對 ChaCha20-Poly1305 的支援
Libsodium 為 ChaCha20-Poly1305 的三種版本分別提供了三組函式:
crypto_aead_chacha20poly1305_*()
crypto_aead_chacha20poly1305_ietf_*()
crypto_aead_xchacha20poly1305_ietf_*()
這三組函式在用法上完全一致,因此只要掌握了其中一種,自然也就掌握了其餘兩種。
4.1 加密
int crypto_aead_xchacha20poly1305_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_xchacha20poly1305_ietf_encrypt_detached()
使用金鑰 k
和
不重數 npub
對 mlen
位元組的報文 m
進行加密,並根據密文和 adlen
位元組的附加資料 ad
計算報文鑑別碼。密文將被寫到 c
,而報文鑑別碼將被寫到 mac
,maclen
會被設為 mac
的長度。
密文和明文等長。而金鑰、不重數、報文鑑別碼的長度都是固定的,它們分別等於:
crypto_aead_xchacha20poly1305_ietf_KEYBYTES
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
crypto_aead_xchacha20poly1305_ietf_ABYTES
若沒有關聯的資料,則把 ad
設為 NULL
,並把 adlen
設為 0。
此處
nsec
必須始終設為NULL
,下同。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
// 金鑰
unsigned char k[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] = "123456";
// 不重數
unsigned char npub[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] = {0};
// 明文
int mlen = 5;
unsigned char m[6] = "hello";
// 附加的資料
int adlen = 4;
unsigned char ad[5] = "2020";
// 密文
unsigned char c[6];
// 報文鑑別碼
unsigned char mac[crypto_aead_xchacha20poly1305_ietf_ABYTES];
unsigned long long maclen;
// 加密
crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c,
mac, &maclen,
m, mlen,
ad, adlen, NULL,
npub, k);
// 獲取密文和報文鑑別碼的十六進位制表示
char buf[1024];
sodium_bin2hex(buf, sizeof buf, c, 5);
printf("Ciphertext: %s\n", buf);
sodium_bin2hex(buf, sizeof buf, mac, maclen);
printf("MAC: %s\n", buf);
return 0;
}
Ciphertext: 5abc40d737
MAC: 0be7cd4beaf9ec2a063170aab65fa5aa
函式
sodium_bin2hex()
是 Libsodium 提供的「輔助函式」,具體用法詳見下文。
在金鑰不變的情況下,不重數必須每次都不一樣。建議用 randombytes_buf()
函式產生第一條報文的不重數,再用 sodium_increment()
函式對其進行遞增。
4.2 解密
解密必須提供相同的金鑰 k
、不重數 npub
和附加資料 ad
。
int crypto_aead_xchacha20poly1305_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_xchacha20poly1305_ietf_decrypt_detached()
首先驗證 c
中包含的 tag
是否合法。若函式返回 -1
表示驗證未透過;若驗證透過,則返回 0
,並將解密得到的報文寫到 m
。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
// 金鑰
unsigned char k[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] = "123456";
// 不重數
unsigned char npub[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] = {0};
// 明文
unsigned char m[6];
// 附加的資料
int adlen = 4;
unsigned char ad[5] = "2020";
// 密文
int clen = 5;
unsigned char c[6];
sodium_hex2bin(c, clen, "5abc40d737", 10, NULL, NULL, NULL);
// 報文鑑別碼
unsigned char mac[crypto_aead_xchacha20poly1305_ietf_ABYTES];
sodium_hex2bin(mac, crypto_aead_xchacha20poly1305_ietf_ABYTES,
"0be7cd4beaf9ec2a063170aab65fa5aa", 32, NULL, NULL, NULL);
// 解密
crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m,
NULL,
c, clen,
mac,
ad, adlen,
npub, k);
printf("Message: %s\n", m);
return 0;
}
Message: hello
4.3 合併模式
以上這種將密文和報文鑑別碼分開儲存的方式稱為分開模式。由於大多數需求都是將報文鑑別碼直接追加到密文後面,即合併模式。因此,Libsodium 實際上為每種 AEAD 方案都提供兩組函式:一組實現分開模式;另一組實現合併模式。
為合併模式設計的函式,相比於分開模式的函式,函式名少了字尾 _detached
。
int crypto_aead_xchacha20poly1305_ietf_encrypt(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 unsigned char *k);
金鑰、不重數、附加資料、明文等引數的含義同上。在合併模式下,報文鑑別碼直接追加到密文後面,因此減少了 mac
和 maclen
兩個引數,但引數 c
必須為報文鑑別碼預留儲存空間。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
// 金鑰
unsigned char k[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] = "123456";
// 不重數
unsigned char npub[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] = {0};
// 明文
int mlen = 5;
unsigned char m[6] = "hello";
// 附加的資料
int adlen = 4;
unsigned char ad[5] = "2020";
// 密文
unsigned char c1[6];
unsigned char c2[6 + crypto_aead_xchacha20poly1305_ietf_ABYTES];
unsigned long long clen;
// 報文鑑別碼
unsigned char mac[crypto_aead_xchacha20poly1305_ietf_ABYTES];
unsigned long long maclen;
// 加密(分開模式)
crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c1,
mac, &maclen,
m, mlen,
ad, adlen, NULL,
npub, k);
char buf[1024];
sodium_bin2hex(buf, sizeof buf, c1, 5);
printf("Ciphertext: %s\n", buf);
sodium_bin2hex(buf, sizeof buf, mac, maclen);
printf("MAC: %s\n", buf);
// 加密(合併模式)
crypto_aead_xchacha20poly1305_ietf_encrypt(c2,
&clen,
m, mlen,
ad, adlen, NULL,
npub, k);
sodium_bin2hex(buf, sizeof buf, c2, clen);
printf("Ciphertext: %s\n", buf);
return 0;
}
Ciphertext: 5abc40d737
MAC: 0be7cd4beaf9ec2a063170aab65fa5aa
Ciphertext: 5abc40d7370be7cd4beaf9ec2a063170aab65fa5aa
? 5. 金鑰的派生
在實際應用中,不應從始至終都使用同一個金鑰,更不能直接使用密碼(通常是簡短的字串)作為金鑰,否則很容易遭受「字典攻擊」。應當為每次會話專門準備一個子金鑰。這就需要一種能夠產生大量子金鑰的機制。
5.1 KDF
KDF 是 Key Derivation Function 的縮寫,即金鑰派生函式。能夠滿足上述需求。這類函式透過引入隨機數、增加雜湊迭代次數,增加暴力破解難度。常用的 KDF 有:
- PBKDF2
- Scrypt
- Argon2
Argon2 是最新的演算法,也是 Libsodium 首推及其底層預設使用的演算法。
5.2 基於密碼派生金鑰
根據給定的密碼和一個長度固定的隨機數生成指定長度的金鑰。
int crypto_pwhash(unsigned char * const out,
unsigned long long outlen,
const char * const passwd,
unsigned long long passwdlen,
const unsigned char * const salt,
unsigned long long opslimit,
size_t memlimit, int alg);
函式 crypto_pwhash()
根據 passwdlen
位元組的密碼 passwd
和 crypto_pwhash_SALTBYTES
位元組的隨機數 salt
派生出 outlen
位元組的金鑰並儲存到 out
中。全部引數相同時,生成相同的金鑰。
\ | passwdlen |
outlen |
---|---|---|
最小值 | crypto_pwhash_PASSWD_MIN |
crypto_pwhash_BYTES_MIN |
最大值 | crypto_pwhash_PASSWD_MAX |
crypto_pwhash_BYTES_MAX |
倒數兩個引數 opslimit
和 memlimit
與效能和記憶體佔用有關,取值如下:
\ | opslimit |
memlimit |
---|---|---|
最小值 | crypto_pwhash_OPSLIMIT_MIN |
crypto_pwhash_MEMLIMIT_MIN |
較快/小 | crypto_pwhash_OPSLIMIT_INTERACTIVE |
crypto_pwhash_MEMLIMIT_INTERACTIVE |
中等 | crypto_pwhash_OPSLIMIT_MODERATE |
crypto_pwhash_MEMLIMIT_MODERATE |
較慢/大 | crypto_pwhash_OPSLIMIT_SENSITIVE |
crypto_pwhash_MEMLIMIT_SENSITIVE |
最大值 | crypto_pwhash_OPSLIMIT_MAX |
crypto_pwhash_MEMLIMIT_MAX |
最後一個引數 alg
決定選用的演算法,只有下列 3 種取值可選:
crypto_pwhash_ALG_DEFAULT
Libsodium 推薦的選項。crypto_pwhash_ALG_ARGON2I13
Argon2i 1.3。crypto_pwhash_ALG_ARGON2ID13
Argon2id 1.3。
函式返回 0
表示成功;返回 -1
表示失敗(這通常是由於作業系統拒絕分配請求的記憶體)。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
// 密碼
unsigned char passwd[] = "secret";
// 長度固定的隨機數
unsigned char salt[crypto_pwhash_SALTBYTES] = {0};
// 金鑰
unsigned char key[16];
crypto_pwhash(key, sizeof key, passwd, strlen(passwd), salt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT);
char buf[1024];
sodium_bin2hex(buf, sizeof buf, key, sizeof key);
printf("key: %s\n", buf);
return 0;
}
key: a5c2d5ca23026834f7ff177fb8137b62
5.3 基於主金鑰派生子金鑰
根據一個主金鑰生成多個子金鑰。Libsodium 專門為此提供了兩個函式 crypto_kdf_*()
。
這兩個函式可以根據一個主金鑰 key
和一個被稱為上下文的引數 ctx
派生出 2^64 個金鑰,並且單個子金鑰的長度可以在 128(16 位元組)到 512 位(64 位元組)之間。
void crypto_kdf_keygen(uint8_t key[crypto_kdf_KEYBYTES]);
函式 crypto_kdf_keygen()
的作用是生成一個主金鑰。
int crypto_kdf_derive_from_key(unsigned char *subkey, size_t subkey_len,
uint64_t subkey_id,
const char ctx[crypto_kdf_CONTEXTBYTES],
const unsigned char key[crypto_kdf_KEYBYTES]);
函式 crypto_kdf_derive_from_key()
可以根據主金鑰 key
和上下文 ctx
派生出長度為 subkey_len
位元組的子金鑰。subkey_id
是子金鑰的編號,可以是不大於 2^64 - 1
的任意值。
主金鑰的長度必須是 crypto_kdf_KEYBYTES
。子金鑰的長度 subkey_len
必須介於 crypto_kdf_BYTES_MIN
(含)和 crypto_kdf_BYTES_MAX
(含)之間。
上下文 ctx
是一個 8 字元的字串,應能描述子金鑰的用途。不需要保密,並且強度可以很低。比如 "UserName"
、"__auth__"
、"pictures"
和 "userdata"
等。但其長度必須是 crypto_kdf_CONTEXTBYTES
位元組。
使用相同的金鑰,但使用不同的 ctx
,就會得到不同的輸出。正如其名,ctx
可以和程式的上下文對應。當然,就算一個程式從頭到尾只使用一個 ctx
,那也有防止金鑰被不同程式重複使用的作用。
#include <sodium.h>
int main(void)
{
if (sodium_init() == -1) {
return 1;
}
char ctx[] = "Examples";
uint8_t master_key[crypto_kdf_KEYBYTES];
uint8_t subkey1[16];
uint8_t subkey2[16];
uint8_t subkey3[32];
// 建立主金鑰
crypto_kdf_keygen(master_key);
// 派生子金鑰
crypto_kdf_derive_from_key(subkey1, sizeof subkey1, 1, ctx, master_key);
crypto_kdf_derive_from_key(subkey2, sizeof subkey2, 2, ctx, master_key);
crypto_kdf_derive_from_key(subkey3, sizeof subkey3, 3, ctx, master_key);
// 獲取子金鑰的十六進位制表示
char buf[1024];
sodium_bin2hex(buf, sizeof buf, subkey1, sizeof subkey1);
printf("subkey1: %s\n", buf);
sodium_bin2hex(buf, sizeof buf, subkey2, sizeof subkey2);
printf("subkey2: %s\n", buf);
sodium_bin2hex(buf, sizeof buf, subkey3, sizeof subkey3);
printf("subkey3: %s\n", buf);
return 0;
}
subkey1: 0440b65332dc5f6b4a46d262996af08e
subkey2: 73e6d9bbfb25c25d3898ba435f16b710
subkey3: 9fdffff7fd9d4ba7a8b1172c79cdf86b7a823256b418e9a61cb8e21f1170ef1f
? 6. 輔助函式
儘可能使用這些函式,以抵禦「時序攻擊」。
6.1 測試位元組序列
sodium_memcmp()
函式 sodium_memcmp()
可完成兩個等長位元組序列的對比。
int sodium_memcmp(const void * const b1_, const void * const b2_, size_t len);
如果位於 b1_
的 len
個位元組和位於 b2_
的 len
個位元組相同,函式返回 0
,否則返回 -1
。
char b1_[6] = "hello";
char b2_[6] = "hello";
char b3_[6] = "Hello";
if (sodium_memcmp(b1_, b2_, 5) == -1) {
puts("No match");
} else {
puts("Match");
}
if (sodium_memcmp(b1_, b3_, 5) == -1) {
puts("No match");
} else {
puts("Match");
}
Match
No match
sodium_is_zero()
函式 sodium_is_zero()
可判斷給定的位元組序列是否全為 0
。
int sodium_is_zero(const unsigned char *n, const size_t nlen);
若位於 n
的 nlen
個位元組是否全為 0
,則返回 1
,否則返回 0
。
6.2 位元組序列的十六進位制表示
sodium_bin2hex()
函式 sodium_bin2hex()
可獲取位元組序列的十六進位制表示,並由此得到一個字串。
char *sodium_bin2hex(char * const hex, const size_t hex_maxlen,
const unsigned char * const bin, const size_t bin_len);
函式將字串寫到 hex
,這個字串就是從 bin
開始的 bin_len
個位元組的十六進位制表示,包括 '\0'
,故 hex_maxlen
至少為 2*bin_len + 1
。該函式始終返回 hex
。
char hex[9]; // 2*4 + 1 = 9
char bin[5] = "AAAA";
sodium_bin2hex(hex, 9, bin, 4);
puts(hex);
41414141
sodium_hex2bin()
函式 sodium_hex2bin()
作用相反,透過解析位元組序列的十六進位制表示,還原該位元組序列。
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);
函式將位元組序列寫到 bin
。bin_maxlen
表示允許寫入的最大位元組數。而位於 hex
的字串應當是一個位元組序列的十六進位制表示,可以沒有 '\0'
結尾,需要解析的長度由 hex_len
指定。
ignore
是需要跳過的字元組成的字串。比如 ": "
表示跳過冒號和空格。此時 "69:FC"
、"69 FC"
、"69 : FC"
和 "69FC"
都視為合法的輸入,併產生相同的輸出。ignore
可以設為 NULL
,表示不允許任何非法的字元出現。
函式返回 0
表示轉換成功,同時 bin_len
會被設為解析得到的位元組數;返回 -1
則表示失敗。失敗的情況有以下兩種:
- 解析的結果超過
bin_maxlen
位元組; - 遇到非法字元時,如果前面的字元都能順利解析,函式仍然返回
0
,否則返回-1
。
無論如何 hex_end
總是會被設為下一個待解析的字元的地址。
char bin[5] = {0};
char hex[12] = "61*62636472";
size_t bin_len = 0;
const char * hex_end;
sodium_hex2bin(bin, 4, hex, 9, "*", &bin_len, &hex_end);
printf("%d: %s, %c\n", bin_len, bin, *hex_end);
4: abcd, 7
6.3 Base64 編碼/解碼
sodium_bin2base64()
函式 sodium_bin2base64()
可獲取位元組序列的 Base64 編碼。
char *sodium_bin2base64(char * const b64, const size_t b64_maxlen,
const unsigned char * const bin, const size_t bin_len,
const int variant);
Base64 編碼有多種變體,採用哪種變體由 variant
指定,有下列 4 種取值可選:
sodium_base64_VARIANT_ORIGINAL
sodium_base64_VARIANT_ORIGINAL_NO_PADDING
sodium_base64_VARIANT_URLSAFE
sodium_base64_VARIANT_URLSAFE_NO_PADDING
這些 Base64 編碼並不提供任何形式的加密;就像十六進位制編碼一樣,任何人都可以對它們進行解碼。
可以令 b64_maxlen
等於宏 sodium_base64_ENCODED_LEN(BIN_LEN, VARIANT)
,它表示使用 VARIANT
這種變體時,BIN_LEN
個位元組的 Base64 編碼(包括 '\0'
)的最小長度。
char bin[6] = "hello";
int b64_len = sodium_base64_ENCODED_LEN(5, sodium_base64_VARIANT_ORIGINAL);
char b64[b64_len];
sodium_bin2base64(b64, b64_len, bin, 5, sodium_base64_VARIANT_ORIGINAL);
printf("%d: %s\n", b64_len, b64);
9: aGVsbG8=
sodium_base642bin()
函式 sodium_base642bin()
可完成 Base64 解碼工作。
int sodium_base642bin(unsigned char * const bin, const size_t bin_maxlen,
const char * const b64, const size_t b64_len,
const char * const ignore, size_t * const bin_len,
const char ** const b64_end, const int variant);
返回 -1
表示錯誤,返回 0
表示解碼成功,同時 bin_len
會被設為解碼得到的位元組數,其他引數的含義參考前文。
size_t bin_len;
char bin[6];
char b64[9] = "aGVsbG8=";
sodium_base642bin(bin, sizeof bin,
b64, strlen(b64), "", &bin_len, NULL,
sodium_base64_VARIANT_ORIGINAL);
printf("%d: %s\n", bin_len, bin);
5: hello
6.4 大數的計算
sodium_increment()
函式 sodium_increment()
用來遞增一個任意長度的無符號數。
void sodium_increment(unsigned char *n, const size_t nlen);
位於 n
的 nlen
位元組的數字將按小端位元組序處理。加密演算法中經常提到的不重數 nonce
就可用此函式進行遞增。
unsigned char nonce[8] = {0};
sodium_increment(nonce, sizeof(nonce));
printf("%d\n", *(int *)nonce);
sodium_increment(nonce, sizeof(nonce));
printf("%d\n", *(int *)nonce);
sodium_increment(nonce, sizeof(nonce));
printf("%d\n", *(int *)nonce);
1
2
3
sodium_add()
函式 sodium_add()
可完成大數的加法。
void sodium_add(unsigned char *a, const unsigned char *b, const size_t len);
位於 a
和 b
的兩個 nlen
位元組的加數均按小端位元組序的無符號數處理。計算結果將覆蓋 a
。
unsigned char a[8] = {1};
unsigned char b[8] = {1};
printf("%d\n", *(int *)a);
sodium_add(a, b, sizeof a); // a = a + b
printf("%d\n", *(int *)a);
1
2
sodium_sub()
函式 sodium_sub()
可完成大數減法。
void sodium_sub(unsigned char *a, const unsigned char *b, const size_t len);
位於 a
和 b
的兩個 nlen
位元組的加數均按小端位元組序的無符號數處理。計算結果將覆蓋 a
。
sodium_compare()
函式 sodium_compare()
可完成兩個大數的比較。兩個大數均按小端位元組序處理。
int sodium_compare(const void * const b1_, const void * const b2_, size_t len);
返回 0
表示相等,返回 -1
表示 b1_
小於 b2_
;返回 1
表示 b1_
大於 b2_
。
? 參考文獻
- libsodium 密碼學庫 中文文件
- 現代密碼學實踐指南[2015年]
- 【翻譯】密碼學一小時必知
- 加密解密學習筆記
- 密碼學基礎系列
- 實用密碼學工具——KDF
- 如何儲存密碼(KDF)
- ldconfig命令
- pkg-config 詳解
- 什麼是 AES-NI(AES指令集)
本作品採用《CC 協議》,轉載必須註明作者和本文連結