You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.5 KiB
185 lines
5.5 KiB
// +build !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; |
|
}
|
|
|