File Coverage

src/pdfmake_crypt.c
Criterion Covered Total %
statement 405 424 95.5
branch 157 192 81.7
condition n/a
subroutine n/a
pod n/a
total 562 616 91.2


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_crypt.c — PDF encryption/decryption implementation
3             *
4             * Standard security handler for R2-R6 per ISO 32000-2:2020 §7.6.
5             */
6              
7             #include "pdfmake_crypt.h"
8             #include "pdfmake_md5.h"
9             #include "pdfmake_sha2.h"
10             #include "pdfmake_rc4.h"
11             #include "pdfmake_aes.h"
12             #include
13             #include
14             #include
15              
16             /*============================================================================
17             * Constants
18             *==========================================================================*/
19              
20             /* Padding string per §7.6.4.3.2 */
21             static const uint8_t PADDING[32] = {
22             0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
23             0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
24             0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
25             0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
26             };
27              
28             /* Salt for AES in crypt filter */
29             static const uint8_t AES_SALT[] = { 0x73, 0x41, 0x6C, 0x54 }; /* "sAlT" */
30              
31             /*============================================================================
32             * Helper functions
33             *==========================================================================*/
34              
35             /* Pad password to 32 bytes */
36 117           static void pad_password(const char *passwd, uint8_t padded[32])
37             {
38 117 50         size_t len = passwd ? strlen(passwd) : 0;
39 117 50         if (len > 32) len = 32;
40            
41 117 100         if (len > 0) {
42 75           memcpy(padded, passwd, len);
43             }
44 117           memcpy(padded + len, PADDING, 32 - len);
45 117           }
46              
47             /* Simple random bytes generator */
48 42           static void random_bytes(uint8_t *buf, size_t len)
49             {
50             static uint32_t state = 0;
51             size_t i;
52              
53 42 100         if (state == 0) {
54 5           state = (uint32_t)time(NULL) ^ 0x5A5A5A5A;
55             }
56            
57 518 100         for (i = 0; i < len; i++) {
58 476           state ^= state << 13;
59 476           state ^= state >> 17;
60 476           state ^= state << 5;
61 476           buf[i] = (uint8_t)(state & 0xFF);
62             }
63 42           }
64              
65             /*============================================================================
66             * R2-R4 key derivation (Algorithm 2)
67             *==========================================================================*/
68              
69 63           static void compute_file_key_r2_r4(pdfmake_crypt_ctx_t *ctx,
70             const uint8_t *passwd_padded,
71             const uint8_t *O,
72             int32_t P,
73             const uint8_t *doc_id,
74             size_t doc_id_len,
75             int key_length,
76             int encrypt_metadata)
77             {
78             pdfmake_md5_ctx_t md5;
79             uint8_t digest[16];
80             uint8_t p_bytes[4];
81             int i;
82            
83             /* Step 1-4: MD5(passwd || O || P || ID || metadata_flag) */
84 63           pdfmake_md5_init(&md5);
85 63           pdfmake_md5_update(&md5, passwd_padded, 32);
86 63           pdfmake_md5_update(&md5, O, 32);
87            
88 63           p_bytes[0] = (uint8_t)(P & 0xFF);
89 63           p_bytes[1] = (uint8_t)((P >> 8) & 0xFF);
90 63           p_bytes[2] = (uint8_t)((P >> 16) & 0xFF);
91 63           p_bytes[3] = (uint8_t)((P >> 24) & 0xFF);
92 63           pdfmake_md5_update(&md5, p_bytes, 4);
93            
94 63           pdfmake_md5_update(&md5, doc_id, doc_id_len);
95            
96             /* R4 only: if not encrypting metadata, add 0xFFFFFFFF */
97 63 100         if (ctx->R >= 4 && !encrypt_metadata) {
    50          
98             uint8_t meta_flag[4];
99 0           meta_flag[0] = 0xFF;
100 0           meta_flag[1] = 0xFF;
101 0           meta_flag[2] = 0xFF;
102 0           meta_flag[3] = 0xFF;
103 0           pdfmake_md5_update(&md5, meta_flag, 4);
104             }
105            
106 63           pdfmake_md5_final(&md5, digest);
107            
108             /* R3+: iterate MD5 50 times */
109 63 100         if (ctx->R >= 3) {
110 1428 100         for (i = 0; i < 50; i++) {
111 1400           pdfmake_md5(digest, key_length, digest);
112             }
113             }
114            
115 63           memcpy(ctx->file_key, digest, key_length);
116 63           ctx->file_key_len = key_length;
117 63           }
118              
119             /*============================================================================
120             * R2-R4 O value computation (Algorithm 3)
121             *==========================================================================*/
122              
123 15           static void compute_O_value_r2_r4(pdfmake_crypt_ctx_t *ctx,
124             const char *owner_passwd,
125             const char *user_passwd,
126             int key_length)
127             {
128             uint8_t owner_padded[32], user_padded[32];
129             const char *pw;
130             uint8_t digest[16];
131             uint8_t temp_key[16];
132             int i;
133             int j;
134            
135             /* Use owner password or fall back to user password */
136 15 50         pw = (owner_passwd && *owner_passwd) ? owner_passwd : user_passwd;
    50          
137 15           pad_password(pw, owner_padded);
138 15           pad_password(user_passwd, user_padded);
139            
140             /* Step 1: MD5(owner_passwd) */
141 15           pdfmake_md5(owner_padded, 32, digest);
142            
143             /* R3+: iterate MD5 50 times */
144 15 100         if (ctx->R >= 3) {
145 612 100         for (i = 0; i < 50; i++) {
146 600           pdfmake_md5(digest, key_length, digest);
147             }
148             }
149            
150             /* Step 4: RC4 encrypt user password */
151 15           memcpy(ctx->O, user_padded, 32);
152 15           pdfmake_rc4(digest, key_length, ctx->O, 32);
153            
154             /* R3+: iterate with modified keys */
155 15 100         if (ctx->R >= 3) {
156 240 100         for (i = 1; i <= 19; i++) {
157 3876 100         for (j = 0; j < key_length; j++) {
158 3648           temp_key[j] = digest[j] ^ (uint8_t)i;
159             }
160 228           pdfmake_rc4(temp_key, key_length, ctx->O, 32);
161             }
162             }
163 15           }
164              
165             /*============================================================================
166             * R2-R4 U value computation (Algorithm 4/5)
167             *==========================================================================*/
168              
169 3           static void compute_U_value_r2(pdfmake_crypt_ctx_t *ctx)
170             {
171             /* Algorithm 4: RC4 encrypt padding string */
172 3           memcpy(ctx->U, PADDING, 32);
173 3           pdfmake_rc4(ctx->file_key, ctx->file_key_len, ctx->U, 32);
174 3           }
175              
176 12           static void compute_U_value_r3_r4(pdfmake_crypt_ctx_t *ctx)
177             {
178             /* Algorithm 5: MD5(padding || ID), then RC4 */
179             uint8_t digest[16];
180             pdfmake_md5_ctx_t md5;
181             uint8_t temp_key[16];
182             int i;
183             int j;
184            
185 12           pdfmake_md5_init(&md5);
186 12           pdfmake_md5_update(&md5, PADDING, 32);
187 12           pdfmake_md5_update(&md5, ctx->doc_id, ctx->doc_id_len);
188 12           pdfmake_md5_final(&md5, digest);
189            
190 12           pdfmake_rc4(ctx->file_key, ctx->file_key_len, digest, 16);
191            
192             /* Iterate with modified keys */
193 240 100         for (i = 1; i <= 19; i++) {
194 3876 100         for (j = 0; j < ctx->file_key_len; j++) {
195 3648           temp_key[j] = ctx->file_key[j] ^ (uint8_t)i;
196             }
197 228           pdfmake_rc4(temp_key, ctx->file_key_len, digest, 16);
198             }
199            
200 12           memcpy(ctx->U, digest, 16);
201             /* Arbitrary padding for remaining 16 bytes */
202 12           memset(ctx->U + 16, 0, 16);
203 12           }
204              
205             /*============================================================================
206             * R6 key derivation (ISO 32000-2 Algorithm 2.A/2.B)
207             *==========================================================================*/
208              
209             /* Intermediate hash function for R6 */
210 49           static void r6_intermediate_hash(const uint8_t *passwd, size_t passwd_len,
211             const uint8_t *data, size_t data_len,
212             const uint8_t *user_key, /* NULL for user validation */
213             uint8_t result[32])
214             {
215             /* K = SHA-256(passwd || data || user_key?) */
216             pdfmake_sha256_ctx_t sha;
217             uint8_t K[64];
218             int hash_len;
219             int round;
220             uint8_t last_e_byte;
221             size_t k1_single;
222             size_t k1_len;
223             uint8_t *K1;
224             pdfmake_aes_ctx_t aes;
225             uint8_t *E;
226             uint8_t iv[16];
227             uint8_t block[16];
228             int i;
229             int j;
230             size_t pos;
231             uint32_t sum;
232             int hash_type;
233              
234 49           pdfmake_sha256_init(&sha);
235 49           pdfmake_sha256_update(&sha, passwd, passwd_len);
236 49           pdfmake_sha256_update(&sha, data, data_len);
237 49 100         if (user_key) {
238 24           pdfmake_sha256_update(&sha, user_key, 48);
239             }
240 49           pdfmake_sha256_final(&sha, K);
241            
242 49           hash_len = 32; /* SHA-256 */
243              
244             /* Algorithm 2.B: at least 64 rounds, then keep going until
245             * (round - 32) >= E[last_byte], where round is 1-based. The loop
246             * body is identical each round; only the termination differs. */
247 49           round = 0;
248 49           last_e_byte = 0;
249 3416 100         while (round < 64 || (round - 32) < last_e_byte) {
    100          
250             /* Build K1: passwd || K || user_key? repeated 64 times */
251 3367 100         k1_single = passwd_len + hash_len + (user_key ? 48 : 0);
252 3367           k1_len = k1_single * 64;
253 3367           K1 = malloc(k1_len);
254 3367 50         if (!K1) return;
255              
256 218855 100         for (i = 0; i < 64; i++) {
257 215488           size_t off = i * k1_single;
258 215488           memcpy(K1 + off, passwd, passwd_len);
259 215488           memcpy(K1 + off + passwd_len, K, hash_len);
260 215488 100         if (user_key) {
261 104512           memcpy(K1 + off + passwd_len + hash_len, user_key, 48);
262             }
263             }
264              
265             /* E = AES-128-CBC(K[:16], K[16:32], K1) - no padding */
266 3367           pdfmake_aes_init(&aes, K, 16);
267              
268 3367           E = malloc(k1_len);
269 3367 50         if (!E) { free(K1); return; }
270              
271 3367           memcpy(iv, K + 16, 16);
272              
273             /* CBC encrypt without padding */
274 1027659 100         for (pos = 0; pos < k1_len; pos += 16) {
275 17412964 100         for (j = 0; j < 16; j++) {
276 16388672           block[j] = K1[pos + j] ^ iv[j];
277             }
278 1024292           pdfmake_aes_encrypt_block(&aes, block, E + pos);
279 1024292           memcpy(iv, E + pos, 16);
280             }
281              
282             /* Take first 16 bytes of E as a big-endian unsigned int, mod 3.
283             * Because 256 ≡ 1 (mod 3), this reduces to (sum of bytes) mod 3. */
284 3367           sum = 0;
285 57239 100         for (i = 0; i < 16; i++) sum += E[i];
286 3367           hash_type = sum % 3;
287              
288 3367 100         if (hash_type == 0) {
289 1100           pdfmake_sha256(E, k1_len, K);
290 1100           hash_len = 32;
291 2267 100         } else if (hash_type == 1) {
292 1052           pdfmake_sha384(E, k1_len, K);
293 1052           hash_len = 48;
294             } else {
295 1215           pdfmake_sha512(E, k1_len, K);
296 1215           hash_len = 64;
297             }
298              
299 3367           last_e_byte = E[k1_len - 1];
300 3367           free(K1);
301 3367           free(E);
302 3367           round++;
303             }
304              
305             /* Return first 32 bytes of final K */
306 49           memcpy(result, K, 32);
307             }
308              
309             /* Generate random 8-byte salt */
310 28           static void r6_generate_salt(uint8_t salt[8])
311             {
312 28           random_bytes(salt, 8);
313 28           }
314              
315             /* Compute U and UE for R6 */
316 7           static void compute_U_UE_r6(pdfmake_crypt_ctx_t *ctx, const char *user_passwd)
317             {
318             size_t passwd_len;
319             uint8_t validation_salt[8], key_salt[8];
320             uint8_t hash[32];
321             uint8_t iv[16];
322             pdfmake_aes_ctx_t aes;
323             uint8_t padded_key[32];
324             uint8_t block[16];
325             int i;
326             int j;
327              
328 7 50         passwd_len = user_passwd ? strlen(user_passwd) : 0;
329 7 50         if (passwd_len > 127) passwd_len = 127;
330            
331 7           r6_generate_salt(validation_salt);
332 7           r6_generate_salt(key_salt);
333            
334             /* U = hash(passwd || validation_salt) || validation_salt || key_salt */
335 7           r6_intermediate_hash((const uint8_t *)user_passwd, passwd_len,
336             validation_salt, 8, NULL, hash);
337            
338 7           memcpy(ctx->U, hash, 32);
339 7           memcpy(ctx->U + 32, validation_salt, 8);
340 7           memcpy(ctx->U + 40, key_salt, 8);
341            
342             /* UE = AES-256-CBC(hash(passwd || key_salt), zeros, file_key) */
343 7           r6_intermediate_hash((const uint8_t *)user_passwd, passwd_len,
344             key_salt, 8, NULL, hash);
345            
346 7           memset(iv, 0, 16);
347 7           pdfmake_aes_init(&aes, hash, 32);
348            
349             /* Encrypt file key (pad to 32 bytes) */
350 7           memcpy(padded_key, ctx->file_key, 32);
351 21 100         for (i = 0; i < 32; i += 16) {
352 238 100         for (j = 0; j < 16; j++) {
353 224           block[j] = padded_key[i + j] ^ iv[j];
354             }
355 14           pdfmake_aes_encrypt_block(&aes, block, ctx->UE + i);
356 14           memcpy(iv, ctx->UE + i, 16);
357             }
358 7           }
359              
360             /* Compute O and OE for R6 */
361 7           static void compute_O_OE_r6(pdfmake_crypt_ctx_t *ctx, const char *owner_passwd)
362             {
363             size_t passwd_len;
364             uint8_t validation_salt[8], key_salt[8];
365             uint8_t hash[32];
366             uint8_t iv[16];
367             pdfmake_aes_ctx_t aes;
368             uint8_t padded_key[32];
369             uint8_t block[16];
370             int i;
371             int j;
372              
373 7 50         passwd_len = owner_passwd ? strlen(owner_passwd) : 0;
374 7 50         if (passwd_len > 127) passwd_len = 127;
375            
376 7           r6_generate_salt(validation_salt);
377 7           r6_generate_salt(key_salt);
378            
379             /* O = hash(passwd || validation_salt || U) || validation_salt || key_salt */
380 7           r6_intermediate_hash((const uint8_t *)owner_passwd, passwd_len,
381 7           validation_salt, 8, ctx->U, hash);
382            
383 7           memcpy(ctx->O, hash, 32);
384 7           memcpy(ctx->O + 32, validation_salt, 8);
385 7           memcpy(ctx->O + 40, key_salt, 8);
386            
387             /* OE = AES-256-CBC(hash(passwd || key_salt || U), zeros, file_key) */
388 7           r6_intermediate_hash((const uint8_t *)owner_passwd, passwd_len,
389 7           key_salt, 8, ctx->U, hash);
390            
391 7           memset(iv, 0, 16);
392 7           pdfmake_aes_init(&aes, hash, 32);
393            
394             /* Encrypt file key */
395 7           memcpy(padded_key, ctx->file_key, 32);
396 21 100         for (i = 0; i < 32; i += 16) {
397 238 100         for (j = 0; j < 16; j++) {
398 224           block[j] = padded_key[i + j] ^ iv[j];
399             }
400 14           pdfmake_aes_encrypt_block(&aes, block, ctx->OE + i);
401 14           memcpy(iv, ctx->OE + i, 16);
402             }
403 7           }
404              
405             /* Compute Perms for R6 */
406 7           static void compute_Perms_r6(pdfmake_crypt_ctx_t *ctx)
407             {
408             uint8_t perms_data[16];
409             pdfmake_aes_ctx_t aes;
410            
411             /* Bytes 0-3: P (little-endian) */
412 7           perms_data[0] = (uint8_t)(ctx->P & 0xFF);
413 7           perms_data[1] = (uint8_t)((ctx->P >> 8) & 0xFF);
414 7           perms_data[2] = (uint8_t)((ctx->P >> 16) & 0xFF);
415 7           perms_data[3] = (uint8_t)((ctx->P >> 24) & 0xFF);
416            
417             /* Bytes 4-7: 0xFFFFFFFF */
418 7           perms_data[4] = 0xFF;
419 7           perms_data[5] = 0xFF;
420 7           perms_data[6] = 0xFF;
421 7           perms_data[7] = 0xFF;
422            
423             /* Byte 8: 'T' if encrypting metadata, 'F' otherwise */
424 7 50         perms_data[8] = ctx->encrypt_metadata ? 'T' : 'F';
425            
426             /* Byte 9: 'a' */
427 7           perms_data[9] = 'a';
428            
429             /* Byte 10: 'd' */
430 7           perms_data[10] = 'd';
431            
432             /* Byte 11: 'b' */
433 7           perms_data[11] = 'b';
434            
435             /* Bytes 12-15: random */
436 7           random_bytes(perms_data + 12, 4);
437            
438             /* Encrypt with file key (ECB mode, single block) */
439 7           pdfmake_aes_init(&aes, ctx->file_key, 32);
440 7           pdfmake_aes_encrypt_block(&aes, perms_data, ctx->Perms);
441 7           }
442              
443             /*============================================================================
444             * Public API: Initialization
445             *==========================================================================*/
446              
447 90           void pdfmake_crypt_init(pdfmake_crypt_ctx_t *ctx)
448             {
449 90           memset(ctx, 0, sizeof(*ctx));
450 90           ctx->encrypt_metadata = 1; /* Default: encrypt metadata */
451 90           }
452              
453 22           int pdfmake_crypt_setup(pdfmake_crypt_ctx_t *ctx,
454             pdfmake_crypt_algo_t algorithm,
455             const char *user_passwd,
456             const char *owner_passwd,
457             int32_t permissions,
458             const uint8_t *doc_id,
459             size_t doc_id_len)
460             {
461 22           pdfmake_crypt_init(ctx);
462            
463 22           ctx->algorithm = algorithm;
464 22           ctx->P = permissions | 0xFFFFF000; /* Set required high bits */
465            
466             /* Copy document ID */
467 22 100         if (doc_id_len > 16) doc_id_len = 16;
468 22           memcpy(ctx->doc_id, doc_id, doc_id_len);
469 22           ctx->doc_id_len = (int)doc_id_len;
470            
471             /* Set parameters based on algorithm */
472 22           switch (algorithm) {
473 3           case PDFMAKE_CRYPT_RC4_40:
474 3           ctx->V = 1;
475 3           ctx->R = 2;
476 3           ctx->key_length = 5; /* 40 bits */
477 3           break;
478 7           case PDFMAKE_CRYPT_RC4_128:
479 7           ctx->V = 2;
480 7           ctx->R = 3;
481 7           ctx->key_length = 16; /* 128 bits */
482 7           break;
483 5           case PDFMAKE_CRYPT_AES_128:
484 5           ctx->V = 4;
485 5           ctx->R = 4;
486 5           ctx->key_length = 16; /* 128 bits */
487 5           break;
488 7           case PDFMAKE_CRYPT_AES_256:
489 7           ctx->V = 5;
490 7           ctx->R = 6;
491 7           ctx->key_length = 32; /* 256 bits */
492 7           break;
493 0           default:
494 0           return -1;
495             }
496            
497 22 100         if (ctx->R <= 4) {
498             uint8_t passwd_padded[32];
499             /* R2-R4: Compute O, then file key, then U */
500 15           compute_O_value_r2_r4(ctx, owner_passwd, user_passwd, ctx->key_length);
501            
502 15           pad_password(user_passwd, passwd_padded);
503 15           compute_file_key_r2_r4(ctx, passwd_padded, ctx->O, ctx->P,
504 15           ctx->doc_id, ctx->doc_id_len,
505             ctx->key_length, ctx->encrypt_metadata);
506            
507 15 100         if (ctx->R == 2) {
508 3           compute_U_value_r2(ctx);
509             } else {
510 12           compute_U_value_r3_r4(ctx);
511             }
512             } else {
513             const char *op;
514             /* R6: Generate random file key, then compute U/UE/O/OE/Perms */
515 7           random_bytes(ctx->file_key, 32);
516 7           ctx->file_key_len = 32;
517            
518             /* Use owner password or fall back to user password */
519 7 50         op = (owner_passwd && *owner_passwd) ? owner_passwd : user_passwd;
    50          
520            
521 7           compute_U_UE_r6(ctx, user_passwd);
522 7           compute_O_OE_r6(ctx, op);
523 7           compute_Perms_r6(ctx);
524             }
525            
526 22           ctx->initialized = 1;
527 22           ctx->authenticated = 1;
528 22           ctx->is_owner = 1;
529            
530 22           return 0;
531             }
532              
533 25           int pdfmake_crypt_load(pdfmake_crypt_ctx_t *ctx,
534             int V, int R, int key_length,
535             const uint8_t *O, size_t O_len,
536             const uint8_t *U, size_t U_len,
537             const uint8_t *OE, size_t OE_len,
538             const uint8_t *UE, size_t UE_len,
539             const uint8_t *Perms, size_t Perms_len,
540             int32_t P,
541             const uint8_t *doc_id, size_t doc_id_len,
542             int encrypt_metadata)
543             {
544 25           pdfmake_crypt_init(ctx);
545            
546 25           ctx->V = V;
547 25           ctx->R = R;
548 25           ctx->key_length = key_length / 8; /* Convert bits to bytes */
549 25           ctx->P = P;
550 25           ctx->encrypt_metadata = encrypt_metadata;
551            
552             /* Determine algorithm from V/R */
553 25 100         if (V == 1) {
554 16           ctx->algorithm = PDFMAKE_CRYPT_RC4_40;
555 16           ctx->key_length = 5;
556 9 100         } else if (V == 2) {
557 2           ctx->algorithm = PDFMAKE_CRYPT_RC4_128;
558 7 100         } else if (V == 4) {
559 2           ctx->algorithm = PDFMAKE_CRYPT_AES_128;
560 2           ctx->key_length = 16;
561 5 50         } else if (V == 5) {
562 5           ctx->algorithm = PDFMAKE_CRYPT_AES_256;
563 5           ctx->key_length = 32;
564             } else {
565 0           return -1; /* Unsupported */
566             }
567            
568             /* Copy values */
569 25 50         if (O && O_len > 0) {
    50          
570 25           size_t copy_len = O_len > 48 ? 48 : O_len;
571 25           memcpy(ctx->O, O, copy_len);
572             }
573 25 50         if (U && U_len > 0) {
    50          
574 25           size_t copy_len = U_len > 48 ? 48 : U_len;
575 25           memcpy(ctx->U, U, copy_len);
576             }
577 25 100         if (OE && OE_len == 32) {
    50          
578 5           memcpy(ctx->OE, OE, 32);
579             }
580 25 100         if (UE && UE_len == 32) {
    50          
581 5           memcpy(ctx->UE, UE, 32);
582             }
583 25 100         if (Perms && Perms_len == 16) {
    50          
584 5           memcpy(ctx->Perms, Perms, 16);
585             }
586            
587 25 50         if (doc_id && doc_id_len > 0) {
    50          
588 25           size_t copy_len = doc_id_len > 16 ? 16 : doc_id_len;
589 25           memcpy(ctx->doc_id, doc_id, copy_len);
590 25           ctx->doc_id_len = (int)copy_len;
591             }
592            
593 25           ctx->initialized = 1;
594            
595 25           return 0;
596             }
597              
598             /*============================================================================
599             * Public API: Authentication
600             *==========================================================================*/
601              
602 48           static int authenticate_user_r2_r4(pdfmake_crypt_ctx_t *ctx, const char *passwd)
603             {
604             uint8_t passwd_padded[32];
605             uint8_t computed_U[32];
606             uint8_t digest[16];
607             pdfmake_md5_ctx_t md5;
608             uint8_t temp_key[16];
609             int i;
610             int j;
611              
612 48           pad_password(passwd, passwd_padded);
613            
614             /* Compute file key */
615 48           compute_file_key_r2_r4(ctx, passwd_padded, ctx->O, ctx->P,
616 48           ctx->doc_id, ctx->doc_id_len,
617             ctx->key_length, ctx->encrypt_metadata);
618            
619             /* Compute U and compare */
620 48 100         if (ctx->R == 2) {
621 32           memcpy(computed_U, PADDING, 32);
622 32           pdfmake_rc4(ctx->file_key, ctx->file_key_len, computed_U, 32);
623            
624 32           return memcmp(computed_U, ctx->U, 32) == 0;
625             } else {
626             /* R3/R4: Only compare first 16 bytes */
627 16           pdfmake_md5_init(&md5);
628 16           pdfmake_md5_update(&md5, PADDING, 32);
629 16           pdfmake_md5_update(&md5, ctx->doc_id, ctx->doc_id_len);
630 16           pdfmake_md5_final(&md5, digest);
631            
632 16           pdfmake_rc4(ctx->file_key, ctx->file_key_len, digest, 16);
633            
634 320 100         for (i = 1; i <= 19; i++) {
635 5168 100         for (j = 0; j < ctx->file_key_len; j++) {
636 4864           temp_key[j] = ctx->file_key[j] ^ (uint8_t)i;
637             }
638 304           pdfmake_rc4(temp_key, ctx->file_key_len, digest, 16);
639             }
640            
641 16           return memcmp(digest, ctx->U, 16) == 0;
642             }
643             }
644              
645 24           static int authenticate_owner_r2_r4(pdfmake_crypt_ctx_t *ctx, const char *passwd)
646             {
647             uint8_t passwd_padded[32];
648             uint8_t digest[16];
649             uint8_t user_passwd[32];
650             uint8_t temp_key[16];
651             char user_str[33];
652             int i;
653             int j;
654              
655 24           pad_password(passwd, passwd_padded);
656            
657             /* Compute owner key from password */
658 24           pdfmake_md5(passwd_padded, 32, digest);
659            
660 24 100         if (ctx->R >= 3) {
661 408 100         for (i = 0; i < 50; i++) {
662 400           pdfmake_md5(digest, ctx->key_length, digest);
663             }
664             }
665            
666             /* Decrypt O to get user password */
667 24           memcpy(user_passwd, ctx->O, 32);
668            
669 24 100         if (ctx->R == 2) {
670 16           pdfmake_rc4(digest, ctx->key_length, user_passwd, 32);
671             } else {
672             /* R3+: decrypt in reverse order */
673 168 100         for (i = 19; i >= 0; i--) {
674 2720 100         for (j = 0; j < ctx->key_length; j++) {
675 2560           temp_key[j] = digest[j] ^ (uint8_t)i;
676             }
677 160           pdfmake_rc4(temp_key, ctx->key_length, user_passwd, 32);
678             }
679             }
680            
681             /* Now authenticate with derived user password */
682 24           memcpy(user_str, user_passwd, 32);
683 24           user_str[32] = '\0';
684            
685 24           return authenticate_user_r2_r4(ctx, user_str);
686             }
687              
688 8           static int authenticate_user_r6(pdfmake_crypt_ctx_t *ctx, const char *passwd)
689             {
690             size_t passwd_len;
691             uint8_t hash[32];
692             uint8_t iv[16];
693             pdfmake_aes_ctx_t aes;
694             uint8_t decrypted[16];
695             int i;
696             int j;
697              
698 8 50         passwd_len = passwd ? strlen(passwd) : 0;
699 8 50         if (passwd_len > 127) passwd_len = 127;
700            
701             /* Validate: hash(passwd || U[32:40]) should equal U[0:32] */
702 8           r6_intermediate_hash((const uint8_t *)passwd, passwd_len,
703 8           ctx->U + 32, 8, NULL, hash);
704            
705 8 100         if (memcmp(hash, ctx->U, 32) != 0) {
706 5           return 0; /* Validation failed */
707             }
708            
709             /* Derive file key from UE */
710 3           r6_intermediate_hash((const uint8_t *)passwd, passwd_len,
711 3           ctx->U + 40, 8, NULL, hash);
712            
713             /* Decrypt UE to get file key */
714 3           memset(iv, 0, 16);
715 3           pdfmake_aes_init(&aes, hash, 32);
716            
717 9 100         for (i = 0; i < 32; i += 16) {
718 6           pdfmake_aes_decrypt_block(&aes, ctx->UE + i, decrypted);
719 102 100         for (j = 0; j < 16; j++) {
720 96           ctx->file_key[i + j] = decrypted[j] ^ iv[j];
721             }
722 6           memcpy(iv, ctx->UE + i, 16);
723             }
724 3           ctx->file_key_len = 32;
725            
726 3           return 1;
727             }
728              
729 9           static int authenticate_owner_r6(pdfmake_crypt_ctx_t *ctx, const char *passwd)
730             {
731             size_t passwd_len;
732             uint8_t hash[32];
733             uint8_t iv[16];
734             pdfmake_aes_ctx_t aes;
735             uint8_t decrypted[16];
736             int i;
737             int j;
738              
739 9 50         passwd_len = passwd ? strlen(passwd) : 0;
740 9 50         if (passwd_len > 127) passwd_len = 127;
741            
742             /* Validate: hash(passwd || O[32:40] || U) should equal O[0:32] */
743 9           r6_intermediate_hash((const uint8_t *)passwd, passwd_len,
744 9           ctx->O + 32, 8, ctx->U, hash);
745            
746 9 100         if (memcmp(hash, ctx->O, 32) != 0) {
747 8           return 0; /* Validation failed */
748             }
749            
750             /* Derive file key from OE */
751 1           r6_intermediate_hash((const uint8_t *)passwd, passwd_len,
752 1           ctx->O + 40, 8, ctx->U, hash);
753            
754             /* Decrypt OE to get file key */
755 1           memset(iv, 0, 16);
756 1           pdfmake_aes_init(&aes, hash, 32);
757            
758 3 100         for (i = 0; i < 32; i += 16) {
759 2           pdfmake_aes_decrypt_block(&aes, ctx->OE + i, decrypted);
760 34 100         for (j = 0; j < 16; j++) {
761 32           ctx->file_key[i + j] = decrypted[j] ^ iv[j];
762             }
763 2           memcpy(iv, ctx->OE + i, 16);
764             }
765 1           ctx->file_key_len = 32;
766            
767 1           return 1;
768             }
769              
770 33           int pdfmake_crypt_authenticate(pdfmake_crypt_ctx_t *ctx, const char *passwd)
771             {
772 33 50         if (!ctx->initialized) {
773 0           return -1;
774             }
775            
776 33 100         if (ctx->R <= 4) {
777             /* Try owner password first */
778 24 50         if (authenticate_owner_r2_r4(ctx, passwd)) {
779 0           ctx->authenticated = 1;
780 0           ctx->is_owner = 1;
781 0           return 1;
782             }
783            
784             /* Try user password */
785 24 100         if (authenticate_user_r2_r4(ctx, passwd)) {
786 20           ctx->authenticated = 1;
787 20           ctx->is_owner = 0;
788 20           return 0;
789             }
790             } else {
791             /* R6: Try owner password first */
792 9 100         if (authenticate_owner_r6(ctx, passwd)) {
793 1           ctx->authenticated = 1;
794 1           ctx->is_owner = 1;
795 1           return 1;
796             }
797            
798             /* Try user password */
799 8 100         if (authenticate_user_r6(ctx, passwd)) {
800 3           ctx->authenticated = 1;
801 3           ctx->is_owner = 0;
802 3           return 0;
803             }
804             }
805            
806 9           return -1; /* Authentication failed */
807             }
808              
809 9           int pdfmake_crypt_is_authenticated(const pdfmake_crypt_ctx_t *ctx)
810             {
811 9           return ctx->authenticated;
812             }
813              
814 2           int pdfmake_crypt_is_owner(const pdfmake_crypt_ctx_t *ctx)
815             {
816 2           return ctx->is_owner;
817             }
818              
819             /*============================================================================
820             * Public API: Per-object key derivation
821             *==========================================================================*/
822              
823 131           int pdfmake_crypt_object_key(const pdfmake_crypt_ctx_t *ctx,
824             int obj_num, int gen_num,
825             uint8_t *key_out)
826             {
827             pdfmake_md5_ctx_t md5;
828             uint8_t obj_bytes[5];
829             uint8_t digest[16];
830             int key_len;
831              
832 131 100         if (ctx->R >= 6) {
833             /* R6: Use file key directly */
834 22           memcpy(key_out, ctx->file_key, 32);
835 22           return 32;
836             }
837            
838             /* R2-R4: MD5(file_key || obj_num || gen_num || "sAlT") */
839 109           pdfmake_md5_init(&md5);
840            
841 109           pdfmake_md5_update(&md5, ctx->file_key, ctx->file_key_len);
842            
843 109           obj_bytes[0] = (uint8_t)(obj_num & 0xFF);
844 109           obj_bytes[1] = (uint8_t)((obj_num >> 8) & 0xFF);
845 109           obj_bytes[2] = (uint8_t)((obj_num >> 16) & 0xFF);
846 109           obj_bytes[3] = (uint8_t)(gen_num & 0xFF);
847 109           obj_bytes[4] = (uint8_t)((gen_num >> 8) & 0xFF);
848 109           pdfmake_md5_update(&md5, obj_bytes, 5);
849            
850             /* R4 AES: add "sAlT" */
851 109 100         if (ctx->R >= 4) {
852 8           pdfmake_md5_update(&md5, AES_SALT, 4);
853             }
854            
855 109           pdfmake_md5_final(&md5, digest);
856            
857             /* Key length is min(file_key_len + 5, 16) */
858 109           key_len = ctx->file_key_len + 5;
859 109 100         if (key_len > 16) key_len = 16;
860            
861 109           memcpy(key_out, digest, key_len);
862 109           return key_len;
863             }
864              
865             /*============================================================================
866             * Public API: String/Stream encryption
867             *==========================================================================*/
868              
869 25           int pdfmake_crypt_encrypt_string(const pdfmake_crypt_ctx_t *ctx,
870             int obj_num, int gen_num,
871             const uint8_t *in, size_t in_len,
872             uint8_t *out)
873             {
874             uint8_t key[32];
875 25           int key_len = pdfmake_crypt_object_key(ctx, obj_num, gen_num, key);
876            
877 25 100         if (ctx->R >= 4) {
878             /* AES: prepend IV, apply PKCS#7 */
879 20           return (int)pdfmake_aes_pdf_encrypt(key, key_len, in, in_len, out);
880             } else {
881             /* RC4 */
882 5           memcpy(out, in, in_len);
883 5           pdfmake_rc4(key, key_len, out, in_len);
884 5           return (int)in_len;
885             }
886             }
887              
888 106           int pdfmake_crypt_decrypt_string(const pdfmake_crypt_ctx_t *ctx,
889             int obj_num, int gen_num,
890             const uint8_t *in, size_t in_len,
891             uint8_t *out)
892             {
893             uint8_t key[32];
894 106           int key_len = pdfmake_crypt_object_key(ctx, obj_num, gen_num, key);
895            
896 106 100         if (ctx->R >= 4) {
897             /* AES: IV is first 16 bytes */
898 10           return pdfmake_aes_pdf_decrypt(key, key_len, in, in_len, out);
899             } else {
900             /* RC4 */
901 96           memcpy(out, in, in_len);
902 96           pdfmake_rc4(key, key_len, out, in_len);
903 96           return (int)in_len;
904             }
905             }
906              
907 7           int pdfmake_crypt_encrypt_stream(const pdfmake_crypt_ctx_t *ctx,
908             int obj_num, int gen_num,
909             const uint8_t *in, size_t in_len,
910             uint8_t **out, size_t *out_len)
911             {
912             /* Calculate output size */
913             size_t max_out;
914             int result;
915              
916 7 100         if (ctx->R >= 4) {
917             /* AES: IV + padded data */
918 6           max_out = 16 + in_len + 16;
919             } else {
920 1           max_out = in_len;
921             }
922            
923 7           *out = malloc(max_out);
924 7 50         if (!*out) return -1;
925            
926 7           result = pdfmake_crypt_encrypt_string(ctx, obj_num, gen_num,
927             in, in_len, *out);
928 7 50         if (result < 0) {
929 0           free(*out);
930 0           *out = NULL;
931 0           return -1;
932             }
933            
934 7           *out_len = (size_t)result;
935 7           return 0;
936             }
937              
938 103           int pdfmake_crypt_decrypt_stream(const pdfmake_crypt_ctx_t *ctx,
939             int obj_num, int gen_num,
940             const uint8_t *in, size_t in_len,
941             uint8_t **out, size_t *out_len)
942             {
943             int result;
944              
945 103           *out = malloc(in_len);
946 103 50         if (!*out) return -1;
947            
948 103           result = pdfmake_crypt_decrypt_string(ctx, obj_num, gen_num,
949             in, in_len, *out);
950 103 50         if (result < 0) {
951 0           free(*out);
952 0           *out = NULL;
953 0           return -1;
954             }
955            
956 103           *out_len = (size_t)result;
957 103           return 0;
958             }
959              
960             /*============================================================================
961             * Public API: Permission checking
962             *==========================================================================*/
963              
964 3           int pdfmake_crypt_has_permission(const pdfmake_crypt_ctx_t *ctx, int32_t perm)
965             {
966             /* Owner always has all permissions */
967 3 50         if (ctx->is_owner) {
968 3           return 1;
969             }
970            
971 0           return (ctx->P & perm) != 0;
972             }
973              
974 2           int32_t pdfmake_crypt_get_permissions(const pdfmake_crypt_ctx_t *ctx)
975             {
976 2           return ctx->P;
977             }