diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index a664ecf494..2197bd3a14 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -1025,6 +1025,7 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks) true); RelationOpenSmgr(rel); + PageEncryptInplace(page, MAIN_FORKNUM, lastblock); PageSetChecksumInplace(page, lastblock); smgrextend(rel->rd_smgr, MAIN_FORKNUM, lastblock, zerobuf.data, false); diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 39e33763df..498a18dd33 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -326,6 +326,7 @@ end_heap_rewrite(RewriteState state) true); RelationOpenSmgr(state->rs_new_rel); + PageEncryptInplace(state->rs_buffer, MAIN_FORKNUM, state->rs_blockno); PageSetChecksumInplace(state->rs_buffer, state->rs_blockno); smgrextend(state->rs_new_rel->rd_smgr, MAIN_FORKNUM, state->rs_blockno, @@ -690,6 +691,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup) */ RelationOpenSmgr(state->rs_new_rel); + PageEncryptInplace(page, MAIN_FORKNUM, state->rs_blockno); PageSetChecksumInplace(page, state->rs_blockno); smgrextend(state->rs_new_rel->rd_smgr, MAIN_FORKNUM, diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index c4f22f1c69..f55f66110e 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -175,6 +175,7 @@ btbuildempty(Relation index) * XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record. Therefore, we need * this even when wal_level=minimal. */ + PageEncryptInplace(metapage, INIT_FORKNUM, BTREE_METAPAGE); PageSetChecksumInplace(metapage, BTREE_METAPAGE); smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE, (char *) metapage, true); diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index 8730de25ed..729551d67b 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -665,6 +665,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno) true); } + PageEncryptInplace(page, MAIN_FORKNUM, blkno); PageSetChecksumInplace(page, blkno); /* diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c index e4508a2b92..4a0a24a5a7 100644 --- a/src/backend/access/spgist/spginsert.c +++ b/src/backend/access/spgist/spginsert.c @@ -168,6 +168,7 @@ spgbuildempty(Relation index) * of their existing content when the corresponding create records are * replayed. */ + PageEncryptInplace(page, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO); PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO, (char *) page, true); @@ -177,6 +178,7 @@ spgbuildempty(Relation index) /* Likewise for the root page. */ SpGistInitPage(page, SPGIST_LEAF); + PageEncryptInplace(page, INIT_FORKNUM, SPGIST_ROOT_BLKNO); PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO, (char *) page, true); @@ -186,6 +188,7 @@ spgbuildempty(Relation index) /* Likewise for the null-tuples root page. */ SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS); + PageEncryptInplace(page, INIT_FORKNUM, SPGIST_NULL_BLKNO); PageSetChecksumInplace(page, SPGIST_NULL_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO, (char *) page, true); diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a074dea53e..b95ed8eb0b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -61,6 +61,7 @@ #include "replication/walreceiver.h" #include "replication/walsender.h" #include "storage/bufmgr.h" +#include "storage/encryption.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/large_object.h" @@ -939,6 +940,7 @@ static void ReadControlFile(void); static char *str_time(pg_time_t tnow); static void SetPromoteIsTriggered(void); static bool CheckForStandbyTrigger(void); +static void XLogWritePages(char *from, Size nbytes, uint32 startoffset); #ifdef WAL_DEBUG static void xlog_outrec(StringInfo buf, XLogReaderState *record); @@ -2529,41 +2531,33 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { char *from; Size nbytes; - Size nleft; - int written; /* OK to write the page(s) */ from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ; nbytes = npages * (Size) XLOG_BLCKSZ; - nleft = nbytes; - do - { - errno = 0; - pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); - pgstat_report_wait_end(); - if (written <= 0) - { - char xlogfname[MAXFNAMELEN]; - int save_errno; - if (errno == EINTR) - continue; + if (DataEncryptionEnabled()) + { + int i; - save_errno = errno; - XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo, - wal_segment_size); - errno = save_errno; - ereport(PANIC, - (errcode_for_file_access(), - errmsg("could not write to log file %s " - "at offset %u, length %zu: %m", - xlogfname, startoffset, nleft))); + /* Encrypt xlog pages one by one */ + for (i = 0; i < npages; i++) + { + char *buftowrite; + Size nwrite = Min(nbytes, XLOG_BLCKSZ); + + buftowrite = EncryptXLog(from, nwrite, openLogSegNo, + startoffset); + XLogWritePages(buftowrite, nwrite, startoffset); + startoffset += nwrite; + from += XLOG_BLCKSZ; } - nleft -= written; - from += written; - startoffset += written; - } while (nleft > 0); + } + else + { + XLogWritePages(from, npages * (Size) XLOG_BLCKSZ, startoffset); + startoffset += npages * (Size) XLOG_BLCKSZ; + } npages = 0; @@ -2680,6 +2674,46 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) } } +/* + * Write XLOG pages starting from 'startoffset'. + */ +static void +XLogWritePages(char *from, Size nbytes, uint32 startoffset) +{ + Size nleft; + int written; + + nleft = nbytes; + do + { + errno = 0; + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); + written = pg_pwrite(openLogFile, from, nleft, startoffset); + pgstat_report_wait_end(); + if (written <= 0) + { + char xlogfname[MAXFNAMELEN]; + int save_errno; + + if (errno == EINTR) + continue; + + save_errno = errno; + XLogFileName(xlogfname, ThisTimeLineID, openLogSegNo, + wal_segment_size); + errno = save_errno; + ereport(PANIC, + (errcode_for_file_access(), + errmsg("could not write to log file %s " + "at offset %u, length %zu: %m", + xlogfname, startoffset, nleft))); + } + nleft -= written; + from += written; + startoffset += written; + } while (nleft > 0); +} + /* * Record the LSN for an asynchronous transaction commit/abort * and nudge the WALWriter if there is work for it to do. @@ -12049,6 +12083,16 @@ retry: xlogreader->seg.ws_tli = curFileTLI; + /* + * Decrypt read record so that we can validate page both short header + * and possibly long header. + */ + if (DataEncryptionEnabled()) + { + DecryptXLog(readBuf, XLOG_BLCKSZ, readSegNo, readOff); + xlogreader->encrypted = false; + } + /* * Check the page header immediately, so that we can retry immediately if * it's not valid. This may seem unnecessary, because XLogReadRecord() diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index a63ad8cfd0..3ad57e1a85 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -29,6 +29,7 @@ #ifndef FRONTEND #include "miscadmin.h" +#include "storage/encryption.h" #include "pgstat.h" #include "utils/memutils.h" #endif @@ -615,6 +616,16 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen) /* we can be sure to have enough WAL available, we scrolled back */ Assert(readLen == XLOG_BLCKSZ); +#ifndef FRONTEND + if (state->encrypted) + { + uint32 off = XLogSegmentOffset(targetSegmentPtr, state->segcxt.ws_segsize); + + DecryptXLog(state->readBuf, XLOG_BLCKSZ, targetSegNo, off); + state->encrypted = false; + } +#endif + if (!XLogReaderValidatePageHeader(state, targetSegmentPtr, state->readBuf)) goto err; @@ -650,6 +661,14 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen) goto err; } +#ifndef FRONTEND + if (state->encrypted) + { + DecryptXLog(state->readBuf, XLOG_BLCKSZ, targetSegNo, targetPageOff); + state->encrypted = false; + } +#endif + /* * Now that we know we have the full header, validate it. */ diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index 32a3099c1f..463ec31445 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -25,6 +25,7 @@ #include "access/xlogutils.h" #include "miscadmin.h" #include "pgstat.h" +#include "storage/encryption.h" #include "storage/smgr.h" #include "utils/guc.h" #include "utils/hsearch.h" @@ -946,6 +947,9 @@ read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, &errinfo)) WALReadRaiseError(&errinfo); + if (DataEncryptionEnabled()) + state->encrypted = true; + /* number of valid bytes in the buffer */ return count; } diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index d538f25726..e8f605030f 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -443,7 +443,7 @@ RelationCopyStorage(SMgrRelation src, SMgrRelation dst, smgrread(src, forkNum, blkno, buf.data); - if (!PageIsVerifiedExtended(page, blkno, + if (!PageIsVerifiedExtended(page, forkNum, blkno, PIV_LOG_WARNING | PIV_REPORT_STAT)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c index 9b3d026fc4..4990b42f4a 100644 --- a/src/backend/crypto/kmgr.c +++ b/src/backend/crypto/kmgr.c @@ -51,7 +51,7 @@ static KmgrShmemData *KmgrShmem; char *cluster_passphrase_command = NULL; int file_encryption_keylen = 0; -CryptoKey boostrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS]; +CryptoKey bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS]; extern char *bootstrap_old_key_datadir; extern int bootstrap_file_encryption_keylen; @@ -92,6 +92,7 @@ BootStrapKmgr(void) else { char live_path[MAXPGPATH]; + CryptoKey keys[KMGR_MAX_INTERNAL_KEYS]; if (mkdir(LIVE_KMGR_DIR, pg_dir_create_mode) < 0) ereport(ERROR, @@ -99,7 +100,7 @@ BootStrapKmgr(void) errmsg("could not create cluster file encryption directory \"%s\": %m", LIVE_KMGR_DIR))); - memset(boostrap_keys_wrap, 0, sizeof(boostrap_keys_wrap)); + memset(bootstrap_keys_wrap, 0, sizeof(bootstrap_keys_wrap)); /* bzero keys on exit */ on_proc_exit(bzeroKmgrKeys, 0); @@ -124,12 +125,10 @@ BootStrapKmgr(void) /* Wrap all data encryption keys by key encryption key */ for (int id = 0; id < KMGR_MAX_INTERNAL_KEYS; id++) { - CryptoKey *key; - /* generate a data encryption key */ - key = generate_crypto_key(bootstrap_file_encryption_keylen); + memcpy(&bootstrap_keys_wrap[id], generate_crypto_key(bootstrap_file_encryption_keylen), sizeof(CryptoKey)); - if (!kmgr_wrap_key(ctx, key, &(boostrap_keys_wrap[id]))) + if (!kmgr_wrap_key(ctx, &bootstrap_keys_wrap[id], &(keys[id]))) { pg_free_keywrap_ctx(ctx); elog(ERROR, "failed to wrap data encryption key"); @@ -137,7 +136,7 @@ BootStrapKmgr(void) } /* Save data encryption keys to the disk */ - KmgrSaveCryptoKeys(LIVE_KMGR_DIR, boostrap_keys_wrap); + KmgrSaveCryptoKeys(LIVE_KMGR_DIR, keys); pg_free_keywrap_ctx(ctx); } @@ -256,7 +255,7 @@ KmgrGetKey(int id) Assert(id < KMGR_MAX_INTERNAL_KEYS); return (const CryptoKey *) (IsBootstrapProcessingMode() ? - &(boostrap_keys_wrap[id]) : &(KmgrShmem->intlKeys[id])); + &(bootstrap_keys_wrap[id]) : &(KmgrShmem->intlKeys[id])); } /* Generate an empty CryptoKey */ diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 2eb19ad293..84c4bdee09 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -77,6 +77,7 @@ #include "replication/walsender.h" #include "replication/walsender_private.h" #include "storage/condition_variable.h" +#include "storage/encryption.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pmsignal.h" @@ -858,6 +859,10 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req XLByteToSeg(targetPagePtr, segno, state->segcxt.ws_segsize); CheckXLogRemoved(segno, state->seg.ws_tli); + /* Decrypt xlog page if needed */ + if (DataEncryptionEnabled()) + state->encrypted = true; + return count; } diff --git a/src/backend/storage/Makefile b/src/backend/storage/Makefile index 8376cdfca2..3f99e317e1 100644 --- a/src/backend/storage/Makefile +++ b/src/backend/storage/Makefile @@ -8,6 +8,6 @@ subdir = src/backend/storage top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -SUBDIRS = buffer file freespace ipc large_object lmgr page smgr sync +SUBDIRS = buffer encryption file freespace ipc large_object lmgr page smgr sync include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index ad0d1a9abc..3003090058 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -918,7 +918,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, } /* check for garbage data */ - if (!PageIsVerifiedExtended((Page) bufBlock, blockNum, + if (!PageIsVerifiedExtended((Page) bufBlock, forkNum, blockNum, PIV_LOG_WARNING | PIV_REPORT_STAT)) { if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages) @@ -2793,12 +2793,15 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln) */ bufBlock = BufHdrGetBlock(buf); + bufToWrite = PageEncryptCopy((Page) bufBlock, buf->tag.forkNum, + buf->tag.blockNum); + /* * Update page checksum if desired. Since we have only shared lock on the * buffer, other processes might be updating hint bits in it, so we must * copy the page to private storage if we do checksumming. */ - bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum); + bufToWrite = PageSetChecksumCopy((Page) bufToWrite, buf->tag.blockNum); if (track_io_timing) INSTR_TIME_SET_CURRENT(io_start); @@ -3275,6 +3278,9 @@ FlushRelationBuffers(Relation rel) localpage = (char *) LocalBufHdrGetBlock(bufHdr); + PageEncryptInplace(localpage, bufHdr->tag.forkNum, + bufHdr->tag.blockNum); + /* Setup error traceback support for ereport() */ errcallback.callback = local_buffer_write_error_callback; errcallback.arg = (void *) bufHdr; diff --git a/src/backend/storage/encryption/Makefile b/src/backend/storage/encryption/Makefile new file mode 100644 index 0000000000..b9c722104c --- /dev/null +++ b/src/backend/storage/encryption/Makefile @@ -0,0 +1,19 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for storage/encryption +# +# IDENTIFICATION +# src/backend/storage/encryption/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/backend/storage/encryption/ +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +OBJS = \ + bufenc.o \ + walenc.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/storage/encryption/bufenc.c b/src/backend/storage/encryption/bufenc.c new file mode 100644 index 0000000000..ed0242d857 --- /dev/null +++ b/src/backend/storage/encryption/bufenc.c @@ -0,0 +1,93 @@ +/*------------------------------------------------------------------------- + * + * bufenc.c + * + * Copyright (c) 2019, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/storage/encryption/bufenc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "storage/bufpage.h" +#include "storage/encryption.h" +#include "storage/fd.h" +#include "crypto/kmgr.h" + +static uint8 buf_encryption_iv[PG_AES_IV_SIZE]; +static void set_buffer_encryption_iv(Page page, BlockNumber blocknum); + +void +EncryptBufferBlock(BlockNumber blocknum, Page page) +{ + PgCipherCtx *relkeyctx; + const CryptoKey *relkey; + uint8 *key; + int outlen; + + relkey = KmgrGetKey(KMGR_REL_KEY_ID); + key = (uint8 *) pstrdup((const char *) relkey->key); + relkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, relkey->klen / 8); + + set_buffer_encryption_iv(page, blocknum); + pg_cipher_encrypt(relkeyctx, + (uint8*)(page + PageEncryptOffset), + SizeOfPageEncryption, + (uint8*)(page + PageEncryptOffset), + &outlen, + buf_encryption_iv); + + Assert(SizeOfPageEncryption == outlen); +} + +void +DecryptBufferBlock(BlockNumber blocknum, Page page) +{ + PgCipherCtx *relkeyctx; + const CryptoKey *relkey; + uint8 *key; + int outlen; + + relkey = KmgrGetKey(KMGR_REL_KEY_ID); + key = (uint8 *) pstrdup((const char *) relkey->key); + relkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, relkey->klen / 8); + + set_buffer_encryption_iv(page, blocknum); + pg_cipher_decrypt(relkeyctx, + (uint8*)(page + PageEncryptOffset), + SizeOfPageEncryption, + (uint8*)(page + PageEncryptOffset), + &outlen, + buf_encryption_iv); + + Assert(SizeOfPageEncryption == outlen); +} + +/* + * Nonce for buffer encryption consists of page lsn, block number + * and counter. The counter is a counter value for CTR cipher mode. + */ +static void +set_buffer_encryption_iv(Page page, BlockNumber blocknum) +{ + uint8 *p = buf_encryption_iv; + + MemSet(buf_encryption_iv, 0, PG_AES_IV_SIZE); + + /* page lsn (8 byte) */ + memcpy(p, &((PageHeader) page)->pd_lsn, sizeof(PageXLogRecPtr)); + p += sizeof(PageXLogRecPtr); + + /* block number (4 byte) */ + memcpy(p, &blocknum, sizeof(BlockNumber)); + p += sizeof(BlockNumber); + + /* Space for counter (4 byte) */ + memset(p, 0, ENC_BUFFER_AES_COUNTER_SIZE); + p += ENC_BUFFER_AES_COUNTER_SIZE; +} + diff --git a/src/backend/storage/encryption/walenc.c b/src/backend/storage/encryption/walenc.c new file mode 100644 index 0000000000..97cecab558 --- /dev/null +++ b/src/backend/storage/encryption/walenc.c @@ -0,0 +1,110 @@ +/*------------------------------------------------------------------------- + * + * walenc.c + * + * Copyright (c) 2019, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/storage/encryption/walenc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/xlog.h" +#include "access/xlog_internal.h" +#include "storage/encryption.h" +#include "crypto/kmgr.h" + +static uint8 wal_encryption_iv[PG_AES_IV_SIZE]; +static char wal_encryption_buf[XLOG_BLCKSZ]; + +static void +set_wal_encryption_iv(XLogSegNo segment, uint32 offset) +{ + uint8 *p = wal_encryption_iv; + uint32 pageno = offset / XLOG_BLCKSZ; + + /* Space for counter (4 byte) */ + memset(p, 0, ENC_WAL_AES_COUNTER_SIZE); + p += ENC_WAL_AES_COUNTER_SIZE; + + /* Segment number (8 byte) */ + memcpy(p, &segment, sizeof(XLogSegNo)); + p += sizeof(XLogSegNo); + + /* Page number within a WAL segment (4 byte) */ + memcpy(p, &pageno, sizeof(uint32)); +} + +/* + * Copy the contents of WAL page and encrypt it. Returns the copied and + * encrypted WAL page. + */ +char * +EncryptXLog(char *page, Size nbytes, XLogSegNo segno, uint32 offset) +{ +#if 0 + const CryptoKey *walkey; + PgCipherCtx *walkeyctx = NULL; + uint8 *key; + int outlen; + + Assert(nbytes <= XLOG_BLCKSZ); + + walkey = KmgrGetKey(KMGR_WAL_KEY_ID); + key = (uint8 *) pstrdup((const char *) walkey->key); + walkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, walkey->klen / 8); + + set_wal_encryption_iv(segno, offset); + + /* Copy to work buffer */ + memcpy(wal_encryption_buf, page, XLOG_BLCKSZ); + + pg_cipher_encrypt(walkeyctx, + (uint8*)(wal_encryption_buf + XLogEncryptionOffset), + nbytes - XLogEncryptionOffset, + (uint8*)(wal_encryption_buf + XLogEncryptionOffset), + &outlen, + wal_encryption_iv); + + Assert(outlen == (nbytes - XLogEncryptionOffset)); + return wal_encryption_buf; +#endif + return page; +} + +/* + * Decrypt a WAL page and return. Unlike EncryptXLog, this function encrypt + * the given buffer directly. + */ +void +DecryptXLog(char *page, Size nbytes, XLogSegNo segno, uint32 offset) +{ +#if 0 + const CryptoKey *walkey; + PgCipherCtx *walkeyctx = NULL; + uint8 *key; + int outlen; + + Assert(nbytes <= XLOG_BLCKSZ); + + walkey = KmgrGetKey(KMGR_WAL_KEY_ID); + key = (uint8 *) pstrdup((const char *) walkey->key); + walkeyctx = pg_cipher_ctx_create(PG_CIPHER_AES_CTR, key, walkey->klen / 8); + + set_wal_encryption_iv(segno, offset); + + pg_cipher_decrypt(walkeyctx, + (uint8*)(page + XLogEncryptionOffset), + nbytes - XLogEncryptionOffset, + (uint8*)(page + XLogEncryptionOffset), + &outlen, + wal_encryption_iv); + + Assert(outlen == (nbytes - XLogEncryptionOffset)); +#endif +} + diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index ddf18079e2..5ad4d35c44 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -19,6 +19,7 @@ #include "access/xlog.h" #include "pgstat.h" #include "storage/checksum.h" +#include "storage/encryption.h" #include "utils/memdebug.h" #include "utils/memutils.h" @@ -26,7 +27,6 @@ /* GUC variable */ bool ignore_checksum_failure = false; - /* ---------------------------------------------------------------- * Page support functions * ---------------------------------------------------------------- @@ -85,7 +85,7 @@ PageInit(Page page, Size pageSize, Size specialSize) * to pgstat. */ bool -PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags) +PageIsVerifiedExtended(Page page, ForkNumber forknum, BlockNumber blkno, int flags) { PageHeader p = (PageHeader) page; size_t *pagebytes; @@ -108,6 +108,8 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags) checksum_failure = true; } + PageDecryptInplace(page, forknum, blkno); + /* * The following checks don't prove the header is correct, only that * it looks sane enough to allow into the buffer pool. Later usage of @@ -1427,3 +1429,47 @@ PageSetChecksumInplace(Page page, BlockNumber blkno) ((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno); } + +char* +PageEncryptCopy(Page page, ForkNumber forknum, BlockNumber blkno) +{ + static char *pageCopy = NULL; + + if (PageIsNew(page) || !DataEncryptionEnabled() || !EncryptForkNum(forknum)) + return (char *) page; + + /* + * We allocate the copy space once and use it over on each subsequent + * call. The point of palloc'ing here, rather than having a static char + * array, is first to ensure adequate alignment for the checksumming code + * and second to avoid wasting space in processes that never call this. + */ + if (pageCopy == NULL) + pageCopy = MemoryContextAlloc(TopMemoryContext, BLCKSZ); + + memcpy(pageCopy, (char *) page, BLCKSZ); + EncryptBufferBlock(blkno, pageCopy); + return pageCopy; +} + +void +PageEncryptInplace(Page page, ForkNumber forknum, BlockNumber blkno) +{ + Assert(forknum <= MAX_FORKNUM && blkno != InvalidBlockNumber); + + if (PageIsNew(page) || !DataEncryptionEnabled() || !EncryptForkNum(forknum)) + return; + + EncryptBufferBlock(blkno, page); +} + +void +PageDecryptInplace(Page page, ForkNumber forknum, BlockNumber blkno) +{ + Assert(forknum <= MAX_FORKNUM && blkno != InvalidBlockNumber); + + if (PageIsNew(page) || !DataEncryptionEnabled() || !EncryptForkNum(forknum)) + return; + + DecryptBufferBlock(blkno, page); +} diff --git a/src/common/cipher_openssl.c b/src/common/cipher_openssl.c index 1ab5a390e7..8a822c3901 100644 --- a/src/common/cipher_openssl.c +++ b/src/common/cipher_openssl.c @@ -34,6 +34,7 @@ typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void); static ossl_EVP_cipher_func get_evp_aes_cbc(int klen); +static ossl_EVP_cipher_func get_evp_aes_ctr(int klen); static ossl_EVP_cipher_func get_evp_aes_cbc(int klen) @@ -51,6 +52,22 @@ get_evp_aes_cbc(int klen) } } +static ossl_EVP_cipher_func +get_evp_aes_ctr(int klen) +{ + switch (klen) + { + case PG_AES128_KEY_LEN: + return EVP_aes_128_ctr; + case PG_AES192_KEY_LEN: + return EVP_aes_192_ctr; + case PG_AES256_KEY_LEN: + return EVP_aes_256_ctr; + default: + return NULL; + } +} + /* * Initialize and return an EVP_CIPHER_CTX. Return NULL if the given * cipher algorithm is not supported or on failure.. @@ -68,13 +85,16 @@ ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc) { case PG_CIPHER_AES_CBC: func = get_evp_aes_cbc(klen); - if (!func) - goto failed; + break; + case PG_CIPHER_AES_CTR: + func = get_evp_aes_ctr(klen); break; default: goto failed; } + if (!func) + goto failed; if (enc) ret = EVP_EncryptInit_ex(ctx, (const EVP_CIPHER *) func(), NULL, key, NULL); @@ -84,7 +104,7 @@ ossl_cipher_ctx_create(int cipher, uint8 *key, int klen, bool enc) if (!ret) goto failed; - if (!EVP_CIPHER_CTX_set_key_length(ctx, PG_AES256_KEY_LEN)) + if (!EVP_CIPHER_CTX_set_key_length(ctx, klen)) goto failed; /* diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 4146753d47..488ae2987f 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -50,6 +50,7 @@ typedef struct XLogPageHeaderData } XLogPageHeaderData; #define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData)) +#define XLogEncryptionOffset SizeOfXLogShortPHD typedef XLogPageHeaderData *XLogPageHeader; diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h index 0b6d00dd7d..f268e396d2 100644 --- a/src/include/access/xlogreader.h +++ b/src/include/access/xlogreader.h @@ -209,6 +209,7 @@ struct XLogReaderState */ char *readBuf; uint32 readLen; + bool encrypted; /* readBuf is encrypted? */ /* last read XLOG position for data currently in readBuf */ WALSegmentContext segcxt; diff --git a/src/include/common/cipher.h b/src/include/common/cipher.h index e3fb1d04b5..c8148714fd 100644 --- a/src/include/common/cipher.h +++ b/src/include/common/cipher.h @@ -25,7 +25,8 @@ * algorithm. */ #define PG_CIPHER_AES_CBC 0 -#define PG_MAX_CIPHER_ID 1 +#define PG_CIPHER_AES_CTR 1 +#define PG_MAX_CIPHER_ID 2 /* AES128/192/256 various length definitions */ #define PG_AES128_KEY_LEN (128 / 8) diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index d0a52f8e08..67be7e50af 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -15,6 +15,7 @@ #define BUFPAGE_H #include "access/xlogdefs.h" +#include "common/relpath.h" #include "storage/block.h" #include "storage/item.h" #include "storage/off.h" @@ -165,6 +166,9 @@ typedef struct PageHeaderData typedef PageHeaderData *PageHeader; +#define PageEncryptOffset offsetof(PageHeaderData, pd_linp) +#define SizeOfPageEncryption (BLCKSZ - PageEncryptOffset) + /* * pd_flags contains the following flag bits. Undefined bits are initialized * to zero and may be used in the future. @@ -418,8 +422,8 @@ do { \ ((overwrite) ? PAI_OVERWRITE : 0) | \ ((is_heap) ? PAI_IS_HEAP : 0)) -#define PageIsVerified(page, blkno) \ - PageIsVerifiedExtended(page, blkno, \ +#define PageIsVerified(page, forknum, blkno) \ + PageIsVerifiedExtended(page, forknum, blkno, \ PIV_LOG_WARNING | PIV_REPORT_STAT) /* @@ -433,7 +437,7 @@ StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)), "BLCKSZ has to be a multiple of sizeof(size_t)"); extern void PageInit(Page page, Size pageSize, Size specialSize); -extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags); +extern bool PageIsVerifiedExtended(Page page, ForkNumber forknum, BlockNumber blkno, int flags); extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size, OffsetNumber offsetNumber, int flags); extern Page PageGetTempPage(Page page); @@ -452,5 +456,8 @@ extern bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize); extern char *PageSetChecksumCopy(Page page, BlockNumber blkno); extern void PageSetChecksumInplace(Page page, BlockNumber blkno); +extern char *PageEncryptCopy(Page page, ForkNumber forknum, BlockNumber blkno); +extern void PageEncryptInplace(Page page, ForkNumber forknum, BlockNumber blkno); +extern void PageDecryptInplace(Page page, ForkNumber forknum, BlockNumber blkno); #endif /* BUFPAGE_H */ diff --git a/src/include/storage/encryption.h b/src/include/storage/encryption.h new file mode 100644 index 0000000000..a61c47ea83 --- /dev/null +++ b/src/include/storage/encryption.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * encryption.h + * Cluster encryption functions. + * + * Portions Copyright (c) 2019, PostgreSQL Global Development Group + * + * src/include/storage/encryption.h + * + *------------------------------------------------------------------------- + */ +#ifndef ENCRYPTION_H +#define ENCRYPTION_H + +#include "access/xlogdefs.h" +#include "common/cipher.h" +#include "crypto/kmgr.h" +#include "storage/bufpage.h" + +#define DataEncryptionEnabled() true + +/* Cluster encryption encrypts only main fork */ +#define EncryptForkNum(forknum) \ + ((forknum) == MAIN_FORKNUM || (forknum) == INIT_FORKNUM) + +/* + * The size in byte for counter of AES-CTR mode in nonce. + */ +#define ENC_BUFFER_AES_COUNTER_SIZE 4 +#define ENC_WAL_AES_COUNTER_SIZE 4 + +/* bufenc.c */ +extern void DecryptBufferBlock(BlockNumber blocknum, Page page); +extern void EncryptBufferBlock(BlockNumber blocknum, Page page); + +/* walenc.c */ +extern char *EncryptXLog(char *page, Size nbytes, XLogSegNo segno, + uint32 offset); +extern void DecryptXLog(char *page, Size nbytes, XLogSegNo segno, + uint32 offset); + +#endif /* ENCRYPTION_H */