1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-30 05:39:12 +02:00

Add raw mode to CipherBlock to save space.

The magic in the header is only required so that command-line openssl will recognize the file as being encrypted. In cases where the encrypted data cannot be read with the command-line tool it makes sense to omit the header magic to save some space.

Unfortunately this cannot be enabled for file bundling because it would break backward compatibility. However, it should be possible to enable it for the combination of bundling and block incremental.
This commit is contained in:
David Steele 2022-11-10 10:28:49 +09:30
parent c9db7bc274
commit 58b3c91bab
4 changed files with 35 additions and 13 deletions

View File

@ -94,6 +94,7 @@
<commit subject="Add backupFileRepoPathP()."/>
<commit subject="Shorten names in real/all integration test matrix."/>
<commit subject="Update cipherBlockNew() to allow optional parameters."/>
<commit subject="Add raw mode to CipherBlock to save space."/>
<release-item-contributor-list>
<release-item-contributor id="david.steele"/>

View File

@ -32,6 +32,7 @@ Object type
typedef struct CipherBlock
{
CipherMode mode; // Mode encrypt/decrypt
bool raw; // Omit header magic to save space
bool saltDone; // Has the salt been read/generated?
bool processDone; // Has any data been processed?
size_t passSize; // Size of passphrase in bytes
@ -133,9 +134,12 @@ cipherBlockProcessBlock(CipherBlock *this, const unsigned char *source, size_t s
if (this->mode == cipherModeEncrypt)
{
// Add magic to the destination buffer so openssl knows the file is salted
memcpy(destination, CIPHER_BLOCK_MAGIC, CIPHER_BLOCK_MAGIC_SIZE);
destination += CIPHER_BLOCK_MAGIC_SIZE;
destinationSize += CIPHER_BLOCK_MAGIC_SIZE;
if (!this->raw)
{
memcpy(destination, CIPHER_BLOCK_MAGIC, CIPHER_BLOCK_MAGIC_SIZE);
destination += CIPHER_BLOCK_MAGIC_SIZE;
destinationSize += CIPHER_BLOCK_MAGIC_SIZE;
}
// Add salt to the destination buffer
cryptoRandomBytes(destination, PKCS5_SALT_LEN);
@ -147,19 +151,21 @@ cipherBlockProcessBlock(CipherBlock *this, const unsigned char *source, size_t s
else if (sourceSize > 0)
{
// Check if the entire header has been read
if (this->headerSize + sourceSize >= CIPHER_BLOCK_HEADER_SIZE)
const size_t headerExpected = this->raw ? PKCS5_SALT_LEN : CIPHER_BLOCK_HEADER_SIZE;
if (this->headerSize + sourceSize >= headerExpected)
{
// Copy header (or remains of header) from source into the header buffer
memcpy(this->header + this->headerSize, source, CIPHER_BLOCK_HEADER_SIZE - this->headerSize);
salt = this->header + CIPHER_BLOCK_MAGIC_SIZE;
memcpy(this->header + this->headerSize, source, headerExpected - this->headerSize);
salt = this->header + (this->raw ? 0 : CIPHER_BLOCK_MAGIC_SIZE);
// Advance source and source size by the number of bytes read
source += CIPHER_BLOCK_HEADER_SIZE - this->headerSize;
sourceSize -= CIPHER_BLOCK_HEADER_SIZE - this->headerSize;
source += headerExpected - this->headerSize;
sourceSize -= headerExpected - this->headerSize;
// The first bytes of the file to decrypt should be equal to the magic. If not then this is not an
// encrypted file, or at least not in a format we recognize.
if (memcmp(this->header, CIPHER_BLOCK_MAGIC, CIPHER_BLOCK_MAGIC_SIZE) != 0)
if (!this->raw && memcmp(this->header, CIPHER_BLOCK_MAGIC, CIPHER_BLOCK_MAGIC_SIZE) != 0)
THROW(CryptoError, "cipher header invalid");
}
// Else copy what was provided into the header buffer and return 0
@ -389,6 +395,7 @@ cipherBlockNew(CipherMode mode, CipherType cipherType, const Buffer *pass, Ciphe
FUNCTION_LOG_PARAM(STRING_ID, cipherType);
FUNCTION_TEST_PARAM(BUFFER, pass); // Use FUNCTION_TEST so passphrase is not logged
FUNCTION_LOG_PARAM(STRING, param.digest);
FUNCTION_LOG_PARAM(BOOL, param.raw);
FUNCTION_LOG_END();
ASSERT(pass != NULL);
@ -428,6 +435,7 @@ cipherBlockNew(CipherMode mode, CipherType cipherType, const Buffer *pass, Ciphe
*driver = (CipherBlock)
{
.mode = mode,
.raw = param.raw,
.cipher = cipher,
.digest = digest,
.passSize = bufUsed(pass),
@ -448,6 +456,7 @@ cipherBlockNew(CipherMode mode, CipherType cipherType, const Buffer *pass, Ciphe
pckWriteU64P(packWrite, cipherType);
pckWriteBinP(packWrite, pass);
pckWriteStrP(packWrite, param.digest);
pckWriteBoolP(packWrite, param.raw);
pckWriteEndP(packWrite);
paramList = pckMove(pckWriteResult(packWrite), memContextPrior());
@ -476,8 +485,9 @@ cipherBlockNewPack(const Pack *const paramList)
const CipherType cipherType = (CipherType)pckReadU64P(paramListPack);
const Buffer *const pass = pckReadBinP(paramListPack);
const String *const digest = pckReadStrP(paramListPack);
const bool raw = pckReadBoolP(paramListPack);
result = ioFilterMove(cipherBlockNewP(cipherMode, cipherType, pass, .digest = digest), memContextPrior());
result = ioFilterMove(cipherBlockNewP(cipherMode, cipherType, pass, .digest = digest, .raw = raw), memContextPrior());
}
MEM_CONTEXT_TEMP_END();

View File

@ -19,6 +19,7 @@ typedef struct CipherBlockNewParam
{
VAR_PARAM_HEADER;
const String *digest; // Digest to use (defaults to SHA-1)
bool raw; // Omit header magic to save space
} CipherBlockNewParam;
#define cipherBlockNewP(mode, cipherType, pass, ...) \

View File

@ -209,19 +209,29 @@ testRun(void)
ioFilterFree(blockDecryptFilter);
// Encrypt zero byte file and decrypt it
// -------------------------------------------------------------------------------------------------------------------------
blockEncryptFilter = cipherBlockNewP(cipherModeEncrypt, cipherTypeAes256Cbc, testPass);
TEST_TITLE("encrypt zero byte file with no magic");
blockEncryptFilter = cipherBlockNewP(cipherModeEncrypt, cipherTypeAes256Cbc, testPass, .raw = true);
blockEncrypt = (CipherBlock *)ioFilterDriver(blockEncryptFilter);
bufUsedZero(encryptBuffer);
ioFilterProcessInOut(blockEncryptFilter, NULL, encryptBuffer);
TEST_RESULT_UINT(bufUsed(encryptBuffer), 32, "check remaining size");
TEST_RESULT_UINT(bufUsed(encryptBuffer), 24, "check remaining size");
ioFilterFree(blockEncryptFilter);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on decrypt expecting magic");
blockDecryptFilter = cipherBlockNewP(cipherModeDecrypt, cipherTypeAes256Cbc, testPass);
TEST_ERROR(ioFilterProcessInOut(blockDecryptFilter, encryptBuffer, decryptBuffer), CryptoError, "cipher header invalid");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("decrypt zero byte file with no magic");
blockDecryptFilter = cipherBlockNewP(cipherModeDecrypt, cipherTypeAes256Cbc, testPass, .raw = true);
blockDecrypt = (CipherBlock *)ioFilterDriver(blockDecryptFilter);
bufUsedZero(decryptBuffer);