Refactor crypto callback to have separate input/output pointers. Add initial writing support.

This commit is contained in:
Michael R Sweet 2021-10-12 09:13:30 -04:00
parent 0caea44f32
commit c24243a2bc
No known key found for this signature in database
GPG Key ID: 999559A027815955
7 changed files with 151 additions and 28 deletions

View File

@ -170,23 +170,34 @@ _pdfioCryptoAESInit(
// //
// '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES. // '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES.
// //
// "inbuffer" and "outbuffer" can point to the same memory.
//
void void
_pdfioCryptoAESDecrypt( _pdfioCryptoAESDecrypt(
_pdfio_aes_t *ctx, // I - AES context _pdfio_aes_t *ctx, // I - AES context
uint8_t *buffer, // I - Buffer uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Number of bytes to decrypt size_t len) // I - Number of bytes to decrypt
{ {
uint8_t next_iv[16]; // Next IV value uint8_t next_iv[16]; // Next IV value
if (inbuffer != outbuffer)
{
// Not the most efficient, but we can optimize later - the sample AES code
// manipulates the data directly in memory and doesn't support separate
// input and output buffers...
memcpy(outbuffer, inbuffer, len);
}
while (len > 15) while (len > 15)
{ {
memcpy(next_iv, buffer, 16); memcpy(next_iv, outbuffer, 16);
InvCipher((state_t *)buffer, ctx); InvCipher((state_t *)outbuffer, ctx);
XorWithIv(buffer, ctx->iv); XorWithIv(outbuffer, ctx->iv);
memcpy(ctx->iv, next_iv, 16); memcpy(ctx->iv, next_iv, 16);
buffer += 16; outbuffer += 16;
len -= 16; len -= 16;
} }
@ -196,14 +207,14 @@ _pdfioCryptoAESDecrypt(
uint8_t temp[16]; // Temporary buffer uint8_t temp[16]; // Temporary buffer
memset(temp, 16 - len, sizeof(temp)); memset(temp, 16 - len, sizeof(temp));
memcpy(temp, buffer, len); memcpy(temp, outbuffer, len);
memcpy(next_iv, temp, 16); memcpy(next_iv, temp, 16);
InvCipher((state_t *)temp, ctx); InvCipher((state_t *)temp, ctx);
XorWithIv(temp, ctx->iv); XorWithIv(temp, ctx->iv);
memcpy(ctx->iv, next_iv, 16); memcpy(ctx->iv, next_iv, 16);
memcpy(buffer, temp, len); memcpy(outbuffer, temp, len);
} }
} }
@ -211,23 +222,34 @@ _pdfioCryptoAESDecrypt(
// //
// '_pdfioCryptoAESEncrypt()' - Encrypt a block of bytes with AES. // '_pdfioCryptoAESEncrypt()' - Encrypt a block of bytes with AES.
// //
// "inbuffer" and "outbuffer" can point to the same memory.
//
void void
_pdfioCryptoAESEncrypt( _pdfioCryptoAESEncrypt(
_pdfio_aes_t *ctx, // I - AES context _pdfio_aes_t *ctx, // I - AES context
uint8_t *buffer, // I - Buffer uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Number of bytes to decrypt size_t len) // I - Number of bytes to decrypt
{ {
uint8_t *iv = ctx->iv; // Current IV for CBC uint8_t *iv = ctx->iv; // Current IV for CBC
uint8_t temp[16]; // Temporary buffer uint8_t temp[16]; // Temporary buffer
if (inbuffer != outbuffer)
{
// Not the most efficient, but we can optimize later - the sample AES code
// manipulates the data directly in memory and doesn't support separate
// input and output buffers...
memcpy(outbuffer, inbuffer, len);
}
while (len > 15) while (len > 15)
{ {
XorWithIv(buffer, iv); XorWithIv(outbuffer, iv);
Cipher((state_t*)buffer, ctx); Cipher((state_t*)outbuffer, ctx);
iv = buffer; iv = outbuffer;
buffer += 16; outbuffer += 16;
len -= 16; len -= 16;
} }
@ -235,13 +257,13 @@ _pdfioCryptoAESEncrypt(
{ {
// Pad the final buffer with (16 - len)... // Pad the final buffer with (16 - len)...
memset(temp, 16 - len, sizeof(temp)); memset(temp, 16 - len, sizeof(temp));
memcpy(temp, buffer, len); memcpy(temp, outbuffer, len);
XorWithIv(temp, iv); XorWithIv(temp, iv);
Cipher((state_t*)temp, ctx); Cipher((state_t*)temp, ctx);
iv = temp; iv = temp;
memcpy(buffer, temp, len); memcpy(outbuffer, temp, len);
} }
/* store Iv in ctx for next call */ /* store Iv in ctx for next call */

View File

@ -1159,7 +1159,7 @@ pdfioFileSetPermissions(
encrypt_key[j] = (uint8_t)(digest[j] ^ i); encrypt_key[j] = (uint8_t)(digest[j] ^ i);
_pdfioCryptoRC4Init(&rc4, encrypt_key, sizeof(encrypt_key)); _pdfioCryptoRC4Init(&rc4, encrypt_key, sizeof(encrypt_key));
_pdfioCryptoRC4Crypt(&rc4, pdf->owner_key, sizeof(pdf->owner_key)); _pdfioCryptoRC4Crypt(&rc4, pdf->owner_key, pdf->owner_key, sizeof(pdf->owner_key));
} }
// Generate the encryption key // Generate the encryption key
@ -1204,7 +1204,7 @@ pdfioFileSetPermissions(
digest[j] = (uint8_t)(pdf->encryption_key[j] ^ i); digest[j] = (uint8_t)(pdf->encryption_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, digest, 16); _pdfioCryptoRC4Init(&rc4, digest, 16);
_pdfioCryptoRC4Crypt(&rc4, pdf->user_key, sizeof(pdf->user_key)); _pdfioCryptoRC4Crypt(&rc4, pdf->user_key, pdf->user_key, sizeof(pdf->user_key));
} }
// Save everything in the dictionary... // Save everything in the dictionary...

View File

@ -175,7 +175,6 @@ typedef struct _pdfio_value_s // Value structure
} value; // Value union } value; // Value union
} _pdfio_value_t; } _pdfio_value_t;
typedef struct _pdfio_aes_s // AES encryption state typedef struct _pdfio_aes_s // AES encryption state
{ {
size_t round_size; // Size of round key size_t round_size; // Size of round key
@ -212,7 +211,7 @@ typedef union _pdfio_crypto_ctx_u // Cryptographic contexts
_pdfio_aes_t aes; // AES-128/256 context _pdfio_aes_t aes; // AES-128/256 context
_pdfio_rc4_t rc4; // RC4-40/128 context _pdfio_rc4_t rc4; // RC4-40/128 context
} _pdfio_crypto_ctx_t; } _pdfio_crypto_ctx_t;
typedef void (*_pdfio_crypto_cb_t)(_pdfio_crypto_ctx_t *ctx, uint8_t *buffer, size_t len); typedef void (*_pdfio_crypto_cb_t)(_pdfio_crypto_ctx_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len);
struct _pdfio_array_s struct _pdfio_array_s
{ {
@ -343,8 +342,8 @@ extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _PD
extern bool _pdfioArrayWrite(pdfio_array_t *a) _PDFIO_INTERNAL; extern bool _pdfioArrayWrite(pdfio_array_t *a) _PDFIO_INTERNAL;
extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL; extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL;
extern void _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *buffer, size_t len) _PDFIO_INTERNAL; extern void _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern void _pdfioCryptoAESEncrypt(_pdfio_aes_t *ctx, uint8_t *buffer, size_t len) _PDFIO_INTERNAL; extern void _pdfioCryptoAESEncrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern void _pdfioCryptoMakeRandom(uint8_t *buffer, size_t bytes) _PDFIO_INTERNAL; extern void _pdfioCryptoMakeRandom(uint8_t *buffer, size_t bytes) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeReader(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL; extern _pdfio_crypto_cb_t _pdfioCryptoMakeReader(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeWriter(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL; extern _pdfio_crypto_cb_t _pdfioCryptoMakeWriter(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
@ -352,7 +351,7 @@ extern void _pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_
extern void _pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16]) _PDFIO_INTERNAL; extern void _pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16]) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Init(_pdfio_md5_t *pms) _PDFIO_INTERNAL; extern void _pdfioCryptoMD5Init(_pdfio_md5_t *pms) _PDFIO_INTERNAL;
extern void _pdfioCryptoRC4Init(_pdfio_rc4_t *ctx, const uint8_t *key, size_t keylen) _PDFIO_INTERNAL; extern void _pdfioCryptoRC4Init(_pdfio_rc4_t *ctx, const uint8_t *key, size_t keylen) _PDFIO_INTERNAL;
extern void _pdfioCryptoRC4Crypt(_pdfio_rc4_t *ctx, uint8_t *buffer, size_t len) _PDFIO_INTERNAL; extern void _pdfioCryptoRC4Crypt(_pdfio_rc4_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Append(_pdfio_sha256_t *, const uint8_t *bytes, size_t bytecount) _PDFIO_INTERNAL; extern void _pdfioCryptoSHA256Append(_pdfio_sha256_t *, const uint8_t *bytes, size_t bytecount) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL; extern void _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _PDFIO_INTERNAL; extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _PDFIO_INTERNAL;

View File

@ -66,11 +66,14 @@ _pdfioCryptoRC4Init(
// //
// '_pdfioCryptoRC4Crypt()' - De/encrypt the given buffer. // '_pdfioCryptoRC4Crypt()' - De/encrypt the given buffer.
// //
// "inbuffer" and "outbuffer" can point to the same memory.
//
void void
_pdfioCryptoRC4Crypt( _pdfioCryptoRC4Crypt(
_pdfio_rc4_t *ctx, // I - Context _pdfio_rc4_t *ctx, // I - Context
uint8_t *buffer, // I - Buffer uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Size of buffers size_t len) // I - Size of buffers
{ {
uint8_t tmp, // Swap variable uint8_t tmp, // Swap variable
@ -97,7 +100,7 @@ _pdfioCryptoRC4Crypt(
t = ctx->sbox[i] + ctx->sbox[j]; t = ctx->sbox[i] + ctx->sbox[j];
// Encrypt using the S box... // Encrypt using the S box...
*buffer++ ^= ctx->sbox[t]; *outbuffer++ = *inbuffer++ ^ ctx->sbox[t];
len --; len --;
} }

View File

@ -61,6 +61,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
goto done; goto done;
} }
if (st->crypto_cb)
{
// Encrypt it first...
(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out);
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out)) if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
{ {
ret = false; ret = false;
@ -74,6 +80,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer)) if (st->flate.avail_out < (uInt)sizeof(st->cbuffer))
{ {
// Write any residuals... // Write any residuals...
if (st->crypto_cb)
{
// Encrypt it first...
(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out);
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out)) if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
{ {
ret = false; ret = false;
@ -160,6 +172,22 @@ _pdfioStreamCreate(
st->length_obj = length_obj; st->length_obj = length_obj;
st->filter = compression; st->filter = compression;
if (obj->pdf->encryption)
{
uint8_t iv[64]; // Initialization vector
size_t ivlen = sizeof(iv); // Length of initialization vector, if any
if ((st->crypto_cb = _pdfioCryptoMakeWriter(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
{
// TODO: Add error message?
free(st);
return (NULL);
}
if (ivlen > 0)
_pdfioFileWrite(st->pdf, iv, ivlen);
}
if (compression == PDFIO_FILTER_FLATE) if (compression == PDFIO_FILTER_FLATE)
{ {
// Flate compression // Flate compression
@ -592,7 +620,7 @@ pdfioStreamPrintf(
// //
// '()' - Write a single character to a stream. // 'pdfioStreamPutChar()' - Write a single character to a stream.
// //
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
@ -722,8 +750,35 @@ pdfioStreamWrite(
// Write it... // Write it...
if (st->filter == PDFIO_FILTER_NONE) if (st->filter == PDFIO_FILTER_NONE)
{ {
// No filtering so just write it... // No filtering...
return (_pdfioFileWrite(st->pdf, buffer, bytes)); if (st->crypto_cb)
{
// Encrypt data before writing...
unsigned char temp[8192]; // Temporary buffer
size_t cbytes; // Current bytes
bufptr = (const unsigned char *)buffer;
while (bytes > 0)
{
if ((cbytes = bytes) > sizeof(temp))
cbytes = sizeof(temp);
(st->crypto_cb)(&st->crypto_ctx, temp, bufptr, cbytes);
if (!_pdfioFileWrite(st->pdf, temp, cbytes))
return (false);
bytes -= cbytes;
bufptr += cbytes;
}
return (true);
}
else
{
// Write unencrypted...
return (_pdfioFileWrite(st->pdf, buffer, bytes));
}
} }
pbline = st->pbsize - 1; pbline = st->pbsize - 1;
@ -1098,6 +1153,12 @@ stream_write(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_out < (sizeof(st->cbuffer) / 8)) if (st->flate.avail_out < (sizeof(st->cbuffer) / 8))
{ {
// Flush the compression buffer... // Flush the compression buffer...
if (st->crypto_cb)
{
// Encrypt it first...
(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out);
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out)) if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
return (false); return (false);

View File

@ -99,7 +99,7 @@ enum pdfio_permission_e // PDF permission bits
PDFIO_PERMISSION_PRINT_HIGH = 0x0800, // PDF allows high quality printing PDFIO_PERMISSION_PRINT_HIGH = 0x0800, // PDF allows high quality printing
PDFIO_PERMISSION_ALL = ~0 // All permissions PDFIO_PERMISSION_ALL = ~0 // All permissions
}; };
typedef unsigned pdfio_permission_t; // PDF permission bitfield typedef int pdfio_permission_t; // PDF permission bitfield
typedef struct pdfio_rect_s // PDF rectangle typedef struct pdfio_rect_s // PDF rectangle
{ {
double x1; // Lower-left X coordinate double x1; // Lower-left X coordinate

View File

@ -763,7 +763,7 @@ do_unit_tests(void)
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
return (1); return (1);
// Create a new PDF file... // Stream a new PDF file...
if ((outfd = open("testpdfio-out2.pdf", O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0) if ((outfd = open("testpdfio-out2.pdf", O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0)
{ {
perror("Unable to open \"testpdfio-out2.pdf\""); perror("Unable to open \"testpdfio-out2.pdf\"");
@ -784,6 +784,44 @@ do_unit_tests(void)
if (read_unit_file("testpdfio-out2.pdf", num_pages, first_image, true)) if (read_unit_file("testpdfio-out2.pdf", num_pages, first_image, true))
return (1); return (1);
// Create new encrypted PDF files...
fputs("pdfioFileCreate(\"testpdfio-rc4.pdf\", ...): ", stdout);
if ((outpdf = pdfioFileCreate("testpdfio-rc4.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioFileSetPermissions(all, RC4-128, no passwords): ", stdout);
if (pdfioFileSetPermissions(outpdf, PDFIO_PERMISSION_ALL, PDFIO_ENCRYPTION_RC4_128, NULL, NULL))
puts("PASS");
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
// if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
// return (1);
fputs("pdfioFileCreate(\"testpdfio-aes.pdf\", ...): ", stdout);
if ((outpdf = pdfioFileCreate("testpdfio-aes.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioFileSetPermissions(no-print, AES-128, passwords='owner' and 'user'): ", stdout);
if (pdfioFileSetPermissions(outpdf, PDFIO_PERMISSION_ALL ^ PDFIO_PERMISSION_PRINT, PDFIO_ENCRYPTION_AES_128, "owner", "user"))
puts("PASS");
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
// if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
// return (1);
return (0); return (0);
} }