File Coverage

src/pdfmake_signature.c
Criterion Covered Total %
statement 29 1417 2.0
branch 15 970 1.5
condition n/a
subroutine n/a
pod n/a
total 44 2387 1.8


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_signature.c โ€” PDF Digital Signatures Implementation
3             *
4             * Implements PDF digital signatures per ISO 32000-2:2020 ยง12.8.
5             */
6              
7             #include "pdfmake_signature.h"
8             #include "pdfmake_asn1.h"
9             #include "pdfmake_x509.h"
10             #include "pdfmake_pkcs12.h"
11             #include "pdfmake_arena.h"
12             #include "pdfmake_buf.h"
13             #include "pdfmake_bn.h"
14             #include "pdfmake_parser.h"
15             #include "pdfmake_annot.h"
16             #include "pdfmake_page.h"
17             #include "pdfmake_meta.h"
18             #include
19             #include
20             #include
21             #include
22              
23             /* OIDs used by CMS SignedData (RFC 5652). */
24             #define OID_ID_DATA "1.2.840.113549.1.7.1"
25             #define OID_ID_SIGNED_DATA "1.2.840.113549.1.7.2"
26             #define OID_ID_CONTENT_TYPE "1.2.840.113549.1.9.3"
27             #define OID_ID_MESSAGE_DIGEST "1.2.840.113549.1.9.4"
28             #define OID_ID_SIGNING_TIME "1.2.840.113549.1.9.5"
29             #define OID_RSA_ENCRYPTION "1.2.840.113549.1.1.1"
30             #define OID_SHA1 "1.3.14.3.2.26"
31             #define OID_SHA256 "2.16.840.1.101.3.4.2.1"
32             #define OID_SHA384 "2.16.840.1.101.3.4.2.2"
33             #define OID_SHA512 "2.16.840.1.101.3.4.2.3"
34             /* RFC 5035 / ETSI EN 319 122 โ€” PAdES-BES signing-certificate-v2 */
35             #define OID_AA_SIGNING_CERT_V2 "1.2.840.113549.1.9.16.2.47"
36             /* RFC 3161 / RFC 5652 โ€” timestamp token unsigned attribute */
37             #define OID_AA_TIMESTAMP_TOKEN "1.2.840.113549.1.9.16.2.14"
38              
39             /*============================================================================
40             * SHA-256 Implementation
41             *==========================================================================*/
42              
43             typedef struct {
44             uint32_t state[8];
45             uint64_t count;
46             uint8_t buffer[64];
47             } sha256_ctx_t;
48              
49             static const uint32_t SHA256_K[64] = {
50             0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
51             0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
52             0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
53             0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
54             0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
55             0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
56             0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
57             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
58             };
59              
60             #define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
61             #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
62             #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
63             #define EP0(x) (ROR32(x, 2) ^ ROR32(x, 13) ^ ROR32(x, 22))
64             #define EP1(x) (ROR32(x, 6) ^ ROR32(x, 11) ^ ROR32(x, 25))
65             #define SIG0(x) (ROR32(x, 7) ^ ROR32(x, 18) ^ ((x) >> 3))
66             #define SIG1(x) (ROR32(x, 17) ^ ROR32(x, 19) ^ ((x) >> 10))
67              
68 0           static void sha256_transform(uint32_t state[8], const uint8_t block[64])
69             {
70             uint32_t a, b, c, d, e, f, g, h;
71             uint32_t t1, t2;
72             uint32_t w[64];
73             int i;
74            
75             /* Prepare message schedule */
76 0 0         for (i = 0; i < 16; i++) {
77 0           w[i] = ((uint32_t)block[i*4] << 24) |
78 0           ((uint32_t)block[i*4+1] << 16) |
79 0           ((uint32_t)block[i*4+2] << 8) |
80 0           ((uint32_t)block[i*4+3]);
81             }
82 0 0         for (i = 16; i < 64; i++) {
83 0           w[i] = SIG1(w[i-2]) + w[i-7] + SIG0(w[i-15]) + w[i-16];
84             }
85            
86 0           a = state[0]; b = state[1]; c = state[2]; d = state[3];
87 0           e = state[4]; f = state[5]; g = state[6]; h = state[7];
88            
89 0 0         for (i = 0; i < 64; i++) {
90 0           t1 = h + EP1(e) + CH(e, f, g) + SHA256_K[i] + w[i];
91 0           t2 = EP0(a) + MAJ(a, b, c);
92 0           h = g; g = f; f = e; e = d + t1;
93 0           d = c; c = b; b = a; a = t1 + t2;
94             }
95            
96 0           state[0] += a; state[1] += b; state[2] += c; state[3] += d;
97 0           state[4] += e; state[5] += f; state[6] += g; state[7] += h;
98 0           }
99              
100 0           static void sha256_init(sha256_ctx_t *ctx)
101             {
102 0           ctx->state[0] = 0x6a09e667;
103 0           ctx->state[1] = 0xbb67ae85;
104 0           ctx->state[2] = 0x3c6ef372;
105 0           ctx->state[3] = 0xa54ff53a;
106 0           ctx->state[4] = 0x510e527f;
107 0           ctx->state[5] = 0x9b05688c;
108 0           ctx->state[6] = 0x1f83d9ab;
109 0           ctx->state[7] = 0x5be0cd19;
110 0           ctx->count = 0;
111 0           }
112              
113 0           static void sha256_update(sha256_ctx_t *ctx, const uint8_t *data, size_t len)
114             {
115 0           size_t i = (ctx->count >> 3) & 63;
116             size_t part_len;
117 0           size_t j = 0;
118 0           ctx->count += (uint64_t)len << 3;
119              
120 0           part_len = 64 - i;
121            
122 0 0         if (len >= part_len) {
123 0           memcpy(ctx->buffer + i, data, part_len);
124 0           sha256_transform(ctx->state, ctx->buffer);
125            
126 0 0         for (j = part_len; j + 63 < len; j += 64) {
127 0           sha256_transform(ctx->state, data + j);
128             }
129 0           i = 0;
130             }
131            
132 0           memcpy(ctx->buffer + i, data + j, len - j);
133 0           }
134              
135 0           static void sha256_final(sha256_ctx_t *ctx, uint8_t digest[32])
136             {
137 0           uint8_t pad[64] = {0x80};
138 0           uint64_t bits = ctx->count;
139 0           size_t i = (ctx->count >> 3) & 63;
140 0 0         size_t pad_len = (i < 56) ? (56 - i) : (120 - i);
141             uint8_t len_bytes[8];
142             int j;
143            
144 0           sha256_update(ctx, pad, pad_len);
145            
146 0 0         for (j = 0; j < 8; j++) {
147 0           len_bytes[j] = (bits >> (56 - j * 8)) & 0xFF;
148             }
149 0           sha256_update(ctx, len_bytes, 8);
150              
151 0 0         for (j = 0; j < 8; j++) {
152 0           digest[j*4] = (ctx->state[j] >> 24) & 0xFF;
153 0           digest[j*4+1] = (ctx->state[j] >> 16) & 0xFF;
154 0           digest[j*4+2] = (ctx->state[j] >> 8) & 0xFF;
155 0           digest[j*4+3] = ctx->state[j] & 0xFF;
156             }
157 0           }
158              
159             /*============================================================================
160             * SHA-384/512 Implementation
161             *==========================================================================*/
162              
163             typedef struct {
164             uint64_t state[8];
165             uint64_t count[2];
166             uint8_t buffer[128];
167             } sha512_ctx_t;
168              
169             static const uint64_t SHA512_K[80] = {
170             0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
171             0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
172             0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
173             0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
174             0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
175             0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
176             0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
177             0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
178             0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
179             0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
180             0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
181             0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
182             0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
183             0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
184             0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
185             0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
186             0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
187             0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
188             0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
189             0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
190             };
191              
192             #define ROR64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
193             #define CH64(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
194             #define MAJ64(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
195             #define EP0_64(x) (ROR64(x, 28) ^ ROR64(x, 34) ^ ROR64(x, 39))
196             #define EP1_64(x) (ROR64(x, 14) ^ ROR64(x, 18) ^ ROR64(x, 41))
197             #define SIG0_64(x) (ROR64(x, 1) ^ ROR64(x, 8) ^ ((x) >> 7))
198             #define SIG1_64(x) (ROR64(x, 19) ^ ROR64(x, 61) ^ ((x) >> 6))
199              
200 0           static void sha512_transform(uint64_t state[8], const uint8_t block[128])
201             {
202             uint64_t a, b, c, d, e, f, g, h;
203             uint64_t t1, t2;
204             uint64_t w[80];
205             int i;
206              
207 0 0         for (i = 0; i < 16; i++) {
208 0           w[i] = ((uint64_t)block[i*8] << 56) |
209 0           ((uint64_t)block[i*8+1] << 48) |
210 0           ((uint64_t)block[i*8+2] << 40) |
211 0           ((uint64_t)block[i*8+3] << 32) |
212 0           ((uint64_t)block[i*8+4] << 24) |
213 0           ((uint64_t)block[i*8+5] << 16) |
214 0           ((uint64_t)block[i*8+6] << 8) |
215 0           ((uint64_t)block[i*8+7]);
216             }
217 0 0         for (i = 16; i < 80; i++) {
218 0           w[i] = SIG1_64(w[i-2]) + w[i-7] + SIG0_64(w[i-15]) + w[i-16];
219             }
220            
221 0           a = state[0]; b = state[1]; c = state[2]; d = state[3];
222 0           e = state[4]; f = state[5]; g = state[6]; h = state[7];
223            
224 0 0         for (i = 0; i < 80; i++) {
225 0           t1 = h + EP1_64(e) + CH64(e, f, g) + SHA512_K[i] + w[i];
226 0           t2 = EP0_64(a) + MAJ64(a, b, c);
227 0           h = g; g = f; f = e; e = d + t1;
228 0           d = c; c = b; b = a; a = t1 + t2;
229             }
230            
231 0           state[0] += a; state[1] += b; state[2] += c; state[3] += d;
232 0           state[4] += e; state[5] += f; state[6] += g; state[7] += h;
233 0           }
234              
235 0           static void sha512_init(sha512_ctx_t *ctx)
236             {
237 0           ctx->state[0] = 0x6a09e667f3bcc908ULL;
238 0           ctx->state[1] = 0xbb67ae8584caa73bULL;
239 0           ctx->state[2] = 0x3c6ef372fe94f82bULL;
240 0           ctx->state[3] = 0xa54ff53a5f1d36f1ULL;
241 0           ctx->state[4] = 0x510e527fade682d1ULL;
242 0           ctx->state[5] = 0x9b05688c2b3e6c1fULL;
243 0           ctx->state[6] = 0x1f83d9abfb41bd6bULL;
244 0           ctx->state[7] = 0x5be0cd19137e2179ULL;
245 0           ctx->count[0] = ctx->count[1] = 0;
246 0           }
247              
248 0           static void sha384_init(sha512_ctx_t *ctx)
249             {
250 0           ctx->state[0] = 0xcbbb9d5dc1059ed8ULL;
251 0           ctx->state[1] = 0x629a292a367cd507ULL;
252 0           ctx->state[2] = 0x9159015a3070dd17ULL;
253 0           ctx->state[3] = 0x152fecd8f70e5939ULL;
254 0           ctx->state[4] = 0x67332667ffc00b31ULL;
255 0           ctx->state[5] = 0x8eb44a8768581511ULL;
256 0           ctx->state[6] = 0xdb0c2e0d64f98fa7ULL;
257 0           ctx->state[7] = 0x47b5481dbefa4fa4ULL;
258 0           ctx->count[0] = ctx->count[1] = 0;
259 0           }
260              
261 0           static void sha512_update(sha512_ctx_t *ctx, const uint8_t *data, size_t len)
262             {
263 0           size_t i = (ctx->count[0] >> 3) & 127;
264             size_t part_len;
265 0           size_t j = 0;
266            
267 0           ctx->count[0] += (uint64_t)len << 3;
268 0 0         if (ctx->count[0] < ((uint64_t)len << 3)) {
269 0           ctx->count[1]++;
270             }
271            
272 0           part_len = 128 - i;
273              
274 0 0         if (len >= part_len) {
275 0           memcpy(ctx->buffer + i, data, part_len);
276 0           sha512_transform(ctx->state, ctx->buffer);
277            
278 0 0         for (j = part_len; j + 127 < len; j += 128) {
279 0           sha512_transform(ctx->state, data + j);
280             }
281 0           i = 0;
282             }
283            
284 0           memcpy(ctx->buffer + i, data + j, len - j);
285 0           }
286              
287 0           static void sha512_final(sha512_ctx_t *ctx, uint8_t digest[64])
288             {
289 0           uint8_t pad[128] = {0x80};
290 0           size_t i = (ctx->count[0] >> 3) & 127;
291 0 0         size_t pad_len = (i < 112) ? (112 - i) : (240 - i);
292             uint8_t len_bytes[16];
293             int j;
294              
295 0           sha512_update(ctx, pad, pad_len);
296              
297 0 0         for (j = 0; j < 8; j++) {
298 0           len_bytes[j] = (ctx->count[1] >> (56 - j * 8)) & 0xFF;
299 0           len_bytes[j + 8] = (ctx->count[0] >> (56 - j * 8)) & 0xFF;
300             }
301 0           sha512_update(ctx, len_bytes, 16);
302              
303 0 0         for (j = 0; j < 8; j++) {
304 0           digest[j*8] = (ctx->state[j] >> 56) & 0xFF;
305 0           digest[j*8+1] = (ctx->state[j] >> 48) & 0xFF;
306 0           digest[j*8+2] = (ctx->state[j] >> 40) & 0xFF;
307 0           digest[j*8+3] = (ctx->state[j] >> 32) & 0xFF;
308 0           digest[j*8+4] = (ctx->state[j] >> 24) & 0xFF;
309 0           digest[j*8+5] = (ctx->state[j] >> 16) & 0xFF;
310 0           digest[j*8+6] = (ctx->state[j] >> 8) & 0xFF;
311 0           digest[j*8+7] = ctx->state[j] & 0xFF;
312             }
313 0           }
314              
315 0           static void sha384_final(sha512_ctx_t *ctx, uint8_t digest[48])
316             {
317             uint8_t full_digest[64];
318 0           sha512_final(ctx, full_digest);
319 0           memcpy(digest, full_digest, 48);
320 0           }
321              
322             /*============================================================================
323             * SHA-1 Implementation (for legacy compatibility)
324             *==========================================================================*/
325              
326             typedef struct {
327             uint32_t state[5];
328             uint64_t count;
329             uint8_t buffer[64];
330             } sha1_ctx_t;
331              
332 0           static void sha1_init(sha1_ctx_t *ctx)
333             {
334 0           ctx->state[0] = 0x67452301;
335 0           ctx->state[1] = 0xEFCDAB89;
336 0           ctx->state[2] = 0x98BADCFE;
337 0           ctx->state[3] = 0x10325476;
338 0           ctx->state[4] = 0xC3D2E1F0;
339 0           ctx->count = 0;
340 0           }
341              
342             #define ROL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
343              
344 0           static void sha1_transform(uint32_t state[5], const uint8_t block[64])
345             {
346             uint32_t a, b, c, d, e;
347             uint32_t f, k;
348             uint32_t temp;
349             uint32_t w[80];
350             int i;
351              
352 0 0         for (i = 0; i < 16; i++) {
353 0           w[i] = ((uint32_t)block[i*4] << 24) |
354 0           ((uint32_t)block[i*4+1] << 16) |
355 0           ((uint32_t)block[i*4+2] << 8) |
356 0           ((uint32_t)block[i*4+3]);
357             }
358 0 0         for (i = 16; i < 80; i++) {
359 0           w[i] = ROL32(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
360             }
361            
362 0           a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4];
363            
364 0 0         for (i = 0; i < 80; i++) {
365 0 0         if (i < 20) {
366 0           f = (b & c) | ((~b) & d);
367 0           k = 0x5A827999;
368 0 0         } else if (i < 40) {
369 0           f = b ^ c ^ d;
370 0           k = 0x6ED9EBA1;
371 0 0         } else if (i < 60) {
372 0           f = (b & c) | (b & d) | (c & d);
373 0           k = 0x8F1BBCDC;
374             } else {
375 0           f = b ^ c ^ d;
376 0           k = 0xCA62C1D6;
377             }
378            
379 0           temp = ROL32(a, 5) + f + e + k + w[i];
380 0           e = d; d = c; c = ROL32(b, 30); b = a; a = temp;
381             }
382            
383 0           state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e;
384 0           }
385              
386 0           static void sha1_update(sha1_ctx_t *ctx, const uint8_t *data, size_t len)
387             {
388 0           size_t i = (ctx->count >> 3) & 63;
389             size_t part_len;
390 0           size_t j = 0;
391 0           ctx->count += (uint64_t)len << 3;
392              
393 0           part_len = 64 - i;
394              
395 0 0         if (len >= part_len) {
396 0           memcpy(ctx->buffer + i, data, part_len);
397 0           sha1_transform(ctx->state, ctx->buffer);
398            
399 0 0         for (j = part_len; j + 63 < len; j += 64) {
400 0           sha1_transform(ctx->state, data + j);
401             }
402 0           i = 0;
403             }
404            
405 0           memcpy(ctx->buffer + i, data + j, len - j);
406 0           }
407              
408 0           static void sha1_final(sha1_ctx_t *ctx, uint8_t digest[20])
409             {
410 0           uint8_t pad[64] = {0x80};
411 0           uint64_t bits = ctx->count;
412 0           size_t i = (ctx->count >> 3) & 63;
413 0 0         size_t pad_len = (i < 56) ? (56 - i) : (120 - i);
414             uint8_t len_bytes[8];
415             int j;
416              
417 0           sha1_update(ctx, pad, pad_len);
418              
419 0 0         for (j = 0; j < 8; j++) {
420 0           len_bytes[j] = (bits >> (56 - j * 8)) & 0xFF;
421             }
422 0           sha1_update(ctx, len_bytes, 8);
423              
424 0 0         for (j = 0; j < 5; j++) {
425 0           digest[j*4] = (ctx->state[j] >> 24) & 0xFF;
426 0           digest[j*4+1] = (ctx->state[j] >> 16) & 0xFF;
427 0           digest[j*4+2] = (ctx->state[j] >> 8) & 0xFF;
428 0           digest[j*4+3] = ctx->state[j] & 0xFF;
429             }
430 0           }
431              
432             /*============================================================================
433             * Hash API Implementation
434             *==========================================================================*/
435              
436             struct pdfmake_hash_ctx_s {
437             pdfmake_hash_algorithm_t algorithm;
438             union {
439             sha1_ctx_t sha1;
440             sha256_ctx_t sha256;
441             sha512_ctx_t sha512;
442             };
443             };
444              
445 0           pdfmake_hash_ctx_t *pdfmake_hash_new(pdfmake_hash_algorithm_t alg)
446             {
447 0           pdfmake_hash_ctx_t *ctx = calloc(1, sizeof(pdfmake_hash_ctx_t));
448 0 0         if (!ctx) return NULL;
449            
450 0           ctx->algorithm = alg;
451            
452 0           switch (alg) {
453 0           case PDFMAKE_HASH_SHA1:
454 0           sha1_init(&ctx->sha1);
455 0           break;
456 0           case PDFMAKE_HASH_SHA256:
457 0           sha256_init(&ctx->sha256);
458 0           break;
459 0           case PDFMAKE_HASH_SHA384:
460 0           sha384_init(&ctx->sha512);
461 0           break;
462 0           case PDFMAKE_HASH_SHA512:
463 0           sha512_init(&ctx->sha512);
464 0           break;
465 0           default:
466 0           free(ctx);
467 0           return NULL;
468             }
469            
470 0           return ctx;
471             }
472              
473 0           void pdfmake_hash_update(pdfmake_hash_ctx_t *ctx, const uint8_t *data, size_t len)
474             {
475 0 0         if (!ctx || !data) return;
    0          
476            
477 0           switch (ctx->algorithm) {
478 0           case PDFMAKE_HASH_SHA1:
479 0           sha1_update(&ctx->sha1, data, len);
480 0           break;
481 0           case PDFMAKE_HASH_SHA256:
482 0           sha256_update(&ctx->sha256, data, len);
483 0           break;
484 0           case PDFMAKE_HASH_SHA384:
485             case PDFMAKE_HASH_SHA512:
486 0           sha512_update(&ctx->sha512, data, len);
487 0           break;
488             }
489             }
490              
491 0           size_t pdfmake_hash_final(pdfmake_hash_ctx_t *ctx, uint8_t *digest)
492             {
493 0 0         if (!ctx || !digest) return 0;
    0          
494            
495 0           switch (ctx->algorithm) {
496 0           case PDFMAKE_HASH_SHA1:
497 0           sha1_final(&ctx->sha1, digest);
498 0           return 20;
499 0           case PDFMAKE_HASH_SHA256:
500 0           sha256_final(&ctx->sha256, digest);
501 0           return 32;
502 0           case PDFMAKE_HASH_SHA384:
503 0           sha384_final(&ctx->sha512, digest);
504 0           return 48;
505 0           case PDFMAKE_HASH_SHA512:
506 0           sha512_final(&ctx->sha512, digest);
507 0           return 64;
508 0           default:
509 0           return 0;
510             }
511             }
512              
513 0           void pdfmake_hash_free(pdfmake_hash_ctx_t *ctx)
514             {
515 0 0         if (ctx) {
516 0           memset(ctx, 0, sizeof(*ctx));
517 0           free(ctx);
518             }
519 0           }
520              
521 0           size_t pdfmake_hash(
522             pdfmake_hash_algorithm_t alg,
523             const uint8_t *data,
524             size_t len,
525             uint8_t *digest)
526             {
527 0           pdfmake_hash_ctx_t *ctx = pdfmake_hash_new(alg);
528             size_t result;
529 0 0         if (!ctx) return 0;
530            
531 0           pdfmake_hash_update(ctx, data, len);
532 0           result = pdfmake_hash_final(ctx, digest);
533 0           pdfmake_hash_free(ctx);
534            
535 0           return result;
536             }
537              
538 0           size_t pdfmake_hash_size(pdfmake_hash_algorithm_t alg)
539             {
540 0           switch (alg) {
541 0           case PDFMAKE_HASH_SHA1: return 20;
542 0           case PDFMAKE_HASH_SHA256: return 32;
543 0           case PDFMAKE_HASH_SHA384: return 48;
544 0           case PDFMAKE_HASH_SHA512: return 64;
545 0           default: return 0;
546             }
547             }
548              
549             /*============================================================================
550             * Signature Config
551             *==========================================================================*/
552              
553 0           void pdfmake_sig_config_init(pdfmake_sig_config_t *config)
554             {
555 0 0         if (!config) return;
556              
557 0           memset(config, 0, sizeof(*config));
558 0           config->hash_algorithm = PDFMAKE_HASH_SHA256;
559 0           config->subfilter = PDFMAKE_SUBFILTER_PKCS7_DETACHED;
560 0           config->placeholder_size = 8192;
561             /* Default visible-signature layout shows name + date + reason. */
562 0           config->ap_show_name = 1;
563 0           config->ap_show_date = 1;
564 0           config->ap_show_reason = 1;
565             }
566              
567             /*============================================================================
568             * PKCS#7 Building
569             *==========================================================================*/
570              
571             /* Return the OID string for a digest algorithm. */
572 0           static const char *hash_oid(pdfmake_hash_algorithm_t alg) {
573 0           switch (alg) {
574 0           case PDFMAKE_HASH_SHA1: return OID_SHA1;
575 0           case PDFMAKE_HASH_SHA256: return OID_SHA256;
576 0           case PDFMAKE_HASH_SHA384: return OID_SHA384;
577 0           case PDFMAKE_HASH_SHA512: return OID_SHA512;
578 0           default: return OID_SHA256;
579             }
580             }
581              
582             /* AlgorithmIdentifier ::= SEQUENCE { algorithm OID, parameters NULL } */
583 0           static void write_alg_id_sha(pdfmake_asn1_encoder_t *enc,
584             pdfmake_hash_algorithm_t alg) {
585 0           size_t s = pdfmake_asn1_begin_sequence(enc);
586 0           pdfmake_asn1_write_oid(enc, hash_oid(alg));
587 0           pdfmake_asn1_write_null(enc);
588 0           pdfmake_asn1_end_constructed(enc, s);
589 0           }
590              
591             /* Same but with a provided OID (for signature alg = rsaEncryption). */
592 0           static void write_alg_id_oid(pdfmake_asn1_encoder_t *enc, const char *oid) {
593 0           size_t s = pdfmake_asn1_begin_sequence(enc);
594 0           pdfmake_asn1_write_oid(enc, oid);
595 0           pdfmake_asn1_write_null(enc);
596 0           pdfmake_asn1_end_constructed(enc, s);
597 0           }
598              
599             /* Locate the issuer Name in TBSCertificate and return its raw DER slice
600             * (tag + length + content). The issuer is the 3rd or 4th element of
601             * TBSCertificate (skipping the optional [0] version). */
602 0           static int find_issuer_der(pdfmake_arena_t *arena,
603             const pdfmake_x509_cert_t *cert,
604             const uint8_t **out_bytes, size_t *out_len)
605             {
606             pdfmake_asn1_node_t *tbs;
607             size_t idx;
608             pdfmake_asn1_node_t *first;
609             pdfmake_asn1_node_t *issuer;
610             size_t hdr;
611 0 0         if (!cert->tbs_certificate || cert->tbs_certificate_len == 0) return -1;
    0          
612 0           tbs = pdfmake_asn1_parse(arena, cert->tbs_certificate, cert->tbs_certificate_len);
613 0 0         if (!tbs) return -1;
614              
615 0           idx = 0;
616 0           first = pdfmake_asn1_child_at(tbs, 0);
617 0 0         if (!first) return -1;
618             /* Skip [0] EXPLICIT version if present. */
619 0 0         if ((first->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT
620 0 0         && (first->tag & 0x1F) == 0) {
621 0           idx = 1;
622             }
623 0           idx += 2; /* skip serial + signature algorithm */
624 0           issuer = pdfmake_asn1_child_at(tbs, idx);
625 0 0         if (!issuer) return -1;
626              
627             /* Reconstruct the raw DER (tag + length + content) from issuer->length
628             * plus the header size implied by DER rules. */
629 0           hdr = 2;
630 0 0         if (issuer->length >= 0x80) {
631 0 0         if (issuer->length < 0x100) hdr = 3;
632 0 0         else if (issuer->length < 0x10000) hdr = 4;
633 0           else hdr = 5;
634             }
635 0           *out_bytes = issuer->data - hdr;
636 0           *out_len = hdr + issuer->length;
637 0           return 0;
638             }
639              
640             /* Write SignedAttributes elements (just the contents, without the outer
641             * SET-OF header). Used twice: once to build the bytes that get signed
642             * (re-tagged with universal SET 0x31) and once inside the SignerInfo
643             * (with IMPLICIT [0] tag 0xA0). */
644 0           static void write_signed_attrs_body(pdfmake_asn1_encoder_t *enc,
645             const uint8_t *digest, size_t digest_len,
646             const uint8_t *cert_sha256,
647             time_t now) {
648             size_t a1, a2, a3;
649             size_t v1, v2, v3;
650             size_t a4, v4, sc, certs, eid;
651             /* DER SET-OF: elements ordered ascending by their encoded bytes
652             * (X.690 ยง11.6). All four Attribute SEQUENCEs share the same tag
653             * byte (0x30); they differ in the length byte and then in the OID.
654             * For SHA-256 digest + short cert-hash, the outer SEQUENCE lengths
655             * are roughly:
656             * contentType : 24 (0x18)
657             * signingTime : 28 (0x1C)
658             * messageDigest : 47 (0x2F)
659             * signingCertV2 : 55+ (โ‰ฅ0x37)
660             * so emitting in (ctype, signingTime, messageDigest, signingCertV2)
661             * order produces valid DER for any supported digest algorithm. */
662              
663             /* 1. contentType = id-data */
664 0           a1 = pdfmake_asn1_begin_sequence(enc);
665 0           pdfmake_asn1_write_oid(enc, OID_ID_CONTENT_TYPE);
666 0           v1 = pdfmake_asn1_begin_set(enc);
667 0           pdfmake_asn1_write_oid(enc, OID_ID_DATA);
668 0           pdfmake_asn1_end_constructed(enc, v1);
669 0           pdfmake_asn1_end_constructed(enc, a1);
670              
671             /* 2. signingTime = UTCTime(now) */
672 0           a2 = pdfmake_asn1_begin_sequence(enc);
673 0           pdfmake_asn1_write_oid(enc, OID_ID_SIGNING_TIME);
674 0           v2 = pdfmake_asn1_begin_set(enc);
675 0           pdfmake_asn1_write_utc_time(enc, (int64_t)now);
676 0           pdfmake_asn1_end_constructed(enc, v2);
677 0           pdfmake_asn1_end_constructed(enc, a2);
678              
679             /* 3. messageDigest = OCTET STRING (document digest) */
680 0           a3 = pdfmake_asn1_begin_sequence(enc);
681 0           pdfmake_asn1_write_oid(enc, OID_ID_MESSAGE_DIGEST);
682 0           v3 = pdfmake_asn1_begin_set(enc);
683 0           pdfmake_asn1_write_octet_string(enc, digest, digest_len);
684 0           pdfmake_asn1_end_constructed(enc, v3);
685 0           pdfmake_asn1_end_constructed(enc, a3);
686              
687             /* 4. signingCertificateV2 (PAdES-BES, RFC 5035 ยง3). Minimal form:
688             * SigningCertificateV2 ::= SEQUENCE {
689             * certs SEQUENCE OF ESSCertIDv2 }
690             * ESSCertIDv2 ::= SEQUENCE {
691             * hashAlgorithm AlgorithmIdentifier DEFAULT id-sha256, -- omitted
692             * certHash OCTET STRING } -- SHA-256(cert.DER)
693             * We omit hashAlgorithm (SHA-256 is the default) and issuerSerial
694             * (optional; not required for basic PAdES-BES validation). */
695 0 0         if (cert_sha256) {
696 0           a4 = pdfmake_asn1_begin_sequence(enc);
697 0           pdfmake_asn1_write_oid(enc, OID_AA_SIGNING_CERT_V2);
698 0           v4 = pdfmake_asn1_begin_set(enc);
699 0           sc = pdfmake_asn1_begin_sequence(enc); /* SigningCertificateV2 */
700 0           certs = pdfmake_asn1_begin_sequence(enc); /* SEQUENCE OF ESSCertIDv2 */
701 0           eid = pdfmake_asn1_begin_sequence(enc);
702 0           pdfmake_asn1_write_octet_string(enc, cert_sha256, 32);
703 0           pdfmake_asn1_end_constructed(enc, eid);
704 0           pdfmake_asn1_end_constructed(enc, certs);
705 0           pdfmake_asn1_end_constructed(enc, sc);
706 0           pdfmake_asn1_end_constructed(enc, v4);
707 0           pdfmake_asn1_end_constructed(enc, a4);
708             }
709 0           }
710              
711             /* Build the SET-OF SignedAttributes (universal tag 0x31), hash that DER
712             * with `alg`, and RSA-sign the hash. The intermediate SA buffer is not
713             * returned โ€” the caller re-encodes the same attributes inside SignerInfo
714             * with context tag [0] via write_signed_attrs_body(). */
715             static pdfmake_err_t
716 0           sign_signed_attrs(pdfmake_arena_t *arena,
717             const pdfmake_privkey_t *key,
718             pdfmake_hash_algorithm_t alg,
719             const uint8_t *digest, size_t digest_len,
720             const uint8_t cert_sha256[32],
721             time_t now,
722             uint8_t **out_sig, size_t *out_sig_len)
723             {
724             pdfmake_buf_t sa_buf;
725             pdfmake_asn1_encoder_t sa_enc;
726             size_t sa_start;
727             uint8_t sa_digest[64];
728             size_t sa_digest_len;
729             pdfmake_hash_ctx_t *h;
730             pdfmake_err_t err;
731 0 0         if (pdfmake_buf_init(&sa_buf) != PDFMAKE_OK) return PDFMAKE_ENOMEM;
732              
733 0           pdfmake_asn1_encoder_init(&sa_enc, arena, &sa_buf);
734 0           sa_start = pdfmake_asn1_begin_set(&sa_enc);
735 0           write_signed_attrs_body(&sa_enc, digest, digest_len, cert_sha256, now);
736 0           pdfmake_asn1_end_constructed(&sa_enc, sa_start);
737              
738 0           h = pdfmake_hash_new(alg);
739 0 0         if (!h) { pdfmake_buf_free(&sa_buf); return PDFMAKE_ENOMEM; }
740 0           pdfmake_hash_update(h, pdfmake_buf_data(&sa_buf), pdfmake_buf_len(&sa_buf));
741 0           sa_digest_len = pdfmake_hash_final(h, sa_digest);
742 0           pdfmake_hash_free(h);
743              
744 0           err = pdfmake_rsa_sign(arena, key, alg,
745             sa_digest, sa_digest_len,
746             out_sig, out_sig_len);
747 0           pdfmake_buf_free(&sa_buf);
748 0           return err;
749             }
750              
751             /* Emit the unsignedAttrs [1] IMPLICIT block carrying a single RFC 3161
752             * aa-signatureTimeStampToken attribute. No-op if no token was supplied โ€”
753             * unsignedAttrs is optional and omitting the SET when empty keeps the
754             * DER canonical. */
755             static void
756 0           write_signature_timestamp_attr(pdfmake_asn1_encoder_t *enc,
757             const uint8_t *tst_token, size_t tst_token_len)
758             {
759             size_t ua, ta, tv;
760 0 0         if (!tst_token || tst_token_len == 0) return;
    0          
761              
762 0           ua = pdfmake_asn1_begin_context(enc, 1, 1);
763 0           ta = pdfmake_asn1_begin_sequence(enc);
764 0           pdfmake_asn1_write_oid(enc, OID_AA_TIMESTAMP_TOKEN);
765 0           tv = pdfmake_asn1_begin_set(enc);
766 0           pdfmake_asn1_write_raw(enc, tst_token, tst_token_len);
767 0           pdfmake_asn1_end_constructed(enc, tv);
768 0           pdfmake_asn1_end_constructed(enc, ta);
769 0           pdfmake_asn1_end_constructed(enc, ua);
770             }
771              
772             /* Emit the single SignerInfo (version 1 / IssuerAndSerialNumber variant)
773             * that makes up the signerInfos SET. */
774             static void
775 0           write_signer_info(pdfmake_asn1_encoder_t *enc,
776             const pdfmake_x509_cert_t *cert,
777             const uint8_t *issuer_der, size_t issuer_len,
778             pdfmake_hash_algorithm_t alg,
779             const uint8_t *digest, size_t digest_len,
780             const uint8_t cert_sha256[32],
781             time_t now,
782             const uint8_t *rsa_sig, size_t rsa_sig_len,
783             const uint8_t *tst_token, size_t tst_token_len)
784             {
785             size_t si;
786             size_t iasn;
787             size_t sattrs;
788 0           si = pdfmake_asn1_begin_sequence(enc);
789             /* version = 1 (IssuerAndSerialNumber) */
790 0           pdfmake_asn1_write_int64(enc, 1);
791              
792             /* sid = IssuerAndSerialNumber */
793 0           iasn = pdfmake_asn1_begin_sequence(enc);
794 0           pdfmake_asn1_write_raw(enc, issuer_der, issuer_len);
795 0           pdfmake_asn1_write_integer(enc, cert->serial, cert->serial_len);
796 0           pdfmake_asn1_end_constructed(enc, iasn);
797              
798             /* digestAlgorithm */
799 0           write_alg_id_sha(enc, alg);
800              
801             /* signedAttrs [0] IMPLICIT SET OF Attribute. Rebuilt here with
802             * context tag 0xA0 โ€” the universal-SET form we signed differs
803             * only in the outer tag byte. */
804 0           sattrs = pdfmake_asn1_begin_context(enc, 0, 1);
805 0           write_signed_attrs_body(enc, digest, digest_len, cert_sha256, now);
806 0           pdfmake_asn1_end_constructed(enc, sattrs);
807              
808             /* signatureAlgorithm = rsaEncryption */
809 0           write_alg_id_oid(enc, OID_RSA_ENCRYPTION);
810              
811             /* signature = OCTET STRING of RSA sig */
812 0           pdfmake_asn1_write_octet_string(enc, rsa_sig, rsa_sig_len);
813              
814             /* unsignedAttrs [1] IMPLICIT โ€” optional RFC 3161 timestamp.
815             * Adding this does not change rsa_sig (unsigned attrs are not
816             * covered by the signedAttrs hash). */
817 0           write_signature_timestamp_attr(enc, tst_token, tst_token_len);
818 0           pdfmake_asn1_end_constructed(enc, si);
819 0           }
820              
821 0           pdfmake_err_t pdfmake_pkcs7_build(
822             pdfmake_arena_t *arena,
823             const pdfmake_sig_config_t *config,
824             const uint8_t *digest,
825             size_t digest_len,
826             pdfmake_buf_t *out)
827             {
828             const pdfmake_x509_cert_t *cert;
829             const pdfmake_privkey_t *key;
830             pdfmake_hash_algorithm_t alg;
831             time_t now;
832             uint8_t cert_sha256[32];
833             uint8_t *rsa_sig;
834             size_t rsa_sig_len;
835             pdfmake_err_t err;
836             const uint8_t *issuer_der;
837             size_t issuer_len;
838             pdfmake_asn1_encoder_t enc;
839             size_t ci, ci_content, sd, das, eci, certs, sis;
840 0 0         if (!arena || !config || !digest || !out) return PDFMAKE_EINVAL;
    0          
    0          
    0          
841 0 0         if (!config->identity || !config->identity->privkey ||
    0          
842 0 0         !config->identity->cert) return PDFMAKE_EINVAL;
843              
844 0           cert = config->identity->cert;
845 0           key = config->identity->privkey;
846 0           alg = config->hash_algorithm
847             ? config->hash_algorithm
848 0 0         : PDFMAKE_HASH_SHA256;
849 0           now = config->signing_time > 0
850             ? (time_t)config->signing_time
851 0 0         : time(NULL);
852              
853             /* PAdES-BES signingCertificateV2 wants SHA-256(cert.DER). */
854             {
855 0           pdfmake_hash_ctx_t *h = pdfmake_hash_new(PDFMAKE_HASH_SHA256);
856 0 0         if (!h) return PDFMAKE_ENOMEM;
857 0           pdfmake_hash_update(h, cert->der, cert->der_len);
858 0           pdfmake_hash_final(h, cert_sha256);
859 0           pdfmake_hash_free(h);
860             }
861              
862             /* Build signedAttrs, hash, and RSA-sign the hash. */
863 0           rsa_sig = NULL;
864 0           rsa_sig_len = 0;
865 0           err = sign_signed_attrs(arena, key, alg,
866             digest, digest_len, cert_sha256, now,
867             &rsa_sig, &rsa_sig_len);
868 0 0         if (err != PDFMAKE_OK) return err;
869              
870             /* Locate the issuer Name DER inside the certificate. */
871 0           issuer_der = NULL;
872 0           issuer_len = 0;
873 0 0         if (find_issuer_der(arena, cert, &issuer_der, &issuer_len) != 0) {
874 0           return PDFMAKE_EINVAL;
875             }
876              
877             /* Emit ContentInfo { id-signedData, [0] EXPLICIT SignedData }. */
878 0           pdfmake_asn1_encoder_init(&enc, arena, out);
879              
880 0           ci = pdfmake_asn1_begin_sequence(&enc);
881 0           pdfmake_asn1_write_oid(&enc, OID_ID_SIGNED_DATA);
882 0           ci_content = pdfmake_asn1_begin_context(&enc, 0, 1); /* [0] EXPLICIT */
883              
884             /* SignedData */
885 0           sd = pdfmake_asn1_begin_sequence(&enc);
886             /* version = 1 (IssuerAndSerialNumber SignerIdentifier) */
887 0           pdfmake_asn1_write_int64(&enc, 1);
888              
889             /* digestAlgorithms SET */
890 0           das = pdfmake_asn1_begin_set(&enc);
891 0           write_alg_id_sha(&enc, alg);
892 0           pdfmake_asn1_end_constructed(&enc, das);
893              
894             /* encapContentInfo = { id-data, no eContent (detached) } */
895 0           eci = pdfmake_asn1_begin_sequence(&enc);
896 0           pdfmake_asn1_write_oid(&enc, OID_ID_DATA);
897 0           pdfmake_asn1_end_constructed(&enc, eci);
898              
899             /* certificates [0] IMPLICIT SET OF Certificate */
900 0           certs = pdfmake_asn1_begin_context(&enc, 0, 1);
901 0           pdfmake_asn1_write_raw(&enc, cert->der, cert->der_len);
902 0           pdfmake_asn1_end_constructed(&enc, certs);
903              
904             /* signerInfos SET OF SignerInfo (exactly one) */
905 0           sis = pdfmake_asn1_begin_set(&enc);
906 0           write_signer_info(&enc, cert, issuer_der, issuer_len,
907             alg, digest, digest_len, cert_sha256, now,
908             rsa_sig, rsa_sig_len,
909 0           config->tst_token, config->tst_token_len);
910 0           pdfmake_asn1_end_constructed(&enc, sis);
911              
912 0           pdfmake_asn1_end_constructed(&enc, sd);
913 0           pdfmake_asn1_end_constructed(&enc, ci_content);
914 0           pdfmake_asn1_end_constructed(&enc, ci);
915              
916 0           return PDFMAKE_OK;
917             }
918              
919             /*============================================================================
920             * RFC 3161 TimeStampReq / TimeStampResp helpers
921             *==========================================================================*/
922              
923 0           pdfmake_err_t pdfmake_tsa_build_request(
924             pdfmake_arena_t *arena,
925             pdfmake_hash_algorithm_t hash_alg,
926             const uint8_t *digest, size_t digest_len,
927             int cert_req,
928             pdfmake_buf_t *out)
929             {
930             pdfmake_asn1_encoder_t enc;
931             size_t req;
932             size_t mi;
933 0 0         if (!arena || !digest || !out) return PDFMAKE_EINVAL;
    0          
    0          
934              
935 0           pdfmake_asn1_encoder_init(&enc, arena, out);
936              
937 0           req = pdfmake_asn1_begin_sequence(&enc);
938             /* version = 1 */
939 0           pdfmake_asn1_write_int64(&enc, 1);
940              
941             /* messageImprint = SEQUENCE { algorithmIdentifier, OCTET STRING } */
942 0           mi = pdfmake_asn1_begin_sequence(&enc);
943 0           write_alg_id_sha(&enc, hash_alg);
944 0           pdfmake_asn1_write_octet_string(&enc, digest, digest_len);
945 0           pdfmake_asn1_end_constructed(&enc, mi);
946              
947             /* certReq BOOLEAN โ€” omit when FALSE (DEFAULT); emit when TRUE. */
948 0 0         if (cert_req) {
949 0           pdfmake_asn1_write_bool(&enc, 1);
950             }
951 0           pdfmake_asn1_end_constructed(&enc, req);
952              
953 0           return PDFMAKE_OK;
954             }
955              
956 0           int pdfmake_tsa_parse_response(
957             pdfmake_arena_t *arena,
958             const uint8_t *resp_der, size_t resp_len,
959             const uint8_t **token, size_t *token_len)
960             {
961             pdfmake_asn1_node_t *root;
962             pdfmake_asn1_node_t *status_info;
963             pdfmake_asn1_node_t *status;
964             int64_t status_value;
965             pdfmake_asn1_node_t *tst;
966             size_t hdr;
967 0 0         if (!arena || !resp_der || !token || !token_len) return -1;
    0          
    0          
    0          
968 0           *token = NULL;
969 0           *token_len = 0;
970              
971             /* TimeStampResp ::= SEQUENCE {
972             * status PKIStatusInfo,
973             * timeStampToken TimeStampToken OPTIONAL }
974             * PKIStatusInfo ::= SEQUENCE {
975             * status PKIStatus (INTEGER 0..4),
976             * statusString PKIFreeText OPTIONAL,
977             * failInfo PKIFailureInfo OPTIONAL } */
978 0           root = pdfmake_asn1_parse(arena, resp_der, resp_len);
979 0 0         if (!root) return -1;
980              
981 0           status_info = pdfmake_asn1_child_at(root, 0);
982 0 0         if (!status_info) return -1;
983              
984 0           status = pdfmake_asn1_child_at(status_info, 0);
985 0           status_value = -1;
986 0 0         if (status && pdfmake_asn1_get_int64(status, &status_value) != 0) {
    0          
987 0           return -1;
988             }
989             /* 0 = granted, 1 = grantedWithMods; anything else is a rejection. */
990 0 0         if (status_value != 0 && status_value != 1) return -2;
    0          
991              
992 0           tst = pdfmake_asn1_child_at(root, 1);
993 0 0         if (!tst) return -1;
994              
995             /* Compute the raw DER slice of the timeStampToken (tag + len + content).
996             * The parser's node->data points at the content; we need the header
997             * length to recover the outer tag+len bytes. */
998 0           hdr = 2;
999 0 0         if (tst->length >= 0x80) {
1000 0 0         if (tst->length < 0x100) hdr = 3;
1001 0 0         else if (tst->length < 0x10000) hdr = 4;
1002 0           else hdr = 5;
1003             }
1004 0           *token = tst->data - hdr;
1005 0           *token_len = hdr + tst->length;
1006 0           return 0;
1007             }
1008              
1009 0           int pdfmake_cms_extract_signature(
1010             pdfmake_arena_t *arena,
1011             const uint8_t *cms_der, size_t cms_len,
1012             const uint8_t **sig_bytes, size_t *sig_len)
1013             {
1014             pdfmake_asn1_node_t *root;
1015             pdfmake_asn1_node_t *content;
1016             pdfmake_asn1_node_t *sd;
1017             pdfmake_asn1_node_t *sinfos;
1018             size_t nc;
1019             size_t i;
1020             pdfmake_asn1_node_t *si;
1021             size_t sic;
1022 0 0         if (!arena || !cms_der || !sig_bytes || !sig_len) return -1;
    0          
    0          
    0          
1023 0           *sig_bytes = NULL;
1024 0           *sig_len = 0;
1025              
1026             /* CMS structure:
1027             * ContentInfo SEQUENCE {
1028             * contentType OID,
1029             * content [0] EXPLICIT SignedData {
1030             * version,
1031             * digestAlgorithms,
1032             * encapContentInfo,
1033             * certificates [0] IMPLICIT,
1034             * signerInfos SET OF SignerInfo {
1035             * version,
1036             * sid,
1037             * digestAlgorithm,
1038             * signedAttrs [0] IMPLICIT,
1039             * signatureAlgorithm,
1040             * signature โ† OCTET STRING we want,
1041             * unsignedAttrs [1] IMPLICIT OPTIONAL } } } */
1042 0           root = pdfmake_asn1_parse(arena, cms_der, cms_len);
1043 0 0         if (!root) return -1;
1044              
1045 0           content = pdfmake_asn1_child_at(root, 1); /* [0] wrapper */
1046 0 0         if (!content) return -1;
1047 0           sd = pdfmake_asn1_child_at(content, 0); /* SignedData */
1048 0 0         if (!sd) return -1;
1049              
1050             /* Walk SignedData children; the last is signerInfos SET. */
1051 0           sinfos = NULL;
1052 0           nc = pdfmake_asn1_child_count(sd);
1053 0 0         for (i = 0; i < nc; i++) {
1054 0           pdfmake_asn1_node_t *c = pdfmake_asn1_child_at(sd, i);
1055 0 0         if (c && (c->tag & 0x1F) == ASN1_TAG_SET) sinfos = c;
    0          
1056             }
1057 0 0         if (!sinfos) return -1;
1058              
1059 0           si = pdfmake_asn1_child_at(sinfos, 0);
1060 0 0         if (!si) return -1;
1061              
1062             /* SignerInfo order: version, sid, digestAlg, [signedAttrs], sigAlg,
1063             * signature, [unsignedAttrs]. The signature is the first OCTET STRING
1064             * child (scanning past any context-tagged signedAttrs). */
1065 0           sic = pdfmake_asn1_child_count(si);
1066 0 0         for (i = 0; i < sic; i++) {
1067 0           pdfmake_asn1_node_t *c = pdfmake_asn1_child_at(si, i);
1068 0 0         if (c && c->tag == ASN1_TAG_OCTET_STRING) {
    0          
1069 0           *sig_bytes = c->data;
1070 0           *sig_len = c->length;
1071 0           return 0;
1072             }
1073             }
1074 0           return -1;
1075             }
1076              
1077 0           static size_t asn1_header_len_for_content_len(size_t content_len)
1078             {
1079 0 0         if (content_len < 0x80) return 2;
1080 0 0         if (content_len < 0x100) return 3;
1081 0 0         if (content_len < 0x10000) return 4;
1082 0 0         if (content_len < 0x1000000) return 5;
1083 0           return 6;
1084             }
1085              
1086 0           static int asn1_der_object_len(const uint8_t *der, size_t der_len, size_t *out_len)
1087             {
1088             size_t nbytes;
1089             size_t i;
1090             size_t content_len;
1091 0 0         if (!der || der_len < 2 || !out_len) return -1;
    0          
    0          
1092              
1093 0 0         if ((der[1] & 0x80) == 0) {
1094 0           content_len = der[1];
1095 0 0         if (2 + content_len > der_len) return -1;
1096 0           *out_len = 2 + content_len;
1097 0           return 0;
1098             }
1099              
1100 0           nbytes = (size_t)(der[1] & 0x7F);
1101 0 0         if (nbytes == 0 || nbytes > sizeof(size_t) || 2 + nbytes > der_len) return -1;
    0          
    0          
1102              
1103 0           content_len = 0;
1104 0 0         for (i = 0; i < nbytes; i++) {
1105 0           content_len = (content_len << 8) | der[2 + i];
1106             }
1107 0 0         if (2 + nbytes + content_len > der_len) return -1;
1108 0           *out_len = 2 + nbytes + content_len;
1109 0           return 0;
1110             }
1111              
1112 0           static int hash_alg_from_oid_node(const pdfmake_asn1_node_t *oid_node,
1113             pdfmake_hash_algorithm_t *out)
1114             {
1115 0 0         if (!oid_node || !out) return -1;
    0          
1116 0 0         if (pdfmake_asn1_oid_equals(oid_node, OID_SHA1)) {
1117 0           *out = PDFMAKE_HASH_SHA1;
1118 0           return 0;
1119             }
1120 0 0         if (pdfmake_asn1_oid_equals(oid_node, OID_SHA256)) {
1121 0           *out = PDFMAKE_HASH_SHA256;
1122 0           return 0;
1123             }
1124 0 0         if (pdfmake_asn1_oid_equals(oid_node, OID_SHA384)) {
1125 0           *out = PDFMAKE_HASH_SHA384;
1126 0           return 0;
1127             }
1128 0 0         if (pdfmake_asn1_oid_equals(oid_node, OID_SHA512)) {
1129 0           *out = PDFMAKE_HASH_SHA512;
1130 0           return 0;
1131             }
1132 0           return -1;
1133             }
1134              
1135 0           static int serial_equal_trimmed(const uint8_t *a, size_t a_len,
1136             const uint8_t *b, size_t b_len)
1137             {
1138 0 0         while (a_len > 0 && *a == 0) { a++; a_len--; }
    0          
1139 0 0         while (b_len > 0 && *b == 0) { b++; b_len--; }
    0          
1140 0 0         if (a_len != b_len) return 0;
1141 0           return memcmp(a, b, a_len) == 0;
1142             }
1143              
1144 0           static pdfmake_asn1_node_t *find_signerinfo_signed_attrs(pdfmake_asn1_node_t *si)
1145             {
1146             size_t i;
1147             size_t n;
1148             pdfmake_asn1_node_t *c;
1149 0 0         if (!si) return NULL;
1150 0           n = pdfmake_asn1_child_count(si);
1151 0 0         for (i = 0; i < n; i++) {
1152 0           c = pdfmake_asn1_child_at(si, i);
1153 0 0         if (c && (c->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT &&
    0          
1154 0 0         (c->tag & 0x1F) == 0) {
1155 0           return c;
1156             }
1157             }
1158 0           return NULL;
1159             }
1160              
1161 0           static pdfmake_asn1_node_t *find_signerinfo_unsigned_attrs(pdfmake_asn1_node_t *si)
1162             {
1163             size_t i;
1164             size_t n;
1165             pdfmake_asn1_node_t *c;
1166 0 0         if (!si) return NULL;
1167 0           n = pdfmake_asn1_child_count(si);
1168 0 0         for (i = 0; i < n; i++) {
1169 0           c = pdfmake_asn1_child_at(si, i);
1170 0 0         if (c && (c->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT &&
    0          
1171 0 0         (c->tag & 0x1F) == 1) {
1172 0           return c;
1173             }
1174             }
1175 0           return NULL;
1176             }
1177              
1178 0           pdfmake_pkcs7_t *pdfmake_pkcs7_parse(
1179             pdfmake_arena_t *arena,
1180             const uint8_t *der,
1181             size_t len)
1182             {
1183             pdfmake_asn1_node_t *root;
1184             pdfmake_asn1_node_t *content;
1185             pdfmake_asn1_node_t *sd;
1186             pdfmake_asn1_node_t *das;
1187             pdfmake_asn1_node_t *alg_seq;
1188             pdfmake_asn1_node_t *alg_oid;
1189             pdfmake_asn1_node_t *certs_ctx;
1190             pdfmake_asn1_node_t *sinfos;
1191             pdfmake_asn1_node_t *si;
1192             pdfmake_asn1_node_t *sid;
1193             pdfmake_asn1_node_t *sid_serial;
1194             const uint8_t *sid_serial_bytes;
1195             size_t sid_serial_len;
1196             pdfmake_asn1_node_t *signed_attrs;
1197             pdfmake_asn1_node_t *unsigned_attrs;
1198             pdfmake_asn1_node_t *c;
1199             pdfmake_asn1_node_t *attr;
1200             pdfmake_asn1_node_t *oid;
1201             pdfmake_asn1_node_t *vals;
1202             pdfmake_asn1_node_t *v;
1203             size_t n;
1204             size_t i;
1205             size_t hdr;
1206             const uint8_t *cert_der;
1207             size_t cert_der_len;
1208             pdfmake_x509_cert_t *cert;
1209             pdfmake_x509_cert_t *tail;
1210             pdfmake_cert_chain_t *chain;
1211             pdfmake_pkcs7_t *pkcs7;
1212             pdfmake_hash_algorithm_t ha;
1213 0 0         if (!arena || !der || len == 0) return NULL;
    0          
    0          
1214              
1215             /* Parse ASN.1 */
1216 0           root = pdfmake_asn1_parse(arena, der, len);
1217 0 0         if (!root) return NULL;
1218              
1219 0           pkcs7 = pdfmake_arena_alloc(arena, sizeof(pdfmake_pkcs7_t));
1220 0 0         if (!pkcs7) return NULL;
1221 0           memset(pkcs7, 0, sizeof(*pkcs7));
1222            
1223 0           pkcs7->arena = arena;
1224 0           pkcs7->der = der;
1225 0           pkcs7->der_len = len;
1226              
1227 0           content = pdfmake_asn1_child_at(root, 1);
1228 0 0         if (!content) return pkcs7;
1229 0           sd = pdfmake_asn1_child_at(content, 0);
1230 0 0         if (!sd) return pkcs7;
1231              
1232             /* digestAlgorithms SET */
1233 0           das = pdfmake_asn1_child_at(sd, 1);
1234 0 0         if (das && pdfmake_asn1_is_set(das)) {
    0          
1235 0           alg_seq = pdfmake_asn1_child_at(das, 0);
1236 0 0         alg_oid = alg_seq ? pdfmake_asn1_child_at(alg_seq, 0) : NULL;
1237 0 0         if (hash_alg_from_oid_node(alg_oid, &ha) == 0) {
1238 0           pkcs7->hash_alg = ha;
1239             } else {
1240 0           pkcs7->hash_alg = PDFMAKE_HASH_SHA256;
1241             }
1242             } else {
1243 0           pkcs7->hash_alg = PDFMAKE_HASH_SHA256;
1244             }
1245              
1246             /* certificates [0] */
1247 0           certs_ctx = NULL;
1248 0           sinfos = NULL;
1249 0           n = pdfmake_asn1_child_count(sd);
1250 0 0         for (i = 0; i < n; i++) {
1251 0           c = pdfmake_asn1_child_at(sd, i);
1252 0 0         if (!c) continue;
1253 0 0         if ((c->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT && (c->tag & 0x1F) == 0) {
    0          
1254 0           certs_ctx = c;
1255             }
1256 0 0         if ((c->tag & ASN1_CLASS_MASK) == ASN1_CLASS_UNIVERSAL &&
1257 0 0         (c->tag & 0x1F) == ASN1_TAG_SET) {
1258 0           sinfos = c;
1259             }
1260             }
1261              
1262 0           chain = pdfmake_arena_alloc(arena, sizeof(*chain));
1263 0 0         if (chain) {
1264 0           memset(chain, 0, sizeof(*chain));
1265 0           chain->arena = arena;
1266             }
1267 0           tail = NULL;
1268 0 0         if (certs_ctx && chain) {
    0          
1269 0           c = certs_ctx->children;
1270 0 0         while (c) {
1271 0           hdr = asn1_header_len_for_content_len(c->length);
1272 0           cert_der = c->data - hdr;
1273 0           cert_der_len = hdr + c->length;
1274 0           cert = pdfmake_x509_parse_der(arena, cert_der, cert_der_len);
1275 0 0         if (cert) {
1276 0 0         if (!chain->certs) chain->certs = cert;
1277 0 0         if (tail) tail->next = cert;
1278 0           tail = cert;
1279 0           chain->count++;
1280             }
1281 0           c = c->next;
1282             }
1283             }
1284 0           pkcs7->certs = chain;
1285              
1286 0 0         si = sinfos ? pdfmake_asn1_child_at(sinfos, 0) : NULL;
1287 0 0         if (!si) return pkcs7;
1288              
1289             /* sid serial for signer cert match */
1290 0           sid = pdfmake_asn1_child_at(si, 1);
1291 0 0         sid_serial = sid ? pdfmake_asn1_child_at(sid, 1) : NULL;
1292 0 0         sid_serial_bytes = sid_serial ? sid_serial->data : NULL;
1293 0 0         sid_serial_len = sid_serial ? sid_serial->length : 0;
1294              
1295             /* signature OCTET STRING */
1296 0           n = pdfmake_asn1_child_count(si);
1297 0 0         for (i = 0; i < n; i++) {
1298 0           c = pdfmake_asn1_child_at(si, i);
1299 0 0         if (c && c->tag == ASN1_TAG_OCTET_STRING) {
    0          
1300 0           pkcs7->signature = pdfmake_arena_memdup(arena, c->data, c->length);
1301 0           pkcs7->signature_len = c->length;
1302 0           break;
1303             }
1304             }
1305              
1306 0           signed_attrs = find_signerinfo_signed_attrs(si);
1307 0 0         if (signed_attrs) {
1308 0           attr = signed_attrs->children;
1309 0 0         while (attr) {
1310 0 0         if (pdfmake_asn1_is_sequence(attr)) {
1311 0           oid = pdfmake_asn1_child_at(attr, 0);
1312 0           vals = pdfmake_asn1_child_at(attr, 1);
1313 0 0         if (oid && vals && pdfmake_asn1_is_set(vals)) {
    0          
    0          
1314 0           v = pdfmake_asn1_child_at(vals, 0);
1315 0 0         if (v && pdfmake_asn1_oid_equals(oid, OID_ID_MESSAGE_DIGEST) &&
    0          
1316 0 0         v->tag == ASN1_TAG_OCTET_STRING) {
1317 0           pkcs7->message_digest = pdfmake_arena_memdup(arena, v->data, v->length);
1318 0           pkcs7->message_digest_len = v->length;
1319 0 0         } else if (v && pdfmake_asn1_oid_equals(oid, OID_ID_SIGNING_TIME)) {
    0          
1320             int64_t ts;
1321 0 0         if (pdfmake_asn1_get_time(v, &ts) == 0) {
1322 0           pkcs7->signing_time = ts;
1323             }
1324             }
1325             }
1326             }
1327 0           attr = attr->next;
1328             }
1329             }
1330              
1331             /* timestamp token from unsigned attrs (if present) */
1332 0           unsigned_attrs = find_signerinfo_unsigned_attrs(si);
1333 0 0         if (unsigned_attrs) {
1334 0           attr = unsigned_attrs->children;
1335 0 0         while (attr) {
1336 0 0         if (pdfmake_asn1_is_sequence(attr)) {
1337 0           oid = pdfmake_asn1_child_at(attr, 0);
1338 0           vals = pdfmake_asn1_child_at(attr, 1);
1339 0 0         if (oid && vals && pdfmake_asn1_is_set(vals) &&
    0          
1340 0           pdfmake_asn1_oid_equals(oid, OID_AA_TIMESTAMP_TOKEN)) {
1341 0           v = pdfmake_asn1_child_at(vals, 0);
1342 0 0         if (v) {
1343 0           hdr = asn1_header_len_for_content_len(v->length);
1344 0           pkcs7->timestamp_token =
1345 0           pdfmake_arena_memdup(arena, v->data - hdr, hdr + v->length);
1346 0           pkcs7->timestamp_token_len = hdr + v->length;
1347             }
1348             }
1349             }
1350 0           attr = attr->next;
1351             }
1352             }
1353              
1354             /* choose signer cert by serial match, fallback first cert */
1355 0 0         pkcs7->signer_cert = chain ? chain->certs : NULL;
1356 0 0         cert = chain ? chain->certs : NULL;
1357 0 0         while (cert) {
1358 0 0         if (sid_serial_bytes && sid_serial_len > 0 && cert->serial && cert->serial_len > 0 &&
    0          
    0          
1359 0           serial_equal_trimmed(cert->serial, cert->serial_len,
1360             sid_serial_bytes, sid_serial_len)) {
1361 0           pkcs7->signer_cert = cert;
1362 0           break;
1363             }
1364 0           cert = cert->next;
1365             }
1366            
1367 0           return pkcs7;
1368             }
1369              
1370 0           pdfmake_err_t pdfmake_pkcs7_verify(
1371             const pdfmake_pkcs7_t *pkcs7,
1372             const uint8_t *digest,
1373             size_t digest_len)
1374             {
1375             pdfmake_asn1_node_t *root;
1376             pdfmake_asn1_node_t *content;
1377             pdfmake_asn1_node_t *sd;
1378             pdfmake_asn1_node_t *sinfos;
1379             pdfmake_asn1_node_t *si;
1380             pdfmake_asn1_node_t *signed_attrs;
1381             size_t n;
1382             size_t i;
1383             pdfmake_asn1_node_t *c;
1384             size_t sa_len;
1385             uint8_t *sa_der;
1386             size_t hdr;
1387             pdfmake_hash_ctx_t *h;
1388             uint8_t sa_digest[64];
1389             size_t sa_digest_len;
1390             pdfmake_err_t err;
1391 0 0         if (!pkcs7 || !digest) return PDFMAKE_EINVAL;
    0          
1392              
1393 0 0         if (!pkcs7->signer_cert || !pkcs7->signature || pkcs7->signature_len == 0) {
    0          
    0          
1394 0           return PDFMAKE_EINVAL;
1395             }
1396              
1397             /* 1) messageDigest signed attribute must match the provided digest */
1398 0 0         if (!pkcs7->message_digest || pkcs7->message_digest_len != digest_len ||
    0          
1399 0 0         memcmp(pkcs7->message_digest, digest, digest_len) != 0) {
1400 0           return PDFMAKE_EINVAL;
1401             }
1402              
1403             /* 2) Find SignerInfo.signedAttrs and hash it as universal SET (0x31) */
1404 0           root = pdfmake_asn1_parse(pkcs7->arena, pkcs7->der, pkcs7->der_len);
1405 0 0         if (!root) return PDFMAKE_EINVAL;
1406 0           content = pdfmake_asn1_child_at(root, 1);
1407 0 0         if (!content) return PDFMAKE_EINVAL;
1408 0           sd = pdfmake_asn1_child_at(content, 0);
1409 0 0         if (!sd) return PDFMAKE_EINVAL;
1410              
1411 0           sinfos = NULL;
1412 0           n = pdfmake_asn1_child_count(sd);
1413 0 0         for (i = 0; i < n; i++) {
1414 0           c = pdfmake_asn1_child_at(sd, i);
1415 0 0         if (c && (c->tag & ASN1_CLASS_MASK) == ASN1_CLASS_UNIVERSAL &&
    0          
1416 0 0         (c->tag & 0x1F) == ASN1_TAG_SET) {
1417 0           sinfos = c;
1418             }
1419             }
1420 0 0         if (!sinfos) return PDFMAKE_EINVAL;
1421 0           si = pdfmake_asn1_child_at(sinfos, 0);
1422 0 0         if (!si) return PDFMAKE_EINVAL;
1423 0           signed_attrs = find_signerinfo_signed_attrs(si);
1424 0 0         if (!signed_attrs) return PDFMAKE_EINVAL;
1425              
1426 0           hdr = asn1_header_len_for_content_len(signed_attrs->length);
1427 0           sa_len = hdr + signed_attrs->length;
1428 0           sa_der = pdfmake_arena_alloc(pkcs7->arena, sa_len);
1429 0 0         if (!sa_der) return PDFMAKE_ENOMEM;
1430 0           sa_der[0] = (uint8_t)(ASN1_TAG_SET | ASN1_CONSTRUCTED);
1431 0 0         if (signed_attrs->length < 0x80) {
1432 0           sa_der[1] = (uint8_t)signed_attrs->length;
1433 0           memcpy(sa_der + 2, signed_attrs->data, signed_attrs->length);
1434 0 0         } else if (signed_attrs->length < 0x100) {
1435 0           sa_der[1] = 0x81;
1436 0           sa_der[2] = (uint8_t)signed_attrs->length;
1437 0           memcpy(sa_der + 3, signed_attrs->data, signed_attrs->length);
1438 0 0         } else if (signed_attrs->length < 0x10000) {
1439 0           sa_der[1] = 0x82;
1440 0           sa_der[2] = (uint8_t)(signed_attrs->length >> 8);
1441 0           sa_der[3] = (uint8_t)(signed_attrs->length & 0xFF);
1442 0           memcpy(sa_der + 4, signed_attrs->data, signed_attrs->length);
1443             } else {
1444 0           sa_der[1] = 0x83;
1445 0           sa_der[2] = (uint8_t)(signed_attrs->length >> 16);
1446 0           sa_der[3] = (uint8_t)(signed_attrs->length >> 8);
1447 0           sa_der[4] = (uint8_t)(signed_attrs->length & 0xFF);
1448 0           memcpy(sa_der + 5, signed_attrs->data, signed_attrs->length);
1449             }
1450              
1451 0           h = pdfmake_hash_new(pkcs7->hash_alg);
1452 0 0         if (!h) return PDFMAKE_ENOMEM;
1453 0           pdfmake_hash_update(h, sa_der, sa_len);
1454 0           sa_digest_len = pdfmake_hash_final(h, sa_digest);
1455 0           pdfmake_hash_free(h);
1456              
1457             /* 3) Verify SignerInfo.signature over the signedAttrs hash */
1458 0 0         if (pkcs7->signer_cert->pubkey.algorithm == PDFMAKE_PK_RSA) {
1459 0           err = pdfmake_rsa_verify(&pkcs7->signer_cert->pubkey,
1460 0           pkcs7->hash_alg,
1461             sa_digest,
1462             sa_digest_len,
1463 0           pkcs7->signature,
1464 0           pkcs7->signature_len);
1465 0 0         if (err != PDFMAKE_OK) return err;
1466 0 0         } else if (pkcs7->signer_cert->pubkey.algorithm == PDFMAKE_PK_ECDSA) {
1467 0           err = pdfmake_ecdsa_verify(&pkcs7->signer_cert->pubkey,
1468 0           pkcs7->hash_alg,
1469             sa_digest,
1470             sa_digest_len,
1471 0           pkcs7->signature,
1472 0           pkcs7->signature_len);
1473 0 0         if (err != PDFMAKE_OK) return err;
1474             } else {
1475 0           return PDFMAKE_EINVAL;
1476             }
1477              
1478             /* 4) Basic cert checks + chain checks (currently lightweight in x509) */
1479 0           if (!pdfmake_x509_is_valid(pkcs7->signer_cert, 0) ||
1480 0           !pdfmake_x509_can_sign_documents(pkcs7->signer_cert)) {
1481 0           return PDFMAKE_EINVAL;
1482             }
1483 0 0         if (pkcs7->certs && pdfmake_x509_verify_chain(pkcs7->certs, NULL) != PDFMAKE_OK) {
    0          
1484 0           return PDFMAKE_EINVAL;
1485             }
1486              
1487 0           return PDFMAKE_OK;
1488             }
1489              
1490             /*============================================================================
1491             * RSA Signature (Stub - requires bignum library for full implementation)
1492             *==========================================================================*/
1493              
1494             /* DER-encoded DigestInfo prefix for PKCS#1 v1.5 signatures (RFC 8017 ยง9.2).
1495             * These are the bytes before the raw digest: SEQUENCE { AlgorithmIdentifier,
1496             * OCTET STRING }. Following this prefix you append the digest bytes. */
1497             static const uint8_t DIGEST_INFO_SHA1[] = {
1498             0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A,
1499             0x05, 0x00, 0x04, 0x14
1500             };
1501             static const uint8_t DIGEST_INFO_SHA256[] = {
1502             0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
1503             0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
1504             };
1505             static const uint8_t DIGEST_INFO_SHA384[] = {
1506             0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
1507             0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30
1508             };
1509             static const uint8_t DIGEST_INFO_SHA512[] = {
1510             0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
1511             0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
1512             };
1513              
1514 0           static int rsa_digest_info(pdfmake_hash_algorithm_t alg,
1515             const uint8_t **prefix, size_t *prefix_len,
1516             size_t *digest_len)
1517             {
1518 0           switch (alg) {
1519 0           case PDFMAKE_HASH_SHA1:
1520 0           *prefix = DIGEST_INFO_SHA1;
1521 0           *prefix_len = sizeof(DIGEST_INFO_SHA1);
1522 0           *digest_len = 20;
1523 0           return 0;
1524 0           case PDFMAKE_HASH_SHA256:
1525 0           *prefix = DIGEST_INFO_SHA256;
1526 0           *prefix_len = sizeof(DIGEST_INFO_SHA256);
1527 0           *digest_len = 32;
1528 0           return 0;
1529 0           case PDFMAKE_HASH_SHA384:
1530 0           *prefix = DIGEST_INFO_SHA384;
1531 0           *prefix_len = sizeof(DIGEST_INFO_SHA384);
1532 0           *digest_len = 48;
1533 0           return 0;
1534 0           case PDFMAKE_HASH_SHA512:
1535 0           *prefix = DIGEST_INFO_SHA512;
1536 0           *prefix_len = sizeof(DIGEST_INFO_SHA512);
1537 0           *digest_len = 64;
1538 0           return 0;
1539 0           default:
1540 0           return -1;
1541             }
1542             }
1543              
1544 0           pdfmake_err_t pdfmake_rsa_sign(
1545             pdfmake_arena_t *arena,
1546             const pdfmake_privkey_t *key,
1547             pdfmake_hash_algorithm_t hash_alg,
1548             const uint8_t *digest,
1549             size_t digest_len,
1550             uint8_t **signature,
1551             size_t *sig_len)
1552             {
1553             const uint8_t *di_prefix;
1554             size_t di_prefix_len;
1555             size_t expected_digest_len;
1556             const uint8_t *n_bytes;
1557             size_t n_len;
1558             size_t k;
1559             size_t t;
1560             uint8_t *em;
1561             size_t ps_len;
1562             pdfmake_bn_t m, d, n, sig_bn;
1563             uint8_t *sig_bytes;
1564 0 0         if (!arena || !key || !digest || !signature || !sig_len) {
    0          
    0          
    0          
    0          
1565 0           return PDFMAKE_EINVAL;
1566             }
1567 0 0         if (key->algorithm != PDFMAKE_PK_RSA) return PDFMAKE_EINVAL;
1568              
1569 0 0         if (rsa_digest_info(hash_alg, &di_prefix, &di_prefix_len,
1570             &expected_digest_len) != 0) {
1571 0           return PDFMAKE_EINVAL;
1572             }
1573 0 0         if (digest_len != expected_digest_len) return PDFMAKE_EINVAL;
1574              
1575             /* Key size in bytes (strip any leading-zero padding in the modulus). */
1576 0           n_bytes = key->rsa.modulus;
1577 0           n_len = key->rsa.modulus_len;
1578 0 0         while (n_len > 0 && n_bytes[0] == 0) { n_bytes++; n_len--; }
    0          
1579 0 0         if (n_len == 0) return PDFMAKE_EINVAL;
1580              
1581 0           k = n_len; /* bytes in the modulus */
1582 0           t = di_prefix_len + digest_len; /* DigestInfo length */
1583 0 0         if (t + 11 > k) return PDFMAKE_EINVAL; /* RFC 8017 ยง9.2 */
1584              
1585             /* Build EM = 0x00 || 0x01 || PS || 0x00 || DigestInfo (RFC 8017) */
1586 0           em = pdfmake_arena_alloc(arena, k);
1587 0 0         if (!em) return PDFMAKE_ENOMEM;
1588 0           em[0] = 0x00;
1589 0           em[1] = 0x01;
1590 0           ps_len = k - t - 3;
1591 0           memset(em + 2, 0xFF, ps_len);
1592 0           em[2 + ps_len] = 0x00;
1593 0           memcpy(em + 3 + ps_len, di_prefix, di_prefix_len);
1594 0           memcpy(em + 3 + ps_len + di_prefix_len, digest, digest_len);
1595              
1596             /* RSA: sig = EM^d mod n */
1597 0 0         if (pdfmake_bn_from_bytes(&m, em, k) != 0) return PDFMAKE_EINVAL;
1598 0 0         if (pdfmake_bn_from_bytes(&d, key->rsa.private_exponent,
1599 0           key->rsa.private_exponent_len) != 0)
1600 0           return PDFMAKE_EINVAL;
1601 0 0         if (pdfmake_bn_from_bytes(&n, n_bytes, n_len) != 0) return PDFMAKE_EINVAL;
1602 0 0         if (pdfmake_bn_mod_exp(&sig_bn, &m, &d, &n) != 0) return PDFMAKE_EINVAL;
1603              
1604 0           sig_bytes = pdfmake_arena_alloc(arena, k);
1605 0 0         if (!sig_bytes) return PDFMAKE_ENOMEM;
1606 0 0         if (pdfmake_bn_to_bytes(&sig_bn, sig_bytes, k) != 0) return PDFMAKE_EINVAL;
1607              
1608 0           *signature = sig_bytes;
1609 0           *sig_len = k;
1610 0           return PDFMAKE_OK;
1611             }
1612              
1613 0           pdfmake_err_t pdfmake_rsa_verify(
1614             const pdfmake_pubkey_t *key,
1615             pdfmake_hash_algorithm_t hash_alg,
1616             const uint8_t *digest,
1617             size_t digest_len,
1618             const uint8_t *signature,
1619             size_t sig_len)
1620             {
1621             const uint8_t *di_prefix;
1622             size_t di_prefix_len;
1623             size_t expected_digest_len;
1624             const uint8_t *n_bytes;
1625             size_t n_len;
1626             size_t k;
1627             pdfmake_bn_t sig_bn;
1628             pdfmake_bn_t e_bn;
1629             pdfmake_bn_t n_bn;
1630             pdfmake_bn_t m_bn;
1631             uint8_t *em;
1632             size_t i;
1633             size_t ps_end;
1634             size_t t_len;
1635 0 0         if (!key || !digest || !signature) return PDFMAKE_EINVAL;
    0          
    0          
1636 0 0         if (key->algorithm != PDFMAKE_PK_RSA) return PDFMAKE_EINVAL;
1637              
1638 0 0         if (rsa_digest_info(hash_alg, &di_prefix, &di_prefix_len,
1639             &expected_digest_len) != 0) {
1640 0           return PDFMAKE_EINVAL;
1641             }
1642 0 0         if (digest_len != expected_digest_len) return PDFMAKE_EINVAL;
1643              
1644 0           n_bytes = key->rsa.modulus;
1645 0           n_len = key->rsa.modulus_len;
1646 0 0         while (n_len > 0 && *n_bytes == 0) { n_bytes++; n_len--; }
    0          
1647 0 0         if (n_len == 0) return PDFMAKE_EINVAL;
1648 0           k = n_len;
1649 0 0         if (sig_len != k) return PDFMAKE_EINVAL;
1650              
1651 0 0         if (pdfmake_bn_from_bytes(&sig_bn, signature, sig_len) != 0) return PDFMAKE_EINVAL;
1652 0 0         if (pdfmake_bn_from_bytes(&e_bn, key->rsa.exponent, key->rsa.exponent_len) != 0) {
1653 0           return PDFMAKE_EINVAL;
1654             }
1655 0 0         if (pdfmake_bn_from_bytes(&n_bn, n_bytes, n_len) != 0) return PDFMAKE_EINVAL;
1656 0 0         if (pdfmake_bn_mod_exp(&m_bn, &sig_bn, &e_bn, &n_bn) != 0) return PDFMAKE_EINVAL;
1657              
1658 0           em = (uint8_t *)malloc(k);
1659 0 0         if (!em) return PDFMAKE_ENOMEM;
1660 0 0         if (pdfmake_bn_to_bytes(&m_bn, em, k) != 0) {
1661 0           free(em);
1662 0           return PDFMAKE_EINVAL;
1663             }
1664              
1665             /* PKCS#1 v1.5 EMSA-PKCS1-v1_5: 0x00 0x01 FF..FF 0x00 DigestInfo */
1666 0 0         if (k < di_prefix_len + digest_len + 11 || em[0] != 0x00 || em[1] != 0x01) {
    0          
    0          
1667 0           free(em);
1668 0           return PDFMAKE_EINVAL;
1669             }
1670 0           ps_end = 2;
1671 0 0         while (ps_end < k && em[ps_end] == 0xFF) ps_end++;
    0          
1672 0 0         if (ps_end < 10 || ps_end >= k || em[ps_end] != 0x00) {
    0          
    0          
1673 0           free(em);
1674 0           return PDFMAKE_EINVAL;
1675             }
1676 0           ps_end++; /* move past separator */
1677              
1678 0           t_len = di_prefix_len + digest_len;
1679 0 0         if (ps_end + t_len != k) {
1680 0           free(em);
1681 0           return PDFMAKE_EINVAL;
1682             }
1683 0 0         if (memcmp(em + ps_end, di_prefix, di_prefix_len) != 0) {
1684 0           free(em);
1685 0           return PDFMAKE_EINVAL;
1686             }
1687 0 0         if (memcmp(em + ps_end + di_prefix_len, digest, digest_len) != 0) {
1688 0           free(em);
1689 0           return PDFMAKE_EINVAL;
1690             }
1691              
1692             /* constant-time-ish cleanup touch */
1693 0 0         for (i = 0; i < k; i++) em[i] = 0;
1694 0           free(em);
1695 0           return PDFMAKE_OK;
1696             }
1697              
1698             /*============================================================================
1699             * ECDSA Signature (Stub - requires EC point arithmetic)
1700             *==========================================================================*/
1701              
1702 0           pdfmake_err_t pdfmake_ecdsa_sign(
1703             pdfmake_arena_t *arena,
1704             const pdfmake_privkey_t *key,
1705             pdfmake_hash_algorithm_t hash_alg,
1706             const uint8_t *digest,
1707             size_t digest_len,
1708             uint8_t **signature,
1709             size_t *sig_len)
1710             {
1711             size_t coord_size;
1712             size_t max_sig_size;
1713             uint8_t *p;
1714 0 0         if (!arena || !key || !digest || !signature || !sig_len) {
    0          
    0          
    0          
    0          
1715 0           return PDFMAKE_EINVAL;
1716             }
1717             (void)hash_alg;
1718             (void)digest_len;
1719            
1720 0 0         if (key->algorithm != PDFMAKE_PK_ECDSA) {
1721 0           return PDFMAKE_EINVAL;
1722             }
1723            
1724             /* ECDSA signature requires:
1725             1. Generate random k
1726             2. Compute R = k * G (point multiplication)
1727             3. r = R.x mod n
1728             4. s = k^-1 * (digest + r * d) mod n
1729             5. Encode (r, s) as DER SEQUENCE
1730            
1731             This requires EC point arithmetic.
1732             For a complete implementation, use a library like:
1733             - OpenSSL
1734             - mbedTLS
1735             - libecc
1736             */
1737            
1738             /* For now, return a dummy signature */
1739 0           coord_size = (key->ecdsa.curve_bits + 7) / 8;
1740 0           max_sig_size = 2 + 2 + coord_size + 1 + 2 + coord_size + 1; /* DER encoding overhead */
1741            
1742 0           *signature = pdfmake_arena_alloc(arena, max_sig_size);
1743 0 0         if (!*signature) return PDFMAKE_ENOMEM;
1744              
1745             /* Build dummy SEQUENCE { INTEGER r, INTEGER s } */
1746 0           p = *signature;
1747 0           *p++ = 0x30; /* SEQUENCE */
1748 0           *p++ = 2 * (coord_size + 2);
1749 0           *p++ = 0x02; /* INTEGER r */
1750 0           *p++ = coord_size;
1751 0           memset(p, 0x01, coord_size);
1752 0           p += coord_size;
1753 0           *p++ = 0x02; /* INTEGER s */
1754 0           *p++ = coord_size;
1755 0           memset(p, 0x02, coord_size);
1756 0           p += coord_size;
1757            
1758 0           *sig_len = p - *signature;
1759            
1760 0           return PDFMAKE_OK;
1761             }
1762              
1763 0           pdfmake_err_t pdfmake_ecdsa_verify(
1764             const pdfmake_pubkey_t *key,
1765             pdfmake_hash_algorithm_t hash_alg,
1766             const uint8_t *digest,
1767             size_t digest_len,
1768             const uint8_t *signature,
1769             size_t sig_len)
1770             {
1771 0 0         if (!key || !digest || !signature) return PDFMAKE_EINVAL;
    0          
    0          
1772 0 0         if (key->algorithm != PDFMAKE_PK_ECDSA) return PDFMAKE_EINVAL;
1773             (void)hash_alg;
1774             (void)digest_len;
1775             (void)sig_len;
1776            
1777             /* ECDSA verification requires:
1778             1. Parse (r, s) from signature
1779             2. Check r, s in [1, n-1]
1780             3. Compute u1 = digest * s^-1 mod n
1781             4. Compute u2 = r * s^-1 mod n
1782             5. Compute R = u1 * G + u2 * Q (point operations)
1783             6. Check r == R.x mod n */
1784            
1785             /* This requires EC point arithmetic - stub for now */
1786 0           return PDFMAKE_OK;
1787             }
1788              
1789             /*============================================================================
1790             * PDF Signature Operations (Stubs)
1791             *==========================================================================*/
1792              
1793 0           pdfmake_sig_field_t *pdfmake_doc_add_signature_field(
1794             pdfmake_doc_t *doc,
1795             const pdfmake_sig_config_t *config,
1796             const char *name)
1797             {
1798             pdfmake_arena_t *arena;
1799             pdfmake_sig_field_t *field;
1800 0 0         if (!doc || !config) return NULL;
    0          
1801              
1802 0           arena = pdfmake_doc_arena(doc);
1803 0           field = calloc(1, sizeof(pdfmake_sig_field_t));
1804 0 0         if (!field) return NULL;
1805              
1806 0 0         field->name = strdup(name ? name : "Signature1");
1807 0           field->page = config->page > 0 ? config->page : 1;
1808 0           memcpy(field->rect, config->rect, sizeof(double) * 4);
1809 0           field->config = *config;
1810              
1811 0           return field;
1812             }
1813              
1814             /*
1815             * PDF signing pipeline:
1816             *
1817             * 1. Serialize PDF normally to get base bytes
1818             * 2. Append a /Sig dictionary with a placeholder /Contents <00...00>
1819             * 3. Record the byte offset + length of the placeholder
1820             * 4. Write ByteRange array [0, sig_offset, sig_offset+sig_len, total-sig_offset-sig_len]
1821             * 5. Hash everything except the placeholder region
1822             * 6. Build PKCS#7 with the hash
1823             * 7. Hex-encode and insert into placeholder
1824             */
1825              
1826             /* Marker string to find placeholder in output */
1827             #define SIG_PLACEHOLDER_SIZE 8192
1828             #define SIG_PLACEHOLDER_TAG "PDFMAKE_SIG_PLACEHOLDER_START"
1829              
1830             /* Helper: find the byte offset of a specific line pattern near the end of
1831             * a serialized PDF. Scans backward from the end for "startxref\nNNN\n".
1832             * Returns the numeric value of NNN, or 0 if not found. */
1833 0           static uint64_t find_startxref_value(const uint8_t *data, size_t len) {
1834 0           const char tag[] = "startxref";
1835 0           size_t tag_len = sizeof(tag) - 1;
1836             size_t i;
1837             size_t p;
1838             uint64_t v;
1839 0 0         for (i = len; i >= tag_len; i--) {
1840 0 0         if (memcmp(data + i - tag_len, tag, tag_len) == 0) {
1841 0           p = i;
1842 0 0         while (p < len && (data[p] == '\n' || data[p] == '\r' || data[p] == ' ')) p++;
    0          
    0          
    0          
1843 0           v = 0;
1844 0 0         while (p < len && data[p] >= '0' && data[p] <= '9') {
    0          
    0          
1845 0           v = v * 10 + (uint64_t)(data[p] - '0');
1846 0           p++;
1847             }
1848 0           return v;
1849             }
1850             }
1851 0           return 0;
1852             }
1853              
1854             /* Helper: append an integer formatted into exactly `width` ASCII digits,
1855             * right-aligned, space-padded on the left. */
1856 0           static void append_fixed_int(pdfmake_buf_t *buf, uint64_t v, int width) {
1857             char tmp[32];
1858 0           int n = snprintf(tmp, sizeof(tmp), "%*lu", width, (unsigned long)v);
1859 0           pdfmake_buf_append(buf, tmp, (size_t)n);
1860 0           }
1861              
1862             /* Build the signature widget annotation and wire it up through /AcroForm
1863             * so Adobe Reader surfaces the signature in its Signatures panel.
1864             *
1865             * Order of operations:
1866             * 1. Add an empty widget dict placeholder (gets a stable obj number).
1867             * 2. Attach widget to page 0's /Annots.
1868             * 3. Finalize (creates /Pages, /Page dicts, /Catalog โ€” their obj numbers
1869             * are now allocated, so after this the next number is what the Sig
1870             * dict will use when we append it later).
1871             * 4. sig_obj_num = doc->obj_count + 1.
1872             * 5. Fill in the widget dict's body now that we know sig_obj_num (for
1873             * the /V reference).
1874             * 6. Mutate catalog to add /AcroForm.
1875             */
1876             /* Build the default visible-signature appearance content stream and append
1877             * into `out`: a white-filled box with black border + up to three lines of
1878             * 10pt Helvetica text (name, date, reason). The caller chooses which of
1879             * those lines appear via config->ap_show_*. Coordinates are local to the
1880             * Form XObject (origin bottom-left, extent w ร— h). */
1881 0           static void build_default_appearance(pdfmake_buf_t *out,
1882             const pdfmake_sig_config_t *config,
1883             const char *signer_name,
1884             const char *date_str,
1885             double w, double h)
1886             {
1887             double cursor_y;
1888             int first;
1889             double inset_x;
1890 0           pdfmake_buf_appendf(out, "q\n");
1891             /* White fill, black 1pt border covering the whole BBox. */
1892 0           pdfmake_buf_appendf(out, "1 1 1 rg 0 0 %.2f %.2f re f\n", w, h);
1893 0           pdfmake_buf_appendf(out, "0 0 0 RG 0.5 w 0.25 0.25 %.2f %.2f re S\n",
1894             w - 0.5, h - 0.5);
1895              
1896             /* Three lines of 10pt Helvetica, 14pt leading, 5pt inset. */
1897 0           cursor_y = h - 15;
1898 0           pdfmake_buf_appendf(out, "BT 0 0 0 rg /Helv 10 Tf\n");
1899              
1900 0           first = 1;
1901 0           inset_x = 5;
1902 0 0         if (config->ap_show_name && signer_name) {
    0          
1903 0           pdfmake_buf_appendf(out, "%.2f %.2f Td (Digitally signed by %s) Tj\n",
1904             inset_x, cursor_y, signer_name);
1905 0           first = 0;
1906             }
1907 0 0         if (config->ap_show_date && date_str) {
    0          
1908 0 0         if (first) pdfmake_buf_appendf(out, "%.2f %.2f Td (Date: %s) Tj\n",
1909             inset_x, cursor_y, date_str);
1910 0           else pdfmake_buf_appendf(out, "0 -14 Td (Date: %s) Tj\n", date_str);
1911 0           first = 0;
1912             }
1913 0 0         if (config->ap_show_reason && config->reason && *config->reason) {
    0          
    0          
1914 0 0         if (first) pdfmake_buf_appendf(out, "%.2f %.2f Td (Reason: %s) Tj\n",
1915 0           inset_x, cursor_y, config->reason);
1916 0           else pdfmake_buf_appendf(out, "0 -14 Td (Reason: %s) Tj\n",
1917 0           config->reason);
1918             }
1919 0           pdfmake_buf_appendf(out, "ET Q\n");
1920 0           }
1921              
1922             /* Build a Form XObject (a stream) with the given content bytes, a
1923             * `/BBox [0 0 w h]`, and a `/Resources /Font` dict built from the
1924             * (name, base_font) pairs. Adds the stream as an indirect object and
1925             * returns its object number, or 0 on error.
1926             *
1927             * When `default_helv` is nonzero, the resource dict also includes a
1928             * Helvetica entry under the name "Helv" โ€” used by the built-in default
1929             * appearance which always references /Helv. */
1930 0           static uint32_t add_form_xobject(pdfmake_doc_t *doc,
1931             const uint8_t *content, size_t content_len,
1932             double w, double h,
1933             const char *const *font_names,
1934             const char *const *font_bases,
1935             size_t font_count,
1936             int default_helv,
1937             const char *const *xobject_names,
1938             const uint32_t *xobject_nums,
1939             size_t xobject_count)
1940             {
1941 0           pdfmake_arena_t *arena = pdfmake_doc_arena(doc);
1942             size_t i;
1943             pdfmake_obj_t font_dict;
1944             pdfmake_obj_t helv;
1945             uint32_t helv_num;
1946             uint32_t k;
1947             pdfmake_obj_t f;
1948             uint32_t fn;
1949             pdfmake_obj_t resources;
1950             pdfmake_obj_t xo_dict;
1951             uint32_t nk;
1952             uint32_t xk;
1953             pdfmake_obj_t procset;
1954             uint32_t pk;
1955             pdfmake_obj_t form;
1956             pdfmake_obj_t form_dict;
1957             pdfmake_obj_t bbox;
1958              
1959             /* /Resources /Font dict */
1960 0           font_dict = pdfmake_dict_new(arena);
1961 0 0         if (default_helv) {
1962             /* Build /Helv = << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> */
1963 0           helv = pdfmake_dict_new(arena);
1964 0           k = pdfmake_arena_intern_name(arena, "Type", 4);
1965 0           pdfmake_dict_set(arena, &helv, k, pdfmake_name_cstr(arena, "Font"));
1966 0           k = pdfmake_arena_intern_name(arena, "Subtype", 7);
1967 0           pdfmake_dict_set(arena, &helv, k, pdfmake_name_cstr(arena, "Type1"));
1968 0           k = pdfmake_arena_intern_name(arena, "BaseFont", 8);
1969 0           pdfmake_dict_set(arena, &helv, k, pdfmake_name_cstr(arena, "Helvetica"));
1970 0           helv_num = pdfmake_doc_add(doc, helv);
1971 0 0         if (helv_num == 0) return 0;
1972 0           k = pdfmake_arena_intern_name(arena, "Helv", 4);
1973 0           pdfmake_dict_set(arena, &font_dict, k, pdfmake_ref(helv_num, 0));
1974             }
1975 0 0         for (i = 0; i < font_count; i++) {
1976 0 0         if (!font_names[i] || !font_bases[i]) continue;
    0          
1977 0           f = pdfmake_dict_new(arena);
1978 0           k = pdfmake_arena_intern_name(arena, "Type", 4);
1979 0           pdfmake_dict_set(arena, &f, k, pdfmake_name_cstr(arena, "Font"));
1980 0           k = pdfmake_arena_intern_name(arena, "Subtype", 7);
1981 0           pdfmake_dict_set(arena, &f, k, pdfmake_name_cstr(arena, "Type1"));
1982 0           k = pdfmake_arena_intern_name(arena, "BaseFont", 8);
1983 0           pdfmake_dict_set(arena, &f, k, pdfmake_name_cstr(arena, font_bases[i]));
1984 0           fn = pdfmake_doc_add(doc, f);
1985 0 0         if (fn == 0) return 0;
1986 0           k = pdfmake_arena_intern_name(arena, font_names[i], strlen(font_names[i]));
1987 0           pdfmake_dict_set(arena, &font_dict, k, pdfmake_ref(fn, 0));
1988             }
1989              
1990 0           resources = pdfmake_dict_new(arena);
1991 0           k = pdfmake_arena_intern_name(arena, "Font", 4);
1992 0           pdfmake_dict_set(arena, &resources, k, font_dict);
1993              
1994             /* /Resources /XObject dict โ€” maps appearance-stream resource names
1995             * (e.g. "Im1") to their doc indirect object refs. Lets a scanned
1996             * signature PNG or another Form XObject be used inside the widget
1997             * appearance. */
1998 0 0         if (xobject_count > 0 && xobject_names && xobject_nums) {
    0          
    0          
1999 0           xo_dict = pdfmake_dict_new(arena);
2000 0 0         for (i = 0; i < xobject_count; i++) {
2001 0 0         if (!xobject_names[i] || xobject_nums[i] == 0) continue;
    0          
2002 0           nk = pdfmake_arena_intern_name(arena,
2003 0           xobject_names[i], strlen(xobject_names[i]));
2004 0           pdfmake_dict_set(arena, &xo_dict, nk,
2005 0           pdfmake_ref(xobject_nums[i], 0));
2006             }
2007 0           xk = pdfmake_arena_intern_name(arena, "XObject", 7);
2008 0           pdfmake_dict_set(arena, &resources, xk, xo_dict);
2009             /* Also advertise /ProcSet /ImageC so older viewers honor the image. */
2010 0           procset = pdfmake_array_new(arena);
2011 0           pdfmake_array_push(arena, &procset, pdfmake_name_cstr(arena, "PDF"));
2012 0           pdfmake_array_push(arena, &procset, pdfmake_name_cstr(arena, "Text"));
2013 0           pdfmake_array_push(arena, &procset, pdfmake_name_cstr(arena, "ImageC"));
2014 0           pk = pdfmake_arena_intern_name(arena, "ProcSet", 7);
2015 0           pdfmake_dict_set(arena, &resources, pk, procset);
2016             }
2017              
2018             /* Form XObject stream */
2019 0           form = pdfmake_stream_new(arena);
2020 0 0         if (form.kind != PDFMAKE_STREAM) return 0;
2021 0           pdfmake_stream_set_data(arena, &form, content, content_len);
2022              
2023 0           form_dict.kind = PDFMAKE_DICT;
2024 0           form_dict.as.dict = pdfmake_stream_dict(&form);
2025 0           k = pdfmake_arena_intern_name(arena, "Type", 4);
2026 0           pdfmake_dict_set(arena, &form_dict, k, pdfmake_name_cstr(arena, "XObject"));
2027 0           k = pdfmake_arena_intern_name(arena, "Subtype", 7);
2028 0           pdfmake_dict_set(arena, &form_dict, k, pdfmake_name_cstr(arena, "Form"));
2029 0           k = pdfmake_arena_intern_name(arena, "FormType", 8);
2030 0           pdfmake_dict_set(arena, &form_dict, k, pdfmake_int(1));
2031 0           k = pdfmake_arena_intern_name(arena, "BBox", 4);
2032 0           bbox = pdfmake_array_new(arena);
2033 0           pdfmake_array_push(arena, &bbox, pdfmake_int(0));
2034 0           pdfmake_array_push(arena, &bbox, pdfmake_int(0));
2035 0           pdfmake_array_push(arena, &bbox, pdfmake_real(w));
2036 0           pdfmake_array_push(arena, &bbox, pdfmake_real(h));
2037 0           pdfmake_dict_set(arena, &form_dict, k, bbox);
2038 0           k = pdfmake_arena_intern_name(arena, "Resources", 9);
2039 0           pdfmake_dict_set(arena, &form_dict, k, resources);
2040              
2041 0           return pdfmake_doc_add(doc, form);
2042             }
2043              
2044 0           static pdfmake_err_t prepare_signature_widget(pdfmake_doc_t *doc,
2045             const pdfmake_sig_config_t *config,
2046             uint32_t *widget_num_out,
2047             uint32_t *sig_obj_num_out) {
2048             pdfmake_arena_t *arena;
2049             size_t page_idx;
2050             pdfmake_page_t *target_page;
2051             uint32_t widget_num;
2052             uint32_t ap_num;
2053             int visible;
2054             pdfmake_err_t err;
2055             uint32_t sig_obj_num;
2056             pdfmake_obj_t *widget;
2057             uint32_t k;
2058             pdfmake_obj_t rect;
2059             pdfmake_obj_t *catalog;
2060             pdfmake_obj_t acro;
2061             pdfmake_obj_t fields;
2062             int i;
2063             size_t p;
2064             pdfmake_obj_t widget_placeholder;
2065             double w;
2066             double h;
2067             const uint8_t *content;
2068             size_t content_len;
2069             pdfmake_buf_t default_buf;
2070             int needs_free;
2071             time_t now;
2072             struct tm *tm;
2073             char date_str[32];
2074             const char *signer;
2075             int use_helv;
2076             pdfmake_obj_t ap;
2077             uint32_t n_k;
2078             uint32_t ap_k;
2079             uint32_t p_k;
2080 0 0         if (!doc || doc->page_count == 0) return PDFMAKE_EINVAL;
    0          
2081 0           arena = pdfmake_doc_arena(doc);
2082              
2083             /* Select target page (1-based in config, default = page 1). */
2084 0           page_idx = 0;
2085 0 0         if (config && config->visible && config->page > 0) {
    0          
    0          
2086 0           p = (size_t)config->page - 1;
2087 0 0         if (p < doc->page_count) page_idx = p;
2088             }
2089 0           target_page = doc->pages[page_idx];
2090              
2091             /* If an earlier call to pdfmake_doc_sign already installed the widget
2092             * + AcroForm machinery, reuse the reserved numbers. Supports the
2093             * two-pass TSA flow: pass 1 prepares + signs, pass 2 re-signs with a
2094             * timestamp token without mutating the doc structure. */
2095 0 0         if (doc->sig_widget_num != 0 && doc->sig_obj_num_reserved != 0) {
    0          
2096 0           *widget_num_out = doc->sig_widget_num;
2097 0           *sig_obj_num_out = doc->sig_obj_num_reserved;
2098 0           return PDFMAKE_OK;
2099             }
2100              
2101             /* Reserve a placeholder widget object number. */
2102 0           widget_placeholder = pdfmake_dict_new(arena);
2103 0 0         if (widget_placeholder.kind != PDFMAKE_DICT) return PDFMAKE_ENOMEM;
2104 0           widget_num = pdfmake_doc_add(doc, widget_placeholder);
2105 0 0         if (widget_num == 0) return PDFMAKE_ENOMEM;
2106              
2107 0 0         if (pdfmake_page_add_annot(target_page, widget_num) != PDFMAKE_OK) {
2108 0           return PDFMAKE_ENOMEM;
2109             }
2110              
2111             /* When the sig is visible, build the appearance Form XObject BEFORE
2112             * finalize so it gets a stable indirect object number. The Form
2113             * XObject may add font indirects too. */
2114 0           ap_num = 0;
2115 0 0         visible = config && config->visible
2116 0 0         && config->rect[2] > config->rect[0]
2117 0 0         && config->rect[3] > config->rect[1];
    0          
2118 0 0         if (visible) {
2119 0           w = config->rect[2] - config->rect[0];
2120 0           h = config->rect[3] - config->rect[1];
2121              
2122             /* Pick appearance bytes: custom if provided, otherwise default. */
2123 0           content = NULL;
2124 0           content_len = 0;
2125 0           needs_free = 0;
2126 0 0         if (config->appearance_stream && config->appearance_stream_len > 0) {
    0          
2127 0           content = config->appearance_stream;
2128 0           content_len = config->appearance_stream_len;
2129             } else {
2130 0           pdfmake_buf_init(&default_buf);
2131 0           needs_free = 1;
2132 0           now = config->signing_time > 0
2133 0 0         ? (time_t)config->signing_time : time(NULL);
2134 0           tm = gmtime(&now);
2135 0           snprintf(date_str, sizeof(date_str),
2136             "%04d-%02d-%02d %02d:%02d:%02d UTC",
2137 0           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
2138             tm->tm_hour, tm->tm_min, tm->tm_sec);
2139 0           signer = config->name;
2140 0 0         if (!signer && config->identity && config->identity->cert
    0          
    0          
2141 0 0         && config->identity->cert->subject.common_name) {
2142 0           signer = config->identity->cert->subject.common_name;
2143             }
2144 0 0         if (!signer) signer = "";
2145 0           build_default_appearance(&default_buf, config, signer, date_str, w, h);
2146 0           content = pdfmake_buf_data(&default_buf);
2147 0           content_len = pdfmake_buf_len(&default_buf);
2148             }
2149              
2150 0           use_helv = (config->appearance_stream == NULL);
2151 0           ap_num = add_form_xobject(doc, content, content_len, w, h,
2152 0           config->appearance_font_names,
2153 0           config->appearance_font_bases,
2154 0           config->appearance_font_count,
2155             use_helv,
2156 0           config->appearance_xobject_names,
2157 0           config->appearance_xobject_nums,
2158 0           config->appearance_xobject_count);
2159 0 0         if (needs_free) pdfmake_buf_free(&default_buf);
2160 0 0         if (ap_num == 0) return PDFMAKE_EINVAL;
2161             }
2162              
2163             /* Finalize so catalog + page dicts exist and the obj table is complete. */
2164 0           err = pdfmake_doc_finalize(doc);
2165 0 0         if (err != PDFMAKE_OK) return err;
2166              
2167             /* Auto-fill metadata now โ€” this adds the /Info indirect dict which
2168             * would otherwise be added during pdfmake_doc_write and steal the
2169             * obj number we're about to reserve for the Sig dict. */
2170 0           pdfmake_meta_auto_fill(doc);
2171              
2172             /* The Sig dict will be appended as an incremental-update indirect
2173             * object, taking the next number. */
2174 0           sig_obj_num = (uint32_t)(doc->obj_count + 1);
2175              
2176             /* Fill in the widget dict body now that sig_obj_num is known. */
2177 0           widget = pdfmake_doc_get(doc, widget_num);
2178 0 0         if (!widget || widget->kind != PDFMAKE_DICT) return PDFMAKE_EINVAL;
    0          
2179              
2180 0           k = pdfmake_arena_intern_name(arena, "Type", 4);
2181 0           pdfmake_dict_set(arena, widget, k, pdfmake_name_cstr(arena, "Annot"));
2182 0           k = pdfmake_arena_intern_name(arena, "Subtype", 7);
2183 0           pdfmake_dict_set(arena, widget, k, pdfmake_name_cstr(arena, "Widget"));
2184 0           k = pdfmake_arena_intern_name(arena, "FT", 2);
2185 0           pdfmake_dict_set(arena, widget, k, pdfmake_name_cstr(arena, "Sig"));
2186 0           k = pdfmake_arena_intern_name(arena, "T", 1);
2187 0           pdfmake_dict_set(arena, widget, k, pdfmake_str_cstr(arena, "Signature1"));
2188             /* F: Print+Locked (132) for both visible and invisible widgets. The
2189             * Locked bit (128) is important โ€” without it viewers like macOS
2190             * Preview treat the widget as an empty signature field and offer to
2191             * drop an image annotation on top, which would shadow the real
2192             * signature's appearance. /Rect [0 0 0 0] is what makes the
2193             * invisible variant invisible; /F alone does not hide it. */
2194 0           k = pdfmake_arena_intern_name(arena, "F", 1);
2195 0           pdfmake_dict_set(arena, widget, k, pdfmake_int(132));
2196             /* /Ff: the field-flag ReadOnly bit (1) marks the field as not
2197             * editable. A signed signature field must refuse further edits. */
2198 0           k = pdfmake_arena_intern_name(arena, "Ff", 2);
2199 0           pdfmake_dict_set(arena, widget, k, pdfmake_int(1));
2200              
2201             /* /Rect: either the visible rect, or [0 0 0 0] for an invisible widget. */
2202 0           rect = pdfmake_array_new(arena);
2203 0 0         if (visible) {
2204 0           pdfmake_array_push(arena, &rect, pdfmake_real(config->rect[0]));
2205 0           pdfmake_array_push(arena, &rect, pdfmake_real(config->rect[1]));
2206 0           pdfmake_array_push(arena, &rect, pdfmake_real(config->rect[2]));
2207 0           pdfmake_array_push(arena, &rect, pdfmake_real(config->rect[3]));
2208             } else {
2209 0 0         for (i = 0; i < 4; i++) pdfmake_array_push(arena, &rect, pdfmake_int(0));
2210             }
2211 0           k = pdfmake_arena_intern_name(arena, "Rect", 4);
2212 0           pdfmake_dict_set(arena, widget, k, rect);
2213              
2214             /* /AP << /N >> for visible signatures. */
2215 0 0         if (visible && ap_num != 0) {
    0          
2216 0           ap = pdfmake_dict_new(arena);
2217 0           n_k = pdfmake_arena_intern_name(arena, "N", 1);
2218 0           pdfmake_dict_set(arena, &ap, n_k, pdfmake_ref(ap_num, 0));
2219 0           ap_k = pdfmake_arena_intern_name(arena, "AP", 2);
2220 0           pdfmake_dict_set(arena, widget, ap_k, ap);
2221              
2222             /* /P = ref to the containing page, so text/annotation tooling can
2223             * walk the hierarchy without a page-tree traversal. */
2224 0 0         if (target_page->page_num > 0) {
2225 0           p_k = pdfmake_arena_intern_name(arena, "P", 1);
2226 0           pdfmake_dict_set(arena, widget, p_k,
2227             pdfmake_ref(target_page->page_num, 0));
2228             }
2229             }
2230              
2231             /* NOTE: /V (signature value reference) is deliberately NOT set here.
2232             * In rev 1 (pre-signature) the widget is a sig-field placeholder with
2233             * no value; the /V reference and the Sig dict itself are added in the
2234             * incremental update (rev 2). This keeps rev 1 self-consistent โ€”
2235             * Adobe's modification-detection logic treats the /V addition plus
2236             * Sig dict creation as a legitimate signature-applying edit. */
2237              
2238             /* Mutate catalog to add /AcroForm. SigFlags bit 1 = SignaturesExist,
2239             * bit 2 = AppendOnly. */
2240 0           catalog = pdfmake_doc_get(doc, doc->root_num);
2241 0 0         if (!catalog || catalog->kind != PDFMAKE_DICT) return PDFMAKE_EINVAL;
    0          
2242              
2243 0           acro = pdfmake_dict_new(arena);
2244 0 0         if (acro.kind != PDFMAKE_DICT) return PDFMAKE_ENOMEM;
2245 0           k = pdfmake_arena_intern_name(arena, "Fields", 6);
2246 0           fields = pdfmake_array_new(arena);
2247 0           pdfmake_array_push(arena, &fields, pdfmake_ref(widget_num, 0));
2248 0           pdfmake_dict_set(arena, &acro, k, fields);
2249 0           k = pdfmake_arena_intern_name(arena, "SigFlags", 8);
2250 0           pdfmake_dict_set(arena, &acro, k, pdfmake_int(3));
2251              
2252 0           k = pdfmake_arena_intern_name(arena, "AcroForm", 8);
2253 0           pdfmake_dict_set(arena, catalog, k, acro);
2254              
2255 0           *widget_num_out = widget_num;
2256 0           *sig_obj_num_out = sig_obj_num;
2257 0           doc->sig_widget_num = widget_num;
2258 0           doc->sig_obj_num_reserved = sig_obj_num;
2259 0           doc->sig_visible = visible;
2260 0 0         doc->sig_ap_num = visible ? ap_num : 0;
2261 0 0         doc->sig_page_num = visible ? target_page->page_num : 0;
2262 0 0         if (visible) {
2263 0 0         for (i = 0; i < 4; i++) doc->sig_rect[i] = config->rect[i];
2264             }
2265 0           return PDFMAKE_OK;
2266             }
2267              
2268 0           pdfmake_err_t pdfmake_doc_sign(
2269             pdfmake_doc_t *doc,
2270             const pdfmake_sig_config_t *config,
2271             pdfmake_buf_t *out)
2272             {
2273             pdfmake_err_t err;
2274             pdfmake_arena_t *arena;
2275             size_t placeholder_size;
2276             size_t i;
2277             uint32_t sig_obj_num;
2278             uint32_t widget_num;
2279             time_t now;
2280             pdfmake_buf_t pdf_buf;
2281             const uint8_t *orig_data;
2282             size_t orig_len;
2283             uint64_t prev_xref;
2284             char header[64];
2285             int hn;
2286             size_t sig_obj_offset;
2287             struct tm *tg;
2288             char date_str[40];
2289             size_t br_offset;
2290             size_t br_len;
2291             size_t contents_start;
2292             size_t contents_end;
2293             size_t widget_obj_offset;
2294             size_t new_xref_offset;
2295             uint32_t nums[2];
2296             size_t offsets[2];
2297             uint32_t tn;
2298             size_t to;
2299             char entry[24];
2300             size_t total_len;
2301             uint64_t b1;
2302             uint64_t l1;
2303             uint64_t b2;
2304             uint64_t l2;
2305             char br_fmt[128];
2306             size_t br_new_len;
2307             uint8_t *out_data;
2308             size_t copy;
2309             size_t pad_i;
2310             pdfmake_hash_algorithm_t hash_alg;
2311             pdfmake_hash_ctx_t *h;
2312             uint8_t digest[64];
2313             size_t digest_len;
2314             pdfmake_buf_t pkcs7_buf;
2315             size_t pkcs7_len;
2316             const uint8_t *pkcs7_data;
2317             static const char hex_chars[] = "0123456789ABCDEF";
2318 0 0         if (!doc || !config || !out) return PDFMAKE_EINVAL;
    0          
    0          
2319 0 0         if (!config->identity) return PDFMAKE_EINVAL;
2320              
2321 0           arena = pdfmake_doc_arena(doc);
2322 0           placeholder_size = config->placeholder_size > 0
2323 0 0         ? config->placeholder_size : SIG_PLACEHOLDER_SIZE;
2324              
2325             /* Add widget + AcroForm wire-up BEFORE serializing so Adobe Reader
2326             * surfaces the signature in its Signatures panel. sig_obj_num is
2327             * reserved here; the Sig dict itself is appended later as an
2328             * incremental-update indirect object with that number. */
2329 0           sig_obj_num = 0;
2330 0           widget_num = 0;
2331 0           err = prepare_signature_widget(doc, config, &widget_num, &sig_obj_num);
2332 0 0         if (err != PDFMAKE_OK) return err;
2333              
2334             /* Pin /Info /ModDate to signing_time so pass 1 and pass 2 (TSA)
2335             * serialize byte-identical /Info dicts. meta_auto_fill is now
2336             * idempotent on ModDate, so setting it here once is sufficient. */
2337 0           now = config->signing_time > 0
2338 0 0         ? (time_t)config->signing_time : time(NULL);
2339 0           pdfmake_meta_set_mod_date(doc, now);
2340              
2341             /* -- Step 1: Serialize the unsigned document to pdf_buf. -- */
2342 0           pdfmake_buf_init(&pdf_buf);
2343 0           err = pdfmake_doc_write(doc, &pdf_buf);
2344 0 0         if (err != PDFMAKE_OK) {
2345 0           pdfmake_buf_free(&pdf_buf);
2346 0           return err;
2347             }
2348 0           orig_data = pdfmake_buf_data(&pdf_buf);
2349 0           orig_len = pdfmake_buf_len(&pdf_buf);
2350              
2351             /* The previous xref offset (for /Prev in the new trailer). */
2352 0           prev_xref = find_startxref_value(orig_data, orig_len);
2353              
2354             /* -- Step 2: Copy the original PDF verbatim into `out`. This keeps
2355             * the original xref and trailer intact so readers can still recover
2356             * the base revision. -- */
2357 0           err = pdfmake_buf_append(out, orig_data, orig_len);
2358 0 0         if (err != PDFMAKE_OK) { pdfmake_buf_free(&pdf_buf); return err; }
2359              
2360             /* sig_obj_num was reserved in prepare_signature_widget so the widget
2361             * could reference it via /V. The incremental update below emits the
2362             * Sig dict with exactly that number. */
2363              
2364             /* -- Step 3: Append the Sig dict as an incremental-update indirect
2365             * object. The /ByteRange and /Contents values are placeholders we
2366             * patch once the final byte offsets are known. -- */
2367 0           hn = snprintf(header, sizeof(header), "\n%u 0 obj\n",
2368             (unsigned)sig_obj_num);
2369 0           sig_obj_offset = pdfmake_buf_len(out) + 1; /* skip leading '\n' */
2370 0           pdfmake_buf_append(out, header, (size_t)hn);
2371              
2372 0           pdfmake_buf_append_cstr(out, "<
2373 0           pdfmake_buf_append_cstr(out, " /Filter /Adobe.PPKLite");
2374 0           pdfmake_buf_append_cstr(out, " /SubFilter /adbe.pkcs7.detached");
2375              
2376 0 0         if (config->name) {
2377 0           pdfmake_buf_appendf(out, " /Name (%s)", config->name);
2378             }
2379 0 0         if (config->reason) {
2380 0           pdfmake_buf_appendf(out, " /Reason (%s)", config->reason);
2381             }
2382 0 0         if (config->location) {
2383 0           pdfmake_buf_appendf(out, " /Location (%s)", config->location);
2384             }
2385 0 0         if (config->contact_info) {
2386 0           pdfmake_buf_appendf(out, " /ContactInfo (%s)", config->contact_info);
2387             }
2388              
2389 0           now = config->signing_time > 0
2390             ? (time_t)config->signing_time
2391 0 0         : time(NULL);
2392 0           tg = gmtime(&now);
2393 0           snprintf(date_str, sizeof(date_str),
2394             "D:%04d%02d%02d%02d%02d%02dZ",
2395 0           tg->tm_year + 1900, tg->tm_mon + 1, tg->tm_mday,
2396             tg->tm_hour, tg->tm_min, tg->tm_sec);
2397 0           pdfmake_buf_appendf(out, " /M (%s)", date_str);
2398              
2399             /* ByteRange placeholder: 4 ten-digit fields, always emitted at the
2400             * same length so we can patch them in place. */
2401 0           br_offset = pdfmake_buf_len(out);
2402 0           pdfmake_buf_append_cstr(out, " /ByteRange [0000000000 0000000000 0000000000 0000000000]");
2403 0           br_len = pdfmake_buf_len(out) - br_offset;
2404              
2405             /* Contents placeholder โ€” 8192 hex zeros (space for a 4096-byte PKCS#7). */
2406 0           pdfmake_buf_append_cstr(out, " /Contents <");
2407 0           contents_start = pdfmake_buf_len(out);
2408 0 0         for (i = 0; i < placeholder_size; i++) {
2409 0           pdfmake_buf_append_byte(out, '0');
2410             }
2411 0           contents_end = pdfmake_buf_len(out);
2412 0           pdfmake_buf_append_cstr(out, ">");
2413 0           pdfmake_buf_append_cstr(out, ">>\nendobj\n");
2414              
2415             /* -- Step 3b: Re-emit the widget annotation with /V added so it points
2416             * at the just-written Sig dict. This is the standard signing workflow:
2417             * rev 1 has the placeholder sig field; rev 2 (this incremental update)
2418             * fills in /V and adds the Sig dict itself. When the widget is visible
2419             * we must also reproduce /Rect, /AP, /P, and /F=4 (Print) so Adobe
2420             * honors the appearance stream drawn in pass 1. */
2421 0           widget_obj_offset = pdfmake_buf_len(out) + 1;
2422 0 0         if (doc->sig_visible && doc->sig_ap_num != 0) {
    0          
2423 0           pdfmake_buf_appendf(out,
2424             "\n%u 0 obj\n<
2425             "/FT /Sig/T (Signature1)/F 132/Ff 1"
2426             "/Rect [%.4f %.4f %.4f %.4f]"
2427             "/AP<>"
2428             "/P %u 0 R"
2429             "/V %u 0 R>>\nendobj\n",
2430             (unsigned)widget_num,
2431             doc->sig_rect[0], doc->sig_rect[1], doc->sig_rect[2], doc->sig_rect[3],
2432 0           (unsigned)doc->sig_ap_num,
2433 0           (unsigned)doc->sig_page_num,
2434             (unsigned)sig_obj_num);
2435             } else {
2436 0           pdfmake_buf_appendf(out,
2437             "\n%u 0 obj\n<
2438             "/FT /Sig/T (Signature1)/F 132/Ff 1"
2439             "/Rect [0 0 0 0]/V %u 0 R>>\nendobj\n",
2440             (unsigned)widget_num, (unsigned)sig_obj_num);
2441             }
2442              
2443             /* -- Step 4: Emit a new xref section + trailer + startxref that
2444             * references the new Sig object AND the updated widget via an
2445             * incremental update. Both objects go into one or two xref
2446             * subsections depending on whether their numbers are consecutive. -- */
2447 0           new_xref_offset = pdfmake_buf_len(out);
2448 0           pdfmake_buf_append_cstr(out, "xref\n");
2449              
2450             /* Emit entries sorted by object number, grouping consecutive ones
2451             * into the same subsection. */
2452 0           nums[0] = widget_num;
2453 0           nums[1] = sig_obj_num;
2454 0           offsets[0] = widget_obj_offset;
2455 0           offsets[1] = sig_obj_offset;
2456             /* Sort nums/offsets ascending by number. */
2457 0 0         if (nums[0] > nums[1]) {
2458 0           tn = nums[0]; nums[0] = nums[1]; nums[1] = tn;
2459 0           to = offsets[0]; offsets[0] = offsets[1]; offsets[1] = to;
2460             }
2461 0 0         if (nums[1] == nums[0] + 1) {
2462             /* Consecutive: single subsection of 2 entries. */
2463 0           pdfmake_buf_appendf(out, "%u 2\n", (unsigned)nums[0]);
2464 0 0         for (i = 0; i < 2; i++) {
2465 0           snprintf(entry, sizeof(entry), "%010lu 00000 n \n",
2466 0           (unsigned long)offsets[i]);
2467 0           pdfmake_buf_append(out, entry, 20);
2468             }
2469             } else {
2470             /* Two subsections. */
2471 0 0         for (i = 0; i < 2; i++) {
2472 0           pdfmake_buf_appendf(out, "%u 1\n", (unsigned)nums[i]);
2473 0           snprintf(entry, sizeof(entry), "%010lu 00000 n \n",
2474 0           (unsigned long)offsets[i]);
2475 0           pdfmake_buf_append(out, entry, 20);
2476             }
2477             }
2478              
2479 0           pdfmake_buf_append_cstr(out, "trailer\n<
2480 0           append_fixed_int(out, (uint64_t)(sig_obj_num + 1), 1);
2481 0 0         if (doc->root_num > 0) {
2482 0           pdfmake_buf_appendf(out, " /Root %u %u R",
2483 0           (unsigned)doc->root_num, (unsigned)doc->root_gen);
2484             }
2485 0 0         if (doc->info_num > 0) {
2486 0           pdfmake_buf_appendf(out, " /Info %u %u R",
2487 0           (unsigned)doc->info_num, (unsigned)doc->info_gen);
2488             }
2489             /* /ID: copy id1/id2 from doc. */
2490 0           pdfmake_buf_append_cstr(out, " /ID[<");
2491 0 0         for (i = 0; i < 16; i++) pdfmake_buf_appendf(out, "%02X", doc->id1[i]);
2492 0           pdfmake_buf_append_cstr(out, "><");
2493 0 0         for (i = 0; i < 16; i++) pdfmake_buf_appendf(out, "%02X", doc->id2[i]);
2494 0           pdfmake_buf_append_cstr(out, ">]");
2495 0 0         if (prev_xref > 0) {
2496 0           pdfmake_buf_appendf(out, " /Prev %lu", (unsigned long)prev_xref);
2497             }
2498 0           pdfmake_buf_append_cstr(out, ">>\n");
2499 0           pdfmake_buf_appendf(out, "startxref\n%lu\n%%%%EOF\n",
2500             (unsigned long)new_xref_offset);
2501              
2502             /* -- Step 5: Patch /ByteRange with the real offsets. Per RFC 5652
2503             * usage in PDF signatures and Adobe/pyhanko convention: region 1
2504             * ends JUST BEFORE the `<` of the hex /Contents value; the ``
2505             * literal (including both angle brackets) is the uncovered middle;
2506             * region 2 starts JUST AFTER the closing `>`. That way the total
2507             * file size = len1 + (hex_len + 2) + len2. -- */
2508 0           total_len = pdfmake_buf_len(out);
2509 0           b1 = 0;
2510 0           l1 = (uint64_t)(contents_start - 1); /* offset of '<' */
2511 0           b2 = (uint64_t)(contents_end + 1); /* byte after '>' */
2512 0           l2 = (uint64_t)(total_len - b2);
2513              
2514             /* Build ByteRange payload, e.g. "[0000000000 0000000123 0000000456 ..."
2515             * matching the placeholder's length exactly. */
2516 0           snprintf(br_fmt, sizeof(br_fmt),
2517             " /ByteRange [%010lu %010lu %010lu %010lu]",
2518             (unsigned long)b1,
2519             (unsigned long)l1,
2520             (unsigned long)b2,
2521             (unsigned long)l2);
2522 0           br_new_len = strlen(br_fmt);
2523 0           out_data = (uint8_t *)pdfmake_buf_data(out);
2524 0 0         if (br_new_len == br_len) {
2525 0           memcpy(out_data + br_offset, br_fmt, br_len);
2526             } else {
2527             /* Widths should match โ€” but if our format choices ever drift, pad
2528             * with spaces to keep the total length identical. */
2529 0           copy = br_new_len < br_len ? br_new_len : br_len;
2530 0           memcpy(out_data + br_offset, br_fmt, copy);
2531 0 0         for (pad_i = copy; pad_i < br_len; pad_i++) out_data[br_offset + pad_i] = ' ';
2532             }
2533              
2534             /* -- Step 6: Hash the file bytes per ByteRange, build PKCS#7,
2535             * hex-encode into the /Contents placeholder. -- */
2536 0           hash_alg = config->hash_algorithm
2537 0 0         ? config->hash_algorithm : PDFMAKE_HASH_SHA256;
2538 0           h = pdfmake_hash_new(hash_alg);
2539 0 0         if (!h) { pdfmake_buf_free(&pdf_buf); return PDFMAKE_ENOMEM; }
2540 0           pdfmake_hash_update(h, out_data + b1, (size_t)l1);
2541 0           pdfmake_hash_update(h, out_data + b2, (size_t)l2);
2542 0           digest_len = pdfmake_hash_final(h, digest);
2543 0           pdfmake_hash_free(h);
2544              
2545 0           pdfmake_buf_init(&pkcs7_buf);
2546 0           err = pdfmake_pkcs7_build(arena, config, digest, digest_len, &pkcs7_buf);
2547 0 0         if (err != PDFMAKE_OK) {
2548 0           pdfmake_buf_free(&pkcs7_buf);
2549 0           pdfmake_buf_free(&pdf_buf);
2550 0           return err;
2551             }
2552              
2553 0           pkcs7_len = pdfmake_buf_len(&pkcs7_buf);
2554 0           pkcs7_data = pdfmake_buf_data(&pkcs7_buf);
2555 0 0         if (pkcs7_len * 2 > placeholder_size) {
2556 0           pdfmake_buf_free(&pkcs7_buf);
2557 0           pdfmake_buf_free(&pdf_buf);
2558 0           return PDFMAKE_EINVAL;
2559             }
2560              
2561 0           out_data = (uint8_t *)pdfmake_buf_data(out);
2562 0 0         for (i = 0; i < pkcs7_len; i++) {
2563 0           out_data[contents_start + i * 2] = hex_chars[(pkcs7_data[i] >> 4) & 0xF];
2564 0           out_data[contents_start + i * 2 + 1] = hex_chars[pkcs7_data[i] & 0xF];
2565             }
2566              
2567 0           pdfmake_buf_free(&pkcs7_buf);
2568 0           pdfmake_buf_free(&pdf_buf);
2569 0           return PDFMAKE_OK;
2570             }
2571              
2572 4           pdfmake_sig_verify_result_t *pdfmake_sig_verify(
2573             pdfmake_arena_t *arena,
2574             const uint8_t *pdf,
2575             size_t len,
2576             int field_index)
2577             {
2578             pdfmake_sig_verify_result_t *r;
2579             pdfmake_parser_t *parser;
2580             pdfmake_doc_t *doc;
2581             pdfmake_err_t err;
2582             pdfmake_obj_t *catalog;
2583             pdfmake_obj_t *acro;
2584             pdfmake_obj_t *fields;
2585             uint32_t acro_key;
2586             uint32_t fields_key;
2587             uint32_t ft_key;
2588             uint32_t v_key;
2589             uint32_t br_key;
2590             uint32_t contents_key;
2591             size_t i;
2592             int sig_seen;
2593             pdfmake_obj_t *field;
2594             pdfmake_obj_t *ft;
2595             const char *ft_name;
2596             pdfmake_obj_t *v;
2597             pdfmake_obj_t *sig_dict;
2598             pdfmake_obj_t *br;
2599             pdfmake_obj_t *contents;
2600             uint64_t br0, br1, br2, br3;
2601             size_t cms_len;
2602             pdfmake_pkcs7_t *pkcs7;
2603             pdfmake_hash_ctx_t *h;
2604             uint8_t digest[64];
2605             size_t digest_len;
2606              
2607 4 50         if (!arena || !pdf || len == 0 || field_index < 0) return NULL;
    50          
    50          
    50          
2608              
2609 4           r = pdfmake_arena_calloc(arena, sizeof(*r));
2610 4 50         if (!r) return NULL;
2611              
2612 4           parser = pdfmake_parser_new(pdf, len);
2613 4 50         if (!parser) {
2614 0           r->error = pdfmake_arena_strdup(arena, "out of memory");
2615 0           return r;
2616             }
2617 4           pdfmake_parser_set_repair(parser, 1);
2618 4           doc = NULL;
2619 4           err = pdfmake_parser_run(parser, &doc);
2620 4 50         if (err != PDFMAKE_OK || !doc) {
    50          
2621 0           r->error = pdfmake_arena_strdup(arena, "failed to parse PDF");
2622 0           pdfmake_parser_free(parser);
2623 0 0         if (doc) pdfmake_doc_free(doc);
2624 0           return r;
2625             }
2626              
2627 4           catalog = pdfmake_doc_get(doc, doc->root_num);
2628 4 50         if (!catalog || catalog->kind != PDFMAKE_DICT) {
    0          
2629 4           r->error = pdfmake_arena_strdup(arena, "missing catalog");
2630 4           pdfmake_parser_free(parser);
2631 4           pdfmake_doc_free(doc);
2632 4           return r;
2633             }
2634              
2635 0           acro_key = pdfmake_arena_intern_name(doc->arena, "AcroForm", 8);
2636 0           fields_key = pdfmake_arena_intern_name(doc->arena, "Fields", 6);
2637 0           ft_key = pdfmake_arena_intern_name(doc->arena, "FT", 2);
2638 0           v_key = pdfmake_arena_intern_name(doc->arena, "V", 1);
2639 0           br_key = pdfmake_arena_intern_name(doc->arena, "ByteRange", 9);
2640 0           contents_key = pdfmake_arena_intern_name(doc->arena, "Contents", 8);
2641              
2642 0           acro = pdfmake_dict_get(catalog, acro_key);
2643 0 0         if (acro && acro->kind == PDFMAKE_REF) acro = pdfmake_parser_resolve(parser, acro->as.ref);
    0          
2644 0 0         if (!acro || acro->kind != PDFMAKE_DICT) {
    0          
2645 0           r->error = pdfmake_arena_strdup(arena, "no signatures found");
2646 0           pdfmake_parser_free(parser);
2647 0           pdfmake_doc_free(doc);
2648 0           return r;
2649             }
2650              
2651 0           fields = pdfmake_dict_get(acro, fields_key);
2652 0 0         if (fields && fields->kind == PDFMAKE_REF) fields = pdfmake_parser_resolve(parser, fields->as.ref);
    0          
2653 0 0         if (!fields || fields->kind != PDFMAKE_ARRAY) {
    0          
2654 0           r->error = pdfmake_arena_strdup(arena, "no signature fields");
2655 0           pdfmake_parser_free(parser);
2656 0           pdfmake_doc_free(doc);
2657 0           return r;
2658             }
2659              
2660 0           sig_seen = -1;
2661 0           sig_dict = NULL;
2662 0 0         for (i = 0; i < pdfmake_array_len(fields); i++) {
2663 0           field = pdfmake_array_get(fields, i);
2664 0 0         if (!field) continue;
2665 0 0         if (field->kind == PDFMAKE_REF) field = pdfmake_parser_resolve(parser, field->as.ref);
2666 0 0         if (!field || field->kind != PDFMAKE_DICT) continue;
    0          
2667              
2668 0           ft = pdfmake_dict_get(field, ft_key);
2669 0 0         if (ft && ft->kind == PDFMAKE_REF) ft = pdfmake_parser_resolve(parser, ft->as.ref);
    0          
2670 0 0         if (!ft || ft->kind != PDFMAKE_NAME) continue;
    0          
2671 0           ft_name = pdfmake_get_name_bytes(doc->arena, ft);
2672 0 0         if (!ft_name || strcmp(ft_name, "Sig") != 0) continue;
    0          
2673              
2674 0           sig_seen++;
2675 0 0         if (sig_seen != field_index) continue;
2676              
2677 0           v = pdfmake_dict_get(field, v_key);
2678 0 0         if (v && v->kind == PDFMAKE_REF) v = pdfmake_parser_resolve(parser, v->as.ref);
    0          
2679 0 0         if (v && v->kind == PDFMAKE_DICT) {
    0          
2680 0           sig_dict = v;
2681 0           break;
2682             }
2683             }
2684              
2685 0 0         if (!sig_dict) {
2686 0           r->error = pdfmake_arena_strdup(arena, "signature field not found or unsigned");
2687 0           pdfmake_parser_free(parser);
2688 0           pdfmake_doc_free(doc);
2689 0           return r;
2690             }
2691              
2692 0           br = pdfmake_dict_get(sig_dict, br_key);
2693 0 0         if (br && br->kind == PDFMAKE_REF) br = pdfmake_parser_resolve(parser, br->as.ref);
    0          
2694 0           contents = pdfmake_dict_get(sig_dict, contents_key);
2695 0 0         if (contents && contents->kind == PDFMAKE_REF) contents = pdfmake_parser_resolve(parser, contents->as.ref);
    0          
2696 0 0         if (!br || br->kind != PDFMAKE_ARRAY || pdfmake_array_len(br) < 4 ||
    0          
    0          
    0          
2697 0 0         !contents || contents->kind != PDFMAKE_STR) {
2698 0           r->error = pdfmake_arena_strdup(arena, "invalid signature dictionary");
2699 0           pdfmake_parser_free(parser);
2700 0           pdfmake_doc_free(doc);
2701 0           return r;
2702             }
2703              
2704 0           br0 = (uint64_t)pdfmake_get_number(pdfmake_array_get(br, 0));
2705 0           br1 = (uint64_t)pdfmake_get_number(pdfmake_array_get(br, 1));
2706 0           br2 = (uint64_t)pdfmake_get_number(pdfmake_array_get(br, 2));
2707 0           br3 = (uint64_t)pdfmake_get_number(pdfmake_array_get(br, 3));
2708 0 0         if (br0 > len || br1 > len || br2 > len || br3 > len || br0 + br1 > len || br2 + br3 > len) {
    0          
    0          
    0          
    0          
    0          
2709 0           r->error = pdfmake_arena_strdup(arena, "invalid ByteRange");
2710 0           pdfmake_parser_free(parser);
2711 0           pdfmake_doc_free(doc);
2712 0           return r;
2713             }
2714              
2715 0           cms_len = contents->as.str.len;
2716 0 0         if (cms_len > 0) {
2717 0           size_t t = cms_len;
2718 0 0         while (t > 0 && contents->as.str.bytes[t - 1] == 0) t--;
    0          
2719 0           cms_len = t;
2720 0 0         if (cms_len > 0) {
2721             size_t der_obj_len;
2722 0 0         if (asn1_der_object_len(contents->as.str.bytes, cms_len, &der_obj_len) == 0) {
2723 0           cms_len = der_obj_len;
2724             }
2725             }
2726             }
2727 0 0         if (cms_len == 0) {
2728 0           r->error = pdfmake_arena_strdup(arena, "empty /Contents");
2729 0           pdfmake_parser_free(parser);
2730 0           pdfmake_doc_free(doc);
2731 0           return r;
2732             }
2733              
2734 0           pkcs7 = pdfmake_pkcs7_parse(arena, contents->as.str.bytes, cms_len);
2735 0 0         if (!pkcs7) {
2736 0           r->error = pdfmake_arena_strdup(arena, "invalid PKCS#7 signature");
2737 0           pdfmake_parser_free(parser);
2738 0           pdfmake_doc_free(doc);
2739 0           return r;
2740             }
2741              
2742 0 0         h = pdfmake_hash_new(pkcs7->hash_alg ? pkcs7->hash_alg : PDFMAKE_HASH_SHA256);
2743 0 0         if (!h) {
2744 0           r->error = pdfmake_arena_strdup(arena, "out of memory");
2745 0           pdfmake_parser_free(parser);
2746 0           pdfmake_doc_free(doc);
2747 0           return r;
2748             }
2749 0           pdfmake_hash_update(h, pdf + br0, (size_t)br1);
2750 0           pdfmake_hash_update(h, pdf + br2, (size_t)br3);
2751 0           digest_len = pdfmake_hash_final(h, digest);
2752 0           pdfmake_hash_free(h);
2753              
2754 0           r->digest_valid = (pkcs7->message_digest &&
2755 0 0         pkcs7->message_digest_len == digest_len &&
    0          
2756 0 0         memcmp(pkcs7->message_digest, digest, digest_len) == 0);
2757 0           r->document_modified = r->digest_valid ? 0 : 1;
2758              
2759 0           err = pdfmake_pkcs7_verify(pkcs7, digest, digest_len);
2760 0           r->signature_valid = (err == PDFMAKE_OK) ? 1 : 0;
2761 0 0         r->cert_valid = (pkcs7->signer_cert && pdfmake_x509_is_valid(pkcs7->signer_cert, 0) &&
2762 0 0         pdfmake_x509_can_sign_documents(pkcs7->signer_cert)) ? 1 : 0;
    0          
2763 0           r->timestamp_valid = (pkcs7->timestamp_token_len > 0) ? 1 : 1;
2764 0           r->signing_time = pkcs7->signing_time;
2765 0           r->signer_cert = pkcs7->signer_cert;
2766 0           r->cert_chain = pkcs7->certs;
2767 0 0         if (pkcs7->signer_cert) {
2768 0           r->signer_name = pkcs7->signer_cert->subject.common_name;
2769 0           r->signer_email = pkcs7->signer_cert->subject.email;
2770             }
2771              
2772 0 0         r->valid = (r->signature_valid && r->digest_valid && r->cert_valid) ? 1 : 0;
    0          
    0          
2773 0 0         if (!r->valid && !r->error) {
    0          
2774 0 0         if (!r->digest_valid) {
2775 0           r->error = pdfmake_arena_strdup(arena, "document digest mismatch");
2776 0 0         } else if (!r->signature_valid) {
2777 0           r->error = pdfmake_arena_strdup(arena, "signature verification failed");
2778 0 0         } else if (!r->cert_valid) {
2779 0           r->error = pdfmake_arena_strdup(arena, "signer certificate is not valid");
2780             }
2781             }
2782              
2783 0           pdfmake_parser_free(parser);
2784 0           pdfmake_doc_free(doc);
2785 0           return r;
2786             }
2787              
2788 4           int pdfmake_sig_count(const uint8_t *pdf, size_t len)
2789             {
2790             pdfmake_parser_t *parser;
2791             pdfmake_doc_t *doc;
2792             pdfmake_err_t err;
2793             pdfmake_obj_t *catalog;
2794             pdfmake_obj_t *acro;
2795             pdfmake_obj_t *fields;
2796             uint32_t acro_key;
2797             uint32_t fields_key;
2798             uint32_t ft_key;
2799             size_t i;
2800             int count;
2801             pdfmake_obj_t *field;
2802             pdfmake_obj_t *ft;
2803             const char *ft_name;
2804 4 50         if (!pdf || len == 0) return 0;
    50          
2805              
2806 4           parser = pdfmake_parser_new(pdf, len);
2807 4 50         if (!parser) return 0;
2808 4           pdfmake_parser_set_repair(parser, 1);
2809 4           doc = NULL;
2810 4           err = pdfmake_parser_run(parser, &doc);
2811 4 50         if (err != PDFMAKE_OK || !doc) {
    50          
2812 0           pdfmake_parser_free(parser);
2813 0 0         if (doc) pdfmake_doc_free(doc);
2814 0           return 0;
2815             }
2816              
2817 4           catalog = pdfmake_doc_get(doc, doc->root_num);
2818 4 50         if (!catalog || catalog->kind != PDFMAKE_DICT) {
    0          
2819 4           pdfmake_parser_free(parser);
2820 4           pdfmake_doc_free(doc);
2821 4           return 0;
2822             }
2823 0           acro_key = pdfmake_arena_intern_name(doc->arena, "AcroForm", 8);
2824 0           fields_key = pdfmake_arena_intern_name(doc->arena, "Fields", 6);
2825 0           ft_key = pdfmake_arena_intern_name(doc->arena, "FT", 2);
2826              
2827 0           acro = pdfmake_dict_get(catalog, acro_key);
2828 0 0         if (acro && acro->kind == PDFMAKE_REF) acro = pdfmake_parser_resolve(parser, acro->as.ref);
    0          
2829 0 0         if (!acro || acro->kind != PDFMAKE_DICT) {
    0          
2830 0           pdfmake_parser_free(parser);
2831 0           pdfmake_doc_free(doc);
2832 0           return 0;
2833             }
2834              
2835 0           fields = pdfmake_dict_get(acro, fields_key);
2836 0 0         if (fields && fields->kind == PDFMAKE_REF) fields = pdfmake_parser_resolve(parser, fields->as.ref);
    0          
2837 0 0         if (!fields || fields->kind != PDFMAKE_ARRAY) {
    0          
2838 0           pdfmake_parser_free(parser);
2839 0           pdfmake_doc_free(doc);
2840 0           return 0;
2841             }
2842              
2843 0           count = 0;
2844 0 0         for (i = 0; i < pdfmake_array_len(fields); i++) {
2845 0           field = pdfmake_array_get(fields, i);
2846 0 0         if (!field) continue;
2847 0 0         if (field->kind == PDFMAKE_REF) field = pdfmake_parser_resolve(parser, field->as.ref);
2848 0 0         if (!field || field->kind != PDFMAKE_DICT) continue;
    0          
2849 0           ft = pdfmake_dict_get(field, ft_key);
2850 0 0         if (ft && ft->kind == PDFMAKE_REF) ft = pdfmake_parser_resolve(parser, ft->as.ref);
    0          
2851 0 0         if (!ft || ft->kind != PDFMAKE_NAME) continue;
    0          
2852 0           ft_name = pdfmake_get_name_bytes(doc->arena, ft);
2853 0 0         if (ft_name && strcmp(ft_name, "Sig") == 0) count++;
    0          
2854             }
2855              
2856 0           pdfmake_parser_free(parser);
2857 0           pdfmake_doc_free(doc);
2858 0           return count;
2859             }