2021-04-01 20:20:54 +00:00
/**
* Author......: See docs/credits.txt
* License.....: MIT
*/
2023-03-07 22:38:09 +00:00
// https://web.archive.org/web/20220306152229/https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
2021-04-09 22:07:00 +00:00
2021-04-01 20:20:54 +00:00
# ifdef KERNEL_STATIC
2022-02-07 08:57:08 +00:00
# include M2S ( INCLUDE_PATH/inc_vendor.h )
# include M2S ( INCLUDE_PATH/inc_types.h )
# include M2S ( INCLUDE_PATH/inc_platform.cl )
# include M2S ( INCLUDE_PATH/inc_common.cl )
# include M2S ( INCLUDE_PATH/inc_hash_md5.cl )
# include M2S ( INCLUDE_PATH/inc_cipher_rc4.cl )
2021-04-01 20:20:54 +00:00
# endif
2022-02-07 12:31:22 +00:00
# define COMPARE_S M2S ( INCLUDE_PATH/inc_comp_single.cl )
# define COMPARE_M M2S ( INCLUDE_PATH/inc_comp_multi.cl )
2021-04-01 20:20:54 +00:00
typedef struct pdf
{
2021-08-20 08:16:14 +00:00
int V ;
int R ;
int P ;
2021-04-01 20:20:54 +00:00
2021-08-20 08:16:14 +00:00
int enc_md ;
2021-04-01 20:20:54 +00:00
2021-08-20 08:16:14 +00:00
u32 id_buf[8] ;
u32 u_buf[32] ;
u32 o_buf[32] ;
u32 u_pass_buf[8] ;
2021-04-01 20:20:54 +00:00
2021-08-20 08:16:14 +00:00
int id_len ;
int o_len ;
int u_len ;
int u_pass_len ;
2021-04-01 20:20:54 +00:00
2021-08-20 08:16:14 +00:00
u32 rc4key[2] ;
u32 rc4data[2] ;
2021-04-01 20:20:54 +00:00
2023-04-11 19:17:25 +00:00
int P_minus ;
2021-04-01 20:20:54 +00:00
} pdf_t ;
typedef struct pdf14_tmp
{
u32 digest[4] ;
2023-03-07 22:38:09 +00:00
u32 out[8] ;
2021-04-01 20:20:54 +00:00
} pdf14_tmp_t ;
KERNEL_FQ void m25400_init ( KERN_ATTR_TMPS_ESALT ( pdf14_tmp_t, pdf_t ) )
{
/**
* base
*/
const u64 gid = get_global_id ( 0 ) ;
//const u64 lid = get_local_id ( 0 ) ;
2022-01-04 21:57:26 +00:00
if ( gid >= GID_CNT ) return ;
2021-04-01 20:20:54 +00:00
u32 w0[4] ;
w0[0] = pws[gid].i[ 0] ;
w0[1] = pws[gid].i[ 1] ;
w0[2] = pws[gid].i[ 2] ;
w0[3] = pws[gid].i[ 3] ;
u32 w1[4] ;
w1[0] = pws[gid].i[ 4] ;
w1[1] = pws[gid].i[ 5] ;
w1[2] = pws[gid].i[ 6] ;
w1[3] = pws[gid].i[ 7] ;
const u32 pw_len = pws[gid].pw_len ;
2021-05-31 06:28:13 +00:00
const u32 padding[8] =
{
0x5e4ebf28,
0x418a754e,
0x564e0064,
0x0801faff,
0xb6002e2e,
0x803e68d0,
0xfea90c2f,
0x7a695364
} ;
2021-04-01 20:20:54 +00:00
/**
* shared
*/
2022-01-04 17:07:18 +00:00
u32 P = esalt_bufs[DIGESTS_OFFSET_HOST].P ; // TODO this is never used, but should be according according to "Algorithm 3.2 Computing an encryption key" line 4.
2021-04-01 20:20:54 +00:00
2021-08-20 08:16:14 +00:00
u32 id_buf[12] ; // TODO this is never used, but should be according according to "Algorithm 3.2 Computing an encryption key" line 5.
2021-04-01 20:20:54 +00:00
2022-01-04 17:07:18 +00:00
id_buf[ 0] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[0] ;
id_buf[ 1] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[1] ;
id_buf[ 2] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[2] ;
id_buf[ 3] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[3] ;
2021-04-01 20:20:54 +00:00
2022-01-04 17:07:18 +00:00
id_buf[ 4] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[4] ;
id_buf[ 5] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[5] ;
id_buf[ 6] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[6] ;
id_buf[ 7] = esalt_bufs[DIGESTS_OFFSET_HOST].id_buf[7] ;
2021-04-01 20:20:54 +00:00
id_buf[ 8] = 0 ;
id_buf[ 9] = 0 ;
id_buf[10] = 0 ;
id_buf[11] = 0 ;
u32 rc4data[2] ;
rc4data[0] = padding[0] ;
rc4data[1] = padding[1] ;
/**
* main init
*/
u32 w0_t[4] ;
u32 w1_t[4] ;
u32 w2_t[4] ;
u32 w3_t[4] ;
// max length supported by pdf11 is 32
w0_t[0] = padding[0] ;
w0_t[1] = padding[1] ;
w0_t[2] = padding[2] ;
w0_t[3] = padding[3] ;
w1_t[0] = padding[4] ;
w1_t[1] = padding[5] ;
w1_t[2] = padding[6] ;
w1_t[3] = padding[7] ;
w2_t[0] = 0 ;
w2_t[1] = 0 ;
w2_t[2] = 0 ;
w2_t[3] = 0 ;
w3_t[0] = 0 ;
w3_t[1] = 0 ;
w3_t[2] = 0 ;
w3_t[3] = 0 ;
switch_buffer_by_offset_le ( w0_t, w1_t, w2_t, w3_t, pw_len ) ;
// add password
// truncate at 32 is wanted, not a bug!
2021-04-09 22:07:00 +00:00
// add padding
2021-04-01 20:20:54 +00:00
w0_t[0] | = w0[0] ;
w0_t[1] | = w0[1] ;
w0_t[2] | = w0[2] ;
w0_t[3] | = w0[3] ;
w1_t[0] | = w1[0] ;
w1_t[1] | = w1[1] ;
w1_t[2] | = w1[2] ;
w1_t[3] | = w1[3] ;
w2_t[0] = 0x80 ;
w2_t[1] = 0 ;
w2_t[2] = 0 ;
w2_t[3] = 0 ;
w3_t[0] = 0 ;
w3_t[1] = 0 ;
w3_t[2] = 32 * 8 ;
w3_t[3] = 0 ;
u32 digest[4] ;
digest[0] = MD5M_A ;
digest[1] = MD5M_B ;
digest[2] = MD5M_C ;
digest[3] = MD5M_D ;
md5_transform ( w0_t, w1_t, w2_t, w3_t, digest ) ;
tmps[gid].digest[0] = digest[0] ;
tmps[gid].digest[1] = digest[1] ;
tmps[gid].digest[2] = digest[2] ;
tmps[gid].digest[3] = digest[3] ;
tmps[gid].out[0] = rc4data[0] ;
tmps[gid].out[1] = rc4data[1] ;
tmps[gid].out[2] = 0 ;
tmps[gid].out[3] = 0 ;
2023-03-07 22:38:09 +00:00
tmps[gid].out[4] = 0 ; // we only need the size of out for the plaintext check
tmps[gid].out[5] = 0 ; // we only need the size of out for the plaintext check
tmps[gid].out[6] = 0 ; // we only need the size of out for the plaintext check
tmps[gid].out[7] = 0 ; // we only need the size of out for the plaintext check
2021-04-01 20:20:54 +00:00
}
KERNEL_FQ void m25400_loop ( KERN_ATTR_TMPS_ESALT ( pdf14_tmp_t, pdf_t ) )
{
/**
* base
*/
const u64 gid = get_global_id ( 0 ) ;
const u64 lid = get_local_id ( 0 ) ;
2022-01-04 21:57:26 +00:00
if ( gid >= GID_CNT ) return ;
2021-04-01 20:20:54 +00:00
/**
* shared
*/
2021-05-31 06:28:13 +00:00
LOCAL_VK u32 S[64 * FIXED_LOCAL_SIZE] ;
2021-04-01 20:20:54 +00:00
/**
* loop
*/
u32 digest[4] ;
digest[0] = tmps[gid].digest[0] ;
digest[1] = tmps[gid].digest[1] ;
digest[2] = tmps[gid].digest[2] ;
digest[3] = tmps[gid].digest[3] ;
2022-01-04 17:07:18 +00:00
for ( u32 i = 0 , j = LOOP_POS ; i < LOOP_CNT; i++, j++)
2021-04-01 20:20:54 +00:00
{
if ( j < 50 )
{
2021-08-20 08:16:14 +00:00
// the owner-key is generated by iterating a md5 hash 50 times
// see: "Algorithm 3.3 Computing the encryption dictionary’ s O (owner password) value"
2021-04-01 20:20:54 +00:00
u32 w0_t[4] ;
u32 w1_t[4] ;
u32 w2_t[4] ;
u32 w3_t[4] ;
w0_t[0] = digest[0] ;
w0_t[1] = digest[1] ;
w0_t[2] = digest[2] ;
w0_t[3] = digest[3] ;
w1_t[0] = 0x80 ;
w1_t[1] = 0 ;
w1_t[2] = 0 ;
w1_t[3] = 0 ;
w2_t[0] = 0 ;
w2_t[1] = 0 ;
w2_t[2] = 0 ;
w2_t[3] = 0 ;
w3_t[0] = 0 ;
w3_t[1] = 0 ;
w3_t[2] = 16 * 8 ;
w3_t[3] = 0 ;
digest[0] = MD5M_A ;
digest[1] = MD5M_B ;
digest[2] = MD5M_C ;
digest[3] = MD5M_D ;
md5_transform ( w0_t, w1_t, w2_t, w3_t, digest ) ;
}
2021-08-20 08:16:14 +00:00
}
2021-04-01 20:20:54 +00:00
2023-03-07 22:38:09 +00:00
u32 out[4] ;
2022-01-04 17:07:18 +00:00
out[0] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[0] ; // store original o-value in out (scratchpad)
out[1] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[1] ;
out[2] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[2] ;
out[3] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[3] ;
2023-03-07 22:38:09 +00:00
u32 out2[4] ;
out2[0] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[4] ; // store original o-value in out (scratchpad)
out2[1] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[5] ;
out2[2] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[6] ;
out2[3] = esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[7] ;
2021-08-20 08:16:14 +00:00
u32 o_rc4_decryption_key[4] ;
o_rc4_decryption_key[0] = digest[0] ; // store the owner-key
o_rc4_decryption_key[1] = digest[1] ;
o_rc4_decryption_key[2] = digest[2] ;
o_rc4_decryption_key[3] = digest[3] ;
// we decrypt the o-value to obtain either the owner-password ( or user-password if no owner-password is set )
// see: "Algorithm 3.3 Computing the encryption dictionary’ s O (owner password) value" : "If there is no owner password, use the user password instead" .
u32 tmp[4] ;
2023-03-07 22:38:09 +00:00
u8 j ;
2021-08-20 08:16:14 +00:00
for ( u32 i = 19 ; i>0; i--)
{
// xor the iterator into the rc4 key
const u32 xv = i << 0
| i << 8
| i << 16
| i << 24 ;
tmp[0] = o_rc4_decryption_key[0] ^ xv ;
tmp[1] = o_rc4_decryption_key[1] ^ xv ;
tmp[2] = o_rc4_decryption_key[2] ^ xv ;
tmp[3] = o_rc4_decryption_key[3] ^ xv ;
2022-02-04 18:54:00 +00:00
rc4_init_128 ( S, tmp, lid ) ;
2023-03-07 22:38:09 +00:00
j = rc4_next_16 ( S, 0 , 0 , out, out, lid ) ;
rc4_next_16 ( S, 16 , j, out2, out2, lid ) ;
2021-04-01 20:20:54 +00:00
}
2022-02-04 18:54:00 +00:00
rc4_init_128 ( S, o_rc4_decryption_key, lid ) ;
2023-03-07 22:38:09 +00:00
j = rc4_next_16 ( S, 0 , 0 , out, out, lid ) ; // output of the rc4 decrypt of the o-value should be the padded user-password
2021-04-01 20:20:54 +00:00
tmps[gid].out[0] = out[0] ;
tmps[gid].out[1] = out[1] ;
tmps[gid].out[2] = out[2] ;
tmps[gid].out[3] = out[3] ;
2023-03-07 22:38:09 +00:00
rc4_next_16 ( S, 16 , j, out2, out2, lid ) ; // decrypt a second block of rc4 to improve plaintext check and limit false positives
tmps[gid].out[4] = out2[0] ;
tmps[gid].out[5] = out2[1] ;
tmps[gid].out[6] = out2[2] ;
tmps[gid].out[7] = out2[3] ;
2023-03-07 22:50:49 +00:00
tmps[gid].digest[0] = digest[0] ;
tmps[gid].digest[1] = digest[1] ;
tmps[gid].digest[2] = digest[2] ;
tmps[gid].digest[3] = digest[3] ;
2021-04-01 20:20:54 +00:00
}
KERNEL_FQ void m25400_comp ( KERN_ATTR_TMPS_ESALT ( pdf14_tmp_t, pdf_t ) )
{
2021-08-20 08:16:14 +00:00
const u32 digest[4] =
{
2022-01-04 17:07:18 +00:00
esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[0],
esalt_bufs[DIGESTS_OFFSET_HOST].o_buf[1],
2021-08-20 08:16:14 +00:00
0x0,// apparently only the first 16 bytes of the digest are used to look it up?
0x0 // apparently only the first 16 bytes of the digest are used to look it up?
} ;
const u32 padding[8] =
{
0x5e4ebf28,
0x418a754e,
0x564e0064,
0x0801faff,
0xb6002e2e,
0x803e68d0,
0xfea90c2f,
0x7a695364
} ;
2021-04-01 20:20:54 +00:00
/**
* modifier
*/
const u64 gid = get_global_id ( 0 ) ;
2022-01-04 21:57:26 +00:00
if ( gid >= GID_CNT ) return ;
2021-04-01 20:20:54 +00:00
const u64 lid = get_local_id ( 0 ) ;
# define il_pos 0
2021-08-20 08:16:14 +00:00
2023-03-07 22:38:09 +00:00
const u32 out[8] =
2021-08-20 08:16:14 +00:00
{
tmps[gid].out[0],
tmps[gid].out[1],
tmps[gid].out[2],
2023-03-07 22:38:09 +00:00
tmps[gid].out[3],
tmps[gid].out[4],
tmps[gid].out[5],
tmps[gid].out[6],
tmps[gid].out[7]
2021-08-20 08:16:14 +00:00
} ;
// the best comparison I can think of is checking each byte
// whether it 's a padding byte or ASCII, if so we 're good,
// if not, decryption was not successful
2021-10-08 15:38:54 +00:00
2021-08-20 08:16:14 +00:00
bool correct = true ;
2021-10-08 15:38:54 +00:00
2021-08-20 08:16:14 +00:00
int i_padding=0 ;
2021-10-08 15:38:54 +00:00
2023-03-07 22:38:09 +00:00
for ( int i = 0 ; i < 32; i++) // check all 32 bytes of the decrypted o-value, this including the padding.
2021-08-20 08:16:14 +00:00
{
// cast out buffer to byte such that we can do a byte per byte comparison
2022-02-04 18:54:00 +00:00
PRIVATE_AS const u32 *u32OutBufPtr = ( PRIVATE_AS u32 * ) out ;
PRIVATE_AS const u8 *u8OutBufPtr = ( PRIVATE_AS u8 * ) u32OutBufPtr ;
2021-08-20 08:16:14 +00:00
// cast padding buffer to byte such that we can do a byte per byte comparison
2022-02-04 18:54:00 +00:00
PRIVATE_AS const u32 *u32OutPadPtr = ( PRIVATE_AS u32 * ) padding ;
PRIVATE_AS const u8 *u8OutPadPtr = ( PRIVATE_AS u8 * ) u32OutPadPtr ;
2021-08-20 08:16:14 +00:00
// we don 't use the user-password in the attack now ( as we don 't need it ) ,
// however we could use it in the comparison of the decrypted o-value,
2023-03-07 22:38:09 +00:00
// yet it may make this attack a bit more fragile, as now we just check for padding and ASCII
2023-03-08 20:29:37 +00:00
if ( is_valid_printable_8 ( u8OutBufPtr[i] ) | |
2021-10-08 15:38:54 +00:00
( u8OutBufPtr[i] == u8OutPadPtr[i_padding] ) )
2021-08-20 08:16:14 +00:00
{
2021-10-08 15:38:54 +00:00
if ( u8OutBufPtr[i] == u8OutPadPtr[i_padding] )
{
2023-03-07 22:50:49 +00:00
//if ( ( gid == 0 ) && ( lid == 0 ) ) printf ( "correct padding byte[%d]=0x%02x==0x%02x\n" , i, u8OutBufPtr[i], u8OutPadPtr[i_padding] ) ;
2021-10-08 15:38:54 +00:00
i_padding = i_padding + 1 ;
2021-08-20 08:16:14 +00:00
}
else
{
2023-03-08 20:29:37 +00:00
if ( is_valid_printable_8 ( u8OutBufPtr[i] ) )
2021-10-08 15:38:54 +00:00
{
2023-03-07 22:50:49 +00:00
//if ( ( gid == 0 ) && ( lid == 0 ) ) printf ( "correct ASCII byte[%d]=0x%02x\n" , i, u8OutBufPtr[i] ) ;
2021-08-20 08:16:14 +00:00
}
}
}
else
{
2023-03-07 22:50:49 +00:00
//if ( ( gid == 0 ) && ( lid == 0 ) ) {
// printf ( "wrong byte[%d]=0x%02x\n" , i, u8OutBufPtr[i] ) ;
//
// printf ( "u8OutBufPtr=0x" ) ;
// for ( int j=0 ;j<32;j++) {
// printf ( "%02x" , u8OutBufPtr[j] ) ;
// }
// printf ( "\n" ) ;
//
// printf ( "u8OutPadPtr=0x" ) ;
// for ( int j=0 ;j<32;j++) {
// printf ( "%02x" , u8OutPadPtr[j] ) ;
// }
// printf ( "\n" ) ;
//}
2021-08-20 08:16:14 +00:00
correct = false ;
break ;
}
}
if ( correct )
{
2022-01-04 17:07:18 +00:00
int digest_pos = find_hash ( digest, DIGESTS_CNT, &digests_buf[DIGESTS_OFFSET_HOST] ) ;
2021-10-08 15:38:54 +00:00
2021-08-20 08:16:14 +00:00
if ( digest_pos != -1 )
{
2022-01-04 17:07:18 +00:00
const u32 final_hash_pos = DIGESTS_OFFSET_HOST + digest_pos ;
2021-08-20 08:16:14 +00:00
if ( hc_atomic_inc ( &hashes_shown[final_hash_pos] ) == 0 )
{
2022-01-04 17:07:18 +00:00
mark_hash ( plains_buf, d_return_buf, SALT_POS_HOST, DIGESTS_CNT, digest_pos, final_hash_pos, gid, il_pos, 0 , 0 ) ;
2021-08-20 08:16:14 +00:00
}
}
}
2021-04-01 20:20:54 +00:00
}