e2ec048a09
Nice deduplication and brings the GCM decrypt speed up to par. internal/speed$ benchstat old new name old time/op new time/op delta StupidGCM-4 4.71µs ± 0% 4.66µs ± 0% -0.99% (p=0.008 n=5+5) StupidGCMDecrypt-4 5.77µs ± 1% 4.51µs ± 0% -21.80% (p=0.008 n=5+5) name old speed new speed delta StupidGCM-4 870MB/s ± 0% 879MB/s ± 0% +1.01% (p=0.008 n=5+5) StupidGCMDecrypt-4 710MB/s ± 1% 908MB/s ± 0% +27.87% (p=0.008 n=5+5)
179 lines
5.4 KiB
C
179 lines
5.4 KiB
C
#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;
|
|
}
|