/src/mupdf/source/pdf/pdf-signature.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2021 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | #include "pdf-annot-imp.h" |
25 | | |
26 | | #include <string.h> |
27 | | #include <time.h> |
28 | | |
29 | | enum |
30 | | { |
31 | | PDF_SIGFLAGS_SIGSEXIST = 1, |
32 | | PDF_SIGFLAGS_APPENDONLY = 2 |
33 | | }; |
34 | | |
35 | | void pdf_write_digest(fz_context *ctx, fz_output *out, pdf_obj *byte_range, pdf_obj *field, size_t hexdigest_offset, size_t hexdigest_length, pdf_pkcs7_signer *signer) |
36 | 0 | { |
37 | 0 | fz_stream *stm = NULL; |
38 | 0 | fz_stream *in = NULL; |
39 | 0 | fz_range *brange = NULL; |
40 | 0 | int brange_len = pdf_array_len(ctx, byte_range)/2; |
41 | 0 | unsigned char *digest = NULL; |
42 | 0 | size_t digest_len; |
43 | 0 | pdf_obj *v = pdf_dict_get(ctx, field, PDF_NAME(V)); |
44 | 0 | size_t len; |
45 | 0 | char *cstr = NULL; |
46 | |
|
47 | 0 | fz_var(stm); |
48 | 0 | fz_var(in); |
49 | 0 | fz_var(brange); |
50 | 0 | fz_var(digest); |
51 | 0 | fz_var(cstr); |
52 | |
|
53 | 0 | if (hexdigest_length < 4) |
54 | 0 | fz_throw(ctx, FZ_ERROR_GENERIC, "Bad parameters to pdf_write_digest"); |
55 | | |
56 | 0 | len = (hexdigest_length - 2) / 2; |
57 | |
|
58 | 0 | fz_try(ctx) |
59 | 0 | { |
60 | 0 | int i; |
61 | 0 | size_t z; |
62 | |
|
63 | 0 | brange = fz_calloc(ctx, brange_len, sizeof(*brange)); |
64 | 0 | for (i = 0; i < brange_len; i++) |
65 | 0 | { |
66 | 0 | brange[i].offset = pdf_array_get_int(ctx, byte_range, 2*i); |
67 | 0 | brange[i].length = pdf_array_get_int(ctx, byte_range, 2*i+1); |
68 | 0 | } |
69 | |
|
70 | 0 | stm = fz_stream_from_output(ctx, out); |
71 | 0 | in = fz_open_range_filter(ctx, stm, brange, brange_len); |
72 | |
|
73 | 0 | digest = fz_malloc(ctx, len); |
74 | 0 | digest_len = signer->create_digest(ctx, signer, in, digest, len); |
75 | 0 | if (digest_len == 0) |
76 | 0 | fz_throw(ctx, FZ_ERROR_GENERIC, "signer provided no signature digest"); |
77 | 0 | if (digest_len > len) |
78 | 0 | fz_throw(ctx, FZ_ERROR_GENERIC, "signature digest larger than space for digest"); |
79 | | |
80 | 0 | fz_drop_stream(ctx, in); |
81 | 0 | in = NULL; |
82 | 0 | fz_drop_stream(ctx, stm); |
83 | 0 | stm = NULL; |
84 | |
|
85 | 0 | fz_seek_output(ctx, out, (int64_t)hexdigest_offset+1, SEEK_SET); |
86 | 0 | cstr = fz_malloc(ctx, len); |
87 | |
|
88 | 0 | for (z = 0; z < len; z++) |
89 | 0 | { |
90 | 0 | int val = z < digest_len ? digest[z] : 0; |
91 | 0 | fz_write_printf(ctx, out, "%02x", val); |
92 | 0 | cstr[z] = val; |
93 | 0 | } |
94 | |
|
95 | 0 | pdf_dict_put_string(ctx, v, PDF_NAME(Contents), cstr, len); |
96 | 0 | } |
97 | 0 | fz_always(ctx) |
98 | 0 | { |
99 | 0 | fz_free(ctx, cstr); |
100 | 0 | fz_free(ctx, digest); |
101 | 0 | fz_free(ctx, brange); |
102 | 0 | fz_drop_stream(ctx, stm); |
103 | 0 | fz_drop_stream(ctx, in); |
104 | 0 | } |
105 | 0 | fz_catch(ctx) |
106 | 0 | { |
107 | 0 | fz_rethrow(ctx); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | typedef struct fieldname_prefix |
112 | | { |
113 | | struct fieldname_prefix *prev; |
114 | | char name[1]; |
115 | | } fieldname_prefix; |
116 | | |
117 | | typedef struct |
118 | | { |
119 | | pdf_locked_fields *locked; |
120 | | fieldname_prefix *prefix; |
121 | | } sig_locking_data; |
122 | | |
123 | | static void |
124 | | check_field_locking(fz_context *ctx, pdf_obj *obj, void *data_, pdf_obj **ff) |
125 | 0 | { |
126 | 0 | fieldname_prefix *prefix = NULL; |
127 | 0 | sig_locking_data *data = (sig_locking_data *)data_; |
128 | |
|
129 | 0 | fz_var(prefix); |
130 | |
|
131 | 0 | fz_try(ctx) |
132 | 0 | { |
133 | 0 | const char *name = NULL; |
134 | 0 | size_t n = 1; |
135 | 0 | pdf_obj *t; |
136 | |
|
137 | 0 | t = pdf_dict_get(ctx, obj, PDF_NAME(T)); |
138 | 0 | if (t != NULL) |
139 | 0 | { |
140 | 0 | name = pdf_to_text_string(ctx, t); |
141 | 0 | n += strlen(name); |
142 | 0 | } |
143 | 0 | if (data->prefix->name[0] && name) |
144 | 0 | n += 1; |
145 | 0 | if (data->prefix->name[0]) |
146 | 0 | n += strlen(data->prefix->name); |
147 | 0 | prefix = fz_calloc(ctx, 1, sizeof(*prefix)+n); |
148 | 0 | prefix->prev = data->prefix; |
149 | 0 | if (data->prefix->name[0]) |
150 | 0 | strcpy(prefix->name, data->prefix->name); |
151 | 0 | if (data->prefix->name[0] && name) |
152 | 0 | strcat(prefix->name, "."); |
153 | 0 | if (name) |
154 | 0 | strcat(prefix->name, name); |
155 | 0 | data->prefix = prefix; |
156 | |
|
157 | 0 | if (pdf_name_eq(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Type)), PDF_NAME(Annot)) && |
158 | 0 | pdf_name_eq(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Subtype)), PDF_NAME(Widget))) |
159 | 0 | { |
160 | 0 | int flags = pdf_to_int(ctx, *ff); |
161 | |
|
162 | 0 | if (((flags & PDF_FIELD_IS_READ_ONLY) == 0) && /* Field is not currently locked */ |
163 | 0 | pdf_is_field_locked(ctx, data->locked, data->prefix->name)) /* Field should be locked */ |
164 | 0 | pdf_dict_put_drop(ctx, obj, PDF_NAME(Ff), pdf_new_int(ctx, flags | PDF_FIELD_IS_READ_ONLY)); |
165 | 0 | } |
166 | 0 | } |
167 | 0 | fz_catch(ctx) |
168 | 0 | { |
169 | 0 | if (prefix) |
170 | 0 | { |
171 | 0 | data->prefix = prefix->prev; |
172 | 0 | fz_free(ctx, prefix); |
173 | 0 | } |
174 | 0 | fz_rethrow(ctx); |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | static void |
179 | | pop_field_locking(fz_context *ctx, pdf_obj *obj, void *data_) |
180 | 0 | { |
181 | 0 | fieldname_prefix *prefix; |
182 | 0 | sig_locking_data *data = (sig_locking_data *)data_; |
183 | |
|
184 | 0 | prefix = data->prefix; |
185 | 0 | data->prefix = data->prefix->prev; |
186 | 0 | fz_free(ctx, prefix); |
187 | 0 | } |
188 | | |
189 | | static void enact_sig_locking(fz_context *ctx, pdf_document *doc, pdf_obj *sig) |
190 | 0 | { |
191 | 0 | pdf_locked_fields *locked = pdf_find_locked_fields_for_sig(ctx, doc, sig); |
192 | 0 | pdf_obj *fields; |
193 | 0 | static pdf_obj *ff_names[2] = { PDF_NAME(Ff), NULL }; |
194 | 0 | pdf_obj *ff = NULL; |
195 | 0 | static fieldname_prefix null_prefix = { NULL, "" }; |
196 | 0 | sig_locking_data data = { locked, &null_prefix }; |
197 | |
|
198 | 0 | if (locked == NULL) |
199 | 0 | return; |
200 | | |
201 | 0 | fz_try(ctx) |
202 | 0 | { |
203 | 0 | fields = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/Fields"); |
204 | 0 | pdf_walk_tree(ctx, fields, PDF_NAME(Kids), check_field_locking, pop_field_locking, &data, &ff_names[0], &ff); |
205 | 0 | } |
206 | 0 | fz_always(ctx) |
207 | 0 | pdf_drop_locked_fields(ctx, locked); |
208 | 0 | fz_catch(ctx) |
209 | 0 | fz_rethrow(ctx); |
210 | 0 | } |
211 | | |
212 | | void |
213 | | pdf_sign_signature_with_appearance(fz_context *ctx, pdf_annot *widget, pdf_pkcs7_signer *signer, int64_t t, fz_display_list *disp_list) |
214 | 0 | { |
215 | 0 | pdf_document *doc = widget->page->doc; |
216 | |
|
217 | 0 | if (pdf_widget_is_readonly(ctx, widget)) |
218 | 0 | fz_throw(ctx, FZ_ERROR_GENERIC, "Signature is read only, it cannot be signed."); |
219 | | |
220 | 0 | pdf_begin_operation(ctx, doc, "Sign signature"); |
221 | |
|
222 | 0 | fz_try(ctx) |
223 | 0 | { |
224 | 0 | pdf_obj *wobj = ((pdf_annot *)widget)->obj; |
225 | 0 | pdf_obj *form; |
226 | 0 | int sf; |
227 | |
|
228 | 0 | pdf_dirty_annot(ctx, widget); |
229 | | |
230 | | /* Ensure that all fields that will be locked by this signature |
231 | | * are marked as ReadOnly. */ |
232 | 0 | enact_sig_locking(ctx, doc, wobj); |
233 | |
|
234 | 0 | if (disp_list) |
235 | 0 | pdf_set_annot_appearance_from_display_list(ctx, widget, "N", NULL, fz_identity, disp_list); |
236 | | |
237 | | /* Update the SigFlags for the document if required */ |
238 | 0 | form = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm"); |
239 | 0 | if (!form) |
240 | 0 | { |
241 | 0 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
242 | 0 | form = pdf_dict_put_dict(ctx, root, PDF_NAME(AcroForm), 1); |
243 | 0 | } |
244 | |
|
245 | 0 | sf = pdf_to_int(ctx, pdf_dict_get(ctx, form, PDF_NAME(SigFlags))); |
246 | 0 | if ((sf & (PDF_SIGFLAGS_SIGSEXIST | PDF_SIGFLAGS_APPENDONLY)) != (PDF_SIGFLAGS_SIGSEXIST | PDF_SIGFLAGS_APPENDONLY)) |
247 | 0 | pdf_dict_put_drop(ctx, form, PDF_NAME(SigFlags), pdf_new_int(ctx, sf | PDF_SIGFLAGS_SIGSEXIST | PDF_SIGFLAGS_APPENDONLY)); |
248 | |
|
249 | 0 | pdf_signature_set_value(ctx, doc, wobj, signer, t); |
250 | 0 | pdf_end_operation(ctx, doc); |
251 | 0 | } |
252 | 0 | fz_catch(ctx) |
253 | 0 | { |
254 | 0 | pdf_abandon_operation(ctx, doc); |
255 | 0 | fz_rethrow(ctx); |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | static pdf_pkcs7_distinguished_name placeholder_dn = { |
260 | | "Your Common Name Here", |
261 | | "Organization", |
262 | | "Organizational Unit", |
263 | | "Email", |
264 | | "Country" |
265 | | }; |
266 | | |
267 | | static char * |
268 | | pdf_format_signature_info(fz_context *ctx, pdf_pkcs7_signer *signer, int flags, const char *reason, const char *location, int64_t now, char **name) |
269 | 0 | { |
270 | 0 | pdf_pkcs7_distinguished_name *dn = NULL; |
271 | 0 | char *info; |
272 | 0 | fz_var(dn); |
273 | 0 | fz_try(ctx) |
274 | 0 | { |
275 | 0 | if (signer) |
276 | 0 | dn = signer->get_signing_name(ctx, signer); |
277 | 0 | if (!dn) |
278 | 0 | dn = &placeholder_dn; |
279 | 0 | *name = fz_strdup(ctx, dn->cn ? dn->cn : "Your Common Name Here"); |
280 | 0 | info = pdf_signature_info(ctx, |
281 | 0 | (flags & PDF_SIGNATURE_SHOW_TEXT_NAME) ? *name : NULL, |
282 | 0 | (flags & PDF_SIGNATURE_SHOW_DN) ? dn : NULL, |
283 | 0 | reason, |
284 | 0 | location, |
285 | 0 | (flags & PDF_SIGNATURE_SHOW_DATE) ? now : -1, |
286 | 0 | (flags & PDF_SIGNATURE_SHOW_LABELS) ? 1 : 0); |
287 | 0 | } |
288 | 0 | fz_always(ctx) |
289 | 0 | { |
290 | 0 | if (dn != &placeholder_dn) |
291 | 0 | pdf_signature_drop_distinguished_name(ctx, dn); |
292 | 0 | } |
293 | 0 | fz_catch(ctx) |
294 | 0 | fz_rethrow(ctx); |
295 | 0 | return info; |
296 | 0 | } |
297 | | |
298 | | |
299 | | void pdf_sign_signature(fz_context *ctx, pdf_annot *widget, |
300 | | pdf_pkcs7_signer *signer, |
301 | | int flags, |
302 | | fz_image *graphic, |
303 | | const char *reason, |
304 | | const char *location) |
305 | 0 | { |
306 | 0 | int logo = flags & PDF_SIGNATURE_SHOW_LOGO; |
307 | 0 | fz_rect rect = pdf_annot_rect(ctx, widget); |
308 | 0 | fz_text_language lang = pdf_annot_language(ctx, widget); |
309 | 0 | int64_t now = time(NULL); |
310 | 0 | char *name = NULL; |
311 | 0 | char *info = NULL; |
312 | 0 | fz_display_list *dlist = NULL; |
313 | |
|
314 | 0 | fz_var(dlist); |
315 | 0 | fz_var(info); |
316 | 0 | fz_var(name); |
317 | | |
318 | | /* Create an appearance stream only if the signature is intended to be visible */ |
319 | 0 | fz_try(ctx) |
320 | 0 | { |
321 | 0 | if (!fz_is_empty_rect(rect)) |
322 | 0 | { |
323 | 0 | info = pdf_format_signature_info(ctx, signer, flags, reason, location, now, &name); |
324 | 0 | if (graphic) |
325 | 0 | dlist = pdf_signature_appearance_signed(ctx, rect, lang, graphic, NULL, info, logo); |
326 | 0 | else if (flags & PDF_SIGNATURE_SHOW_GRAPHIC_NAME) |
327 | 0 | dlist = pdf_signature_appearance_signed(ctx, rect, lang, NULL, name, info, logo); |
328 | 0 | else |
329 | 0 | dlist = pdf_signature_appearance_signed(ctx, rect, lang, NULL, NULL, info, logo); |
330 | 0 | } |
331 | 0 | pdf_sign_signature_with_appearance(ctx, widget, signer, now, dlist); |
332 | 0 | } |
333 | 0 | fz_always(ctx) |
334 | 0 | { |
335 | 0 | fz_free(ctx, info); |
336 | 0 | fz_free(ctx, name); |
337 | 0 | fz_drop_display_list(ctx, dlist); |
338 | 0 | } |
339 | 0 | fz_catch(ctx) |
340 | 0 | fz_rethrow(ctx); |
341 | 0 | } |
342 | | |
343 | | fz_display_list *pdf_preview_signature_as_display_list(fz_context *ctx, |
344 | | float w, float h, fz_text_language lang, |
345 | | pdf_pkcs7_signer *signer, |
346 | | int flags, |
347 | | fz_image *graphic, |
348 | | const char *reason, |
349 | | const char *location) |
350 | 0 | { |
351 | 0 | int logo = flags & PDF_SIGNATURE_SHOW_LOGO; |
352 | 0 | fz_rect rect = fz_make_rect(0, 0, w, h); |
353 | 0 | int64_t now = time(NULL); |
354 | 0 | char *name = NULL; |
355 | 0 | char *info = NULL; |
356 | 0 | fz_display_list *dlist = NULL; |
357 | |
|
358 | 0 | fz_var(dlist); |
359 | 0 | fz_var(info); |
360 | 0 | fz_var(name); |
361 | |
|
362 | 0 | fz_try(ctx) |
363 | 0 | { |
364 | 0 | info = pdf_format_signature_info(ctx, signer, flags, reason, location, now, &name); |
365 | 0 | if (graphic) |
366 | 0 | dlist = pdf_signature_appearance_signed(ctx, rect, lang, graphic, NULL, info, logo); |
367 | 0 | else if (flags & PDF_SIGNATURE_SHOW_GRAPHIC_NAME) |
368 | 0 | dlist = pdf_signature_appearance_signed(ctx, rect, lang, NULL, name, info, logo); |
369 | 0 | else |
370 | 0 | dlist = pdf_signature_appearance_signed(ctx, rect, lang, NULL, NULL, info, logo); |
371 | 0 | } |
372 | 0 | fz_always(ctx) |
373 | 0 | { |
374 | 0 | fz_free(ctx, info); |
375 | 0 | fz_free(ctx, name); |
376 | 0 | } |
377 | 0 | fz_catch(ctx) |
378 | 0 | fz_rethrow(ctx); |
379 | | |
380 | 0 | return dlist; |
381 | 0 | } |
382 | | |
383 | | fz_pixmap *pdf_preview_signature_as_pixmap(fz_context *ctx, |
384 | | int w, int h, fz_text_language lang, |
385 | | pdf_pkcs7_signer *signer, |
386 | | int flags, |
387 | | fz_image *graphic, |
388 | | const char *reason, |
389 | | const char *location) |
390 | 0 | { |
391 | 0 | fz_pixmap *pix; |
392 | 0 | fz_display_list *dlist = pdf_preview_signature_as_display_list(ctx, |
393 | 0 | w, h, lang, |
394 | 0 | signer, flags, graphic, reason, location); |
395 | 0 | fz_try(ctx) |
396 | 0 | pix = fz_new_pixmap_from_display_list(ctx, dlist, fz_identity, fz_device_rgb(ctx), 0); |
397 | 0 | fz_always(ctx) |
398 | 0 | fz_drop_display_list(ctx, dlist); |
399 | 0 | fz_catch(ctx) |
400 | 0 | fz_rethrow(ctx); |
401 | 0 | return pix; |
402 | 0 | } |
403 | | |
404 | | void pdf_clear_signature(fz_context *ctx, pdf_annot *widget) |
405 | 0 | { |
406 | 0 | int flags; |
407 | 0 | fz_display_list *dlist = NULL; |
408 | |
|
409 | 0 | fz_var(dlist); |
410 | 0 | fz_try(ctx) |
411 | 0 | { |
412 | 0 | fz_text_language lang = pdf_annot_language(ctx, (pdf_annot *)widget); |
413 | 0 | fz_rect rect = pdf_annot_rect(ctx, widget); |
414 | |
|
415 | 0 | pdf_begin_operation(ctx, widget->page->doc, "Clear Signature"); |
416 | 0 | if (pdf_widget_is_readonly(ctx, widget)) |
417 | 0 | fz_throw(ctx, FZ_ERROR_GENERIC, "Signature read only, it cannot be cleared."); |
418 | | |
419 | 0 | pdf_xref_remove_unsaved_signature(ctx, ((pdf_annot *)widget)->page->doc, ((pdf_annot *)widget)->obj); |
420 | |
|
421 | 0 | pdf_dirty_annot(ctx, widget); |
422 | |
|
423 | 0 | flags = pdf_dict_get_int(ctx, ((pdf_annot *)widget)->obj, PDF_NAME(F)); |
424 | 0 | flags &= ~PDF_ANNOT_IS_LOCKED; |
425 | 0 | if (flags) |
426 | 0 | pdf_dict_put_int(ctx, ((pdf_annot *)widget)->obj, PDF_NAME(F), flags); |
427 | 0 | else |
428 | 0 | pdf_dict_del(ctx, ((pdf_annot *)widget)->obj, PDF_NAME(F)); |
429 | |
|
430 | 0 | pdf_dict_del(ctx, ((pdf_annot *)widget)->obj, PDF_NAME(V)); |
431 | |
|
432 | 0 | dlist = pdf_signature_appearance_unsigned(ctx, rect, lang); |
433 | 0 | pdf_set_annot_appearance_from_display_list(ctx, widget, "N", NULL, fz_identity, dlist); |
434 | 0 | pdf_end_operation(ctx, widget->page->doc); |
435 | 0 | } |
436 | 0 | fz_always(ctx) |
437 | 0 | fz_drop_display_list(ctx, dlist); |
438 | 0 | fz_catch(ctx) |
439 | 0 | { |
440 | 0 | pdf_abandon_operation(ctx, widget->page->doc); |
441 | 0 | fz_rethrow(ctx); |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | void pdf_drop_signer(fz_context *ctx, pdf_pkcs7_signer *signer) |
446 | 0 | { |
447 | 0 | if (signer) |
448 | 0 | signer->drop(ctx, signer); |
449 | 0 | } |
450 | | |
451 | | void pdf_drop_verifier(fz_context *ctx, pdf_pkcs7_verifier *verifier) |
452 | 0 | { |
453 | 0 | if (verifier) |
454 | 0 | verifier->drop(ctx, verifier); |
455 | 0 | } |
456 | | |
457 | | char *pdf_signature_error_description(pdf_signature_error err) |
458 | 0 | { |
459 | 0 | switch (err) |
460 | 0 | { |
461 | 0 | case PDF_SIGNATURE_ERROR_OKAY: |
462 | 0 | return "OK"; |
463 | 0 | case PDF_SIGNATURE_ERROR_NO_SIGNATURES: |
464 | 0 | return "No signatures."; |
465 | 0 | case PDF_SIGNATURE_ERROR_NO_CERTIFICATE: |
466 | 0 | return "No certificate."; |
467 | 0 | case PDF_SIGNATURE_ERROR_DIGEST_FAILURE: |
468 | 0 | return "Signature invalidated by change to document."; |
469 | 0 | case PDF_SIGNATURE_ERROR_SELF_SIGNED: |
470 | 0 | return "Self-signed certificate."; |
471 | 0 | case PDF_SIGNATURE_ERROR_SELF_SIGNED_IN_CHAIN: |
472 | 0 | return "Self-signed certificate in chain."; |
473 | 0 | case PDF_SIGNATURE_ERROR_NOT_TRUSTED: |
474 | 0 | return "Certificate not trusted."; |
475 | 0 | default: |
476 | 0 | case PDF_SIGNATURE_ERROR_UNKNOWN: |
477 | 0 | return "Unknown error."; |
478 | 0 | } |
479 | 0 | } |
480 | | |
481 | | void pdf_signature_drop_distinguished_name(fz_context *ctx, pdf_pkcs7_distinguished_name *dn) |
482 | 0 | { |
483 | 0 | if (dn) |
484 | 0 | { |
485 | 0 | fz_free(ctx, dn->c); |
486 | 0 | fz_free(ctx, dn->email); |
487 | 0 | fz_free(ctx, dn->ou); |
488 | 0 | fz_free(ctx, dn->o); |
489 | 0 | fz_free(ctx, dn->cn); |
490 | 0 | fz_free(ctx, dn); |
491 | 0 | } |
492 | 0 | } |
493 | | |
494 | | char *pdf_signature_format_distinguished_name(fz_context *ctx, pdf_pkcs7_distinguished_name *name) |
495 | 0 | { |
496 | 0 | const char *parts[] = { |
497 | 0 | "cn=", "", |
498 | 0 | ", o=", "", |
499 | 0 | ", ou=", "", |
500 | 0 | ", email=", "", |
501 | 0 | ", c=", ""}; |
502 | 0 | size_t len = 1; |
503 | 0 | char *s; |
504 | 0 | int i; |
505 | |
|
506 | 0 | if (name == NULL) |
507 | 0 | return NULL; |
508 | | |
509 | 0 | parts[1] = name->cn; |
510 | 0 | parts[3] = name->o; |
511 | 0 | parts[5] = name->ou; |
512 | 0 | parts[7] = name->email; |
513 | 0 | parts[9] = name->c; |
514 | |
|
515 | 0 | for (i = 0; i < (int)nelem(parts); i++) |
516 | 0 | if (parts[i]) |
517 | 0 | len += strlen(parts[i]); |
518 | |
|
519 | 0 | s = fz_malloc(ctx, len); |
520 | 0 | s[0] = '\0'; |
521 | |
|
522 | 0 | for (i = 0; i < (int)nelem(parts); i++) |
523 | 0 | if (parts[i]) |
524 | 0 | fz_strlcat(s, parts[i], len); |
525 | |
|
526 | 0 | return s; |
527 | 0 | } |
528 | | |
529 | | pdf_pkcs7_distinguished_name *pdf_signature_get_widget_signatory(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_annot *widget) |
530 | 0 | { |
531 | 0 | return pdf_signature_get_signatory(ctx, verifier, widget->page->doc, widget->obj); |
532 | 0 | } |
533 | | |
534 | | pdf_pkcs7_distinguished_name *pdf_signature_get_signatory(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_document *doc, pdf_obj *signature) |
535 | 0 | { |
536 | 0 | char *contents = NULL; |
537 | 0 | size_t contents_len; |
538 | 0 | pdf_pkcs7_distinguished_name *dn; |
539 | |
|
540 | 0 | contents_len = pdf_signature_contents(ctx, doc, signature, &contents); |
541 | 0 | if (contents_len == 0) |
542 | 0 | return NULL; |
543 | | |
544 | 0 | fz_try(ctx) |
545 | 0 | dn = verifier->get_signatory(ctx, verifier, (unsigned char *)contents, contents_len); |
546 | 0 | fz_always(ctx) |
547 | 0 | fz_free(ctx, contents); |
548 | 0 | fz_catch(ctx) |
549 | 0 | fz_rethrow(ctx); |
550 | | |
551 | 0 | return dn; |
552 | 0 | } |
553 | | |
554 | | pdf_signature_error pdf_check_widget_digest(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_annot *widget) |
555 | 0 | { |
556 | 0 | return pdf_check_digest(ctx, verifier, widget->page->doc, widget->obj); |
557 | 0 | } |
558 | | |
559 | | pdf_signature_error pdf_check_digest(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_document *doc, pdf_obj *signature) |
560 | 0 | { |
561 | 0 | pdf_signature_error result = PDF_SIGNATURE_ERROR_UNKNOWN; |
562 | 0 | fz_stream *bytes = NULL; |
563 | 0 | char *contents = NULL; |
564 | 0 | size_t contents_len = pdf_signature_contents(ctx, doc, signature, &contents); |
565 | 0 | fz_var(bytes); |
566 | 0 | fz_try(ctx) |
567 | 0 | { |
568 | 0 | bytes = pdf_signature_hash_bytes(ctx, doc, signature); |
569 | 0 | result = verifier->check_digest(ctx, verifier, bytes, (unsigned char *)contents, contents_len); |
570 | 0 | } |
571 | 0 | fz_always(ctx) |
572 | 0 | { |
573 | 0 | fz_drop_stream(ctx, bytes); |
574 | 0 | fz_free(ctx, contents); |
575 | 0 | } |
576 | 0 | fz_catch(ctx) |
577 | 0 | { |
578 | 0 | fz_rethrow(ctx); |
579 | 0 | } |
580 | | |
581 | 0 | return result; |
582 | 0 | } |
583 | | |
584 | | pdf_signature_error pdf_check_widget_certificate(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_annot *w) |
585 | 0 | { |
586 | 0 | return pdf_check_certificate(ctx, verifier, w->page->doc, w->obj); |
587 | 0 | } |
588 | | |
589 | | pdf_signature_error pdf_check_certificate(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_document *doc, pdf_obj *signature) |
590 | 0 | { |
591 | 0 | char *contents = NULL; |
592 | 0 | size_t contents_len = pdf_signature_contents(ctx, doc, signature, &contents); |
593 | 0 | pdf_signature_error result = PDF_SIGNATURE_ERROR_UNKNOWN; |
594 | 0 | fz_try(ctx) |
595 | 0 | result = verifier->check_certificate(ctx, verifier, (unsigned char *)contents, contents_len); |
596 | 0 | fz_always(ctx) |
597 | 0 | fz_free(ctx, contents); |
598 | 0 | fz_catch(ctx) |
599 | 0 | fz_rethrow(ctx); |
600 | 0 | return result; |
601 | 0 | } |
602 | | |
603 | | int pdf_check_signature(fz_context *ctx, pdf_pkcs7_verifier *verifier, pdf_document *doc, pdf_obj *signature, char *ebuf, size_t ebufsize) |
604 | 0 | { |
605 | 0 | int res = 0; |
606 | |
|
607 | 0 | if (pdf_xref_obj_is_unsaved_signature(doc, signature)) |
608 | 0 | { |
609 | 0 | fz_strlcpy(ebuf, "Signed but document yet to be saved.", ebufsize); |
610 | 0 | if (ebufsize > 0) |
611 | 0 | ebuf[ebufsize-1] = 0; |
612 | 0 | return 0; |
613 | 0 | } |
614 | | |
615 | 0 | fz_var(res); |
616 | 0 | fz_try(ctx) |
617 | 0 | { |
618 | 0 | if (pdf_signature_is_signed(ctx, doc, signature)) |
619 | 0 | { |
620 | 0 | pdf_signature_error err; |
621 | |
|
622 | 0 | err = pdf_check_digest(ctx, verifier, doc, signature); |
623 | 0 | if (err == PDF_SIGNATURE_ERROR_OKAY) |
624 | 0 | err = pdf_check_certificate(ctx, verifier, doc, signature); |
625 | |
|
626 | 0 | fz_strlcpy(ebuf, pdf_signature_error_description(err), ebufsize); |
627 | 0 | res = (err == PDF_SIGNATURE_ERROR_OKAY); |
628 | |
|
629 | 0 | switch (err) |
630 | 0 | { |
631 | 0 | case PDF_SIGNATURE_ERROR_SELF_SIGNED: |
632 | 0 | case PDF_SIGNATURE_ERROR_SELF_SIGNED_IN_CHAIN: |
633 | 0 | case PDF_SIGNATURE_ERROR_NOT_TRUSTED: |
634 | 0 | { |
635 | 0 | pdf_pkcs7_distinguished_name *dn; |
636 | |
|
637 | 0 | dn = pdf_signature_get_signatory(ctx, verifier, doc, signature); |
638 | 0 | if (dn) |
639 | 0 | { |
640 | 0 | char *s = pdf_signature_format_distinguished_name(ctx, dn); |
641 | 0 | pdf_signature_drop_distinguished_name(ctx, dn); |
642 | 0 | fz_strlcat(ebuf, " (", ebufsize); |
643 | 0 | fz_strlcat(ebuf, s, ebufsize); |
644 | 0 | fz_free(ctx, s); |
645 | 0 | } |
646 | 0 | else |
647 | 0 | { |
648 | 0 | fz_strlcat(ebuf, "()", ebufsize); |
649 | 0 | } |
650 | |
|
651 | 0 | break; |
652 | 0 | } |
653 | 0 | default: |
654 | 0 | break; |
655 | 0 | } |
656 | 0 | } |
657 | 0 | else |
658 | 0 | { |
659 | 0 | res = 0; |
660 | 0 | fz_strlcpy(ebuf, "Not signed.", ebufsize); |
661 | 0 | } |
662 | 0 | } |
663 | 0 | fz_catch(ctx) |
664 | 0 | { |
665 | 0 | res = 0; |
666 | 0 | fz_strlcpy(ebuf, fz_caught_message(ctx), ebufsize); |
667 | 0 | } |
668 | |
|
669 | 0 | if (ebufsize > 0) |
670 | 0 | ebuf[ebufsize-1] = 0; |
671 | |
|
672 | 0 | return res; |
673 | 0 | } |