|
@@ -0,0 +1,168 @@
|
|
|
+#include <openssl/evp.h>
|
|
|
+#include <openssl/err.h>
|
|
|
+#include "gcm.h"
|
|
|
+
|
|
|
+/* Sample openssl GCM code lightly adapted from
|
|
|
+https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
|
|
|
+*/
|
|
|
+int gcm_encrypt(unsigned char *plaintext, int plaintext_len,
|
|
|
+ unsigned char *aad, int aad_len,
|
|
|
+ unsigned char *key,
|
|
|
+ unsigned char *iv, int iv_len,
|
|
|
+ unsigned char *ciphertext,
|
|
|
+ unsigned char *tag)
|
|
|
+{
|
|
|
+ EVP_CIPHER_CTX *ctx;
|
|
|
+
|
|
|
+ int len;
|
|
|
+
|
|
|
+ int ciphertext_len;
|
|
|
+
|
|
|
+
|
|
|
+ /* Create and initialise the context */
|
|
|
+ if(!(ctx = EVP_CIPHER_CTX_new())) {
|
|
|
+ printf("EVP_CIPHER_CTX_new failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialise the encryption operation. */
|
|
|
+ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
|
|
+ printf("EVP_EncryptInit_ex failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set IV length if default 12 bytes (96 bits) is not appropriate
|
|
|
+ */
|
|
|
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL)) {
|
|
|
+ printf("EVP_CTRL_GCM_SET_IVLEN failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialise key and IV */
|
|
|
+ if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
|
|
|
+ printf("Setting key and iv failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Provide any AAD data. This can be called zero or more times as
|
|
|
+ * required
|
|
|
+ */
|
|
|
+ if(aad_len && 1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) {
|
|
|
+ printf("EVP_EncryptUpdate AAD failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Provide the message to be encrypted, and obtain the encrypted output.
|
|
|
+ * EVP_EncryptUpdate can be called multiple times if necessary
|
|
|
+ */
|
|
|
+ int ret = EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len);
|
|
|
+ if(1 != ret) {
|
|
|
+ printf("EVP_EncryptUpdate failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ ciphertext_len = len;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Finalise the encryption. Normally ciphertext bytes may be written at
|
|
|
+ * this stage, but this does not occur in GCM mode
|
|
|
+ */
|
|
|
+ if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
|
|
|
+ printf("EVP_EncryptFinal_ex failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ ciphertext_len += len;
|
|
|
+
|
|
|
+ /* Get the tag */
|
|
|
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
|
|
|
+ printf("EVP_CTRL_GCM_GET_TAG failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Clean up */
|
|
|
+ EVP_CIPHER_CTX_free(ctx);
|
|
|
+
|
|
|
+ return ciphertext_len;
|
|
|
+}
|
|
|
+
|
|
|
+int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len,
|
|
|
+ unsigned char *aad, int aad_len,
|
|
|
+ unsigned char *tag,
|
|
|
+ unsigned char *key,
|
|
|
+ unsigned char *iv, int iv_len,
|
|
|
+ unsigned char *plaintext)
|
|
|
+{
|
|
|
+ EVP_CIPHER_CTX *ctx;
|
|
|
+ int len;
|
|
|
+ int plaintext_len;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Create and initialise the context */
|
|
|
+ if(!(ctx = EVP_CIPHER_CTX_new())) {
|
|
|
+ printf("EVP_CIPHER_CTX_new failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialise the decryption operation. */
|
|
|
+ if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
|
|
+ printf("EVP_DecryptInit_ex failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
|
|
|
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL)) {
|
|
|
+ printf("EVP_CTRL_GCM_SET_IVLEN failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialise key and IV */
|
|
|
+ if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
|
|
|
+ printf("Setting key and iv failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Provide any AAD data. This can be called zero or more times as
|
|
|
+ * required
|
|
|
+ */
|
|
|
+ if(aad_len && !EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) {
|
|
|
+ printf("EVP_DecryptUpdate AAD failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Provide the message to be decrypted, and obtain the plaintext output.
|
|
|
+ * EVP_DecryptUpdate can be called multiple times if necessary
|
|
|
+ */
|
|
|
+ if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
|
|
|
+ printf("EVP_DecryptUpdate failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ plaintext_len = len;
|
|
|
+
|
|
|
+ /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
|
|
|
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
|
|
|
+ printf("EVP_CTRL_GCM_SET_TAG failed\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Finalise the decryption. A positive return value indicates success,
|
|
|
+ * anything else is a failure - the plaintext is not trustworthy.
|
|
|
+ */
|
|
|
+ ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
|
|
|
+
|
|
|
+ /* Clean up */
|
|
|
+ EVP_CIPHER_CTX_free(ctx);
|
|
|
+
|
|
|
+ if(ret > 0) {
|
|
|
+ /* Success */
|
|
|
+ plaintext_len += len;
|
|
|
+ return plaintext_len;
|
|
|
+ } else {
|
|
|
+ /* Verify failed */
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+}
|