blob: 665f417fac433e389eb01b33bafaf5c702d088d2 [file] [log] [blame]
diff --git a/tools/Makefile b/tools/Makefile
index ccd60a5..e487530 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -38,6 +38,7 @@ tools-$(CONFIG_TARGET_tegra) += cbootimage cbootimage-configs
tools-$(CONFIG_USES_MINOR) += kernel2minor
tools-$(CONFIG_USE_SPARSE) += sparse
tools-y += openssl
+tools-y += aesgcm
# builddir dependencies
$(curdir)/autoconf/compile := $(curdir)/m4/compile
@@ -76,6 +77,7 @@ $(curdir)/squashfs/compile := $(curdir)/lzma-old/compile
$(curdir)/squashfskit4/compile := $(curdir)/xz/compile $(curdir)/zlib/compile
$(curdir)/zlib/compile := $(curdir)/cmake/compile
$(curdir)/zstd/compile := $(curdir)/cmake/compile
+$(curdir)/aesgcm/compile := $(curdir)/openssl/compile
ifneq ($(HOST_OS),Linux)
$(curdir)/squashfskit4/compile += $(curdir)/coreutils/compile
diff --git a/tools/aesgcm/Makefile b/tools/aesgcm/Makefile
new file mode 100644
index 0000000..f7ffc53
--- /dev/null
+++ b/tools/aesgcm/Makefile
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2022 MediaTek Inc. All rights reserved.
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=aesgcm
+PKG_VERSION:=1.0
+
+include $(INCLUDE_DIR)/host-build.mk
+
+define Host/Compile
+ $(MAKE) -C $(HOST_BUILD_DIR) \
+ OPENSSL_INCS_LOCATION=-I$(STAGING_DIR_HOST)/include/openssl-3 \
+ OPENSSL_LIBS_LOCATION=-L$(STAGING_DIR_HOST)/lib/openssl-3
+endef
+
+define Host/Prepare
+ mkdir -p $(HOST_BUILD_DIR)
+ $(CP) -a ./src/* $(HOST_BUILD_DIR)/
+endef
+
+define Host/Install
+ $(CP) $(HOST_BUILD_DIR)/aesgcm $(STAGING_DIR_HOST)/bin/
+endef
+
+$(eval $(call HostBuild))
diff --git a/tools/aesgcm/src/Makefile b/tools/aesgcm/src/Makefile
new file mode 100644
index 0000000..18d2e7b
--- /dev/null
+++ b/tools/aesgcm/src/Makefile
@@ -0,0 +1,12 @@
+CFLAGS = $(OPENSSL_INCS_LOCATION)
+LDFLAGS = $(OPENSSL_LIBS_LOCATION) -lssl -lcrypto -ldl -lpthread
+
+all: aesgcm
+
+aesgcm: aesgcm.o
+
+aesgcm:
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+clean:
+ $(RM) aesgcm
diff --git a/tools/aesgcm/src/aesgcm.c b/tools/aesgcm/src/aesgcm.c
new file mode 100644
index 0000000..05aa373
--- /dev/null
+++ b/tools/aesgcm/src/aesgcm.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2022 Mediatek Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Simple AES GCM authenticated encryption with additional data (AEAD)
+ * demonstration program.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+
+#define MAX_TEXT_LENGTH 4096
+#define MAX_AAD_LENGTH 256
+#define MAX_TAG_LENGTH 32
+
+#define ERR_ENC 1
+#define ERR_DEC 2
+#define ERR_UNK_MOD 3
+
+typedef enum {
+ UNK,
+ ENCRYPT,
+ DECRYPT
+} OPERATION;
+
+/*
+ * A library context and property query can be used to select & filter
+ * algorithm implementations. If they are NULL then the default library
+ * context and properties are used.
+ */
+OSSL_LIB_CTX *libctx = NULL;
+const char *propq = NULL;
+
+int aes_gcm_encrypt(uint8_t *key, uint8_t *iv, long iv_len, uint8_t *aad,
+ long aad_len, uint8_t *pt, long pt_len, BIO *out)
+{
+ int ret = 0;
+ EVP_CIPHER_CTX *ctx;
+ EVP_CIPHER *cipher = NULL;
+ int outlen, tmplen;
+ uint8_t *outbuf;
+ uint8_t *outtag[16];
+ OSSL_PARAM params[2] = {
+ OSSL_PARAM_END, OSSL_PARAM_END
+ };
+
+ outbuf = calloc(MAX_TEXT_LENGTH, sizeof(uint8_t));
+ if (!outbuf)
+ return 0;
+
+ /* Create a context for the encrypt operation */
+ if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+
+ /* Fetch the cipher implementation */
+ if ((cipher = EVP_CIPHER_fetch(libctx, "AES-256-GCM", propq)) == NULL)
+ goto err;
+
+ /* Set IV length if default 96 bits is not appropriate */
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN,
+ &iv_len);
+ /*
+ * Initialise an encrypt operation with the cipher/mode, key, IV and
+ * IV length parameter.
+ * For demonstration purposes the IV is being set here. In a compliant
+ * application the IV would be generated internally so the iv passed in
+ * would be NULL.
+ */
+ if (!EVP_EncryptInit_ex2(ctx, cipher, key, iv, params))
+ goto err;
+
+ /* Zero or more calls to specify any AAD */
+ if (!EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_len))
+ goto err;
+
+ /* Encrypt plaintext */
+ if (!EVP_EncryptUpdate(ctx, outbuf, &outlen, pt, pt_len))
+ goto err;
+
+ /* Finalise: note get no output for GCM */
+ if (!EVP_EncryptFinal_ex(ctx, outbuf, &tmplen))
+ goto err;
+
+ /* Get tag */
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ outtag, 16);
+
+ if (!EVP_CIPHER_CTX_get_params(ctx, params))
+ goto err;
+
+ /* Output IV */
+ if (BIO_write(out, iv, iv_len) <= 0)
+ goto err;
+
+ /* Output tag */
+ if (BIO_write(out, outtag, 16) <= 0)
+ goto err;
+
+ /* Output encrypted block */
+ if (BIO_write(out, outbuf, outlen) <= 0)
+ goto err;
+
+ ret = 1;
+err:
+ if (!ret)
+ ERR_print_errors_fp(stderr);
+
+ free(outbuf);
+ EVP_CIPHER_free(cipher);
+ EVP_CIPHER_CTX_free(ctx);
+
+ return ret;
+}
+
+int aes_gcm_decrypt(uint8_t *key, uint8_t *iv, long iv_len,
+ uint8_t *aad, long aad_len, uint8_t *ct, long ct_len,
+ uint8_t *tag, long tag_len, BIO *out)
+{
+ int ret = 0;
+ EVP_CIPHER_CTX *ctx;
+ EVP_CIPHER *cipher = NULL;
+ int outlen, rv;
+ uint8_t *outbuf;
+ OSSL_PARAM params[2] = {
+ OSSL_PARAM_END, OSSL_PARAM_END
+ };
+
+ outbuf = calloc(MAX_TEXT_LENGTH, sizeof(uint8_t));
+ if (!outbuf)
+ return 0;
+
+ if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+
+ /* Fetch the cipher implementation */
+ if ((cipher = EVP_CIPHER_fetch(libctx, "AES-256-GCM", propq)) == NULL)
+ goto err;
+
+ /* Set IV length if default 96 bits is not appropriate */
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN,
+ &iv_len);
+
+ /*
+ * Initialise an encrypt operation with the cipher/mode, key, IV and
+ * IV length parameter.
+ */
+ if (!EVP_DecryptInit_ex2(ctx, cipher, key, iv, params))
+ goto err;
+
+ /* Zero or more calls to specify any AAD */
+ if (!EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len))
+ goto err;
+
+ /* Decrypt plaintext */
+ if (!EVP_DecryptUpdate(ctx, outbuf, &outlen, ct, ct_len))
+ goto err;
+
+ /* Output decrypted block */
+ if (BIO_write(out, outbuf, outlen) <= 0)
+ goto err;
+
+ /* Set expected tag value. */
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ (void *)tag, tag_len);
+
+ if (!EVP_CIPHER_CTX_set_params(ctx, params))
+ goto err;
+
+ /* Finalise: note get no output for GCM */
+ rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
+ /*
+ * Print out return value. If this is not successful authentication
+ * failed and plaintext is not trustworthy.
+ */
+ printf("Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!");
+
+ ret = rv > 0 ? 1 : 0;
+err:
+ if (!ret)
+ ERR_print_errors_fp(stderr);
+
+ free(outbuf);
+ EVP_CIPHER_free(cipher);
+ EVP_CIPHER_CTX_free(ctx);
+
+ return ret;
+}
+
+void usage(void)
+{
+ printf(
+ "simple aes-256-gcm tool: \n"
+ "Operations:\n"
+ "-e - encrypt\n"
+ "-d - decrypt\n"
+ "Common requirement parameters:\n"
+ "-i infile - input file\n"
+ "-o outfile - out file\n"
+ "-k key(hex) - key in hex\n"
+ "-n iv(hex) - initial vector in hex\n"
+ "-a aad(hex) - additional authentication data in hex\n"
+ "Decryption requirement paremters:\n"
+ "-t intagfile - tag file as input\n");
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0, opt, oper = UNK;
+ long key_len = 0, iv_len = 0, aad_len = 0, tag_len = 0, in_len = 0;
+ BIO *in = NULL, *out = NULL, *in_tag = NULL;
+ uint8_t *in_buf;
+ uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
+ uint8_t iv[EVP_MAX_IV_LENGTH] = {0};
+ uint8_t aad[MAX_AAD_LENGTH] = {0};
+ uint8_t tag[MAX_TAG_LENGTH] = {0};
+
+ in_buf = calloc(MAX_TEXT_LENGTH, sizeof(uint8_t));
+ if (!in_buf)
+ return -ENOMEM;
+
+ while ((opt = getopt(argc, argv, "a:dei:g:k:n:o:t:")) > 0) {
+ switch(opt) {
+ case 'a':
+ ret = OPENSSL_hexstr2buf_ex(aad, MAX_AAD_LENGTH,
+ &aad_len, optarg, '\0');
+ if (!ret) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to read aad\n");
+ goto end;
+ }
+ break;
+ case 'd':
+ if (oper) {
+ ret = -EINVAL;
+ fprintf(stderr, "Duplicate operations\n");
+ goto end;
+ }
+ oper = DECRYPT;
+ break;
+ case 'e':
+ if (oper) {
+ ret = -EINVAL;
+ fprintf(stderr, "Duplicate operations\n");
+ goto end;
+ }
+ oper = ENCRYPT;
+ break;
+ case 'i':
+ in = BIO_new_file(optarg, "rb");
+ if (!in) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to open input file\n");
+ goto end;
+ }
+
+ in_len = BIO_read(in, in_buf, MAX_TEXT_LENGTH);
+ if (in_len <= 0) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to read input file\n");
+ goto end;
+ }
+ break;
+ case 'k':
+ ret = OPENSSL_hexstr2buf_ex(key, EVP_MAX_KEY_LENGTH,
+ &key_len, optarg, '\0');
+ if (!ret) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to read key\n");
+ goto end;
+ }
+ break;
+ case 'n':
+ ret = OPENSSL_hexstr2buf_ex(iv, EVP_MAX_IV_LENGTH,
+ &iv_len, optarg, '\0');
+ if (!ret) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to read iv\n");
+ goto end;
+ }
+ break;
+ case 'o':
+ out = BIO_new_file(optarg, "w");
+ if (!out) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to open output file\n");
+ goto end;
+ }
+ break;
+ case 't':
+ in_tag = BIO_new_file(optarg, "rb");
+ if (!in_tag) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to open tag file\n");
+ goto end;
+ }
+
+ tag_len = BIO_read(in_tag, tag, MAX_TAG_LENGTH);
+ if (tag_len <= 0) {
+ ret = -EINVAL;
+ fprintf(stderr, "Failed to read tag file\n");
+ goto end;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ if (!key_len || !iv_len || !aad_len || !in_len || !out) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (oper == ENCRYPT) {
+ ret = aes_gcm_encrypt(key, iv, iv_len, aad, aad_len,
+ in_buf, in_len, out);
+ if (!ret) {
+ ret = -ERR_ENC;
+ goto end;
+ }
+ } else if (oper == DECRYPT) {
+ if (!tag_len) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = aes_gcm_decrypt(key, iv, iv_len, aad, aad_len,
+ in_buf, in_len, tag, tag_len, out);
+ if (!ret) {
+ ret = -ERR_DEC;
+ goto end;
+ }
+ } else {
+ ret = -ERR_UNK_MOD;
+ goto end;
+ }
+
+end:
+ free(in_buf);
+ if (in)
+ BIO_free(in);
+ if (out)
+ BIO_free(out);
+ if (in_tag)
+ BIO_free(in_tag);
+
+ if (ret == -EINVAL)
+ usage();
+
+ return ret;
+}