aboutsummaryrefslogtreecommitdiff
path: root/internal/stupidgcm/openssl_aead.c
diff options
context:
space:
mode:
Diffstat (limited to 'internal/stupidgcm/openssl_aead.c')
-rw-r--r--internal/stupidgcm/openssl_aead.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/internal/stupidgcm/openssl_aead.c b/internal/stupidgcm/openssl_aead.c
new file mode 100644
index 0000000..9dc6866
--- /dev/null
+++ b/internal/stupidgcm/openssl_aead.c
@@ -0,0 +1,178 @@
+#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;
+}