Save work on AES and RC4.

This commit is contained in:
Michael R Sweet 2021-10-04 21:13:01 -04:00
parent bb91fb4b13
commit 7fe093f3bd
No known key found for this signature in database
GPG Key ID: 999559A027815955
5 changed files with 992 additions and 0 deletions

View File

@ -43,10 +43,13 @@ PUBOBJS = \
pdfio-array.o \
pdfio-common.o \
pdfio-content.o \
pdfio-crypto.o \
pdfio-dict.o \
pdfio-file.o \
pdfio-md5.o \
pdfio-object.o \
pdfio-page.o \
pdfio-rc4.o \
pdfio-stream.o \
pdfio-string.o \
pdfio-token.o \

512
pdfio-crypto.c Normal file
View File

@ -0,0 +1,512 @@
//
// Cryptographic 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
// (<https://github.com/kokke/tiny-AES-c>)
//
//
// 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(uint8_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(uint8_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 ((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)
{
uint8_t round = 0;
// 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++;
}

338
pdfio-md5.c Normal file
View File

@ -0,0 +1,338 @@
//
// MD5 functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 1999 Aladdin Enterprises. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// L. Peter Deutsch
// ghost@aladdin.com
//
#include "pdfio-private.h"
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321.
It is derived directly from the text of the RFC and not from the
reference implementation.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#define T1 0xd76aa478
#define T2 0xe8c7b756
#define T3 0x242070db
#define T4 0xc1bdceee
#define T5 0xf57c0faf
#define T6 0x4787c62a
#define T7 0xa8304613
#define T8 0xfd469501
#define T9 0x698098d8
#define T10 0x8b44f7af
#define T11 0xffff5bb1
#define T12 0x895cd7be
#define T13 0x6b901122
#define T14 0xfd987193
#define T15 0xa679438e
#define T16 0x49b40821
#define T17 0xf61e2562
#define T18 0xc040b340
#define T19 0x265e5a51
#define T20 0xe9b6c7aa
#define T21 0xd62f105d
#define T22 0x02441453
#define T23 0xd8a1e681
#define T24 0xe7d3fbc8
#define T25 0x21e1cde6
#define T26 0xc33707d6
#define T27 0xf4d50d87
#define T28 0x455a14ed
#define T29 0xa9e3e905
#define T30 0xfcefa3f8
#define T31 0x676f02d9
#define T32 0x8d2a4c8a
#define T33 0xfffa3942
#define T34 0x8771f681
#define T35 0x6d9d6122
#define T36 0xfde5380c
#define T37 0xa4beea44
#define T38 0x4bdecfa9
#define T39 0xf6bb4b60
#define T40 0xbebfbc70
#define T41 0x289b7ec6
#define T42 0xeaa127fa
#define T43 0xd4ef3085
#define T44 0x04881d05
#define T45 0xd9d4d039
#define T46 0xe6db99e5
#define T47 0x1fa27cf8
#define T48 0xc4ac5665
#define T49 0xf4292244
#define T50 0x432aff97
#define T51 0xab9423a7
#define T52 0xfc93a039
#define T53 0x655b59c3
#define T54 0x8f0ccc92
#define T55 0xffeff47d
#define T56 0x85845dd1
#define T57 0x6fa87e4f
#define T58 0xfe2ce6e0
#define T59 0xa3014314
#define T60 0x4e0811a1
#define T61 0xf7537e82
#define T62 0xbd3af235
#define T63 0x2ad7d2bb
#define T64 0xeb86d391
static void
md5_process(_pdfio_md5_t *pms, const uint8_t *data /*[64]*/)
{
uint32_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
uint32_t t;
#ifndef ARCH_IS_BIG_ENDIAN
# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
#endif
#if ARCH_IS_BIG_ENDIAN
/*
* On big-endian machines, we must arrange the bytes in the right
* order. (This also works on machines of unknown byte order.)
*/
uint32_t X[16];
const uint8_t *xp = data;
int i;
for (i = 0; i < 16; ++i, xp += 4)
X[i] = xp[0] + (unsigned)(xp[1] << 8) + (unsigned)(xp[2] << 16) + (unsigned)(xp[3] << 24);
#else /* !ARCH_IS_BIG_ENDIAN */
/*
* On little-endian machines, we can process properly aligned data
* without copying it.
*/
uint32_t xbuf[16];
const uint32_t *X;
if (!((data - (const uint8_t *)0) & 3)) {
/* data are properly aligned */
X = (const uint32_t *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
X = xbuf;
}
#endif
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
void
_pdfioCryptoMD5Init(_pdfio_md5_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = 0xefcdab89;
pms->abcd[2] = 0x98badcfe;
pms->abcd[3] = 0x10325476;
}
void
_pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes)
{
const uint8_t *p = data;
size_t left = nbytes;
int offset = (pms->count[0] >> 3) & 63;
uint32_t nbits = (uint32_t)(nbytes << 3);
if (nbytes == 0)
return;
/* Update the message length. */
pms->count[1] += (unsigned)(nbytes >> 29);
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
}
void
_pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16])
{
static const uint8_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
uint8_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
_pdfioCryptoMD5Append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
_pdfioCryptoMD5Append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}

View File

@ -24,6 +24,7 @@
# include "pdfio.h"
# include <stdarg.h>
# include <stdint.h>
# include <string.h>
# include <errno.h>
# include <inttypes.h>
@ -174,6 +175,28 @@ 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
uint8_t round_key[240], // Round key
iv[16]; // Initialization vector
} _pdfio_aes_t;
typedef struct _pdfio_md5_s // MD5 hash state
{
uint32_t count[2]; // Message length in bits, lsw first
uint32_t abcd[4]; // Digest buffer
uint8_t buf[64]; // Accumulate block
} _pdfio_md5_t;
typedef struct _pdfio_rc4_s // RC4 encryption state
{
uint8_t sbox[256]; // S boxes for encryption
uint8_t i, j; // Current indices into S boxes
} _pdfio_rc4_t;
struct _pdfio_array_s
{
pdfio_file_t *pdf; // PDF file
@ -294,6 +317,15 @@ extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_IN
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _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 _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 _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;
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 _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;

107
pdfio-rc4.c Normal file
View File

@ -0,0 +1,107 @@
//
// RC4 functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
//
// Original code by Tim Martin
// Copyright © 1999 by Carnegie Mellon University, All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose and without fee is hereby granted,
// provided that the above copyright notice appear in all copies and that
// both that copyright notice and this permission notice appear in
// supporting documentation, and that the name of Carnegie Mellon
// University not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
// THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
// ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
// OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#include "pdfio-private.h"
//
// '_pdfioCryptoRC4Init()' - Initialize an RC4 context with the specified key.
//
void
_pdfioCryptoRC4Init(
_pdfio_rc4_t *ctx, // IO - Context
const uint8_t *key, // I - Key
size_t keylen) // I - Length of key
{
size_t i; // Looping var
uint8_t j, // S box counter
tmp; // Temporary variable
// Fill in linearly s0=0, s1=1, ...
for (i = 0; i < 256; i ++)
ctx->sbox[i] = (uint8_t)i;
for (i = 0, j = 0; i < 256; i ++)
{
// j = (j + Si + Ki) mod 256
j += ctx->sbox[i] + key[i % keylen];
// Swap Si and Sj...
tmp = ctx->sbox[i];
ctx->sbox[i] = ctx->sbox[j];
ctx->sbox[j] = tmp;
}
// Initialize counters to 0 and return...
ctx->i = 0;
ctx->j = 0;
}
//
// '_pdfioCryptoRC4Crypt()' - De/encrypt the given buffer.
//
void
_pdfioCryptoRC4Crypt(
_pdfio_rc4_t *ctx, // I - Context
uint8_t *buffer, // I - Buffer
size_t len) // I - Size of buffers
{
uint8_t tmp, // Swap variable
i, j, // Looping vars
t; // Current S box
// Loop through the entire buffer...
i = ctx->i;
j = ctx->j;
while (len > 0)
{
// Get the next S box indices...
i ++;
j += ctx->sbox[i];
// Swap Si and Sj...
tmp = ctx->sbox[i];
ctx->sbox[i] = ctx->sbox[j];
ctx->sbox[j] = tmp;
// Get the S box index for this byte...
t = ctx->sbox[i] + ctx->sbox[j];
// Encrypt using the S box...
*buffer++ ^= ctx->sbox[t];
len --;
}
// Copy current S box indices back to context...
ctx->i = i;
ctx->j = j;
}