diff --git a/Makefile b/Makefile index 87c71d1..8c34621 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ PUBHEADERS = \ pdfio.h \ pdfio-content.h PUBOBJS = \ + pdfio-aes.o \ pdfio-array.o \ pdfio-common.o \ pdfio-content.o \ diff --git a/pdfio-aes.c b/pdfio-aes.c new file mode 100644 index 0000000..6bd8348 --- /dev/null +++ b/pdfio-aes.c @@ -0,0 +1,512 @@ +// +// AES functions for PDFio. +// +// Copyright © 2021 by Michael R Sweet. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// +// AES code is adapted from the "tiny-AES-c" project +// () +// + +// +// Include necessary headers... +// + +#include "pdfio-private.h" + + +// +// Local types... +// + +typedef uint8_t state_t[4][4]; // 4x4 AES state table + + +// +// Local globals... +// + +static const uint8_t sbox[256] = // S-box lookup table +{ + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; +static const uint8_t rsbox[256] = // Reverse S-box lookup table +{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +}; + +// The round constant word array, Rcon[i], contains the values given by +// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +static const uint8_t Rcon[11] = // Round constants +{ + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 +}; + + +// +// Local functions... +// + +static void AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey); +static void SubBytes(state_t *state); +static void ShiftRows(state_t *state); +static uint8_t xtime(uint8_t x); +static void MixColumns(state_t *state); +static uint8_t Multiply(uint8_t x, uint8_t y); +static void InvMixColumns(state_t *state); +static void InvSubBytes(state_t *state); +static void InvShiftRows(state_t *state); +static void Cipher(state_t *state, const _pdfio_aes_t *ctx); +static void InvCipher(state_t *state, const _pdfio_aes_t *ctx); +static void XorWithIv(uint8_t *buf, const uint8_t *Iv); + + +// +// '_pdfioCryptoAESInit()' - Initialize an AES context. +// + +void +_pdfioCryptoAESInit( + _pdfio_aes_t *ctx, // I - AES context + const uint8_t *key, // I - Key + size_t keylen, // I - Length of key (must be 16 or 32) + const uint8_t *iv) // I - 16-byte initialization vector +{ + size_t i; // Looping var + uint8_t *rkptr0, // Previous round_key values + *rkptr, // Current round_key values + *rkend, // End of round_key values + tempa[4]; // Used for the column/row operations + size_t roundlen = keylen + 24; // Length of round_key + size_t nwords = keylen / 4; // Number of 32-bit words in key + + + // Clear context + memset(ctx, 0, sizeof(_pdfio_aes_t)); + + // The first round key is the key itself. + memcpy(ctx->round_key, key, keylen); + + // All other round keys are found from the previous round keys. + for (rkptr0 = ctx->round_key, rkptr = rkptr0 + keylen, rkend = rkptr + roundlen, i = nwords; rkptr < rkend; i ++) + { + if ((i % nwords) == 0) + { + // Shifts word left once - [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + tempa[0] = rkptr[-3]; + tempa[1] = rkptr[-2]; + tempa[2] = rkptr[-1]; + tempa[3] = rkptr[-4]; + + // Apply the S-box to each of the four bytes to produce an output word. + tempa[0] = sbox[tempa[0]]; + tempa[1] = sbox[tempa[1]]; + tempa[2] = sbox[tempa[2]]; + tempa[3] = sbox[tempa[3]]; + + tempa[0] = tempa[0] ^ Rcon[i / nwords]; + } + else if (keylen == 32 && (i % nwords) == 4) + { + // Apply the S-box to each of the four bytes to produce an output word. + tempa[0] = sbox[rkptr[-4]]; + tempa[1] = sbox[rkptr[-3]]; + tempa[2] = sbox[rkptr[-2]]; + tempa[3] = sbox[rkptr[-1]]; + } + else + { + tempa[0] = rkptr[-4]; + tempa[1] = rkptr[-3]; + tempa[2] = rkptr[-2]; + tempa[3] = rkptr[-1]; + } + + *rkptr++ = *rkptr0++ ^ tempa[0]; + *rkptr++ = *rkptr0++ ^ tempa[1]; + *rkptr++ = *rkptr0++ ^ tempa[2]; + *rkptr++ = *rkptr0++ ^ tempa[3]; + } + + // Copy the initialization vector... + if (iv) + memcpy(ctx->iv, iv, sizeof(ctx->iv)); +} + + +// +// '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES. +// + +void +_pdfioCryptoAESDecrypt( + _pdfio_aes_t *ctx, // I - AES context + uint8_t *buffer, // I - Buffer + size_t len) // I - Number of bytes to decrypt +{ + uint8_t next_iv[16]; // Next IV value + + + while (len > 15) + { + memcpy(next_iv, buffer, 16); + InvCipher((state_t *)buffer, ctx); + XorWithIv(buffer, ctx->iv); + memcpy(ctx->iv, next_iv, 16); + buffer += 16; + len -= 16; + } + + if (len > 0) + { + // Pad the final buffer with (16 - len)... + uint8_t temp[16]; // Temporary buffer + + memset(temp, 16 - len, sizeof(temp)); + memcpy(temp, buffer, 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); + } +} + + +// +// '_pdfioCryptoAESEncrypt()' - Encrypt a block of bytes with AES. +// + +void +_pdfioCryptoAESEncrypt( + _pdfio_aes_t *ctx, // I - AES context + uint8_t *buffer, // I - 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 + + + while (len > 15) + { + XorWithIv(buffer, iv); + Cipher((state_t*)buffer, ctx); + iv = buffer; + buffer += 16; + len -= 16; + } + + if (len > 0) + { + // Pad the final buffer with (16 - len)... + memset(temp, 16 - len, sizeof(temp)); + memcpy(temp, buffer, len); + + XorWithIv(temp, iv); + Cipher((state_t*)temp, ctx); + iv = temp; + + memcpy(buffer, temp, len); + } + + /* store Iv in ctx for next call */ + memcpy(ctx->iv, iv, 16); +} + + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +static void +AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey) +{ + unsigned i; // Looping var + uint8_t *sptr = (*state)[0]; // Pointer into state + + + for (RoundKey += round * 16, i = 16; i > 0; i --, sptr ++, RoundKey ++) + *sptr ^= *RoundKey; +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void +SubBytes(state_t *state) +{ + unsigned i; // Looping var + uint8_t *sptr = (*state)[0]; // Pointer into state + + + for (i = 16; i > 0; i --, sptr ++) + *sptr = sbox[*sptr]; +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +static void +ShiftRows(state_t *state) +{ + uint8_t *sptr = (*state)[0]; // Pointer into state + uint8_t temp; // Temporary value + + + // Rotate first row 1 columns to left + temp = sptr[1]; + sptr[1] = sptr[5]; + sptr[5] = sptr[9]; + sptr[9] = sptr[13]; + sptr[13] = temp; + + // Rotate second row 2 columns to left + temp = sptr[2]; + sptr[2] = sptr[10]; + sptr[10] = temp; + + temp = sptr[6]; + sptr[6] = sptr[14]; + sptr[14] = temp; + + // Rotate third row 3 columns to left + temp = sptr[3]; + sptr[3] = sptr[15]; + sptr[15] = sptr[11]; + sptr[11] = sptr[7]; + sptr[7] = temp; +} + + +static uint8_t +xtime(uint8_t x) +{ + return ((uint8_t)((x << 1) ^ ((x >> 7) * 0x1b))); +} + + +// MixColumns function mixes the columns of the state matrix +static void +MixColumns(state_t *state) +{ + unsigned i; // Looping var + uint8_t *sptr = (*state)[0]; // Pointer into state + uint8_t Tmp, Tm, t; // Temporary values + + for (i = 4; i > 0; i --, sptr += 4) + { + t = sptr[0]; + Tmp = sptr[0] ^ sptr[1] ^ sptr[2] ^ sptr[3]; + Tm = sptr[0] ^ sptr[1]; + Tm = xtime(Tm); + sptr[0] ^= Tm ^ Tmp; + + Tm = sptr[1] ^ sptr[2]; + Tm = xtime(Tm); + sptr[1] ^= Tm ^ Tmp; + + Tm = sptr[2] ^ sptr[3]; + Tm = xtime(Tm); + sptr[2] ^= Tm ^ Tmp; + + Tm = sptr[3] ^ t; + Tm = xtime(Tm); + sptr[3] ^= Tm ^ Tmp; + } +} + + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y>>1 & 1) * xtime(x)) ^ + ((y>>2 & 1) * xtime(xtime(x))) ^ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ +} + + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +static void +InvMixColumns(state_t *state) +{ + unsigned i; // Looping var + uint8_t *sptr = (*state)[0]; // Pointer into state + uint8_t a, b, c, d; // Temporary values + + + for (i = 4; i > 0; i --) + { + a = sptr[0]; + b = sptr[1]; + c = sptr[2]; + d = sptr[3]; + + *sptr++ = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + *sptr++ = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + *sptr++ = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + *sptr++ = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void +InvSubBytes(state_t *state) +{ + unsigned i; // Looping var + uint8_t *sptr = (*state)[0]; // Pointer into state + + + for (i = 16; i > 0; i --, sptr ++) + *sptr = rsbox[*sptr]; +} + + +static void +InvShiftRows(state_t *state) +{ + uint8_t *sptr = (*state)[0]; // Pointer into state + uint8_t temp; // Temporary value + + + // Rotate first row 1 columns to right + temp = sptr[13]; + sptr[13] = sptr[9]; + sptr[9] = sptr[5]; + sptr[5] = sptr[1]; + sptr[1] = temp; + + // Rotate second row 2 columns to right + temp = sptr[2]; + sptr[2] = sptr[10]; + sptr[10] = temp; + + temp = sptr[6]; + sptr[6] = sptr[14]; + sptr[14] = temp; + + // Rotate third row 3 columns to right + temp = sptr[3]; + sptr[3] = sptr[7]; + sptr[7] = sptr[11]; + sptr[11] = sptr[15]; + sptr[15] = temp; +} + + +// Cipher is the main function that encrypts the PlainText. +static void +Cipher(state_t *state, const _pdfio_aes_t *ctx) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, ctx->round_key); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without MixColumns() + for (round = 1; ; ++round) + { + SubBytes(state); + ShiftRows(state); + if (round == ctx->round_size) + break; + + MixColumns(state); + AddRoundKey(round, state, ctx->round_key); + } + // Add round key to last round + AddRoundKey(ctx->round_size, state, ctx->round_key); +} + + +static void +InvCipher(state_t *state, const _pdfio_aes_t *ctx) +{ + size_t round; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(ctx->round_size, state, ctx->round_key); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without InvMixColumn() + for (round = ctx->round_size - 1; ; round --) + { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, ctx->round_key); + + if (round == 0) + break; + + InvMixColumns(state); + } +} + + +static void +XorWithIv(uint8_t *buf, const uint8_t *Iv) +{ + // 16-byte block... + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; + *buf++ ^= *Iv++; +} diff --git a/pdfio-crypto.c b/pdfio-crypto.c index e0a9a06..1653311 100644 --- a/pdfio-crypto.c +++ b/pdfio-crypto.c @@ -1,14 +1,11 @@ // -// Cryptographic functions for PDFio. +// Cryptographic support functions for PDFio. // // Copyright © 2021 by Michael R Sweet. // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // -// AES code is adapted from the "tiny-AES-c" project -// () -// // // Include necessary headers... @@ -18,495 +15,50 @@ // -// Local types... +// '_pdfioCryptoMakeReader()' - Setup a cryptographic context and callback for reading. // -typedef uint8_t state_t[4][4]; // 4x4 AES state table - - -// -// Local globals... -// - -static const uint8_t sbox[256] = // S-box lookup table +_pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none + _pdfioCryptoMakeReader( + pdfio_file_t *pdf, // I - PDF file + pdfio_obj_t *obj, // I - PDF object + _pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context + uint8_t *iv, // I - Buffer for initialization vector + size_t *ivlen) // IO - Size of initialization vector { - //0 1 2 3 4 5 6 7 8 9 A B C D E F - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 -}; -static const uint8_t rsbox[256] = // Reverse S-box lookup table -{ - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, - 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, - 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, - 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, - 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, - 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, - 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, - 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, - 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, - 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, - 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d -}; - -// The round constant word array, Rcon[i], contains the values given by -// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) -static const uint8_t Rcon[11] = // Round constants -{ - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 -}; + (void)pdf; + (void)obj; + (void)ctx; + (void)iv; -// -// Local functions... -// + // No decryption... + *ivlen = 0; -static void AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey); -static void SubBytes(state_t *state); -static void ShiftRows(state_t *state); -static uint8_t xtime(uint8_t x); -static void MixColumns(state_t *state); -static uint8_t Multiply(uint8_t x, uint8_t y); -static void InvMixColumns(state_t *state); -static void InvSubBytes(state_t *state); -static void InvShiftRows(state_t *state); -static void Cipher(state_t *state, const _pdfio_aes_t *ctx); -static void InvCipher(state_t *state, const _pdfio_aes_t *ctx); -static void XorWithIv(uint8_t *buf, const uint8_t *Iv); - - -// -// '_pdfioCryptoAESInit()' - Initialize an AES context. -// - -void -_pdfioCryptoAESInit( - _pdfio_aes_t *ctx, // I - AES context - const uint8_t *key, // I - Key - size_t keylen, // I - Length of key (must be 16 or 32) - const uint8_t *iv) // I - 16-byte initialization vector -{ - size_t i; // Looping var - uint8_t *rkptr0, // Previous round_key values - *rkptr, // Current round_key values - *rkend, // End of round_key values - tempa[4]; // Used for the column/row operations - size_t roundlen = keylen + 24; // Length of round_key - size_t nwords = keylen / 4; // Number of 32-bit words in key - - - // Clear context - memset(ctx, 0, sizeof(_pdfio_aes_t)); - - // The first round key is the key itself. - memcpy(ctx->round_key, key, keylen); - - // All other round keys are found from the previous round keys. - for (rkptr0 = ctx->round_key, rkptr = rkptr0 + keylen, rkend = rkptr + roundlen, i = nwords; rkptr < rkend; i ++) - { - if ((i % nwords) == 0) - { - // Shifts word left once - [a0,a1,a2,a3] becomes [a1,a2,a3,a0] - tempa[0] = rkptr[-3]; - tempa[1] = rkptr[-2]; - tempa[2] = rkptr[-1]; - tempa[3] = rkptr[-4]; - - // Apply the S-box to each of the four bytes to produce an output word. - tempa[0] = sbox[tempa[0]]; - tempa[1] = sbox[tempa[1]]; - tempa[2] = sbox[tempa[2]]; - tempa[3] = sbox[tempa[3]]; - - tempa[0] = tempa[0] ^ Rcon[i / nwords]; - } - else if (keylen == 32 && (i % nwords) == 4) - { - // Apply the S-box to each of the four bytes to produce an output word. - tempa[0] = sbox[rkptr[-4]]; - tempa[1] = sbox[rkptr[-3]]; - tempa[2] = sbox[rkptr[-2]]; - tempa[3] = sbox[rkptr[-1]]; - } - else - { - tempa[0] = rkptr[-4]; - tempa[1] = rkptr[-3]; - tempa[2] = rkptr[-2]; - tempa[3] = rkptr[-1]; - } - - *rkptr++ = *rkptr0++ ^ tempa[0]; - *rkptr++ = *rkptr0++ ^ tempa[1]; - *rkptr++ = *rkptr0++ ^ tempa[2]; - *rkptr++ = *rkptr0++ ^ tempa[3]; - } - - // Copy the initialization vector... - if (iv) - memcpy(ctx->iv, iv, sizeof(ctx->iv)); + return (NULL); } // -// '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES. +// '_pdfioCryptoMakeWriter()' - Setup a cryptographic context and callback for writing. // -void -_pdfioCryptoAESDecrypt( - _pdfio_aes_t *ctx, // I - AES context - uint8_t *buffer, // I - Buffer - size_t len) // I - Number of bytes to decrypt +_pdfio_crypto_cb_t // O - Encryption callback or `NULL` for none + _pdfioCryptoMakeWriter( + pdfio_file_t *pdf, // I - PDF file + pdfio_obj_t *obj, // I - PDF object + _pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context + uint8_t *iv, // I - Buffer for initialization vector + size_t *ivlen) // IO - Size of initialization vector { - uint8_t next_iv[16]; // Next IV value + (void)pdf; + (void)obj; + (void)ctx; + (void)iv; - while (len > 15) - { - memcpy(next_iv, buffer, 16); - InvCipher((state_t *)buffer, ctx); - XorWithIv(buffer, ctx->iv); - memcpy(ctx->iv, next_iv, 16); - buffer += 16; - len -= 16; - } + // No encryption... + *ivlen = 0; - if (len > 0) - { - // Pad the final buffer with (16 - len)... - uint8_t temp[16]; // Temporary buffer - - memset(temp, 16 - len, sizeof(temp)); - memcpy(temp, buffer, 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); - } -} - - -// -// '_pdfioCryptoAESEncrypt()' - Encrypt a block of bytes with AES. -// - -void -_pdfioCryptoAESEncrypt( - _pdfio_aes_t *ctx, // I - AES context - uint8_t *buffer, // I - 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 - - - while (len > 15) - { - XorWithIv(buffer, iv); - Cipher((state_t*)buffer, ctx); - iv = buffer; - buffer += 16; - len -= 16; - } - - if (len > 0) - { - // Pad the final buffer with (16 - len)... - memset(temp, 16 - len, sizeof(temp)); - memcpy(temp, buffer, len); - - XorWithIv(temp, iv); - Cipher((state_t*)temp, ctx); - iv = temp; - - memcpy(buffer, temp, len); - } - - /* store Iv in ctx for next call */ - memcpy(ctx->iv, iv, 16); -} - - -// This function adds the round key to state. -// The round key is added to the state by an XOR function. -static void -AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey) -{ - unsigned i; // Looping var - uint8_t *sptr = (*state)[0]; // Pointer into state - - - for (RoundKey += round * 16, i = 16; i > 0; i --, sptr ++, RoundKey ++) - *sptr ^= *RoundKey; -} - - -// The SubBytes Function Substitutes the values in the -// state matrix with values in an S-box. -static void -SubBytes(state_t *state) -{ - unsigned i; // Looping var - uint8_t *sptr = (*state)[0]; // Pointer into state - - - for (i = 16; i > 0; i --, sptr ++) - *sptr = sbox[*sptr]; -} - -// The ShiftRows() function shifts the rows in the state to the left. -// Each row is shifted with different offset. -// Offset = Row number. So the first row is not shifted. -static void -ShiftRows(state_t *state) -{ - uint8_t *sptr = (*state)[0]; // Pointer into state - uint8_t temp; // Temporary value - - - // Rotate first row 1 columns to left - temp = sptr[1]; - sptr[1] = sptr[5]; - sptr[5] = sptr[9]; - sptr[9] = sptr[13]; - sptr[13] = temp; - - // Rotate second row 2 columns to left - temp = sptr[2]; - sptr[2] = sptr[10]; - sptr[10] = temp; - - temp = sptr[6]; - sptr[6] = sptr[14]; - sptr[14] = temp; - - // Rotate third row 3 columns to left - temp = sptr[3]; - sptr[3] = sptr[15]; - sptr[15] = sptr[11]; - sptr[11] = sptr[7]; - sptr[7] = temp; -} - - -static uint8_t -xtime(uint8_t x) -{ - return ((uint8_t)((x << 1) ^ ((x >> 7) * 0x1b))); -} - - -// MixColumns function mixes the columns of the state matrix -static void -MixColumns(state_t *state) -{ - unsigned i; // Looping var - uint8_t *sptr = (*state)[0]; // Pointer into state - uint8_t Tmp, Tm, t; // Temporary values - - for (i = 4; i > 0; i --, sptr += 4) - { - t = sptr[0]; - Tmp = sptr[0] ^ sptr[1] ^ sptr[2] ^ sptr[3]; - Tm = sptr[0] ^ sptr[1]; - Tm = xtime(Tm); - sptr[0] ^= Tm ^ Tmp; - - Tm = sptr[1] ^ sptr[2]; - Tm = xtime(Tm); - sptr[1] ^= Tm ^ Tmp; - - Tm = sptr[2] ^ sptr[3]; - Tm = xtime(Tm); - sptr[2] ^= Tm ^ Tmp; - - Tm = sptr[3] ^ t; - Tm = xtime(Tm); - sptr[3] ^= Tm ^ Tmp; - } -} - - -// Multiply is used to multiply numbers in the field GF(2^8) -// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary -// The compiler seems to be able to vectorize the operation better this way. -// See https://github.com/kokke/tiny-AES-c/pull/34 -static uint8_t Multiply(uint8_t x, uint8_t y) -{ - return (((y & 1) * x) ^ - ((y>>1 & 1) * xtime(x)) ^ - ((y>>2 & 1) * xtime(xtime(x))) ^ - ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ - ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ -} - - -// MixColumns function mixes the columns of the state matrix. -// The method used to multiply may be difficult to understand for the inexperienced. -// Please use the references to gain more information. -static void -InvMixColumns(state_t *state) -{ - unsigned i; // Looping var - uint8_t *sptr = (*state)[0]; // Pointer into state - uint8_t a, b, c, d; // Temporary values - - - for (i = 4; i > 0; i --) - { - a = sptr[0]; - b = sptr[1]; - c = sptr[2]; - d = sptr[3]; - - *sptr++ = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); - *sptr++ = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); - *sptr++ = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); - *sptr++ = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); - } -} - - -// The SubBytes Function Substitutes the values in the -// state matrix with values in an S-box. -static void -InvSubBytes(state_t *state) -{ - unsigned i; // Looping var - uint8_t *sptr = (*state)[0]; // Pointer into state - - - for (i = 16; i > 0; i --, sptr ++) - *sptr = rsbox[*sptr]; -} - - -static void -InvShiftRows(state_t *state) -{ - uint8_t *sptr = (*state)[0]; // Pointer into state - uint8_t temp; // Temporary value - - - // Rotate first row 1 columns to right - temp = sptr[13]; - sptr[13] = sptr[9]; - sptr[9] = sptr[5]; - sptr[5] = sptr[1]; - sptr[1] = temp; - - // Rotate second row 2 columns to right - temp = sptr[2]; - sptr[2] = sptr[10]; - sptr[10] = temp; - - temp = sptr[6]; - sptr[6] = sptr[14]; - sptr[14] = temp; - - // Rotate third row 3 columns to right - temp = sptr[3]; - sptr[3] = sptr[7]; - sptr[7] = sptr[11]; - sptr[11] = sptr[15]; - sptr[15] = temp; -} - - -// Cipher is the main function that encrypts the PlainText. -static void -Cipher(state_t *state, const _pdfio_aes_t *ctx) -{ - uint8_t round = 0; - - // Add the First round key to the state before starting the rounds. - AddRoundKey(0, state, ctx->round_key); - - // There will be Nr rounds. - // The first Nr-1 rounds are identical. - // These Nr rounds are executed in the loop below. - // Last one without MixColumns() - for (round = 1; ; ++round) - { - SubBytes(state); - ShiftRows(state); - if (round == ctx->round_size) - break; - - MixColumns(state); - AddRoundKey(round, state, ctx->round_key); - } - // Add round key to last round - AddRoundKey(ctx->round_size, state, ctx->round_key); -} - - -static void -InvCipher(state_t *state, const _pdfio_aes_t *ctx) -{ - size_t round; - - // Add the First round key to the state before starting the rounds. - AddRoundKey(ctx->round_size, state, ctx->round_key); - - // There will be Nr rounds. - // The first Nr-1 rounds are identical. - // These Nr rounds are executed in the loop below. - // Last one without InvMixColumn() - for (round = ctx->round_size - 1; ; round --) - { - InvShiftRows(state); - InvSubBytes(state); - AddRoundKey(round, state, ctx->round_key); - - if (round == 0) - break; - - InvMixColumns(state); - } -} - - -static void -XorWithIv(uint8_t *buf, const uint8_t *Iv) -{ - // 16-byte block... - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; - *buf++ ^= *Iv++; + return (NULL); } diff --git a/pdfio-private.h b/pdfio-private.h index df2e36b..df3b609 100644 --- a/pdfio-private.h +++ b/pdfio-private.h @@ -207,6 +207,13 @@ typedef struct _pdfio_sha265_s // SHA-256 hash state int Corrupted; // Cumulative corruption code } _pdfio_sha256_t; +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); + struct _pdfio_array_s { pdfio_file_t *pdf; // PDF file @@ -314,6 +321,8 @@ struct _pdfio_stream_s // Stream unsigned char cbuffer[4096], // Compressed data buffer *prbuffer, // Raw buffer (previous line), as needed *psbuffer; // PNG filter buffer, as needed + _pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any + _pdfio_crypto_ctx_t crypto_ctx; // Cryptographic context }; @@ -330,6 +339,8 @@ 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 _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 void _pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes) _PDFIO_INTERNAL; extern void _pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16]) _PDFIO_INTERNAL; extern void _pdfioCryptoMD5Init(_pdfio_md5_t *pms) _PDFIO_INTERNAL; diff --git a/pdfio.xcodeproj/project.pbxproj b/pdfio.xcodeproj/project.pbxproj index 80714fd..fa61cf4 100644 --- a/pdfio.xcodeproj/project.pbxproj +++ b/pdfio.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 279E1035267D043B00D3A349 /* ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = 279E1033267D043B00D3A349 /* ttf.h */; }; 279E1036267D043B00D3A349 /* ttf.c in Sources */ = {isa = PBXBuildFile; fileRef = 279E1034267D043B00D3A349 /* ttf.c */; }; 279E103B267D04E600D3A349 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 279E103A267D04E600D3A349 /* libz.tbd */; }; + 27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */ = {isa = PBXBuildFile; fileRef = 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */; }; 27ECBD8926419DAB0025312A /* libpdfio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 273440B0263D6FE200FBFD63 /* libpdfio.a */; }; 27F2F0602710BE92008ECD36 /* pdfio-md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05D2710BE92008ECD36 /* pdfio-md5.c */; }; 27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */; }; @@ -83,6 +84,7 @@ 279E1033267D043B00D3A349 /* ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttf.h; sourceTree = ""; }; 279E1034267D043B00D3A349 /* ttf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ttf.c; sourceTree = ""; }; 279E103A267D04E600D3A349 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-aes.c"; sourceTree = ""; }; 27F2F05D2710BE92008ECD36 /* pdfio-md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-md5.c"; sourceTree = ""; }; 27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-rc4.c"; sourceTree = ""; }; 27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-crypto.c"; sourceTree = ""; }; @@ -158,6 +160,7 @@ 279E1038267D045C00D3A349 /* Library */ = { isa = PBXGroup; children = ( + 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */, 273440BA263D727800FBFD63 /* pdfio-array.c */, 273440BB263D727800FBFD63 /* pdfio-common.c */, 271EA703265B2B1000ACDD39 /* pdfio-content.c */, @@ -300,6 +303,7 @@ 273440C7263D727800FBFD63 /* pdfio-object.c in Sources */, 27F2F0602710BE92008ECD36 /* pdfio-md5.c in Sources */, 273440C4263D727800FBFD63 /* pdfio-string.c in Sources */, + 27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */, 271EA705265B2B1000ACDD39 /* pdfio-content.c in Sources */, 27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */, 273440C6263D727800FBFD63 /* pdfio-common.c in Sources */,