1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
#include "chacha.h"
#include <openssl/evp.h>
#include <stdio.h>
//#cgo pkg-config: libcrypto
static void panic(const char* const msg)
{
fprintf(stderr, "panic in C code: %s\n", msg);
__builtin_trap();
}
// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode
int aead_seal(
const enum aeadType cipherId,
const unsigned char* const plaintext,
const int plaintextLen,
const unsigned char* const authData,
const int authDataLen,
const unsigned char* const key,
const int keyLen,
const unsigned char* const iv,
const int ivLen,
unsigned char* const ciphertext,
const int ciphertextBufLen)
{
const EVP_CIPHER* evpCipher = NULL;
switch (cipherId) {
case aeadTypeChacha:
evpCipher = EVP_chacha20_poly1305();
break;
case aeadTypeGcm:
evpCipher = EVP_aes_256_gcm();
break;
default:
panic("unknown cipherId");
}
// Create scratch space "context"
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
panic("EVP_CIPHER_CTX_new failed");
}
// Set cipher
if (EVP_EncryptInit_ex(ctx, evpCipher, NULL, NULL, NULL) != 1) {
panic("EVP_EncryptInit_ex set cipher failed");
}
// Check keyLen by trying to set it (fails if keyLen != 32)
if (EVP_CIPHER_CTX_set_key_length(ctx, keyLen) != 1) {
panic("keyLen mismatch");
}
// Set IV length so we do not depend on the default
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, ivLen, NULL) != 1) {
panic("EVP_CTRL_AEAD_SET_IVLEN failed");
}
// Set key and IV
if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv) != 1) {
panic("EVP_EncryptInit_ex set key & iv failed");
}
// Provide authentication data
int outLen = 0;
if (EVP_EncryptUpdate(ctx, NULL, &outLen, authData, authDataLen) != 1) {
panic("EVP_EncryptUpdate authData failed");
}
if (outLen != authDataLen) {
panic("EVP_EncryptUpdate authData: unexpected length");
}
// Encrypt "plaintext" into "ciphertext"
if (plaintextLen > ciphertextBufLen) {
panic("plaintext overflows output buffer");
}
if (EVP_EncryptUpdate(ctx, ciphertext, &outLen, plaintext, plaintextLen) != 1) {
panic("EVP_EncryptUpdate ciphertext failed");
}
if (outLen != plaintextLen) {
panic("EVP_EncryptUpdate ciphertext: unexpected length");
}
int ciphertextLen = outLen;
// Finalise encryption
// Normally ciphertext bytes may be written at this stage, but this does not occur in GCM mode
if (EVP_EncryptFinal_ex(ctx, ciphertext + plaintextLen, &outLen) != 1) {
panic("EVP_EncryptFinal_ex failed");
}
if (outLen != 0) {
panic("EVP_EncryptFinal_ex: unexpected length");
}
// We only support 16-byte tags
const int tagLen = 16;
// Get MAC tag and append it to the ciphertext
if (ciphertextLen + tagLen > ciphertextBufLen) {
panic("tag overflows output buffer");
}
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagLen, ciphertext + plaintextLen) != 1) {
panic("EVP_CTRL_AEAD_GET_TAG failed");
}
ciphertextLen += tagLen;
// Free scratch space
EVP_CIPHER_CTX_free(ctx);
return ciphertextLen;
}
|