blob: 8c62943bceca6cd7b799f0ae238eaada8605ee2c [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* bufenc.c
*
* Copyright (c) 2020, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* src/backend/crypto/bufenc.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "lib/stringinfo.h"
#include "access/gist.h"
#include "access/xlog.h"
#include "crypto/bufenc.h"
#include "crypto/sm4_ofb.h"
#include "storage/bufpage.h"
#include "storage/fd.h"
#include "storage/shmem.h"
extern XLogRecPtr LSNForEncryption(bool use_wal_lsn);
/*
* We use the page LSN, page number, and permanent-bit to indicate if a fake
* LSN was used to create a nonce for each page.
*/
#define BUFENC_IV_SIZE 16
static unsigned char buf_encryption_iv[BUFENC_IV_SIZE];
void *BufEncCtx = NULL;
void *BufDecCtx = NULL;
static void set_buffer_encryption_iv(Page page, BlockNumber blkno);
void
InitializeBufferEncryption(void)
{
const CryptoKey *key;
if (!FileEncryptionEnabled)
return;
key = KmgrGetKey(KMGR_KEY_ID_REL);
if (CheckIsSM4Method())
{
bool found;
BufEncCtx = ShmemInitStruct("sm4 encryption method encrypt ctx",
sizeof(sm4_ctx), &found);
BufDecCtx = ShmemInitStruct("sm4 encryption method decrypt ctx",
sizeof(sm4_ctx), &found);
sm4_ofb_setkey_enc((sm4_ctx *)BufEncCtx, (unsigned char *)key->key);
sm4_ofb_setkey_dec((sm4_ctx *)BufDecCtx, (unsigned char *)key->key);
}
else
{
BufEncCtx = (void *)pg_cipher_ctx_create(PG_CIPHER_AES_CTR,
(unsigned char *) key->key,
(key->klen), true);
BufDecCtx = (void *)pg_cipher_ctx_create(PG_CIPHER_AES_CTR,
(unsigned char *) key->key,
(key->klen), false);
}
if (!BufEncCtx)
elog(ERROR, "cannot intialize encryption context");
if (!BufDecCtx)
elog(ERROR, "cannot intialize decryption context");
}
/* Encrypt the given page with the relation key */
void
EncryptPage(Page page, BlockNumber blkno)
{
unsigned char *ptr = (unsigned char *) page + PageEncryptOffset;
int enclen;
Assert(BufEncCtx != NULL);
set_buffer_encryption_iv(page, blkno);
if (CheckIsSM4Method())
{
/* sm4 ofb mode use enc ctx, not dec ctx */
sm4_ofb_cipher(
(sm4_ctx *)BufEncCtx,
ptr,
(const unsigned char *) ptr,
SizeOfPageEncryption,
buf_encryption_iv);
}
else
{
if (unlikely(!pg_cipher_encrypt((PgCipherCtx *)BufEncCtx, PG_CIPHER_AES_CTR,
(const unsigned char *) ptr, /* input */
SizeOfPageEncryption,
ptr, /* length */
&enclen, /* resulting length */
buf_encryption_iv, /* iv */
BUFENC_IV_SIZE,
NULL, 0)))
elog(ERROR, "cannot encrypt page %u", blkno);
Assert(enclen == SizeOfPageEncryption);
}
}
/* Decrypt the given page with the relation key */
void
DecryptPage(Page page, BlockNumber blkno)
{
unsigned char *ptr = (unsigned char *) page + PageEncryptOffset;
int enclen;
Assert(BufDecCtx != NULL);
set_buffer_encryption_iv(page, blkno);
if (CheckIsSM4Method())
{
/* sm4 ofb mode use enc ctx, not dec ctx */
sm4_ofb_cipher(
(sm4_ctx *)BufEncCtx,
ptr,
(const unsigned char *) ptr,
SizeOfPageEncryption,
buf_encryption_iv);
}
else
{
if (unlikely(!pg_cipher_decrypt((PgCipherCtx *)BufDecCtx, PG_CIPHER_AES_CTR,
(const unsigned char *) ptr, /* input */
SizeOfPageEncryption,
ptr, /* output */
&enclen, /* resulting length */
buf_encryption_iv, /* iv */
BUFENC_IV_SIZE,
NULL, 0)))
elog(ERROR, "cannot decrypt page %u", blkno);
Assert(enclen == SizeOfPageEncryption);
}
}
/* Construct iv for the given page */
static void
set_buffer_encryption_iv(Page page, BlockNumber blkno)
{
unsigned char *p = buf_encryption_iv;
MemSet(buf_encryption_iv, 0, BUFENC_IV_SIZE);
/* block number (4 byte) */
memcpy(p, &blkno, sizeof(BlockNumber));
p += sizeof(BlockNumber);
/*
* set the last remain 12 bytes
*/
for (int i = 0 ; i < 12; i++)
*p++ = 0x80;
}
/* Construct iv for the given page */
static void
set_buffer_encryption_iv_for_ao(RelFileLocator *file_node)
{
unsigned char *p = buf_encryption_iv;
MemSet(buf_encryption_iv, 0, BUFENC_IV_SIZE);
/* copy the whole file node (4 bytes) */
memcpy(p, &file_node->dbOid, sizeof(file_node->dbOid));
p += sizeof(file_node->dbOid);
/*
* set the last remain 12 bytes
*/
for (int i = 0 ; i < 12; i++)
*p++ = 0x80;
}
void
EncryptAOBLock(unsigned char *data_buf, const int buf_len,
RelFileLocator *file_node)
{
int enclen;
Assert(BufEncCtx != NULL);
set_buffer_encryption_iv_for_ao(file_node);
if (CheckIsSM4Method())
{
sm4_ofb_cipher(
(sm4_ctx *)BufEncCtx,
data_buf,
(const unsigned char *)data_buf,
buf_len,
buf_encryption_iv);
}
else
{
if (unlikely(!pg_cipher_encrypt((PgCipherCtx *)BufEncCtx, PG_CIPHER_AES_CTR,
(const unsigned char *) data_buf, /* input */
buf_len,
data_buf, /* length */
&enclen, /* resulting length */
buf_encryption_iv, /* iv */
BUFENC_IV_SIZE,
NULL, 0)))
elog(ERROR, "cannot encrypt AO block");
Assert(buf_len == enclen);
}
}
/* Decrypt the given page with the relation key */
void
DecryptAOBlock(unsigned char *data_buf, const int buf_len,
RelFileLocator *file_node)
{
int enclen;
Assert(BufDecCtx != NULL);
set_buffer_encryption_iv_for_ao(file_node);
if (CheckIsSM4Method())
{
/* sm4 ofb mode use enc ctx, not dec ctx */
sm4_ofb_cipher(
(sm4_ctx *)BufEncCtx,
data_buf,
(const unsigned char *)data_buf,
buf_len,
buf_encryption_iv);
}
else
{
if (unlikely(!pg_cipher_decrypt((PgCipherCtx *)BufDecCtx, PG_CIPHER_AES_CTR,
(const unsigned char *) data_buf, /* input */
buf_len,
data_buf, /* output */
&enclen, /* resulting length */
buf_encryption_iv, /* iv */
BUFENC_IV_SIZE,
NULL, 0)))
elog(ERROR, "cannot decrypt ao block");
Assert(enclen == buf_len);
}
}