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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
//go:build cgo && !without_openssl
#include "openssl_aead.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();
}
// We only support 16-byte tags
static const int supportedTagLen = 16;
// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode
int openssl_aead_seal(
const EVP_CIPHER* evpCipher,
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)
{
// Create scratch space "ctx"
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");
}
// Get MAC tag and append it to the ciphertext
if (ciphertextLen + supportedTagLen > ciphertextBufLen) {
panic("tag overflows output buffer");
}
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, supportedTagLen, ciphertext + plaintextLen) != 1) {
panic("EVP_CTRL_AEAD_GET_TAG failed");
}
ciphertextLen += supportedTagLen;
// Free scratch space
EVP_CIPHER_CTX_free(ctx);
return ciphertextLen;
}
int openssl_aead_open(
const EVP_CIPHER* evpCipher,
const unsigned char* const ciphertext,
const int ciphertextLen,
const unsigned char* const authData,
const int authDataLen,
unsigned char* const tag,
const int tagLen,
const unsigned char* const key,
const int keyLen,
const unsigned char* const iv,
const int ivLen,
unsigned char* const plaintext,
const int plaintextBufLen)
{
// Create scratch space "ctx"
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
panic("EVP_CIPHER_CTX_new failed");
}
// Set cipher
if (EVP_DecryptInit_ex(ctx, evpCipher, NULL, NULL, NULL) != 1) {
panic("EVP_DecryptInit_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_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1) {
panic("EVP_DecryptInit_ex set key & iv failed");
}
// Provide authentication data
int outLen = 0;
if (EVP_DecryptUpdate(ctx, NULL, &outLen, authData, authDataLen) != 1) {
panic("EVP_DecryptUpdate authData failed");
}
if (outLen != authDataLen) {
panic("EVP_DecryptUpdate authData: unexpected length");
}
// Decrypt "ciphertext" into "plaintext"
if (ciphertextLen > plaintextBufLen) {
panic("ciphertextLen overflows output buffer");
}
if (EVP_DecryptUpdate(ctx, plaintext, &outLen, ciphertext, ciphertextLen) != 1) {
panic("EVP_DecryptUpdate failed");
}
int plaintextLen = outLen;
// Check tag
if (tagLen != supportedTagLen) {
panic("unsupported tag length");
}
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tagLen, tag) != 1) {
panic("EVP_CTRL_AEAD_SET_TAG failed");
}
if (EVP_DecryptFinal_ex(ctx, plaintext + plaintextLen, &outLen) != 1) {
// authentication failed
return -1;
}
if (outLen != 0) {
panic("EVP_EncryptFinal_ex: unexpected length");
}
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return plaintextLen;
}
// This functions exists to benchmark the C call overhead from Go.
void noop_c_function(void) {
return;
}
|