diff --git a/src/ChangeLog b/src/ChangeLog index 9365013..0fd7059 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,504 +1,505 @@ 2004-01-19 Werner Koch - * cms.c (build_signed_data_attributes): Include the messageDigest - last for the sake of a buggy proprietary implementation. + * cms.c (build_signed_data_attributes): Argh. We need to sort the + attributes. + (compare_attrarray): New. 2003-12-19 Werner Koch * cms.c (build_signed_data_header): Fixed silly ==/= bug. 2003-12-09 Werner Koch * cms.c (ksba_cms_set_sig_val): Fixed a code cleanup I did on Nov 25 and led to bad signature creation. Unfortunatley gpgsm has currently no real regression tests. BTW, why the hell didn't I wrote an entry for that cleanup. 2003-11-28 Werner Koch * ocsp.h, ocsp.c: New. * ksba.h (ksba_status_t, ksba_ocsp_response_status_t): New. (ksba_ocsp_t): New. 2003-11-25 Werner Koch * ksba.h: Added a few more CRL reason codes. 2003-11-21 Werner Koch * oid.c (ksba_oid_from_str): Changed RBUF to unsigned char* and changed all callers to avoid incompatible pointer warnings. 2003-11-20 Werner Koch * cert.c (_ksba_cert_get_issuer_dn_ptr): New. (_ksba_cert_get_public_key_ptr): New. (_ksba_cert_get_serial_ptr): New. * util.c (_ksba_hash_buffer): New. (ksba_set_hash_buffer_function): New. * cms.c: Include sexp-parse.h. (ksba_cms_set_sig_val): Use snext and smatch instead of open coding it. * sexp-parse.h: New. Taken from GnuPG 1.9. 2003-11-18 Werner Koch * reader.c (ksba_reader_read): Return GPG_ERR_EOF and not -1. 2003-11-17 Werner Koch * cert.c (get_name, ksba_cert_get_extension): Return GPG_ERR_EOF and not just -1. * ber-decoder.c (_ksba_ber_decoder_decode): Ditto. 2003-11-14 Werner Koch * cms.c (write_encrypted_cont): Fixed test on EOF. * cert.c (ksba_cert_is_ca, ksba_cert_get_cert_policies) (ksba_cert_get_auth_key_id): Ditto. * ber-decoder.c (_ksba_ber_decoder_dump) (_ksba_ber_decoder_decode): Ditto. 2003-11-13 Werner Koch * ber-decoder.c (eof_or_error): Replaced READ_ERROR with a system provided error. * crl.c (parse_to_next_update): Ditto. * cms-parser.c (parse_cms_version): Ditto. (_ksba_cms_parse_signed_data_part_1): * ber-help.c (eof_or_error): Ditto. * reader.c (ksba_reader_error): Changed to return gpg_error_t. * writer.c (ksba_writer_error): Ditto. 2003-11-12 Werner Koch * writer.c (do_writer_write): Replaced WRITE_ERROR by one generated from errno. * asn1-parse.y (ksba_asn_parse_file): Replaced FILE_ERROR by one generated from errno. * cms.c (ksba_cms_new): Changed Interface to return an error code. * name.c (ksba_name_new): Ditto. * writer.c (ksba_writer_new): Ditto. * reader.c (ksba_reader_new): Ditto. * certreq.c (ksba_certreq_new): Ditto. * crl.c (ksba_crl_new): Ditto. * ksba.h: Removed all KSBA_ error codes and replaced them all over the package by the gpg-error.h codes. Include gpg-error.h * mkerrors: Removed. * Makefile.am: Removed generation of errors.c * asn1-gentables.c (one_file): Simplified error printing * ksba-config.in (libs,cflags): Add values for gpg-error. Remove duplicates. 2003-11-11 Werner Koch * cert.c (ksba_cert_new): Changed interface. 2003-10-31 Werner Koch Changed the representation of time. * ksba.h (ksba_isotime_t): New. * convert.h, cms.h, crl.h: Changed all data structure to use that new time object. * time.c (_ksba_asntime_to_epoch): Renamed to .. (_ksba_asntime_to_iso): .. this and rewrote for the new time format. (_ksba_asntime_from_epoch): Removed. Was never used. (_ksba_assert_time_format, _ksba_copy_time, _ksba_cmp_time) (_ksab_current_time): New. * cert.c (ksba_cert_get_validity): Changed for new time data type. * der-encoder.c (_ksba_der_store_time): Ditto. * crl.c (ksba_crl_get_update_times, ksba_crl_get_item) (parse_to_next_update, parse_crl_entry): Ditto. * cms.c (ksba_cms_get_signing_time, ksba_cms_set_signing_time): Ditto. 2003-10-21 Werner Koch * dn.c (oid_name_tbl): Changed OIDLEN to size_t. (parse_rdn): Ditto. * ber-decoder.c (dump_tlv): Fixed format specifier for NHDR. Reported by Stephane Corthesy. 2003-08-20 Marcus Brinkmann * ber-decoder.c (new_decoder_state): Initialize DS->cur.in_any. 2003-03-17 Werner Koch * dn.c (count_quoted_string): Fixed detection of nonprintable chars for hex pairs. Use IA5STRING instead of UTF8 if the only reason is an at-sign. (parse_rdn): Fixed parsing of hash prefixed hex pairs. Use IA5STRING instead of UTF8 if the only reason is an at-sign. 2002-12-12 Werner Koch * keyinfo.c (sig_algo_table): Added sha-1WithRSAEncryption. 2002-12-03 Werner Koch * ber-decoder.c (cmp_tag): Handle ANY tag even when the classes don't match. Test case arecertificates with a id-aa-encrypKeyPref. 2002-11-25 Werner Koch * cms.c (ksba_cms_get_sigattr_oids): New. (build_signed_data_attributes): Store the content-type. * der-encoder.c (_ksba_der_store_oid): Handle TYPE_ANY. 2002-10-22 Werner Koch * asn1-parse.y (yyparse): Prefix with _ksba_asn1_. * asn1-func.c (_asn1_find_left): Made static. * dn.c (parse_rdn): Add a parse-only mode to determine the end os a part. (_ksba_dn_from_str): Parse the string in reversed order as specified by rfc2253. 2002-08-23 Werner Koch * ksba.m4: Removed unnecessary libs * ksba-config.in: Made --prefix work for --libs. * Makefile.am (EXTRA_DIST): Add ksba.m4 2002-08-21 Werner Koch * der-encoder.c (_ksba_der_copy_tree) * crl.c (ksba_crl_get_issuer) * cms.c (ksba_cms_get_issuer_serial) (ksba_cms_get_sig_val) (ksba_cms_get_enc_val) * cert.c (ksba_cert_get_image) (ksba_cert_hash) (ksba_cert_get_serial) (ksba_cert_get_sig_val): Removed all debugging output for encoding errors. 2002-08-13 Werner Koch * ksba.m4: New. 2002-08-06 Werner Koch * cms.h (signer_info_s, sig_val_s): New. Actually moved out of ksba_cms_s and made it pointers there. * cms.c (ksba_cms_release): Changed for new types of signer_info and sig_val. (ksba_cms_get_issuer_serial): Implemented index for signer_info. (ksba_cms_get_digest_algo): Ditto. (ksba_cms_get_message_digest): Ditto. (ksba_cms_get_sig_val): Ditto. (ksba_cms_hash_signed_attrs): Ditto. (build_signed_data_attributes): Build the list of signer_infos. (build_signed_data_rest): And process the list. (ksba_cms_set_sig_val): Append values. (build_signed_data_rest): Use the sig_val list. (ct_build_signed_data): Changed check for set sig_val. (build_signed_data_header): Fixed writing of more than one algo. * cms-parser.c (_ksba_cms_parse_signed_data_part_2): Create a list of signer_infos and not just one. 2002-08-05 Werner Koch * cms.c (ksba_cms_add_cert): Don't add duplicates. * cert.c (_ksba_cert_cmp): New. 2002-07-04 Werner Koch * cms.c (ksba_cms_identify): Make sure to read the full first 20 bytes. 2002-07-02 Werner Koch * certreq.c (ksba_certreq_set_sig_val): Don't store a leanding zero. 2002-06-27 Werner Koch * cert.c (ksba_cert_get_auth_key_id): Skip keyIdentifier tag. 2002-06-17 Werner Koch * cms.c (write_encrypted_cont): Don't use write_octet_stream here because this is used with an implicit tag and the outer context already provides the constructed containter. 2002-06-13 Werner Koch * writer.c (ksba_writer_write_octet_string): Correctly write undefined length octet strings. * cms.c (ct_build_signed_data): Write an end tag for non-detached sigs. (build_enveloped_data_header): Fix doc; it is an explicit octect string. (read_encrypted_cont): Read constructed octet strings. 2002-06-12 Werner Koch * writer.c (ksba_writer_write_octet_string): New. * cms.c (write_encrypted_cont): Use it here. 2002-05-17 Werner Koch * Makefile.am: Tweaked to avoid double compilations. * cms.c (ksba_cms_identify): New. 2002-05-16 Werner Koch * cms-parser.c (_ksba_cms_parse_signed_data_part_1): Never allocate 0 bytes - this is not defined. 2002-05-04 Werner Koch * dn.c (parse_rdn): Better detection of empty elements. 2002-04-27 Werner Koch * cms.c (read_and_hash_cont): Handle constructed octet strings. * cms-parser.c (_ksba_cms_parse_signed_data_part_2): Eat one pending end tag. 2002-04-15 Werner Koch * version.c: New. * dn.c (append_ucs4_value,append_ucs2_value): Implemented. * cert.c (ksba_cert_get_auth_key_id): New. * name.c (_ksba_name_new_from_der): Add support for "Name" type. * dn.c (_ksba_derdn_to_str): New. 2002-03-22 Werner Koch * cert.c (get_name): Fixed enumerating of alternate names. * name.c: New. * ksba.h (KsbaName): New. Added defintions for the name functions. (KsbaCRLReason): Changed values to allow use as bit flags. * cert.c (parse_distribution_point): New. (ksba_cert_get_crl_dist_point): New. 2002-03-15 Werner Koch * asn1-func.c (_ksba_asn_check_identifier): Better check against overflow, also the data used is static. 2002-03-13 Werner Koch * cms.h (value_tree_s): New and use it for recp_info. (enc_val_s): New, use it in certlist and remove it from the ksba_cms_s and change all users. * cms.c (ksba_cms_set_enc_val): Allow multiple recipients. (ksba_cms_add_signer): Keep ordering of signers so that they can be implictly counted by an index. (build_enveloped_data_header): Fixed the creation of the RID. * cms.c (release_value_tree): New. (ksba_cms_release): And use it here. (ksba_cms_get_issuer_serial): Use the new recp_list structure and take the IDX into account. (ksba_cms_get_enc_val): Ditto. (ksba_cms_set_sig_val,ksba_cms_set_enc_val): Don't store a leading zero. * cms-parser.c (_ksba_cms_parse_enveloped_data_part_1): Handle multiple recipients. * cms.c (build_signed_data_rest): Write 3 end tags. (ct_build_enveloped_data): Write 4 end tags. 2002-03-12 Werner Koch * cms-parser.c (_ksba_cms_parse_signed_data_part_2): Allow an empty set of signer infos. 2002-03-11 Werner Koch * keyinfo.c: Fixed last change; forgot to set the length. 2002-03-09 Werner Koch * keyinfo.c: Add algo for Telesec NetKey cards. 2002-02-19 Werner Koch * cert.c (ksba_cert_get_cert_policies): New. 2002-02-07 Werner Koch * cert.c (ksba_cert_release): Release the nodes. 2002-02-01 Marcus Brinkmann * asn1-parse.y: Add missing colon at end of block. 2002-02-01 Werner Koch * cms.c (ksba_cms_set_sig_val): Add kludge to allow "rsa" instead of an OID. 2002-01-28 Werner Koch * oid.c (ksba_oid_from_str): Fixed docs and return type. * certreq.c (ksba_certreq_set_subject): Renamed to.. (ksba_certreq_add_subject): this and added logic to store subjectAltNames. (build_extensions): New. (build_cri): Write the extensions. * ber-help.c (_ksba_ber_encode_tl): New. 2002-01-25 Werner Koch * cms.c (build_signed_data_attributes): Write the optional certs. * cert.c (ksba_cert_get_image): Don't return the image length but the parsed length. 2002-01-24 Werner Koch * cms.h: Add new member cert_info_list. * cms.c (ksba_cms_add_cert): New. (ksba_cms_release): Release the cert info list. 2002-01-23 Werner Koch * crl.c (parse_to_next_update): Use measured length to fixup the tbs_len after reading the name. Check tbs_len before checking for the entry list. * ksba.h (KsbaKeyUsage): New. * cert.c (ksba_cert_get_key_usage): New. * cert.c (get_name): New, implemented rfc822 AltNames. (ksba_cert_get_issuer,ksba_cert_get_subject): Use it. 2002-01-22 Werner Koch * cert.c (ksba_cert_is_ca): New. * ber-help.c (premature_eof): New. (eof_or_error): Use it here. (_ksba_ber_parse_tl): New. 2002-01-21 Werner Koch * cert.c (ksba_cert_get_extension): New. (read_extensions): New. (ksba_cert_release): Release the cached extension info. 2002-01-14 Werner Koch * dn.c: Fixed oid table according to rfc2253. (append_atv): Do only use the allowed names. 2002-01-11 Werner Koch * dn.c (append_atv): Don't write a trailing hash sign. * time.c (_ksba_asntime_to_epoch): Kludge to cope with the Y2038 problem. * crl.c (do_hash): New. Apply the hashing where needed. 2002-01-10 Werner Koch * certreq.c, certreq.h: New. * keyinfo.c (_ksba_keyinfo_from_sexp): New. * der-encoder.c: Removed commented code. 2002-01-09 Werner Koch * dn.c (count_quoted_string, parse_rdn, write_escaped): New. (_ksba_dn_from_str): Implemented. * ber-help.c (_ksba_ber_count_tl): New. * util.h (spacep): New. (hexdigitp): Now takes a pointer as arg. * writer.c (do_writer_write): Set the error flag when we are out of core. (ksba_writer_snatch_mem): New. 2002-01-08 Werner Koch * writer.c (ksba_writer_set_mem): New. (do_writer_write): Implemented it here. (ksba_writer_get_mem): New. * tmttv2.asn (CertificationRequestInfo): Added. 2002-01-07 Werner Koch * ksba.h (KsbaCRLReason): New. * crl.c: Basically works. 2002-01-05 Werner Koch * asn1-func.c (_ksba_asn_find_node): Moved code to .. (find_node): new function which allows resolving of identifiers. (_ksba_asn_expand_tree): Use the resolving find_node here. * keyinfo.c (_ksba_parse_algorithm_identifier2): Reset len2 so that a given r_parm returns correct values. * ksba.h (KsbaCRL): New typedef and prototypes for the CRL functions. * crl.c, crl.h: New but not yet complete. 2001-12-20 Werner Koch * cms.c (read_and_hash_cont): New. (ct_parse_signed_data): Use it here for non-detached signatures. * cms-parser.c (_ksba_cms_parse_signed_data_part_1): Store the inner content length. (_ksba_cms_parse_signed_data_part_2): Add dummy read code for CRLs. * cert.c (ksba_cert_set_serial): Changed to return an entire S-Exp and not just an octet string. * cms.c (ksba_cms_get_issuer_serial): Ditto. 2001-12-18 Werner Koch * Makefile.am (DISTCLEANFILES): Add asn1-tables.c 2001-12-17 Werner Koch * cert.c (ksba_cert_get_serial): Does now return a S-expression. * cms.c (ksba_cms_get_issuer_serial): Ditto. * keyinfo.c: Changed all functions to return canonical encoded S-Exp. * Makefile.am (asn1-tables.c): Fixed rule to work with VPATH builds. 2001-12-14 Werner Koch * oid.c, time.c, cms.c : Moved atoi macros and digitp macro to .. * util.h: .. here. Fixed digitp macro. Added hexdigitp. 2001-12-13 Werner Koch * cert.c (ksba_cert_get_issuer): Added an idx argument so that the function can be used to enumerate alternate names. (ksba_cert_get_subject): Ditto. Copyright 2001, 2002 g10 Code GmbH This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/src/cms.c b/src/cms.c index 7b1d7b8..47162e4 100644 --- a/src/cms.c +++ b/src/cms.c @@ -1,2891 +1,2921 @@ /* cms.c - cryptographic message syntax main functions * Copyright (C) 2001, 2003, 2004 g10 Code GmbH * * This file is part of KSBA. * * KSBA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Fountion; either version 2 of the License, or * (at your option) any later version. * * KSBA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include "util.h" #include "cms.h" #include "convert.h" #include "keyinfo.h" #include "der-encoder.h" #include "ber-help.h" #include "sexp-parse.h" #include "cert.h" /* need to access cert->root and cert->image */ static gpg_error_t ct_parse_data (ksba_cms_t cms); static gpg_error_t ct_parse_signed_data (ksba_cms_t cms); static gpg_error_t ct_parse_enveloped_data (ksba_cms_t cms); static gpg_error_t ct_parse_digested_data (ksba_cms_t cms); static gpg_error_t ct_parse_encrypted_data (ksba_cms_t cms); static gpg_error_t ct_build_data (ksba_cms_t cms); static gpg_error_t ct_build_signed_data (ksba_cms_t cms); static gpg_error_t ct_build_enveloped_data (ksba_cms_t cms); static gpg_error_t ct_build_digested_data (ksba_cms_t cms); static gpg_error_t ct_build_encrypted_data (ksba_cms_t cms); static struct { const char *oid; ksba_content_type_t ct; gpg_error_t (*parse_handler)(ksba_cms_t); gpg_error_t (*build_handler)(ksba_cms_t); } content_handlers[] = { { "1.2.840.113549.1.7.1", KSBA_CT_DATA, ct_parse_data , ct_build_data }, { "1.2.840.113549.1.7.2", KSBA_CT_SIGNED_DATA, ct_parse_signed_data , ct_build_signed_data }, { "1.2.840.113549.1.7.3", KSBA_CT_ENVELOPED_DATA, ct_parse_enveloped_data, ct_build_enveloped_data }, { "1.2.840.113549.1.7.5", KSBA_CT_DIGESTED_DATA, ct_parse_digested_data , ct_build_digested_data }, { "1.2.840.113549.1.7.6", KSBA_CT_ENCRYPTED_DATA, ct_parse_encrypted_data, ct_build_encrypted_data }, { "1.2.840.113549.1.9.16.1.2", KSBA_CT_AUTH_DATA }, { NULL } }; static char oidstr_contentType[] = "1.2.840.113549.1.9.3"; /*static char oid_contentType[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x03";*/ static char oidstr_messageDigest[] = "1.2.840.113549.1.9.4"; static char oid_messageDigest[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x04"; static char oidstr_signingTime[] = "1.2.840.113549.1.9.5"; static char oid_signingTime[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x05"; /* copy all the bytes from the reader to the writer and hash them if a a hash function has been set. The writer may be NULL to just do the hashing */ static gpg_error_t read_and_hash_cont (ksba_cms_t cms) { gpg_error_t err = 0; unsigned long nleft; char buffer[4096]; size_t n, nread; struct tag_info ti; if (cms->inner_cont_ndef) { for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { /* next chunk */ nleft = ti.length; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; if (cms->hash_fnc) cms->hash_fnc (cms->hash_fnc_arg, buffer, nread); if (cms->writer) err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && ti.is_constructed) { /* next chunk is constructed */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { nleft = ti.length; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; if (cms->hash_fnc) cms->hash_fnc (cms->hash_fnc_arg, buffer, nread); if (cms->writer) err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* ready with this chunk */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) return 0; /* ready */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else { nleft = cms->inner_cont_len; /* first read the octet string but allow all types here */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (nleft < ti.nhdr) return gpg_error (GPG_ERR_ENCODING_PROBLEM); nleft -= ti.nhdr; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; if (cms->hash_fnc) cms->hash_fnc (cms->hash_fnc_arg, buffer, nread); if (cms->writer) err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } return 0; } /* copy all the encrypted bytes from the reader to the writer. Handles indefinite length encoding */ static gpg_error_t read_encrypted_cont (ksba_cms_t cms) { gpg_error_t err = 0; unsigned long nleft; char buffer[4096]; size_t n, nread; if (cms->inner_cont_ndef) { struct tag_info ti; /* fixme: this ist mostly a duplicate of the code in read_and_hash_cont(). */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { /* next chunk */ nleft = ti.length; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && ti.is_constructed) { /* next chunk is constructed */ for (;;) { err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING && !ti.is_constructed) { nleft = ti.length; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; if (cms->writer) err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* ready with this chunk */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) return 0; /* ready */ else return gpg_error (GPG_ERR_ENCODING_PROBLEM); } } else { nleft = cms->inner_cont_len; while (nleft) { n = nleft < sizeof (buffer)? nleft : sizeof (buffer); err = ksba_reader_read (cms->reader, buffer, n, &nread); if (err) return err; nleft -= nread; err = ksba_writer_write (cms->writer, buffer, nread); if (err) return err; } } return 0; } /* copy data from reader to writer. Assume that it is an octet string and insert undefinite length headers where needed */ static gpg_error_t write_encrypted_cont (ksba_cms_t cms) { gpg_error_t err = 0; char buffer[4096]; size_t nread; /* we do it the simple way: the parts are made up from the chunks we got from the read function. Fixme: We should write the tag here, and write a definite length header if everything fits into our local buffer. Actually pretty simple to do, but I am too lazy right now. */ while (!(err = ksba_reader_read (cms->reader, buffer, sizeof buffer, &nread)) ) { err = _ksba_ber_write_tl (cms->writer, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0, nread); if (!err) err = ksba_writer_write (cms->writer, buffer, nread); } if (gpg_err_code (err) == GPG_ERR_EOF) /* write the end tag */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); return err; } /* Figure out whether the data read from READER is a CMS object and return its content type. This function does only peek at the READER and tries to identify the type with besto effort. */ ksba_content_type_t ksba_cms_identify (ksba_reader_t reader) { struct tag_info ti; unsigned char buffer[20]; const unsigned char*p; size_t n, count; char *oid; int i; if (!reader) return KSBA_CT_NONE; /* oops */ /* This is a common example of a CMS object - it is obvious that we only need to read a few bytes to get to the OID: 30 82 0B 59 06 09 2A 86 48 86 F7 0D 01 07 02 A0 82 0B 4A 30 82 0B 46 02 ----------- ======-------------------------- SEQUENCE oid(signedData) (2 byte len) */ for (count = sizeof buffer; count; count -= n) { if (ksba_reader_read (reader, buffer+sizeof (buffer)-count, count, &n)) return KSBA_CT_NONE; /* too short */ } n = sizeof buffer; if (ksba_reader_unread (reader, buffer, n)) return KSBA_CT_NONE; /* oops */ p = buffer; if (_ksba_ber_parse_tl (&p, &n, &ti)) return KSBA_CT_NONE; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return KSBA_CT_NONE; if (_ksba_ber_parse_tl (&p, &n, &ti)) return KSBA_CT_NONE; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed && ti.length) || ti.length > n) return KSBA_CT_NONE; oid = ksba_oid_to_str (p, ti.length); if (!oid) return KSBA_CT_NONE; /* out of core */ for (i=0; content_handlers[i].oid; i++) { if (!strcmp (content_handlers[i].oid, oid)) break; } if (!content_handlers[i].oid) return KSBA_CT_NONE; /* unknown */ return content_handlers[i].ct; } /** * ksba_cms_new: * * Create a new and empty CMS object * * Return value: A CMS object or an error code. **/ gpg_error_t ksba_cms_new (ksba_cms_t *r_cms) { *r_cms = xtrycalloc (1, sizeof **r_cms); if (!*r_cms) return gpg_error_from_errno (errno); return 0; } /* Release a list of value trees. */ static void release_value_tree (struct value_tree_s *tree) { while (tree) { struct value_tree_s *tmp = tree->next; _ksba_asn_release_nodes (tree->root); xfree (tree->image); xfree (tree); tree = tmp; } } /** * ksba_cms_release: * @cms: A CMS object * * Release a CMS object. **/ void ksba_cms_release (ksba_cms_t cms) { if (!cms) return; xfree (cms->content.oid); while (cms->digest_algos) { struct oidlist_s *ol = cms->digest_algos->next; xfree (cms->digest_algos->oid); xfree (cms->digest_algos); cms->digest_algos = ol; } while (cms->cert_list) { struct certlist_s *cl = cms->cert_list->next; ksba_cert_release (cms->cert_list->cert); xfree (cms->cert_list->enc_val.algo); xfree (cms->cert_list->enc_val.value); xfree (cms->cert_list); cms->cert_list = cl; } while (cms->cert_info_list) { struct certlist_s *cl = cms->cert_info_list->next; ksba_cert_release (cms->cert_info_list->cert); xfree (cms->cert_info_list->enc_val.algo); xfree (cms->cert_info_list->enc_val.value); xfree (cms->cert_info_list); cms->cert_info_list = cl; } xfree (cms->inner_cont_oid); xfree (cms->encr_algo_oid); xfree (cms->encr_iv); xfree (cms->data.digest); while (cms->signer_info) { struct signer_info_s *tmp = cms->signer_info->next; _ksba_asn_release_nodes (cms->signer_info->root); xfree (cms->signer_info->image); xfree (cms->signer_info->cache.digest_algo); cms->signer_info = tmp; } release_value_tree (cms->recp_info); while (cms->sig_val) { struct sig_val_s *tmp = cms->sig_val->next; xfree (cms->sig_val->algo); xfree (cms->sig_val->value); cms->sig_val = tmp; } xfree (cms); } gpg_error_t ksba_cms_set_reader_writer (ksba_cms_t cms, ksba_reader_t r, ksba_writer_t w) { if (!cms || !(r || w)) return gpg_error (GPG_ERR_INV_VALUE); if ((r && cms->reader) || (w && cms->writer) ) return gpg_error (GPG_ERR_CONFLICT); /* already set */ cms->reader = r; cms->writer = w; return 0; } gpg_error_t ksba_cms_parse (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason) { gpg_error_t err; int i; if (!cms || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); *r_stopreason = KSBA_SR_RUNNING; if (!cms->stop_reason) { /* Initial state: start parsing */ err = _ksba_cms_parse_content_info (cms); if (err) return err; for (i=0; content_handlers[i].oid; i++) { if (!strcmp (content_handlers[i].oid, cms->content.oid)) break; } if (!content_handlers[i].oid) return gpg_error (GPG_ERR_UNKNOWN_CMS_OBJ); if (!content_handlers[i].parse_handler) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); cms->content.ct = content_handlers[i].ct; cms->content.handler = content_handlers[i].parse_handler; cms->stop_reason = KSBA_SR_GOT_CONTENT; } else if (cms->content.handler) { err = cms->content.handler (cms); if (err) return err; } else return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); *r_stopreason = cms->stop_reason; return 0; } gpg_error_t ksba_cms_build (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason) { gpg_error_t err; if (!cms || !r_stopreason) return gpg_error (GPG_ERR_INV_VALUE); *r_stopreason = KSBA_SR_RUNNING; if (!cms->stop_reason) { /* Initial state: check that the content handler is known */ if (!cms->writer) return gpg_error (GPG_ERR_MISSING_ACTION); if (!cms->content.handler) return gpg_error (GPG_ERR_MISSING_ACTION); if (!cms->inner_cont_oid) return gpg_error (GPG_ERR_MISSING_ACTION); cms->stop_reason = KSBA_SR_GOT_CONTENT; } else if (cms->content.handler) { err = cms->content.handler (cms); if (err) return err; } else return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); *r_stopreason = cms->stop_reason; return 0; } /* Return the content type. A WHAT of 0 returns the real content type whereas a 1 returns the inner content type. */ ksba_content_type_t ksba_cms_get_content_type (ksba_cms_t cms, int what) { int i; if (!cms) return 0; if (!what) return cms->content.ct; if (what == 1 && cms->inner_cont_oid) { for (i=0; content_handlers[i].oid; i++) { if (!strcmp (content_handlers[i].oid, cms->inner_cont_oid)) return content_handlers[i].ct; } } return 0; } /* Return the object ID of the current cms. This is a constant string valid as long as the context is valid and no new parse is started. */ const char * ksba_cms_get_content_oid (ksba_cms_t cms, int what) { if (!cms) return NULL; if (!what) return cms->content.oid; if (what == 1) return cms->inner_cont_oid; if (what == 2) return cms->encr_algo_oid; return NULL; } /* copy the initialization vector into iv and its len into ivlen. The caller should provide a suitable large buffer */ gpg_error_t ksba_cms_get_content_enc_iv (ksba_cms_t cms, unsigned char *iv, size_t maxivlen, size_t *ivlen) { if (!cms || !iv || !ivlen) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->encr_ivlen) return gpg_error (GPG_ERR_NO_DATA); if (cms->encr_ivlen > maxivlen) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); memcpy (iv, cms->encr_iv, cms->encr_ivlen); *ivlen = cms->encr_ivlen; return 0; } /** * ksba_cert_get_digest_algo_list: * @cert: Initialized certificate object * @idx: enumerator * * Figure out the the digest algorithm used for the signature and * return its OID. Note that the algos returned are just hints on * what to hash. * * Return value: NULL for no more algorithms or a string valid as long * as the the cms object is valid. **/ const char * ksba_cms_get_digest_algo_list (ksba_cms_t cms, int idx) { struct oidlist_s *ol; if (!cms) return NULL; for (ol=cms->digest_algos; ol && idx; ol = ol->next, idx-- ) ; if (!ol) return NULL; return ol->oid; } /** * ksba_cms_get_issuer_serial: * @cms: CMS object * @idx: index number * @r_issuer: returns the issuer * @r_serial: returns the serial number * * This functions returns the issuer and serial number either from the * sid or the rid elements of a CMS object. * * Return value: 0 on success or an error code. An error code of -1 * is returned to indicate that there is no issuer with that idx, * GPG_ERR_No_Data is returned to indicate that there is no issuer at * all. **/ gpg_error_t ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx, char **r_issuer, ksba_sexp_t *r_serial) { gpg_error_t err; const char *issuer_path, *serial_path; AsnNode root; const unsigned char *image; AsnNode n; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); if (cms->signer_info) { struct signer_info_s *si; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; issuer_path = "SignerInfo.sid.issuerAndSerialNumber.issuer"; serial_path = "SignerInfo.sid.issuerAndSerialNumber.serialNumber"; root = si->root; image = si->image; } else if (cms->recp_info) { struct value_tree_s *tmp; issuer_path = "KeyTransRecipientInfo.rid.issuerAndSerialNumber.issuer"; serial_path = "KeyTransRecipientInfo.rid.issuerAndSerialNumber.serialNumber"; for (tmp=cms->recp_info; tmp && idx; tmp=tmp->next, idx-- ) ; if (!tmp) return -1; root = tmp->root; image = tmp->image; } else return gpg_error (GPG_ERR_NO_DATA); if (r_issuer) { n = _ksba_asn_find_node (root, issuer_path); if (!n || !n->down) return gpg_error (GPG_ERR_NO_VALUE); n = n->down; /* dereference the choice node */ if (n->off == -1) { /* fputs ("get_issuer problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_GENERAL); } err = _ksba_dn_to_str (image, n, r_issuer); if (err) return err; } if (r_serial) { char numbuf[22]; int numbuflen; unsigned char *p; /* fixme: we do not release the r_issuer stuff on error */ n = _ksba_asn_find_node (root, serial_path); if (!n) return gpg_error (GPG_ERR_NO_VALUE); if (n->off == -1) { /* fputs ("get_serial problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return gpg_error (GPG_ERR_GENERAL); } sprintf (numbuf,"(%u:", (unsigned int)n->len); numbuflen = strlen (numbuf); p = xtrymalloc (numbuflen + n->len + 2); if (!p) return gpg_error (GPG_ERR_ENOMEM); strcpy (p, numbuf); memcpy (p+numbuflen, image + n->off + n->nhdr, n->len); p[numbuflen + n->len] = ')'; p[numbuflen + n->len + 1] = 0; *r_serial = p; } return 0; } /** * ksba_cms_get_digest_algo: * @cms: CMS object * @idx: index of signer * * Figure out the the digest algorithm used by the signer @idx return * its OID. This is the algorithm acually used to calculate the * signature. * * Return value: NULL for no such signer or a constn string valid as * long as the CMS object lives. **/ const char * ksba_cms_get_digest_algo (ksba_cms_t cms, int idx) { AsnNode n; char *algo; struct signer_info_s *si; if (!cms) return NULL; if (!cms->signer_info) return NULL; if (idx < 0) return NULL; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return NULL; if (si->cache.digest_algo) return si->cache.digest_algo; n = _ksba_asn_find_node (si->root, "SignerInfo.digestAlgorithm.algorithm"); algo = _ksba_oid_node_to_str (si->image, n); if (algo) { si->cache.digest_algo = algo; } return algo; } /** * ksba_cms_get_cert: * @cms: CMS object * @idx: enumerator * * Get the certificate out of a CMS. The caller should use this in a * loop to get all certificates. The returned certificate is a * shallow copy of the original one; the caller must still use * ksba_cert_release() to free it. * * Return value: A Certificate object or NULL for end of list or error **/ ksba_cert_t ksba_cms_get_cert (ksba_cms_t cms, int idx) { struct certlist_s *cl; if (!cms || idx < 0) return NULL; for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return NULL; ksba_cert_ref (cl->cert); return cl->cert; } /* Return the extension attribute messageDigest */ gpg_error_t ksba_cms_get_message_digest (ksba_cms_t cms, int idx, char **r_digest, size_t *r_digest_len) { AsnNode nsiginfo, n; struct signer_info_s *si; if (!cms || !r_digest || !r_digest_len) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->signer_info) return gpg_error (GPG_ERR_NO_DATA); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; *r_digest = NULL; *r_digest_len = 0; nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!nsiginfo) return gpg_error (GPG_ERR_BUG); n = _ksba_asn_find_type_value (si->image, nsiginfo, 0, oid_messageDigest, DIM(oid_messageDigest)); if (!n) return 0; /* this is okay, because the element is optional */ /* check that there is only one */ if (_ksba_asn_find_type_value (si->image, nsiginfo, 1, oid_messageDigest, DIM(oid_messageDigest))) return gpg_error (GPG_ERR_DUP_VALUE); /* the value is is a SET OF OCTECT STRING but the set must have excactly one OCTECT STRING. (rfc2630 11.2) */ if ( !(n->type == TYPE_SET_OF && n->down && n->down->type == TYPE_OCTET_STRING && !n->down->right)) return gpg_error (GPG_ERR_INV_CMS_OBJ); n = n->down; if (n->off == -1) return gpg_error (GPG_ERR_BUG); *r_digest_len = n->len; *r_digest = xtrymalloc (n->len); if (!*r_digest) return gpg_error (GPG_ERR_ENOMEM); memcpy (*r_digest, si->image + n->off + n->nhdr, n->len); return 0; } /* Return the extension attribute signing time, which may be empty for no signing time available. */ gpg_error_t ksba_cms_get_signing_time (ksba_cms_t cms, int idx, ksba_isotime_t r_sigtime) { AsnNode nsiginfo, n; struct signer_info_s *si; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); *r_sigtime = 0; if (!cms->signer_info) return gpg_error (GPG_ERR_NO_DATA); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; *r_sigtime = 0; nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!nsiginfo) return 0; /* This is okay because signedAttribs are optional. */ n = _ksba_asn_find_type_value (si->image, nsiginfo, 0, oid_signingTime, DIM(oid_signingTime)); if (!n) return 0; /* This is okay because signing time is optional. */ /* check that there is only one */ if (_ksba_asn_find_type_value (si->image, nsiginfo, 1, oid_signingTime, DIM(oid_signingTime))) return gpg_error (GPG_ERR_DUP_VALUE); /* the value is is a SET OF CHOICE but the set must have excactly one CHOICE of generalized or utctime. (rfc2630 11.3) */ if ( !(n->type == TYPE_SET_OF && n->down && (n->down->type == TYPE_GENERALIZED_TIME || n->down->type == TYPE_UTC_TIME) && !n->down->right)) return gpg_error (GPG_ERR_INV_CMS_OBJ); n = n->down; if (n->off == -1) return gpg_error (GPG_ERR_BUG); return _ksba_asntime_to_iso (si->image + n->off + n->nhdr, n->len, r_sigtime); } /* Return a list of OIDs stored as signed attributes for the signature number IDX. All the values (OIDs) for the the requested OID REQOID are returned delimited by a linefeed. Caller must free that list. -1 is returned when IDX is larger than the number of signatures, GPG_ERR_No_Data is returned when there is no such attribute for the given signer. */ gpg_error_t ksba_cms_get_sigattr_oids (ksba_cms_t cms, int idx, const char *reqoid, char **r_value) { gpg_error_t err; AsnNode nsiginfo, n; struct signer_info_s *si; unsigned char *reqoidbuf; size_t reqoidlen; char *retstr = NULL; int i; if (!cms || !r_value) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->signer_info) return gpg_error (GPG_ERR_NO_DATA); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); *r_value = NULL; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; /* no more signers */ nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!nsiginfo) return -1; /* this is okay, because signedAttribs are optional */ err = ksba_oid_from_str (reqoid, &reqoidbuf, &reqoidlen); if(err) return err; for (i=0; (n = _ksba_asn_find_type_value (si->image, nsiginfo, i, reqoidbuf, reqoidlen)); i++) { char *line, *p; /* the value is is a SET OF OBJECT ID but the set must have excactly one OBJECT ID. (rfc2630 11.1) */ if ( !(n->type == TYPE_SET_OF && n->down && n->down->type == TYPE_OBJECT_ID && !n->down->right)) { xfree (reqoidbuf); xfree (retstr); return gpg_error (GPG_ERR_INV_CMS_OBJ); } n = n->down; if (n->off == -1) { xfree (reqoidbuf); xfree (retstr); return gpg_error (GPG_ERR_BUG); } p = _ksba_oid_node_to_str (si->image, n); if (!p) { xfree (reqoidbuf); xfree (retstr); return gpg_error (GPG_ERR_INV_CMS_OBJ); } if (!retstr) line = retstr = xtrymalloc (strlen (p) + 2); else { char *tmp = xtryrealloc (retstr, strlen (retstr) + 1 + strlen (p) + 2); if (!tmp) line = NULL; else { retstr = tmp; line = stpcpy (retstr + strlen (retstr), "\n"); } } if (!line) { xfree (reqoidbuf); xfree (retstr); xfree (p); return gpg_error (GPG_ERR_ENOMEM); } strcpy (line, p); xfree (p); } xfree (reqoidbuf); if (!n && !i) return -1; /* no such attribute */ *r_value = retstr; return 0; } /** * ksba_cms_get_sig_val: * @cms: CMS object * @idx: index of signer * * Return the actual signature of signer @idx in a format suitable to * be used as input to Libgcrypt's verification function. The caller * must free the returned string. * * Return value: NULL or a string with a S-Exp. **/ ksba_sexp_t ksba_cms_get_sig_val (ksba_cms_t cms, int idx) { AsnNode n, n2; gpg_error_t err; ksba_sexp_t string; struct signer_info_s *si; if (!cms) return NULL; if (!cms->signer_info) return NULL; if (idx < 0) return NULL; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return NULL; n = _ksba_asn_find_node (si->root, "SignerInfo.signatureAlgorithm"); if (!n) return NULL; if (n->off == -1) { /* fputs ("ksba_cms_get_sig_val problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } n2 = n->right; /* point to the actual value */ err = _ksba_sigval_to_sexp (si->image + n->off, n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)), &string); if (err) return NULL; return string; } /** * ksba_cms_get_enc_val: * @cms: CMS object * @idx: index of recipient info * * Return the encrypted value (the session key) of recipient @idx in a * format suitable to be used as input to Libgcrypt's verification * function. The caller must free the returned string. * * Return value: NULL or a string with a S-Exp. **/ ksba_sexp_t ksba_cms_get_enc_val (ksba_cms_t cms, int idx) { AsnNode n, n2; gpg_error_t err; ksba_sexp_t string; struct value_tree_s *vt; if (!cms) return NULL; if (!cms->recp_info) return NULL; if (idx < 0) return NULL; for (vt=cms->recp_info; vt && idx; vt=vt->next, idx--) ; if (!vt) return NULL; /* No value at this IDX */ n = _ksba_asn_find_node (vt->root, "KeyTransRecipientInfo.keyEncryptionAlgorithm"); if (!n) return NULL; if (n->off == -1) { /* fputs ("ksba_cms_get_enc_val problem at node:\n", stderr); */ /* _ksba_asn_node_dump_all (n, stderr); */ return NULL; } n2 = n->right; /* point to the actual value */ err = _ksba_encval_to_sexp (vt->image + n->off, n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)), &string); if (err) return NULL; return string; } /* Provide a hash function so that we are able to hash the data */ void ksba_cms_set_hash_function (ksba_cms_t cms, void (*hash_fnc)(void *, const void *, size_t), void *hash_fnc_arg) { if (cms) { cms->hash_fnc = hash_fnc; cms->hash_fnc_arg = hash_fnc_arg; } } /* hash the signed attributes of the given signer */ gpg_error_t ksba_cms_hash_signed_attrs (ksba_cms_t cms, int idx) { AsnNode n; struct signer_info_s *si; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (!cms->hash_fnc) return gpg_error (GPG_ERR_MISSING_ACTION); if (idx < 0) return -1; for (si=cms->signer_info; si && idx; si = si->next, idx-- ) ; if (!si) return -1; n = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!n || n->off == -1) return gpg_error (GPG_ERR_NO_VALUE); /* We don't hash the implicit tag [0] but a SET tag */ cms->hash_fnc (cms->hash_fnc_arg, "\x31", 1); cms->hash_fnc (cms->hash_fnc_arg, si->image + n->off + 1, n->nhdr + n->len - 1); return 0; } /* Code to create CMS structures */ /** * ksba_cms_set_content_type: * @cms: A CMS object * @what: 0 for content type, 1 for inner content type * @type: Tyep constant * * Set the content type used for build operations. This should be the * first operation before starting to create a CMS message. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_set_content_type (ksba_cms_t cms, int what, ksba_content_type_t type) { int i; char *oid; if (!cms || what < 0 || what > 1 ) return gpg_error (GPG_ERR_INV_VALUE); for (i=0; content_handlers[i].oid; i++) { if (content_handlers[i].ct == type) break; } if (!content_handlers[i].oid) return gpg_error (GPG_ERR_UNKNOWN_CMS_OBJ); if (!content_handlers[i].build_handler) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); oid = xtrystrdup (content_handlers[i].oid); if (!oid) return gpg_error (GPG_ERR_ENOMEM); if (!what) { cms->content.oid = oid; cms->content.ct = content_handlers[i].ct; cms->content.handler = content_handlers[i].build_handler; } else { cms->inner_cont_oid = oid; } return 0; } /** * ksba_cms_add_digest_algo: * @cms: A CMS object * @oid: A stringified object OID describing the hash algorithm * * Set the algorithm to be used for creating the hash. Note, that we * currently can't do a per-signer hash. * * Return value: o on success or an error code **/ gpg_error_t ksba_cms_add_digest_algo (ksba_cms_t cms, const char *oid) { struct oidlist_s *ol; if (!cms || !oid) return gpg_error (GPG_ERR_INV_VALUE); ol = xtrymalloc (sizeof *ol); if (!ol) return gpg_error (GPG_ERR_ENOMEM); ol->oid = xtrystrdup (oid); if (!ol->oid) { xfree (ol); return gpg_error (GPG_ERR_ENOMEM); } ol->next = cms->digest_algos; cms->digest_algos = ol; return 0; } /** * ksba_cms_add_signer: * @cms: A CMS object * @cert: A certificate used to describe the signer. * * This functions starts assembly of a new signed data content or adds * another signer to the list of signers. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_cms_add_signer (ksba_cms_t cms, ksba_cert_t cert) { struct certlist_s *cl, *cl2; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); cl = xtrycalloc (1,sizeof *cl); if (!cl) return gpg_error (GPG_ERR_ENOMEM); ksba_cert_ref (cert); cl->cert = cert; if (!cms->cert_list) cms->cert_list = cl; else { for (cl2=cms->cert_list; cl2->next; cl2 = cl2->next) ; cl2->next = cl; } return 0; } /** * ksba_cms_add_cert: * @cms: A CMS object * @cert: A certificate to be send along with the signed data. * * This functions adds a certificate to the list of certificates send * along with the signed data. Using this is optional but it is very * common to include at least the certificate of the signer it self. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_cms_add_cert (ksba_cms_t cms, ksba_cert_t cert) { struct certlist_s *cl; if (!cms || !cert) return gpg_error (GPG_ERR_INV_VALUE); /* first check whether this is a duplicate. */ for (cl = cms->cert_info_list; cl; cl = cl->next) { if (!_ksba_cert_cmp (cert, cl->cert)) return 0; /* duplicate */ } /* Okay, add it. */ cl = xtrycalloc (1,sizeof *cl); if (!cl) return gpg_error (GPG_ERR_ENOMEM); ksba_cert_ref (cert); cl->cert = cert; cl->next = cms->cert_info_list; cms->cert_info_list = cl; return 0; } /** * ksba_cms_set_message_digest: * @cms: A CMS object * @idx: The index of the signer * @digest: a message digest * @digest_len: the length of the message digest * * Set a message digest into the signedAttributes of the signer with * the index IDX. The index of a signer is determined by the sequence * of ksba_cms_add_signer() calls; the first signer has the index 0. * This function is to be used when the hash value of the data has * been calculated and before the create function requests the sign * operation. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_set_message_digest (ksba_cms_t cms, int idx, const char *digest, size_t digest_len) { struct certlist_s *cl; if (!cms || !digest) return gpg_error (GPG_ERR_INV_VALUE); if (!digest_len || digest_len > DIM(cl->msg_digest)) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store it */ cl->msg_digest_len = digest_len; memcpy (cl->msg_digest, digest, digest_len); return 0; } /** * ksba_cms_set_signing_time: * @cms: A CMS object * @idx: The index of the signer * @sigtime: a time or an emty value to use the current time * * Set a signing time into the signedAttributes of the signer with * the index IDX. The index of a signer is determined by the sequence * of ksba_cms_add_signer() calls; the first signer has the index 0. * * Return value: 0 on success or an error code **/ gpg_error_t ksba_cms_set_signing_time (ksba_cms_t cms, int idx, const ksba_isotime_t sigtime) { struct certlist_s *cl; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store it */ /* Fixme: We might want to check the validity of the pased time string. */ if (!*sigtime) _ksba_current_time (cl->signing_time); else _ksba_copy_time (cl->signing_time, sigtime); return 0; } /* r_sig = (sig-val ( ( ) ... ( ) )) The sexp must be in canonical form. Note the must be given as a stringified OID or the special string "rsa". Note that IDX is only used for consistency checks. */ gpg_error_t ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval) { const unsigned char *s; unsigned long n; struct sig_val_s *sv, **sv_tail; int i; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); /* only one signer for now */ s = sigval; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; for (i=0, sv_tail=&cms->sig_val; *sv_tail; sv_tail=&(*sv_tail)->next, i++) ; if (i != idx) return gpg_error (GPG_ERR_INV_INDEX); if (!(n = snext (&s))) return gpg_error (GPG_ERR_INV_SEXP); if (!smatch (&s, 7, "sig-val")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* Break out the algorithm ID. */ if (!(n = snext (&s))) return gpg_error (GPG_ERR_INV_SEXP); sv = xtrycalloc (1, sizeof *sv); if (!sv) return gpg_error (GPG_ERR_ENOMEM); if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a') { /* kludge to allow "rsa" to be passed as algorithm name */ sv->algo = xtrystrdup ("1.2.840.113549.1.1.1"); if (!sv->algo) { xfree (sv); return gpg_error (GPG_ERR_ENOMEM); } } else { sv->algo = xtrymalloc (n+1); if (!sv->algo) { xfree (sv); return gpg_error (GPG_ERR_ENOMEM); } memcpy (sv->algo, s, n); sv->algo[n] = 0; } s += n; /* And now the values - FIXME: For now we only support one */ /* fixme: start loop */ if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; if (!(n = snext (&s))) { xfree (sv->algo); xfree (sv); return gpg_error (GPG_ERR_INV_SEXP); } s += n; /* ignore the name of the parameter */ if (!digitp(s)) { xfree (sv->algo); xfree (sv); return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ } if (!(n = snext (&s))) { xfree (sv->algo); xfree (sv); return gpg_error (GPG_ERR_INV_SEXP); } if (n > 1 && !*s) { /* We might have a leading zero due to the way we encode MPIs - this zero should not go into the OCTECT STRING. */ s++; n--; } sv->value = xtrymalloc (n); if (!sv->value) { xfree (sv->algo); xfree (sv); return gpg_error (GPG_ERR_ENOMEM); } memcpy (sv->value, s, n); sv->valuelen = n; s += n; if ( *s != ')') { xfree (sv->value); xfree (sv->algo); xfree (sv); return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ } s++; /* fixme: end loop over parameters */ /* we need 2 closing parenthesis */ if ( *s != ')' || s[1] != ')') { xfree (sv->value); xfree (sv->algo); xfree (sv); return gpg_error (GPG_ERR_INV_SEXP); } *sv_tail = sv; return 0; } /* Set the content encryption algorithm to OID and optionally set the initialization vector to IV */ gpg_error_t ksba_cms_set_content_enc_algo (ksba_cms_t cms, const char *oid, const unsigned char *iv, size_t ivlen) { if (!cms || !oid) return gpg_error (GPG_ERR_INV_VALUE); xfree (cms->encr_iv); cms->encr_iv = NULL; cms->encr_ivlen = 0; cms->encr_algo_oid = xtrystrdup (oid); if (!cms->encr_algo_oid) return gpg_error (GPG_ERR_ENOMEM); if (iv) { cms->encr_iv = xtrymalloc (ivlen); if (!cms->encr_iv) return gpg_error (GPG_ERR_ENOMEM); memcpy (cms->encr_iv, iv, ivlen); cms->encr_ivlen = ivlen; } return 0; } /* * encval is expected to be a canonical encoded S-Exp of this form: * (enc-val * ( * ( ) * ... * ( ) * )) * * Note the must be given as a stringified OID or the special * string "rsa" */ gpg_error_t ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval) { /*FIXME: This shares most code with ...set_sig_val */ struct certlist_s *cl; const char *s, *endp; unsigned long n; if (!cms) return gpg_error (GPG_ERR_INV_VALUE); if (idx < 0) return gpg_error (GPG_ERR_INV_INDEX); for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--) ; if (!cl) return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store the value */ s = encval; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s!=':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; if (n != 7 || memcmp (s, "enc-val", 7)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); s += 7; if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; /* break out the algorithm ID */ n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */ s++; xfree (cl->enc_val.algo); if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a') { /* kludge to allow "rsa" to be passed as algorithm name */ cl->enc_val.algo = xtrystrdup ("1.2.840.113549.1.1.1"); if (!cl->enc_val.algo) return gpg_error (GPG_ERR_ENOMEM); } else { cl->enc_val.algo = xtrymalloc (n+1); if (!cl->enc_val.algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.algo, s, n); cl->enc_val.algo[n] = 0; } s += n; /* And now the values - FIXME: For now we only support one */ /* fixme: start loop */ if (*s != '(') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; s += n; /* ignore the name of the parameter */ if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; if (n > 1 && !*s) { /* We might have a leading zero due to the way we encode MPIs - this zero should not go into the OCTECT STRING. */ s++; n--; } xfree (cl->enc_val.value); cl->enc_val.value = xtrymalloc (n); if (!cl->enc_val.value) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.value, s, n); cl->enc_val.valuelen = n; s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */ s++; /* fixme: end loop over parameters */ /* we need 2 closing parenthesis */ if ( *s != ')' || s[1] != ')') return gpg_error (GPG_ERR_INV_SEXP); return 0; } /** * ksba_cms_add_recipient: * @cms: A CMS object * @cert: A certificate used to describe the recipient. * * This functions starts assembly of a new enveloped data content or adds * another recipient to the list of recipients. * * Note: after successful completion of this function ownership of * @cert is transferred to @cms. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_cms_add_recipient (ksba_cms_t cms, ksba_cert_t cert) { /* for now we use the same structure */ return ksba_cms_add_signer (cms, cert); } /* Content handler for parsing messages */ static gpg_error_t ct_parse_data (ksba_cms_t cms) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_parse_signed_data (ksba_cms_t cms) { enum { sSTART, sGOT_HASH, sIN_DATA, sERROR } state = sERROR; ksba_stop_reason_t stop_reason = cms->stop_reason; gpg_error_t err = 0; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) { state = sSTART; } else if (stop_reason == KSBA_SR_NEED_HASH) { state = sGOT_HASH; } else if (stop_reason == KSBA_SR_BEGIN_DATA) { if (!cms->hash_fnc) err = gpg_error (GPG_ERR_MISSING_ACTION); else state = sIN_DATA; } else if (stop_reason == KSBA_SR_END_DATA) { state = sGOT_HASH; } else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) err = _ksba_cms_parse_signed_data_part_1 (cms); else if (state == sGOT_HASH) err = _ksba_cms_parse_signed_data_part_2 (cms); else if (state == sIN_DATA) err = read_and_hash_cont (cms); else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { if (cms->detached_data && !cms->data.digest) { /* We use this stop reason to inform the caller about a detached signatures. Actually there is no need for him to hash the data now, he can do this also later. */ stop_reason = KSBA_SR_NEED_HASH; } else { /* The user must now provide a hash function so that we can hash the data in the next round */ stop_reason = KSBA_SR_BEGIN_DATA; } } else if (state == sIN_DATA) stop_reason = KSBA_SR_END_DATA; else if (state ==sGOT_HASH) stop_reason = KSBA_SR_READY; cms->stop_reason = stop_reason; return 0; } static gpg_error_t ct_parse_enveloped_data (ksba_cms_t cms) { enum { sSTART, sREST, sINDATA, sERROR } state = sERROR; ksba_stop_reason_t stop_reason = cms->stop_reason; gpg_error_t err = 0; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) { state = sSTART; } else if (stop_reason == KSBA_SR_DETACHED_DATA) { state = sREST; } else if (stop_reason == KSBA_SR_BEGIN_DATA) { state = sINDATA; } else if (stop_reason == KSBA_SR_END_DATA) { state = sREST; } else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) err = _ksba_cms_parse_enveloped_data_part_1 (cms); else if (state == sREST) err = _ksba_cms_parse_enveloped_data_part_2 (cms); else if (state == sINDATA) err = read_encrypted_cont (cms); else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { stop_reason = cms->detached_data? KSBA_SR_DETACHED_DATA : KSBA_SR_BEGIN_DATA; } else if (state == sINDATA) stop_reason = KSBA_SR_END_DATA; else if (state ==sREST) stop_reason = KSBA_SR_READY; cms->stop_reason = stop_reason; return 0; } static gpg_error_t ct_parse_digested_data (ksba_cms_t cms) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_parse_encrypted_data (ksba_cms_t cms) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } /* Content handlers for building messages */ static gpg_error_t ct_build_data (ksba_cms_t cms) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } /* Write everything up to the encapsulated data content type. */ static gpg_error_t build_signed_data_header (ksba_cms_t cms) { gpg_error_t err; unsigned char *buf; const char *s; size_t len; int i; /* Write the outer contentInfo. */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->content.oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; /* The SEQUENCE */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; /* figure out the CMSVersion to be used */ if (0 /* fixme: have_attribute_certificates || encapsulated_content != data || any_signer_info_is_version_3*/ ) s = "\x03"; else s = "\x01"; err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (err) return err; err = ksba_writer_write (cms->writer, s, 1); if (err) return err; /* SET OF DigestAlgorithmIdentifier */ { unsigned char *value; size_t valuelen; ksba_writer_t tmpwrt; err = ksba_writer_new (&tmpwrt); if (err) return err; err = ksba_writer_set_mem (tmpwrt, 512); if (err) { ksba_writer_release (tmpwrt); return err; } for (i=0; (s = ksba_cms_get_digest_algo_list (cms, i)); i++) { int j; const char *s2; /* (make sure not to write duplicates) */ for (j=0; j < i && (s2=ksba_cms_get_digest_algo_list (cms, j)); j++) { if (!strcmp (s, s2)) break; } if (j == i) { err = _ksba_der_write_algorithm_identifier (tmpwrt, s, NULL, 0); if (err) { ksba_writer_release (tmpwrt); return err; } } } value = ksba_writer_snatch_mem (tmpwrt, &valuelen); ksba_writer_release (tmpwrt); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); return err; } err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (cms->writer, value, valuelen); xfree (value); if (err) return err; } /* Write the (inner) encapsulatedContentInfo */ /* if we have a detached signature we don't need to use undefinite length here - but it doesn't matter either */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->inner_cont_oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; if ( !cms->detached_data) { /* write the tag */ err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; } return err; } /* Set the issuer/serial from the cert to the node. mode 0: sid mode 1: rid */ static gpg_error_t set_issuer_serial (AsnNode info, ksba_cert_t cert, int mode) { gpg_error_t err; AsnNode dst, src; if (!info || !cert) return gpg_error (GPG_ERR_INV_VALUE); src = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.serialNumber"); dst = _ksba_asn_find_node (info, mode? "rid.issuerAndSerialNumber.serialNumber": "sid.issuerAndSerialNumber.serialNumber"); err = _ksba_der_copy_tree (dst, src, cert->image); if (err) return err; src = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.issuer"); dst = _ksba_asn_find_node (info, mode? "rid.issuerAndSerialNumber.issuer": "sid.issuerAndSerialNumber.issuer"); err = _ksba_der_copy_tree (dst, src, cert->image); if (err) return err; return 0; } + +/* An object used to construct the signed attributes. */ +struct attrarray_s { + AsnNode root; + unsigned char *image; + size_t imagelen; +}; + + +/* Thank you ASN.1 committee for allowing us to employ a sort to make + that DER encoding even more complicate. */ +static int +compare_attrarray (const void *a_v, const void *b_v) +{ + const struct attrarray_s *a = a_v; + const struct attrarray_s *b = b_v; + const unsigned char *ap, *bp; + size_t an, bn; + + ap = a->image; + an = a->imagelen; + bp = b->image; + bn = b->imagelen; + for (; an && bn; an--, bn--, ap++, bp++ ) + if (*ap != *bp) + return *ap - *bp; + + return (an == bn)? 0 : (an > bn)? 1 : -1; +} + + + + /* Write the END of data NULL tag and everything we can write before the user can calculate the signature */ static gpg_error_t build_signed_data_attributes (ksba_cms_t cms) { gpg_error_t err; int signer; ksba_asn_tree_t cms_tree; struct certlist_s *certlist; struct oidlist_s *digestlist; struct signer_info_s *si, **si_tail; /* Write the End tag */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (err) return err; if (cms->signer_info) - return gpg_error (GPG_ERR_CONFLICT); /* This list must be empty at this point. */ + return gpg_error (GPG_ERR_CONFLICT); /* This list must be empty at + this point. */ /* Write optional certificates */ if (cms->cert_info_list) { unsigned long totallen = 0; const unsigned char *der; size_t n; for (certlist = cms->cert_info_list; certlist; certlist = certlist->next) { if (!ksba_cert_get_image (certlist->cert, &n)) - return gpg_error (GPG_ERR_GENERAL); /* user passed an unitialized cert */ + return gpg_error (GPG_ERR_GENERAL); /* User passed an + unitialized cert */ totallen += n; } err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, totallen); if (err) return err; for (certlist = cms->cert_info_list; certlist; certlist = certlist->next) { if (!(der=ksba_cert_get_image (certlist->cert, &n))) return gpg_error (GPG_ERR_BUG); err = ksba_writer_write (cms->writer, der, n); if (err ) return err; } } /* If we ever support it, here is the right place to do it: Write the optional CRLs */ /* Now we have to prepare the signer info. For now we will just build the signedAttributes, so that the user can do the signature calculation */ err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; /* fixme: we must release root and cms_tree on error */ certlist = cms->cert_list; if (!certlist) return gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ digestlist = cms->digest_algos; if (!digestlist) return gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ si_tail = &cms->signer_info; for (signer=0; certlist; signer++, certlist = certlist->next, digestlist = digestlist->next) { AsnNode attr, root; AsnNode n; unsigned char *image; + size_t imagelen; int i; - struct { - AsnNode root; - unsigned char *image; - } attrarray[3]; + struct attrarray_s attrarray[3]; int attridx = 0; if (!digestlist) return gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ if (!certlist->cert || !digestlist->oid) return gpg_error (GPG_ERR_BUG); + /* Include the pretty important message digest. */ + attr = _ksba_asn_expand_tree (cms_tree->parse_tree, + "CryptographicMessageSyntax.Attribute"); + if (!attr) + return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); + n = _ksba_asn_find_node (attr, "Attribute.attrType"); + if (!n) + return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); + err = _ksba_der_store_oid (n, oidstr_messageDigest); + if (err) + return err; + n = _ksba_asn_find_node (attr, "Attribute.attrValues"); + if (!n || !n->down) + return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); + n = n->down; /* fixme: ugly hack */ + assert (certlist && certlist->msg_digest_len); + err = _ksba_der_store_octet_string (n, certlist->msg_digest, + certlist->msg_digest_len); + if (err) + return err; + err = _ksba_der_encode_tree (attr, &image, &imagelen); + if (err) + return err; + attrarray[attridx].root = attr; + attrarray[attridx].image = image; + attrarray[attridx].imagelen = imagelen; + attridx++; + /* Include the content-type attribute. */ attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_oid (n, oidstr_contentType); if (err) return err; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); n = n->down; /* fixme: ugly hack */ err = _ksba_der_store_oid (n, cms->inner_cont_oid); if (err) return err; - err = _ksba_der_encode_tree (attr, &image, NULL); + err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) - return err; + return err; attrarray[attridx].root = attr; attrarray[attridx].image = image; + attrarray[attridx].imagelen = imagelen; attridx++; /* Include the signing time */ if (certlist->signing_time) { attr = _ksba_asn_expand_tree (cms_tree->parse_tree, - "CryptographicMessageSyntax.Attribute"); + "CryptographicMessageSyntax.Attribute"); if (!attr) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_oid (n, oidstr_signingTime); if (err) return err; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); n = n->down; /* fixme: ugly hack */ err = _ksba_der_store_time (n, certlist->signing_time); if (err) return err; - err = _ksba_der_encode_tree (attr, &image, NULL); + err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) return err; - /* we will use the attributes again - so save them */ + /* We will use the attributes again - so save them */ attrarray[attridx].root = attr; attrarray[attridx].image = image; + attrarray[attridx].imagelen = imagelen; attridx++; } - /* The message digest is pretty important; include it. - - Note: We used to include this attribute first, but during an - interoperability tests with the proprietary Crypotovision - v2.1.1 software for MS Windows, it turned out that this - software somehow rebuilds the set of signed attributes in a - different way before calculating the hash - therefore the - signature always turned out to be bad. To help those guys we - are really kind and sweap the attributes despite the fact - that there is - of course - no defined sequence of signed - attributes. - */ - attr = _ksba_asn_expand_tree (cms_tree->parse_tree, - "CryptographicMessageSyntax.Attribute"); - if (!attr) - return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); - n = _ksba_asn_find_node (attr, "Attribute.attrType"); - if (!n) - return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); - err = _ksba_der_store_oid (n, oidstr_messageDigest); - if (err) - return err; - n = _ksba_asn_find_node (attr, "Attribute.attrValues"); - if (!n || !n->down) - return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); - n = n->down; /* fixme: ugly hack */ - assert (certlist && certlist->msg_digest_len); - err = _ksba_der_store_octet_string (n, certlist->msg_digest, - certlist->msg_digest_len); - if (err) - return err; - err = _ksba_der_encode_tree (attr, &image, NULL); - if (err) - return err; - attrarray[attridx].root = attr; - attrarray[attridx].image = image; - attridx++; + /* Arggh. That silly ASN.1 DER encoding rules: We need to sort + the SET values. */ + qsort (attrarray, attridx+1, sizeof (struct attrarray_s), + compare_attrarray); /* Now copy them to an SignerInfo tree. This tree is not complete but suitable for ksba_cms_hash_signed_attributes() */ root = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.SignerInfo"); n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs"); if (!n || !n->down) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); /* This is another ugly hack to move to the element we want */ for (n = n->down->down; n && n->type != TYPE_SEQUENCE; n = n->right) ; if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); for (i=0; i < attridx; i++) { if (i) { if ( !(n=_ksba_asn_insert_copy (n))) return gpg_error (GPG_ERR_ENOMEM); } err = _ksba_der_copy_tree (n, attrarray[i].root, attrarray[i].image); if (err) return err; /* fixme: release this array slot */ } err = _ksba_der_encode_tree (root, &image, NULL); if (err) return err; si = xtrycalloc (1, sizeof *si); if (!si) return gpg_error (GPG_ERR_ENOMEM); si->root = root; si->image = image; /* Hmmm, we don't set the length of the image. */ *si_tail = si; si_tail = &si->next; } return 0; } /* The user has calculated the signatures and we can therefore write everything left over to do. */ static gpg_error_t build_signed_data_rest (ksba_cms_t cms) { gpg_error_t err; int signer; ksba_asn_tree_t cms_tree; struct certlist_s *certlist; struct oidlist_s *digestlist; struct signer_info_s *si; struct sig_val_s *sv; ksba_writer_t tmpwrt = NULL; /* Now we can really write the signer info */ err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; /* fixme: we must release root and cms_tree on error */ certlist = cms->cert_list; if (!certlist) return gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ /* To construct the set we use a temporary writer object. */ err = ksba_writer_new (&tmpwrt); if (err) return err; err = ksba_writer_set_mem (tmpwrt, 2048); if (err) return err; digestlist = cms->digest_algos; si = cms->signer_info; sv = cms->sig_val; for (signer=0; certlist; signer++, certlist = certlist->next, digestlist = digestlist->next, si = si->next, sv = sv->next) { AsnNode root, n, n2; unsigned char *image; size_t imagelen; if (!digestlist || !si || !sv) return gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ if (!certlist->cert || !digestlist->oid) return gpg_error (GPG_ERR_BUG); root = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.SignerInfo"); /* We store a version of 1 because we use the issuerAndSerialNumber */ n = _ksba_asn_find_node (root, "SignerInfo.version"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_integer (n, "\x00\x00\x00\x01\x01"); if (err) return err; /* Store the sid */ n = _ksba_asn_find_node (root, "SignerInfo.sid"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = set_issuer_serial (n, certlist->cert, 0); if (err) return err; /* store the digestAlgorithm */ n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.algorithm"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_oid (n, digestlist->oid); if (err) return err; n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.parameters"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_null (n); if (err) return err; /* and the signed attributes */ n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs"); if (!n || !n->down) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); assert (si->root); assert (si->image); n2 = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!n2 || !n->down) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_copy_tree (n, n2, si->image); if (err) return err; image = NULL; /* store the signatureAlgorithm */ n = _ksba_asn_find_node (root, "SignerInfo.signatureAlgorithm.algorithm"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); if (!sv->algo) return gpg_error (GPG_ERR_MISSING_VALUE); err = _ksba_der_store_oid (n, sv->algo); if (err) return err; n = _ksba_asn_find_node (root, "SignerInfo.signatureAlgorithm.parameters"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_null (n); if (err) return err; /* store the signature */ if (!sv->value) return gpg_error (GPG_ERR_MISSING_VALUE); n = _ksba_asn_find_node (root, "SignerInfo.signature"); if (!n) return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); err = _ksba_der_store_octet_string (n, sv->value, sv->valuelen); if (err) return err; /* Make the DER encoding and write it out. */ err = _ksba_der_encode_tree (root, &image, &imagelen); if (err) return err; err = ksba_writer_write (tmpwrt, image, imagelen); if (err ) return err; /* fixme: release what we don't need */ } /* Write out the SET filled with all signer infos */ { unsigned char *value; size_t valuelen; value = ksba_writer_snatch_mem (tmpwrt, &valuelen); if (!value) { return gpg_error (GPG_ERR_ENOMEM); } err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (cms->writer, value, valuelen); xfree (value); if (err) return err; } /* FIXME: release tmpwrt on error */ ksba_writer_release (tmpwrt); /* Write 3 end tags */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); return err; } static gpg_error_t ct_build_signed_data (ksba_cms_t cms) { enum { sSTART, sDATAREADY, sGOTSIG, sERROR } state = sERROR; ksba_stop_reason_t stop_reason; gpg_error_t err = 0; stop_reason = cms->stop_reason; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) { state = sSTART; } else if (stop_reason == KSBA_SR_BEGIN_DATA) { /* fixme: check that the message digest has been set */ state = sDATAREADY; } else if (stop_reason == KSBA_SR_END_DATA) state = sDATAREADY; else if (stop_reason == KSBA_SR_NEED_SIG) { if (!cms->sig_val) err = gpg_error (GPG_ERR_MISSING_ACTION); /* No ksba_cms_set_sig_val () called */ state = sGOTSIG; } else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) { /* figure out whether a detached signature is requested */ if (cms->cert_list && cms->cert_list->msg_digest_len) cms->detached_data = 1; else cms->detached_data = 0; /* and start encoding */ err = build_signed_data_header (cms); } else if (state == sDATAREADY) { if (!cms->detached_data) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = build_signed_data_attributes (cms); } else if (state == sGOTSIG) err = build_signed_data_rest (cms); else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { /* user should write the data and calculate the hash or do nothing in case of END_DATA */ stop_reason = cms->detached_data? KSBA_SR_END_DATA : KSBA_SR_BEGIN_DATA; } else if (state == sDATAREADY) stop_reason = KSBA_SR_NEED_SIG; else if (state == sGOTSIG) stop_reason = KSBA_SR_READY; cms->stop_reason = stop_reason; return 0; } /* write everything up to the encryptedContentInfo including the tag */ static gpg_error_t build_enveloped_data_header (ksba_cms_t cms) { gpg_error_t err; int recpno; ksba_asn_tree_t cms_tree; struct certlist_s *certlist; unsigned char *buf; const char *s; size_t len; ksba_writer_t tmpwrt = NULL; /* Write the outer contentInfo */ /* fixme: code is shared with signed_data_header */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->content.oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; /* The SEQUENCE */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; /* figure out the CMSVersion to be used (from rfc2630): version is the syntax version number. If originatorInfo is present, then version shall be 2. If any of the RecipientInfo structures included have a version other than 0, then the version shall be 2. If unprotectedAttrs is present, then version shall be 2. If originatorInfo is absent, all of the RecipientInfo structures are version 0, and unprotectedAttrs is absent, then version shall be 0. For SPHINX the version number must be 0. */ s = "\x00"; err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1); if (err) return err; err = ksba_writer_write (cms->writer, s, 1); if (err) return err; /* Note: originatorInfo is not yet implemented and must not be used for SPHINX */ /* Now we write the recipientInfo */ err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; /* fixme: we must cms_tree on error */ certlist = cms->cert_list; if (!certlist) return gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ /* To construct the set we use a temporary writer object */ err = ksba_writer_new (&tmpwrt); if (err) return err; err = ksba_writer_set_mem (tmpwrt, 2048); if (err) goto leave; for (recpno=0; certlist; recpno++, certlist = certlist->next) { AsnNode root, n; unsigned char *image; size_t imagelen; if (!certlist->cert) { err = gpg_error (GPG_ERR_BUG); goto leave; } root = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.RecipientInfo"); /* We store a version of 0 because we are only allowed to use the issuerAndSerialNumber for SPHINX */ n = _ksba_asn_find_node (root, "RecipientInfo.ktri.version"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_integer (n, "\x00\x00\x00\x01\x00"); if (err) goto leave; /* Store the rid */ n = _ksba_asn_find_node (root, "RecipientInfo.ktri.rid"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = set_issuer_serial (n, certlist->cert, 1); if (err) goto leave; /* store the keyEncryptionAlgorithm */ if (!certlist->enc_val.algo || !certlist->enc_val.value) return gpg_error (GPG_ERR_MISSING_VALUE); n = _ksba_asn_find_node (root, "RecipientInfo.ktri.keyEncryptionAlgorithm.algorithm"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, certlist->enc_val.algo); if (err) goto leave; n = _ksba_asn_find_node (root, "RecipientInfo.ktri.keyEncryptionAlgorithm.parameters"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_null (n); if (err) goto leave; /* store the encryptedKey */ if (!certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } n = _ksba_asn_find_node (root, "RecipientInfo.ktri.encryptedKey"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_octet_string (n, certlist->enc_val.value, certlist->enc_val.valuelen); if (err) goto leave; /* Make the DER encoding and write it out */ err = _ksba_der_encode_tree (root, &image, &imagelen); if (err) goto leave; err = ksba_writer_write (tmpwrt, image, imagelen); if (err ) goto leave; /* fixme: release what we don't need */ } /* Write out the SET filled with all recipient infos */ { unsigned char *value; size_t valuelen; value = ksba_writer_snatch_mem (tmpwrt, &valuelen); if (!value) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen); if (!err) err = ksba_writer_write (cms->writer, value, valuelen); xfree (value); if (err) goto leave; } /* Write the (inner) encryptedContentInfo */ err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0); if (err) return err; err = ksba_oid_from_str (cms->inner_cont_oid, &buf, &len); if (err) return err; err = _ksba_ber_write_tl (cms->writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (cms->writer, buf, len); xfree (buf); if (err) return err; /* and the encryptionAlgorithm */ err = _ksba_der_write_algorithm_identifier (cms->writer, cms->encr_algo_oid, cms->encr_iv, cms->encr_ivlen); if (err) return err; /* write the tag for the encrypted data, it is an implicit octect string in constructed form and indefinite length */ err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0); if (err) return err; /* Now the encrypted data should be written */ leave: ksba_writer_release (tmpwrt); return err; } static gpg_error_t ct_build_enveloped_data (ksba_cms_t cms) { enum { sSTART, sINDATA, sREST, sERROR } state = sERROR; ksba_stop_reason_t stop_reason; gpg_error_t err = 0; stop_reason = cms->stop_reason; cms->stop_reason = KSBA_SR_RUNNING; /* Calculate state from last reason and do some checks */ if (stop_reason == KSBA_SR_GOT_CONTENT) state = sSTART; else if (stop_reason == KSBA_SR_BEGIN_DATA) state = sINDATA; else if (stop_reason == KSBA_SR_END_DATA) state = sREST; else if (stop_reason == KSBA_SR_RUNNING) err = gpg_error (GPG_ERR_INV_STATE); else if (stop_reason) err = gpg_error (GPG_ERR_BUG); if (err) return err; /* Do the action */ if (state == sSTART) err = build_enveloped_data_header (cms); else if (state == sINDATA) err = write_encrypted_cont (cms); else if (state == sREST) { /* SPHINX does not allow for unprotectedAttributes */ /* Write 5 end tags */ err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); if (!err) err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0); } else err = gpg_error (GPG_ERR_INV_STATE); if (err) return err; /* Calculate new stop reason */ if (state == sSTART) { /* user should now write the encrypted data */ stop_reason = KSBA_SR_BEGIN_DATA; } else if (state == sINDATA) { /* tell the user that we wrote everything */ stop_reason = KSBA_SR_END_DATA; } else if (state == sREST) { stop_reason = KSBA_SR_READY; } cms->stop_reason = stop_reason; return 0; } static gpg_error_t ct_build_digested_data (ksba_cms_t cms) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_build_encrypted_data (ksba_cms_t cms) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); }