mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2024-12-26 13:28:22 +01:00
Refactor crypto callback to have separate input/output pointers. Add initial writing support.
This commit is contained in:
parent
0caea44f32
commit
c24243a2bc
50
pdfio-aes.c
50
pdfio-aes.c
@ -170,23 +170,34 @@ _pdfioCryptoAESInit(
|
||||
//
|
||||
// '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES.
|
||||
//
|
||||
// "inbuffer" and "outbuffer" can point to the same memory.
|
||||
//
|
||||
|
||||
void
|
||||
_pdfioCryptoAESDecrypt(
|
||||
_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
|
||||
{
|
||||
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)
|
||||
{
|
||||
memcpy(next_iv, buffer, 16);
|
||||
InvCipher((state_t *)buffer, ctx);
|
||||
XorWithIv(buffer, ctx->iv);
|
||||
memcpy(next_iv, outbuffer, 16);
|
||||
InvCipher((state_t *)outbuffer, ctx);
|
||||
XorWithIv(outbuffer, ctx->iv);
|
||||
memcpy(ctx->iv, next_iv, 16);
|
||||
buffer += 16;
|
||||
outbuffer += 16;
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
@ -196,14 +207,14 @@ _pdfioCryptoAESDecrypt(
|
||||
uint8_t temp[16]; // Temporary buffer
|
||||
|
||||
memset(temp, 16 - len, sizeof(temp));
|
||||
memcpy(temp, buffer, len);
|
||||
memcpy(temp, outbuffer, len);
|
||||
|
||||
memcpy(next_iv, temp, 16);
|
||||
InvCipher((state_t *)temp, ctx);
|
||||
XorWithIv(temp, ctx->iv);
|
||||
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.
|
||||
//
|
||||
// "inbuffer" and "outbuffer" can point to the same memory.
|
||||
//
|
||||
|
||||
void
|
||||
_pdfioCryptoAESEncrypt(
|
||||
_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
|
||||
{
|
||||
uint8_t *iv = ctx->iv; // Current IV for CBC
|
||||
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)
|
||||
{
|
||||
XorWithIv(buffer, iv);
|
||||
Cipher((state_t*)buffer, ctx);
|
||||
iv = buffer;
|
||||
buffer += 16;
|
||||
XorWithIv(outbuffer, iv);
|
||||
Cipher((state_t*)outbuffer, ctx);
|
||||
iv = outbuffer;
|
||||
outbuffer += 16;
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
@ -235,13 +257,13 @@ _pdfioCryptoAESEncrypt(
|
||||
{
|
||||
// Pad the final buffer with (16 - len)...
|
||||
memset(temp, 16 - len, sizeof(temp));
|
||||
memcpy(temp, buffer, len);
|
||||
memcpy(temp, outbuffer, len);
|
||||
|
||||
XorWithIv(temp, iv);
|
||||
Cipher((state_t*)temp, ctx);
|
||||
iv = temp;
|
||||
|
||||
memcpy(buffer, temp, len);
|
||||
memcpy(outbuffer, temp, len);
|
||||
}
|
||||
|
||||
/* store Iv in ctx for next call */
|
||||
|
@ -1159,7 +1159,7 @@ pdfioFileSetPermissions(
|
||||
encrypt_key[j] = (uint8_t)(digest[j] ^ i);
|
||||
|
||||
_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
|
||||
@ -1204,7 +1204,7 @@ pdfioFileSetPermissions(
|
||||
digest[j] = (uint8_t)(pdf->encryption_key[j] ^ i);
|
||||
|
||||
_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...
|
||||
|
@ -175,7 +175,6 @@ typedef struct _pdfio_value_s // Value structure
|
||||
} value; // Value union
|
||||
} _pdfio_value_t;
|
||||
|
||||
|
||||
typedef struct _pdfio_aes_s // AES encryption state
|
||||
{
|
||||
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_rc4_t rc4; // RC4-40/128 context
|
||||
} _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
|
||||
{
|
||||
@ -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 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 _pdfioCryptoAESEncrypt(_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 *outbuffer, const uint8_t *inbuffer, size_t len) _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 _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 _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 _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 _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL;
|
||||
extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _PDFIO_INTERNAL;
|
||||
|
@ -66,11 +66,14 @@ _pdfioCryptoRC4Init(
|
||||
//
|
||||
// '_pdfioCryptoRC4Crypt()' - De/encrypt the given buffer.
|
||||
//
|
||||
// "inbuffer" and "outbuffer" can point to the same memory.
|
||||
//
|
||||
|
||||
void
|
||||
_pdfioCryptoRC4Crypt(
|
||||
_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
|
||||
{
|
||||
uint8_t tmp, // Swap variable
|
||||
@ -97,7 +100,7 @@ _pdfioCryptoRC4Crypt(
|
||||
t = ctx->sbox[i] + ctx->sbox[j];
|
||||
|
||||
// Encrypt using the S box...
|
||||
*buffer++ ^= ctx->sbox[t];
|
||||
*outbuffer++ = *inbuffer++ ^ ctx->sbox[t];
|
||||
len --;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
|
||||
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))
|
||||
{
|
||||
ret = false;
|
||||
@ -74,6 +80,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
|
||||
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer))
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
ret = false;
|
||||
@ -160,6 +172,22 @@ _pdfioStreamCreate(
|
||||
st->length_obj = length_obj;
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
@ -722,8 +750,35 @@ pdfioStreamWrite(
|
||||
// Write it...
|
||||
if (st->filter == PDFIO_FILTER_NONE)
|
||||
{
|
||||
// No filtering so just write it...
|
||||
return (_pdfioFileWrite(st->pdf, buffer, bytes));
|
||||
// No filtering...
|
||||
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;
|
||||
@ -1098,6 +1153,12 @@ stream_write(pdfio_stream_t *st, // I - Stream
|
||||
if (st->flate.avail_out < (sizeof(st->cbuffer) / 8))
|
||||
{
|
||||
// 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))
|
||||
return (false);
|
||||
|
||||
|
2
pdfio.h
2
pdfio.h
@ -99,7 +99,7 @@ enum pdfio_permission_e // PDF permission bits
|
||||
PDFIO_PERMISSION_PRINT_HIGH = 0x0800, // PDF allows high quality printing
|
||||
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
|
||||
{
|
||||
double x1; // Lower-left X coordinate
|
||||
|
40
testpdfio.c
40
testpdfio.c
@ -763,7 +763,7 @@ do_unit_tests(void)
|
||||
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
|
||||
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)
|
||||
{
|
||||
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))
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user