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:
parent
c9db7bc274
commit
58b3c91bab
@ -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"/>
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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, ...) \
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user