From 94cba72826818437196b400a0fbe7a35a22b99ad Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Tue, 26 May 2020 13:15:41 +0900
Subject: [PATCH v11 2/7] Add AEAD functions for both frontend and backend.

---
 src/common/Makefile       |   1 +
 src/common/aead.c         | 147 ++++++++++++++++++++++++++++++++++++++
 src/include/common/aead.h |  54 ++++++++++++++
 3 files changed, 202 insertions(+)
 create mode 100644 src/common/aead.c
 create mode 100644 src/include/common/aead.h

diff --git a/src/common/Makefile b/src/common/Makefile
index 6429fd6af2..b220fa4dcf 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -46,6 +46,7 @@ LIBS += $(PTHREAD_LIBS)
 # If you add objects here, see also src/tools/msvc/Mkvcbuild.pm
 
 OBJS_COMMON = \
+	aead.o \
 	archive.o \
 	base64.o \
 	checksum_helper.o \
diff --git a/src/common/aead.c b/src/common/aead.c
new file mode 100644
index 0000000000..fbdfe905ce
--- /dev/null
+++ b/src/common/aead.c
@@ -0,0 +1,147 @@
+/*-------------------------------------------------------------------------
+ *
+ * aead.c
+ *	  Shared frontend/backend code for AEAD
+ *
+ * This contains the common low-level functions needed in both frontend and
+ * backend, for implement the Authenticated Encryption with Associated
+ * Data (AEAD) that are based on the composition of the Advanced Encryption
+ * Standard (AES) in the Cipher Block Chaining (CBC) mode of operation for
+ * encryption, and the HMAC-SHA message authentication code (MAC).
+ *
+ * This AEAD algorithm is derived from the specification draft of
+ * Authenticated Encryption with AES-CBC and HMAC-SHA[1].  It uses an
+ * Authenticated Encryption with Associated Data, following an
+ * Encrypt-then-MAC approach. Our AEAD algorithm uses AES-256 encryption
+ * in the CBC mode of operation for encryption to encrypt data with a random
+ * initialization vector (IV), and then compute HMAC-SHA512 of encrypted data.
+ * The cipher text consists of the HMAC, IV and encrypted data.
+ *
+ * [1] https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
+ *
+ * Portions Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/common/aead.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/aead.h"
+#include "common/cipher.h"
+
+/* Return an AEAD context initialized with the given keys */
+PgAeadCtx *
+pg_create_aead_ctx(uint8 key[PG_AEAD_ENC_KEY_LEN], uint8 mackey[PG_AEAD_MAC_KEY_LEN])
+{
+	PgAeadCtx *ctx;
+
+	ctx = (PgAeadCtx *) palloc0(sizeof(PgAeadCtx));
+
+	/* Create and initialize a cipher context */
+	ctx->cipherctx = pg_cipher_ctx_create(PG_CIPHER_AES_CBC, key, PG_AEAD_ENC_KEY_LEN);
+	if (ctx->cipherctx == NULL)
+		return NULL;
+
+	/* Set encryption key and MAC key */
+	memcpy(ctx->key, key, PG_AEAD_ENC_KEY_LEN);
+	memcpy(ctx->mackey, mackey, PG_AEAD_MAC_KEY_LEN);
+
+	return ctx;
+}
+
+/* Free the AEAD context */
+void
+pg_free_aead_ctx(PgAeadCtx *ctx)
+{
+	if (!ctx)
+		return;
+
+	Assert(ctx->cipherctx);
+
+	pg_cipher_ctx_free(ctx->cipherctx);
+
+#ifndef FRONTEND
+	pfree(ctx);
+#else
+	pg_free(ctx);
+#endif
+}
+
+/*
+ * Encrypt the given data. Return true and set encrypted data to 'out' if
+ * success.  Otherwise return false. The caller must allocate sufficient space
+ * for cipher data calculated by using AEADSizeOfCipherText(). Please note that
+ * this function modifies 'out' data even on failure case.
+ */
+bool
+pg_aead_encrypt(PgAeadCtx *ctx, uint8 *in, int inlen, uint8 *out, int *outlen)
+{
+	uint8	*hmac;
+	uint8	*iv;
+	uint8	*enc;
+	int		enclen;
+
+	Assert(ctx && in && out);
+
+	hmac = out;
+	iv = hmac + PG_AEAD_HMAC_LEN;
+	enc = iv + PG_AES_IV_SIZE;
+
+	/* Generate IV */
+	if (!pg_strong_random(iv, PG_AES_IV_SIZE))
+		return false;
+
+	if (!pg_cipher_encrypt(ctx->cipherctx, in, inlen, enc, &enclen, iv))
+		return false;
+
+	if (!pg_HMAC_SHA512(ctx->mackey, enc, enclen, hmac))
+		return false;
+
+	*outlen = AEADSizeOfCipherText(inlen);;
+	Assert(*outlen == PG_AEAD_HMAC_LEN + PG_AES_IV_SIZE + enclen);
+
+	return true;
+}
+
+/*
+ * Decrypt the given Data. Return true and set plain text data to `out` if
+ * success.  Otherwise return false. The caller must allocate sufficient space
+ * for cipher data calculated by using AEADSizeOfPlainText(). Please note that
+ * this function modifies 'out' data even on failure case.
+ */
+bool
+pg_aead_decrypt(PgAeadCtx *ctx, uint8 *in, int inlen, uint8 *out, int *outlen)
+{
+	uint8	hmac[PG_AEAD_HMAC_LEN];
+	uint8   *expected_hmac;
+	uint8   *iv;
+	uint8	*enc;
+	int		enclen;
+
+	Assert(ctx && in && out);
+
+	expected_hmac = in;
+	iv = expected_hmac + PG_AEAD_HMAC_LEN;
+	enc = iv + PG_AES_IV_SIZE;
+	enclen = inlen - (enc - in);
+
+	/* Verify the correctness of HMAC */
+	if (!pg_HMAC_SHA512(ctx->mackey, enc, enclen, hmac))
+		return false;
+
+	if (memcmp(hmac, expected_hmac, PG_AEAD_HMAC_LEN) != 0)
+		return false;
+
+	/* Decrypt encrypted data */
+	if (!pg_cipher_decrypt(ctx->cipherctx, enc, enclen, out, outlen, iv))
+		return false;
+
+	return true;
+}
diff --git a/src/include/common/aead.h b/src/include/common/aead.h
new file mode 100644
index 0000000000..d295387fa8
--- /dev/null
+++ b/src/include/common/aead.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * aead.h
+ *		Declarations for utility function for AEAD
+ *
+ * Portions Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * src/include/common/aead.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _PG_AEAD_H
+#define _PG_AEAD_H
+
+#include "common/cipher.h"
+
+/* Encryption key and MAC key used for AEAD */
+#define PG_AEAD_ENC_KEY_LEN			PG_AES256_KEY_LEN
+#define PG_AEAD_MAC_KEY_LEN			PG_HMAC_SHA512_KEY_LEN
+#define PG_AEAD_HMAC_LEN			PG_HMAC_SHA512_LEN
+
+/* AEAD key consists of encryption key and mac key */
+#define PG_AEAD_KEY_LEN				(PG_AEAD_ENC_KEY_LEN + PG_AEAD_MAC_KEY_LEN)
+
+/*
+ * Size of encrypted key size with padding. We use PKCS#7 padding,
+ * described in RFC 5652.
+ */
+#define SizeOfDataWithPadding(klen) \
+	((int)(klen) + (PG_AES_BLOCK_SIZE - ((int)(klen) % PG_AES_BLOCK_SIZE)))
+
+/* Macros to compute the size of cipher text and plain text */
+#define AEADSizeOfCipherText(len) \
+	(PG_AEAD_MAC_KEY_LEN + PG_AES_IV_SIZE + SizeOfDataWithPadding((int)(len)))
+#define AEADSizeOfPlainText(klen) \
+	((int)(klen) - (PG_AEAD_MAC_KEY_LEN + PG_AES_IV_SIZE))
+
+/* Key wrapping cipher context */
+typedef struct PgAeadCtx
+{
+	uint8			key[PG_AEAD_ENC_KEY_LEN];
+	uint8			mackey[PG_AEAD_MAC_KEY_LEN];
+	PgCipherCtx		*cipherctx;
+} PgAeadCtx;
+
+extern PgAeadCtx *pg_create_aead_ctx(uint8 key[PG_AEAD_ENC_KEY_LEN],
+								  uint8 mackey[PG_AEAD_MAC_KEY_LEN]);
+extern void pg_free_aead_ctx(PgAeadCtx *ctx);
+extern bool pg_aead_encrypt(PgAeadCtx *ctx, uint8 *in, int inlen,
+							uint8 *out, int *outlen);
+extern bool pg_aead_decrypt(PgAeadCtx *ctx, uint8 *in, int inlen,
+							uint8 *out, int *outlen);
+
+#endif	/* _PG_AEAD_H */
-- 
2.23.0

