diff options
-rw-r--r-- | include/mupdf/pdf/crypt.h | 26 | ||||
-rw-r--r-- | include/mupdf/pdf/document.h | 19 | ||||
-rw-r--r-- | include/mupdf/pdf/field.h | 1 | ||||
-rw-r--r-- | include/mupdf/pdf/object.h | 3 | ||||
-rw-r--r-- | source/pdf/pdf-appearance.c | 24 | ||||
-rw-r--r-- | source/pdf/pdf-form.c | 51 | ||||
-rw-r--r-- | source/pdf/pdf-object.c | 7 | ||||
-rw-r--r-- | source/pdf/pdf-pkcs7.c | 392 | ||||
-rw-r--r-- | source/pdf/pdf-write.c | 119 | ||||
-rw-r--r-- | source/pdf/pdf-xref.c | 11 |
10 files changed, 638 insertions, 15 deletions
diff --git a/include/mupdf/pdf/crypt.h b/include/mupdf/pdf/crypt.h index 79a72aaa..0e692941 100644 --- a/include/mupdf/pdf/crypt.h +++ b/include/mupdf/pdf/crypt.h @@ -23,6 +23,25 @@ unsigned char *pdf_crypt_key(pdf_document *doc); void pdf_print_crypt(pdf_crypt *crypt); #endif +typedef struct pdf_designated_name_s +{ + char *cn; + char *o; + char *ou; + char *email; + char *c; +} +pdf_designated_name; + +void pdf_free_designated_name(pdf_designated_name *dn); + + +pdf_signer *pdf_read_pfx(fz_context *ctx, char *sigfile, char *password); +pdf_signer *pdf_keep_signer(pdf_signer *signer); +void pdf_drop_signer(pdf_signer *signer); +pdf_designated_name *pdf_signer_designated_name(pdf_signer *signer); +void pdf_write_digest(pdf_document *doc, char *filename, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer); + /* pdf_signature_widget_byte_range: retrieve the byte range for a signature widget */ @@ -34,8 +53,13 @@ int pdf_signature_widget_byte_range(pdf_document *doc, pdf_widget *widget, int ( int pdf_signature_widget_contents(pdf_document *doc, pdf_widget *widget, char **contents); /* - fz_check_signature: check a signature's certificate chain and digest + pdf_check_signature: check a signature's certificate chain and digest */ int pdf_check_signature(pdf_document *doc, pdf_widget *widget, char *file, char *ebuf, int ebufsize); +/* + pdf_sign_signature: sign a signature form field +*/ +void pdf_sign_signature(pdf_document *doc, pdf_widget *widget, char *sigfile, char *password); + #endif diff --git a/include/mupdf/pdf/document.h b/include/mupdf/pdf/document.h index 3afabfdb..a1837f4a 100644 --- a/include/mupdf/pdf/document.h +++ b/include/mupdf/pdf/document.h @@ -180,6 +180,23 @@ struct } pdf_obj_read_state_s; +typedef struct pdf_signer_s pdf_signer; + +/* Unsaved signature fields */ +typedef struct pdf_unsaved_sig_s pdf_unsaved_sig; + +struct pdf_unsaved_sig_s +{ + pdf_obj *field; + int byte_range_start; + int byte_range_end; + int contents_start; + int contents_end; + pdf_signer *signer; + pdf_unsaved_sig *next; +}; + + struct pdf_document_s { fz_document super; @@ -256,6 +273,8 @@ struct pdf_document_s pdf_js *js; int recalculating; int dirty; + pdf_unsaved_sig *unsaved_sigs; + void (*update_appearance)(pdf_document *doc, pdf_annot *annot); pdf_doc_event_cb *event_cb; diff --git a/include/mupdf/pdf/field.h b/include/mupdf/pdf/field.h index 83a626f4..d31051a2 100644 --- a/include/mupdf/pdf/field.h +++ b/include/mupdf/pdf/field.h @@ -28,6 +28,7 @@ void pdf_field_set_border_style(pdf_document *doc, pdf_obj *field, char *text); void pdf_field_set_button_caption(pdf_document *doc, pdf_obj *field, char *text); void pdf_field_set_fill_color(pdf_document *doc, pdf_obj *field, pdf_obj *col); void pdf_field_set_text_color(pdf_document *doc, pdf_obj *field, pdf_obj *col); +void pdf_signature_set_value(pdf_document *doc, pdf_obj *field, pdf_signer *signer); int pdf_field_display(pdf_document *doc, pdf_obj *field); char *pdf_field_name(pdf_document *doc, pdf_obj *field); void pdf_field_set_display(pdf_document *doc, pdf_obj *field, int d); diff --git a/include/mupdf/pdf/object.h b/include/mupdf/pdf/object.h index 38c9166d..15886233 100644 --- a/include/mupdf/pdf/object.h +++ b/include/mupdf/pdf/object.h @@ -105,6 +105,9 @@ void pdf_sort_dict(pdf_obj *dict); */ void pdf_set_obj_parent(pdf_obj *obj, int num); +int pdf_obj_parent_num(pdf_obj *obj); + +int pdf_sprint_obj(char *s, int n, pdf_obj *obj, int tight); int pdf_fprint_obj(FILE *fp, pdf_obj *obj, int tight); #ifndef NDEBUG diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c index e91b699e..968a6d9b 100644 --- a/source/pdf/pdf-appearance.c +++ b/source/pdf/pdf-appearance.c @@ -1679,7 +1679,6 @@ static fz_text *layout_text(fz_context *ctx, font_info *font_rec, char *str, flo fz_matrix tm; fz_font *font = font_rec->font->font; fz_text *text; - int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; fz_scale(&tm, font_rec->da_rec.font_size, font_rec->da_rec.font_size); text = fz_new_text(ctx, font, &tm, 0); @@ -1877,7 +1876,7 @@ static void insert_signature_appearance_layers(pdf_document *doc, pdf_annot *ann { fz_context *ctx = doc->ctx; pdf_obj *ap = pdf_dict_getp(annot->obj, "AP/N"); - pdf_obj *main = NULL; + pdf_obj *main_ap = NULL; pdf_obj *frm = NULL; pdf_obj *n0 = NULL; fz_rect bbox; @@ -1885,21 +1884,21 @@ static void insert_signature_appearance_layers(pdf_document *doc, pdf_annot *ann pdf_to_rect(ctx, pdf_dict_gets(ap, "BBox"), &bbox); - fz_var(main); + fz_var(main_ap); fz_var(frm); fz_var(n0); fz_var(fzbuf); fz_try(ctx) { - main = pdf_new_xobject(doc, &bbox, &fz_identity); + main_ap = pdf_new_xobject(doc, &bbox, &fz_identity); frm = pdf_new_xobject(doc, &bbox, &fz_identity); n0 = pdf_new_xobject(doc, &bbox, &fz_identity); - pdf_dict_putp(main, "Resources/XObject/FRM", frm); + pdf_dict_putp(main_ap, "Resources/XObject/FRM", frm); fzbuf = fz_new_buffer(ctx, 8); fz_buffer_printf(ctx, fzbuf, "/FRM Do"); - pdf_update_stream(doc, pdf_to_num(main), fzbuf); - pdf_dict_puts_drop(main, "Length", pdf_new_int(doc, fzbuf->len)); + pdf_update_stream(doc, pdf_to_num(main_ap), fzbuf); + pdf_dict_puts_drop(main_ap, "Length", pdf_new_int(doc, fzbuf->len)); fz_drop_buffer(ctx, fzbuf); fzbuf = NULL; @@ -1919,11 +1918,11 @@ static void insert_signature_appearance_layers(pdf_document *doc, pdf_annot *ann fz_drop_buffer(ctx, fzbuf); fzbuf = NULL; - pdf_dict_putp(annot->obj, "AP/N", main); + pdf_dict_putp(annot->obj, "AP/N", main_ap); } fz_always(ctx) { - pdf_drop_obj(main); + pdf_drop_obj(main_ap); pdf_drop_obj(frm); pdf_drop_obj(n0); } @@ -2000,9 +1999,10 @@ void pdf_set_signature_appearance(pdf_document *doc, pdf_annot *annot, char *nam /* Display the distinguished name in the right-hand half */ fzbuf = fz_new_buffer(ctx, 256); - fz_buffer_printf(ctx, fzbuf, "Digitally signed by %s\n", name); - fz_buffer_printf(ctx, fzbuf, "DN: %s\n", dn); - fz_buffer_printf(ctx, fzbuf, "Date: %s", date); + fz_buffer_printf(ctx, fzbuf, "Digitally signed by %s", name); + fz_buffer_printf(ctx, fzbuf, "\nDN: %s", dn); + if (date) + fz_buffer_printf(ctx, fzbuf, "\nDate: %s", date); (void)fz_buffer_storage(ctx, fzbuf, &bufstr); rect = annot->rect; rect.x0 = (rect.x0 + rect.x1)/2.0f; diff --git a/source/pdf/pdf-form.c b/source/pdf/pdf-form.c index 3aa55065..0bf59cc5 100644 --- a/source/pdf/pdf-form.c +++ b/source/pdf/pdf-form.c @@ -1551,3 +1551,54 @@ int pdf_signature_widget_contents(pdf_document *doc, pdf_widget *widget, char ** *contents = pdf_to_str_buf(c); return pdf_to_str_len(c); } + +void pdf_signature_set_value(pdf_document *doc, pdf_obj *field, pdf_signer *signer) +{ + fz_context *ctx = doc->ctx; + pdf_obj *v; + pdf_obj *indv; + int vnum; + pdf_obj *byte_range; + pdf_obj *contents; + char buf[2048]; + pdf_unsaved_sig *unsaved_sig; + + memset(buf, 0, sizeof(buf)); + + vnum = pdf_create_object(doc); + indv = pdf_new_indirect(doc, vnum, 0); + pdf_dict_puts_drop(field, "V", indv); + + fz_var(v); + fz_try(ctx) + { + v = pdf_new_dict(doc, 4); + pdf_update_object(doc, vnum, v); + } + fz_always(ctx) + { + pdf_drop_obj(v); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + byte_range = pdf_new_array(doc, 4); + pdf_dict_puts_drop(v, "ByteRange", byte_range); + + contents = pdf_new_string(doc, buf, sizeof(buf)); + pdf_dict_puts_drop(v, "Contents", contents); + + pdf_dict_puts_drop(v, "Filter", pdf_new_name(doc, "Adobe.PPKLite")); + pdf_dict_puts_drop(v, "SubFilter", pdf_new_name(doc, "adbe.pkcs7.detached")); + + /* Record details within the document structure so that contents + * and byte_range can be updated with their correct values at + * saving time */ + unsaved_sig = fz_malloc_struct(doc->ctx, pdf_unsaved_sig); + unsaved_sig->field = pdf_keep_obj(field); + unsaved_sig->signer = pdf_keep_signer(signer); + unsaved_sig->next = doc->unsaved_sigs; + doc->unsaved_sigs = unsaved_sig; +} diff --git a/source/pdf/pdf-object.c b/source/pdf/pdf-object.c index 708794ee..704a71df 100644 --- a/source/pdf/pdf-object.c +++ b/source/pdf/pdf-object.c @@ -1373,6 +1373,11 @@ pdf_set_obj_parent(pdf_obj *obj, int num) } } +int pdf_obj_parent_num(pdf_obj *obj) +{ + return obj->parent_num; +} + pdf_obj *pdf_new_obj_from_str(pdf_document *doc, const char *src) { pdf_obj *result; @@ -1679,7 +1684,7 @@ static void fmt_obj(struct fmt *fmt, pdf_obj *obj) fmt_puts(fmt, "<unknown object>"); } -static int +int pdf_sprint_obj(char *s, int n, pdf_obj *obj, int tight) { struct fmt fmt; diff --git a/source/pdf/pdf-pkcs7.c b/source/pdf/pdf-pkcs7.c index 6515abc6..23e8af84 100644 --- a/source/pdf/pdf-pkcs7.c +++ b/source/pdf/pdf-pkcs7.c @@ -10,6 +10,7 @@ #include "openssl/objects.h" #include "openssl/pem.h" #include "openssl/pkcs7.h" +#include "openssl/pkcs12.h" enum { @@ -279,6 +280,8 @@ static int verify_sig(char *sig, int sig_len, char *file, int (*byte_range)[2], goto exit; bdata = BIO_new(BIO_s_file()); + if (bdata == NULL) + goto exit; BIO_read_filename(bdata, file); bsegs = BIO_new(BIO_f_segments()); @@ -339,6 +342,335 @@ exit: return res; } +typedef struct pdf_designated_name_openssl_s +{ + pdf_designated_name base; + fz_context *ctx; + char buf[8192]; +} pdf_designated_name_openssl; + +struct pdf_signer_s +{ + fz_context *ctx; + int refs; + X509 *x509; + EVP_PKEY *pkey; +}; + +void pdf_free_designated_name(pdf_designated_name *dn) +{ + if (dn) + fz_free(((pdf_designated_name_openssl *)dn)->ctx, dn); +} + + +static void add_from_bags(X509 **pX509, EVP_PKEY **pPkey, STACK_OF(PKCS12_SAFEBAG) *bags, char *pw); + +static void add_from_bag(X509 **pX509, EVP_PKEY **pPkey, PKCS12_SAFEBAG *bag, char *pw) +{ + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + PKCS8_PRIV_KEY_INFO *p8 = NULL; + switch (M_PKCS12_bag_type(bag)) + { + case NID_keyBag: + p8 = bag->value.keybag; + pkey = EVP_PKCS82PKEY(p8); + break; + + case NID_pkcs8ShroudedKeyBag: + p8 = PKCS12_decrypt_skey(bag, pw, (int)strlen(pw)); + if (p8) + { + pkey = EVP_PKCS82PKEY(p8); + PKCS8_PRIV_KEY_INFO_free(p8); + } + break; + + case NID_certBag: + if (M_PKCS12_cert_bag_type(bag) == NID_x509Certificate) + x509 = PKCS12_certbag2x509(bag); + break; + + case NID_safeContentsBag: + add_from_bags(pX509, pPkey, bag->value.safes, pw); + break; + } + + if (pkey) + { + if (!*pPkey) + *pPkey = pkey; + else + EVP_PKEY_free(pkey); + } + + if (x509) + { + if (!*pX509) + *pX509 = x509; + else + X509_free(x509); + } +} + +static void add_from_bags(X509 **pX509, EVP_PKEY **pPkey, STACK_OF(PKCS12_SAFEBAG) *bags, char *pw) +{ + int i; + + for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) + add_from_bag(pX509, pPkey, sk_PKCS12_SAFEBAG_value(bags, i), pw); +} + +pdf_signer *pdf_read_pfx(fz_context *ctx, char *pfile, char *pw) +{ + BIO *pfxbio = NULL; + PKCS12 *p12 = NULL; + STACK_OF(PKCS7) *asafes; + pdf_signer *signer = NULL; + int i; + + fz_var(pfxbio); + fz_var(p12); + fz_var(signer); + fz_try(ctx) + { + signer = fz_malloc_struct(ctx, pdf_signer); + signer->ctx = ctx; + signer->refs = 1; + + OpenSSL_add_all_algorithms(); + + EVP_add_digest(EVP_md5()); + EVP_add_digest(EVP_sha1()); + + ERR_load_crypto_strings(); + + ERR_clear_error(); + + pfxbio = BIO_new_file(pfile, "r"); + if (pfxbio == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Can't open pfx file: %s", pfile); + + p12 = d2i_PKCS12_bio(pfxbio, NULL); + if (p12 == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid pfx file: %s", pfile); + + asafes = PKCS12_unpack_authsafes(p12); + if (asafes == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid pfx file: %s", pfile); + + /* Nothing in this for loop can fz_throw */ + for (i = 0; i < sk_PKCS7_num(asafes); i++) + { + PKCS7 *p7; + STACK_OF(PKCS12_SAFEBAG) *bags; + int bagnid; + + p7 = sk_PKCS7_value(asafes, i); + bagnid = OBJ_obj2nid(p7->type); + switch (bagnid) + { + case NID_pkcs7_data: + bags = PKCS12_unpack_p7data(p7); + break; + case NID_pkcs7_encrypted: + bags = PKCS12_unpack_p7encdata(p7, pw, (int)strlen(pw)); + break; + default: + continue; + } + + if (bags) + { + add_from_bags(&signer->x509, &signer->pkey, bags, pw); + sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); + } + } + sk_PKCS7_pop_free (asafes, PKCS7_free); + + if (signer->pkey == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to obtain public key"); + + if (signer->x509 == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to obtain certificate"); + } + fz_always(ctx) + { + BIO_free(pfxbio); + PKCS12_free(p12); + } + fz_catch(ctx) + { + pdf_drop_signer(signer); + fz_rethrow(ctx); + } + + return signer; +} + +pdf_signer *pdf_keep_signer(pdf_signer *signer) +{ + if (signer) + signer->refs++; + + return signer; +} + +void pdf_drop_signer(pdf_signer *signer) +{ + if (signer) + { + if (--signer->refs == 0) + { + X509_free(signer->x509); + EVP_PKEY_free(signer->pkey); + fz_free(signer->ctx, signer); + } + } +} + +pdf_designated_name *pdf_signer_designated_name(pdf_signer *signer) +{ + fz_context *ctx = signer->ctx; + pdf_designated_name_openssl *dn = fz_malloc_struct(ctx, pdf_designated_name_openssl); + char *p; + + dn->ctx = ctx; + X509_NAME_oneline(X509_get_subject_name(signer->x509), dn->buf, sizeof(dn->buf)); + p = strstr(dn->buf, "/CN="); + if (p) dn->base.cn = p+4; + p = strstr(dn->buf, "/O="); + if (p) dn->base.o = p+3; + p = strstr(dn->buf, "/OU="); + if (p) dn->base.ou = p+4; + p = strstr(dn->buf, "/emailAddress="); + if (p) dn->base.email = p+14; + p = strstr(dn->buf, "/C="); + if (p) dn->base.c = p+3; + + for (p = dn->buf; *p; p++) + if (*p == '/') + *p = 0; + + return (pdf_designated_name *)dn; +} + +void pdf_write_digest(pdf_document *doc, char *filename, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer) +{ + fz_context *ctx = doc->ctx; + BIO *bdata = NULL; + BIO *bsegs = NULL; + BIO *bp7in = NULL; + BIO *bp7 = NULL; + PKCS7 *p7 = NULL; + PKCS7_SIGNER_INFO *si; + FILE *f = NULL; + + int (*brange)[2] = NULL; + int brange_len = pdf_array_len(byte_range)/2; + + fz_var(bdata); + fz_var(bsegs); + fz_var(bp7in); + fz_var(bp7); + fz_var(p7); + fz_var(f); + + fz_try(ctx) + { + unsigned char *p7_ptr; + int p7_len; + int i; + + brange = fz_calloc(ctx, brange_len, sizeof(*brange)); + for (i = 0; i < brange_len; i++) + { + brange[i][0] = pdf_to_int(pdf_array_get(byte_range, 2*i)); + brange[i][1] = pdf_to_int(pdf_array_get(byte_range, 2*i+1)); + } + + bdata = BIO_new(BIO_s_file()); + if (bdata == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create file BIO"); + BIO_read_filename(bdata, filename); + + bsegs = BIO_new(BIO_f_segments()); + if (bsegs == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create segment filter"); + + bsegs->next_bio = bdata; + BIO_set_segments(bsegs, brange, brange_len); + + p7 = PKCS7_new(); + if (p7 == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create p7 object"); + + PKCS7_set_type(p7, NID_pkcs7_signed); + si = PKCS7_add_signature(p7, signer->x509, signer->pkey, EVP_sha1()); + if (si == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to add signature"); + + PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)); + PKCS7_add_certificate(p7, signer->x509); + + PKCS7_content_new(p7, NID_pkcs7_data); + PKCS7_set_detached(p7, 1); + + bp7in = PKCS7_dataInit(p7, NULL); + if (bp7in == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to write to digest"); + + while(1) + { + char buf[4096]; + int n = BIO_read(bsegs, buf, sizeof(buf)); + if (n <= 0) + break; + BIO_write(bp7in, buf, n); + } + + if (!PKCS7_dataFinal(p7, bp7in)) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to write to digest"); + + BIO_free(bsegs); + bsegs = NULL; + BIO_free(bdata); + bdata = NULL; + + bp7 = BIO_new(BIO_s_mem()); + if (bp7 == NULL || !i2d_PKCS7_bio(bp7, p7)) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create memory buffer for digest"); + + p7_len = BIO_get_mem_data(bp7, &p7_ptr); + if (p7_len*2 + 2 > digest_length) + fz_throw(ctx, FZ_ERROR_GENERIC, "Insufficient space for digest"); + + f = fopen(filename, "rb+"); + if (f == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to write digest"); + + fseek(f, digest_offset+1, SEEK_SET); + + for (i = 0; i < p7_len; i++) + fprintf(f, "%02x", p7_ptr[i]); + } + fz_always(ctx) + { + PKCS7_free(p7); + BIO_free(bsegs); + BIO_free(bdata); + BIO_free(bp7in); + BIO_free(bp7); + if (f) + fclose(f); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + int pdf_check_signature(pdf_document *doc, pdf_widget *widget, char *file, char *ebuf, int ebufsize) { fz_context *ctx = doc->ctx; @@ -387,6 +719,53 @@ int pdf_check_signature(pdf_document *doc, pdf_widget *widget, char *file, char return res; } +void pdf_sign_signature(pdf_document *doc, pdf_widget *widget, char *sigfile, char *password) +{ + fz_context *ctx = doc->ctx; + pdf_signer *signer = pdf_read_pfx(ctx, sigfile, password); + pdf_designated_name *dn = NULL; + fz_buffer *fzbuf = NULL; + + fz_try(ctx) + { + unsigned char *dn_str; + int first = 1; + + pdf_signature_set_value(doc, ((pdf_annot *)widget)->obj, signer); + dn = pdf_signer_designated_name(signer); + fzbuf = fz_new_buffer(ctx, 256); + if (!dn->cn) + fz_throw(ctx, FZ_ERROR_GENERIC, "Certificat has no common name"); + + fz_buffer_printf(ctx, fzbuf, "cn=%s", dn->cn); + + if (dn->o) + fz_buffer_printf(ctx, fzbuf, ", o=%s", dn->o); + + if (dn->ou) + fz_buffer_printf(ctx, fzbuf, ", ou=%s", dn->ou); + + if (dn->email) + fz_buffer_printf(ctx, fzbuf, ", email=%s", dn->email); + + if (dn->c) + fz_buffer_printf(ctx, fzbuf, ", c=%s", dn->c); + + (void)fz_buffer_storage(ctx, fzbuf, &dn_str); + pdf_set_signature_appearance(doc, (pdf_annot *)widget, dn->cn, dn_str, NULL); + } + fz_always(ctx) + { + pdf_drop_signer(signer); + pdf_free_designated_name(dn); + fz_drop_buffer(ctx, fzbuf); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + #else /* HAVE_OPENSSL */ int pdf_check_signature(pdf_document *doc, pdf_widget *widget, char *file, char *ebuf, int ebufsize) @@ -395,4 +774,17 @@ int pdf_check_signature(pdf_document *doc, pdf_widget *widget, char *file, char return 0; } +pdf_signer *pdf_keep_signer(pdf_signer *signer) +{ + return NULL; +} + +void pdf_drop_signer(pdf_signer *signer) +{ +} + +void pdf_write_digest(pdf_document *doc, char *filename, pdf_obj *byte_range, int digest_offset, int digest_length, pdf_signer *signer) +{ +} + #endif /* HAVE_OPENSSL */ diff --git a/source/pdf/pdf-write.c b/source/pdf/pdf-write.c index a161c763..fd126aac 100644 --- a/source/pdf/pdf-write.c +++ b/source/pdf/pdf-write.c @@ -2270,6 +2270,117 @@ static void dump_object_details(pdf_document *doc, pdf_write_options *opts) } #endif +static void presize_unsaved_signature_byteranges(pdf_document *doc) +{ + if (doc->unsaved_sigs) + { + /* The ByteRange objects of signatures are initially written out with + * dummy values, and then overwritten later. We need to make sure their + * initial form at least takes enough sufficient file space */ + pdf_unsaved_sig *usig; + int n = 0; + + for (usig = doc->unsaved_sigs; usig; usig = usig->next) + n++; + + for (usig = doc->unsaved_sigs; usig; usig = usig->next) + { + /* There will be segments of bytes at the beginning, at + * the end and between each consecutive pair of signatures, + * hence n + 1 */ + int i; + pdf_obj *byte_range = pdf_dict_getp(usig->field, "V/ByteRange"); + + for (i = 0; i < n+1; i++) + { + pdf_array_push_drop(byte_range, pdf_new_int(doc, INT_MAX)); + pdf_array_push_drop(byte_range, pdf_new_int(doc, INT_MAX)); + } + } + } +} + +static void complete_signatures(pdf_document *doc, pdf_write_options *opts, char *filename) +{ + fz_context *ctx = doc->ctx; + pdf_unsaved_sig *usig; + FILE *f; + char buf[5120]; + int i; + int flen; + int last_end; + + if (doc->unsaved_sigs) + { + pdf_obj *byte_range; + + f = fopen(filename, "rb+"); + if (!f) + fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to open %s to complete signatures", filename); + + fseek(f, 0, SEEK_END); + flen = ftell(f); + + /* Locate the byte ranges and contents in the saved file */ + for (usig = doc->unsaved_sigs; usig; usig = usig->next) + { + char *bstr, *cstr, *fstr; + int pnum = pdf_obj_parent_num(pdf_dict_getp(usig->field, "V/ByteRange")); + fseek(f, opts->ofs_list[pnum], SEEK_SET); + (void)fread(buf, 1, sizeof(buf), f); + buf[sizeof(buf)-1] = 0; + + bstr = strstr(buf, "/ByteRange"); + cstr = strstr(buf, "/Contents"); + fstr = strstr(buf, "/Filter"); + + if (bstr && cstr && fstr && bstr < cstr && cstr < fstr) + { + usig->byte_range_start = bstr - buf + 10 + opts->ofs_list[pnum]; + usig->byte_range_end = cstr - buf + opts->ofs_list[pnum]; + usig->contents_start = cstr - buf + 9 + opts->ofs_list[pnum]; + usig->contents_end = fstr - buf + opts->ofs_list[pnum]; + } + } + + /* Recreate ByteRange with correct values. Initially store the + * recreated object in the first of the unsaved signatures */ + byte_range = pdf_new_array(doc, 4); + pdf_dict_putp_drop(doc->unsaved_sigs->field, "V/ByteRange", byte_range); + + last_end = 0; + for (usig = doc->unsaved_sigs; usig; usig = usig->next) + { + pdf_array_push_drop(byte_range, pdf_new_int(doc, last_end)); + pdf_array_push_drop(byte_range, pdf_new_int(doc, usig->contents_start - last_end)); + last_end = usig->contents_end; + } + pdf_array_push_drop(byte_range, pdf_new_int(doc, last_end)); + pdf_array_push_drop(byte_range, pdf_new_int(doc, flen - last_end)); + + /* Copy the new ByteRange to the other unsaved signatures */ + for (usig = doc->unsaved_sigs->next; usig; usig = usig->next) + pdf_dict_putp_drop(usig->field, "V/ByteRange", pdf_copy_array(byte_range)); + + /* Write the byte range into buf, padding with spaces*/ + i = pdf_sprint_obj(buf, sizeof(buf), byte_range, 1); + memset(buf+i, ' ', sizeof(buf)-i); + + /* Write the byte range to the file */ + for (usig = doc->unsaved_sigs; usig; usig = usig->next) + { + fseek(f, usig->byte_range_start, SEEK_SET); + fwrite(buf, 1, usig->byte_range_end - usig->byte_range_start, f); + } + + fclose(f); + + /* Write the digests into the file */ + for (usig = doc->unsaved_sigs; usig; usig = usig->next) + pdf_write_digest(doc, filename, byte_range, usig->contents_start, usig->contents_end - usig->contents_start, usig->signer); + } +} + void pdf_write_document(pdf_document *doc, char *filename, fz_write_options *fz_opts) { int lastfree; @@ -2285,6 +2396,7 @@ void pdf_write_document(pdf_document *doc, char *filename, fz_write_options *fz_ ctx = doc->ctx; pdf_finish_edit(doc); + presize_unsaved_signature_byteranges(doc); xref_len = pdf_xref_len(doc); @@ -2415,6 +2527,10 @@ void pdf_write_document(pdf_document *doc, char *filename, fz_write_options *fz_ writexref(doc, &opts, 0, xref_len, 1, 0, opts.first_xref_offset); } + fclose(opts.out); + opts.out = NULL; + complete_signatures(doc, &opts, filename); + doc->dirty = 0; } fz_always(ctx) @@ -2439,7 +2555,8 @@ void pdf_write_document(pdf_document *doc, char *filename, fz_write_options *fz_ pdf_drop_obj(opts.hints_s); pdf_drop_obj(opts.hints_length); page_objects_list_destroy(ctx, opts.page_object_lists); - fclose(opts.out); + if (opts.out) + fclose(opts.out); doc->freeze_updates = 0; } fz_catch(ctx) diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c index 27c039cd..eaf250cf 100644 --- a/source/pdf/pdf-xref.c +++ b/source/pdf/pdf-xref.c @@ -1165,6 +1165,7 @@ void pdf_close_document(pdf_document *doc) { fz_context *ctx; + pdf_unsaved_sig *usig; int i; if (!doc) @@ -1201,6 +1202,14 @@ pdf_close_document(pdf_document *doc) fz_free(ctx, doc->hint_shared); fz_free(ctx, doc->hint_obj_offsets); + while ((usig = doc->unsaved_sigs) != NULL) + { + doc->unsaved_sigs = usig->next; + pdf_drop_obj(usig->field); + pdf_drop_signer(usig->signer); + fz_free(ctx, usig); + } + for (i=0; i < doc->num_type3_fonts; i++) { fz_decouple_type3_font(ctx, doc->type3_fonts[i], (void *)doc); @@ -1780,6 +1789,8 @@ pdf_update_object(pdf_document *doc, int num, pdf_obj *newobj) x->type = 'n'; x->ofs = 0; x->obj = pdf_keep_obj(newobj); + + pdf_set_obj_parent(newobj, num); } void |