v12-0001-Add-encryption-functions-for-both-frontend-and-b.patch
application/octet-stream
Filename: v12-0001-Add-encryption-functions-for-both-frontend-and-b.patch
Type: application/octet-stream
Part: 2
Message:
Re: Internal key management system
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v12-0001
Subject: Add encryption functions for both frontend and backend.
| File | + | − |
|---|---|---|
| configure | 1 | 1 |
| configure.in | 1 | 1 |
| src/common/cipher.c | 87 | 0 |
| src/common/cipher_openssl.c | 188 | 0 |
| src/common/Makefile | 2 | 0 |
| src/include/common/cipher.h | 78 | 0 |
| src/include/common/cipher_openssl.h | 37 | 0 |
| src/include/pg_config.h.in | 3 | 0 |
From b3196891f9210f1c7e1252dfdd6a4d16f4946808 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Mon, 25 May 2020 16:45:07 +0900
Subject: [PATCH v12 1/3] Add encryption functions for both frontend and
backend.
---
configure | 2 +-
configure.in | 2 +-
src/common/Makefile | 2 +
src/common/cipher.c | 87 +++++++++++++
src/common/cipher_openssl.c | 188 ++++++++++++++++++++++++++++
src/include/common/cipher.h | 78 ++++++++++++
src/include/common/cipher_openssl.h | 37 ++++++
src/include/pg_config.h.in | 3 +
8 files changed, 397 insertions(+), 2 deletions(-)
create mode 100644 src/common/cipher.c
create mode 100644 src/common/cipher_openssl.c
create mode 100644 src/include/common/cipher.h
create mode 100644 src/include/common/cipher_openssl.h
diff --git a/configure b/configure
index 2feff37fe3..56b887f2cf 100755
--- a/configure
+++ b/configure
@@ -12313,7 +12313,7 @@ done
# defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
# doesn't have these OpenSSL 1.1.0 functions. So check for individual
# functions.
- for ac_func in OPENSSL_init_ssl BIO_get_data BIO_meth_new ASN1_STRING_get0_data
+ for ac_func in OPENSSL_init_ssl OPENSSL_init_crypto BIO_get_data BIO_meth_new ASN1_STRING_get0_data
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.in
index 0188c6ff07..f9dbd788a3 100644
--- a/configure.in
+++ b/configure.in
@@ -1218,7 +1218,7 @@ if test "$with_openssl" = yes ; then
# defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
# doesn't have these OpenSSL 1.1.0 functions. So check for individual
# functions.
- AC_CHECK_FUNCS([OPENSSL_init_ssl BIO_get_data BIO_meth_new ASN1_STRING_get0_data])
+ AC_CHECK_FUNCS([OPENSSL_init_ssl OPENSSL_init_crypto BIO_get_data BIO_meth_new ASN1_STRING_get0_data])
# OpenSSL versions before 1.1.0 required setting callback functions, for
# thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
# function was removed.
diff --git a/src/common/Makefile b/src/common/Makefile
index 16619e4ba8..1a6355c0c1 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -49,6 +49,7 @@ OBJS_COMMON = \
archive.o \
base64.o \
checksum_helper.o \
+ cipher.o \
config_info.o \
controldata_utils.o \
d2s.o \
@@ -79,6 +80,7 @@ OBJS_COMMON = \
ifeq ($(with_openssl),yes)
OBJS_COMMON += \
+ cipher_openssl.o \
protocol_openssl.o \
sha2_openssl.o
else
diff --git a/src/common/cipher.c b/src/common/cipher.c
new file mode 100644
index 0000000000..908afe8446
--- /dev/null
+++ b/src/common/cipher.c
@@ -0,0 +1,87 @@
+/*-------------------------------------------------------------------------
+ *
+ * cipher.c
+ * Shared frontend/backend for cryptographic functions
+ *
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/common/cipher.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/cipher.h"
+#ifdef USE_OPENSSL
+#include "common/cipher_openssl.h"
+#endif
+
+/*
+ * Return a newly created cipher context. 'cipher' specifies cipher algorithm
+ * by identifer like PG_CIPHER_XXX.
+ */
+PgCipherCtx *
+pg_cipher_ctx_create(int cipher, uint8 *key, int klen)
+{
+ PgCipherCtx *ctx = NULL;
+
+ if (cipher >= PG_MAX_CIPHER_ID)
+ return NULL;
+
+#ifdef USE_OPENSSL
+ ctx = (PgCipherCtx *) palloc0(sizeof(PgCipherCtx));
+
+ ctx->encctx = ossl_cipher_ctx_create(cipher, key, klen, true);
+ ctx->decctx = ossl_cipher_ctx_create(cipher, key, klen, false);
+#endif
+
+ return ctx;
+}
+
+void
+pg_cipher_ctx_free(PgCipherCtx *ctx)
+{
+#ifdef USE_OPENSSL
+ ossl_cipher_ctx_free(ctx->encctx);
+ ossl_cipher_ctx_free(ctx->decctx);
+#endif
+}
+
+bool
+pg_cipher_encrypt(PgCipherCtx *ctx, const uint8 *in, int inlen,
+ uint8 *out, int *outlen, const uint8 *iv)
+{
+ bool r = false;
+#ifdef USE_OPENSSL
+ r = ossl_cipher_encrypt(ctx->encctx, in, inlen, out, outlen, iv);
+#endif
+ return r;
+}
+
+bool
+pg_cipher_decrypt(PgCipherCtx *ctx, const uint8 *in, int inlen,
+ uint8 *out, int *outlen, const uint8 *iv)
+{
+ bool r = false;
+#ifdef USE_OPENSSL
+ r = ossl_cipher_decrypt(ctx->decctx, in, inlen, out, outlen, iv);
+#endif
+ return r;
+}
+
+bool
+pg_HMAC_SHA512(const uint8 *key, const uint8 *in, int inlen,
+ uint8 *out)
+{
+ bool r = false;
+#ifdef USE_OPENSSL
+ r = ossl_HMAC_SHA512(key, in, inlen, out);
+#endif
+ return r;
+}
diff --git a/src/common/cipher_openssl.c b/src/common/cipher_openssl.c
new file mode 100644
index 0000000000..a1d6d59bbd
--- /dev/null
+++ b/src/common/cipher_openssl.c
@@ -0,0 +1,188 @@
+/*-------------------------------------------------------------------------
+ * cipher_openssl.c
+ * Cryptographic function using OpenSSL
+ *
+ * This contains the common low-level functions needed in both frontend and
+ * backend, for implement the database encryption.
+ *
+ * Portions Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/common/cipher_openssl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/sha2.h"
+#include "common/cipher_openssl.h"
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+
+/*
+ * prototype for the EVP functions that return an algorithm, e.g.
+ * EVP_aes_128_cbc().
+ */
+typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void);
+
+static bool ossl_initialized = false;
+
+static bool ossl_cipher_setup(void);
+static ossl_EVP_cipher_func get_evp_aes_cbc(int klen);
+
+static bool
+ossl_cipher_setup(void)
+{
+#ifdef HAVE_OPENSSL_INIT_CRYPTO
+ /* Setup OpenSSL */
+ if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
+ return false;
+#else
+ OPENSSL_config(NULL);
+#endif
+ return false;
+}
+
+static ossl_EVP_cipher_func
+get_evp_aes_cbc(int klen)
+{
+ switch (klen)
+ {
+ case PG_AES128_KEY_LEN:
+ return EVP_aes_128_cbc;
+ case PG_AES192_KEY_LEN:
+ return EVP_aes_192_cbc;
+ case PG_AES256_KEY_LEN:
+ return EVP_aes_256_cbc;
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * Initialize and return an EVP_CIPHER_CTX. Return NULL if the given
+ * cipher algorithm is not supported or on failure..
+ */
+EVP_CIPHER_CTX *
+ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc)
+{
+ EVP_CIPHER_CTX *ctx;
+ ossl_EVP_cipher_func func;
+ int ret;
+
+ if (!ossl_initialized)
+ {
+ ossl_cipher_setup();
+ ossl_initialized = true;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ switch (cipher)
+ {
+ case PG_CIPHER_AES_CBC:
+ func = get_evp_aes_cbc(klen);
+ if (!func)
+ goto failed;
+ break;
+ default:
+ goto failed;
+ }
+
+
+ if (enc)
+ ret = EVP_EncryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
+ else
+ ret = EVP_DecryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL);
+
+ if (!ret)
+ goto failed;
+
+ if (!EVP_CIPHER_CTX_set_key_length(ctx, PG_AES256_KEY_LEN))
+ goto failed;
+
+ /*
+ * Always enable padding. We don't need to check the return value as
+ * EVP_CIPHER_CTX_set_padding always returns 1.
+ */
+ EVP_CIPHER_CTX_set_padding(ctx, 1);
+
+ return ctx;
+
+failed:
+ EVP_CIPHER_CTX_free(ctx);
+ return NULL;
+}
+
+void
+ossl_cipher_ctx_free(EVP_CIPHER_CTX *ctx)
+{
+ return EVP_CIPHER_CTX_free(ctx);
+}
+
+bool
+ossl_cipher_encrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv)
+{
+ int len;
+ int enclen;
+
+ if (!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv))
+ return false;
+
+ if (!EVP_EncryptUpdate(ctx, out, &len, in, inlen))
+ return false;
+
+ enclen = len;
+
+ if (!EVP_EncryptFinal_ex(ctx, (uint8 *) ((char *) out + enclen),
+ &len))
+ return false;
+
+ *outlen = enclen + len;
+
+ return true;
+}
+
+bool
+ossl_cipher_decrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv)
+{
+ int declen;
+ int len;
+
+ if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv))
+ return false;
+
+ if (!EVP_DecryptUpdate(ctx, out, &len, in, inlen))
+ return false;
+
+ declen = len;
+
+ if (!EVP_DecryptFinal_ex(ctx, (uint8 *) ((char *) out + declen),
+ &len))
+ return false;
+
+ *outlen = declen + len;
+
+ return true;
+}
+
+bool
+ossl_HMAC_SHA512(const uint8 *key, const uint8 *in, int inlen,
+ uint8 *out)
+{
+ return HMAC(EVP_sha512(), key, PG_SHA512_DIGEST_LENGTH,
+ in, (uint32) inlen, out, NULL);
+}
diff --git a/src/include/common/cipher.h b/src/include/common/cipher.h
new file mode 100644
index 0000000000..f78279115f
--- /dev/null
+++ b/src/include/common/cipher.h
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------------------------
+ *
+ * cipher.h
+ * Declarations for cryptographic functions
+ *
+ * Portions Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * src/include/common/cipher.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CIPHER_H
+#define CIPHER_H
+
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#endif
+
+/*
+ * Supported symmetric encryption algorithm. These identifiers are passed
+ * to pg_cipher_ctx_create() function, and then actual encryption
+ * implementations need to initialize their context of the given encryption
+ * algorithm.
+ */
+#define PG_CIPHER_AES_CBC 0
+#define PG_MAX_CIPHER_ID 1
+
+/* AES128/192/256 various length definitions */
+#define PG_AES128_KEY_LEN (128 / 8)
+#define PG_AES192_KEY_LEN (192 / 8)
+#define PG_AES256_KEY_LEN (256 / 8)
+
+/*
+ * The encrypted data is a series of blocks of size. Initialization
+ * vector(IV) is the same size of cipher block.
+ */
+#define PG_AES_BLOCK_SIZE 16
+#define PG_AES_IV_SIZE (PG_AES_BLOCK_SIZE)
+
+/* HMAC key and HMAC length. We use HMAC-SHA256 */
+#define PG_HMAC_SHA512_KEY_LEN 64
+#define PG_HMAC_SHA512_LEN 64
+
+#ifdef USE_OPENSSL
+typedef EVP_CIPHER_CTX cipher_private_ctx;
+#else
+typedef void cipher_private_ctx;
+#endif
+
+/*
+ * This struct has two implementation-private context for
+ * encryption and decryption. The caller must create the encryption
+ * context using by pg_cipher_ctx_create() and pass the context to
+ * pg_cipher_encrypt() or pg_cipher_decrypt().
+ */
+typedef struct PgCipherCtx
+{
+ cipher_private_ctx *encctx;
+ cipher_private_ctx *decctx;
+} PgCipherCtx;
+
+extern PgCipherCtx *pg_cipher_ctx_create(int cipher, uint8 *key, int klen);
+extern void pg_cipher_ctx_free(PgCipherCtx *ctx);
+extern bool pg_cipher_encrypt(PgCipherCtx *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+extern bool pg_cipher_decrypt(PgCipherCtx *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+extern bool pg_HMAC_SHA512(const uint8 *key,
+ const uint8 *in, int inlen,
+ uint8 *out);
+
+#endif /* CIPHER_H */
diff --git a/src/include/common/cipher_openssl.h b/src/include/common/cipher_openssl.h
new file mode 100644
index 0000000000..0fd1308bc9
--- /dev/null
+++ b/src/include/common/cipher_openssl.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * cipher_openssl.h
+ * Declarations for helper functions using OpenSSL
+ *
+ * Portions Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * src/include/common/cipher_openssl.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CIPHER_OPENSSL_H
+#define CIPHER_OPENSSL_H
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/cipher.h"
+
+extern EVP_CIPHER_CTX *ossl_cipher_ctx_create(int cipher, uint8 *key, int klen,
+ bool enc);
+extern void ossl_cipher_ctx_free(EVP_CIPHER_CTX *ctx);
+extern bool ossl_cipher_encrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+extern bool ossl_cipher_decrypt(EVP_CIPHER_CTX *ctx,
+ const uint8 *in, int inlen,
+ uint8 *out, int *outlen,
+ const uint8 *iv);
+extern bool ossl_HMAC_SHA512(const uint8 *key,
+ const uint8 *in, int inlen,
+ uint8 *out);
+#endif
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c199cd46d2..ceb39da1ca 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -385,6 +385,9 @@
/* Define to 1 if you have the `OPENSSL_init_ssl' function. */
#undef HAVE_OPENSSL_INIT_SSL
+/* Define to 1 if you have the `OPENSSL_init_crypto' function. */
+#undef HAVE_OPENSSL_INIT_CRYPTO
+
/* Define to 1 if you have the <ossp/uuid.h> header file. */
#undef HAVE_OSSP_UUID_H
--
2.23.0