From 94cba72826818437196b400a0fbe7a35a22b99ad Mon Sep 17 00:00:00 2001 From: Masahiko Sawada 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