diff --git a/src/cms-parser.c b/src/cms-parser.c index f7f727d..b44a04a 100644 --- a/src/cms-parser.c +++ b/src/cms-parser.c @@ -1,974 +1,975 @@ /* cms-parse.c - parse cryptographic message syntax * Copyright (C) 2001, 2012 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 either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* We handle CMS by using a handcrafted parser for the outer structures and the generic parser of the parts we can handle in memory. Extending the generic parser to allow hooks for indefinite length objects and to auto select the object depending on the content type OID is too complicated. */ #include #include #include #include #include #include "util.h" #include "cms.h" #include "asn1-func.h" /* need some constants */ #include "ber-decoder.h" #include "ber-help.h" #include "keyinfo.h" static int read_byte (ksba_reader_t reader) { unsigned char buf; size_t nread; int rc; do rc = ksba_reader_read (reader, &buf, 1, &nread); while (!rc && !nread); return rc? -1: buf; } /* read COUNT bytes into buffer. Return 0 on success */ static int read_buffer (ksba_reader_t reader, char *buffer, size_t count) { size_t nread; while (count) { if (ksba_reader_read (reader, buffer, count, &nread)) return -1; buffer += nread; count -= nread; } return 0; } /* Create a new decoder and run it for the given element */ static gpg_error_t create_and_run_decoder (ksba_reader_t reader, const char *elem_name, unsigned int flags, AsnNode *r_root, unsigned char **r_image, size_t *r_imagelen) { gpg_error_t err; ksba_asn_tree_t cms_tree; BerDecoder decoder; err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; decoder = _ksba_ber_decoder_new (); if (!decoder) { ksba_asn_tree_release (cms_tree); return gpg_error (GPG_ERR_ENOMEM); } err = _ksba_ber_decoder_set_reader (decoder, reader); if (err) { ksba_asn_tree_release (cms_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_set_module (decoder, cms_tree); if (err) { ksba_asn_tree_release (cms_tree); _ksba_ber_decoder_release (decoder); return err; } err = _ksba_ber_decoder_decode (decoder, elem_name, flags, r_root, r_image, r_imagelen); _ksba_ber_decoder_release (decoder); ksba_asn_tree_release (cms_tree); return err; } /* Parse this structure and return the oid of the content. The read position is then located at the value of content. This fucntion is the core for parsing ContentInfo and EncapsulatedContentInfo. ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType } ContentType ::= OBJECT IDENTIFIER Returns: 0 on success or an error code. Other values are returned by the parameters. */ static gpg_error_t parse_content_info (ksba_reader_t reader, unsigned long *r_len, int *r_ndef, char **r_oid, int *has_content) { struct tag_info ti; gpg_error_t err; int content_ndef; unsigned long content_len; unsigned char oidbuf[100]; /* pretty large for an OID */ char *oid = NULL; /* read the sequence triplet */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); content_len = ti.length; content_ndef = ti.ndef; if (!content_ndef && content_len < 3) return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */ /* read the OID */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed && ti.length) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ content_len -= ti.length; } if (ti.length >= DIM(oidbuf)) return gpg_error (GPG_ERR_TOO_LARGE); err = read_buffer (reader, oidbuf, ti.length); if (err) return err; oid = ksba_oid_to_str (oidbuf, ti.length); if (!oid) return gpg_error (GPG_ERR_ENOMEM); if (!content_ndef && !content_len) { /* no data */ *has_content = 0; } else { /* now read the explicit tag 0 which is optional */ err = _ksba_ber_read_tl (reader, &ti); if (err) { xfree (oid); return err; } if ( ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed ) { *has_content = 1; } else if ( ti.class == CLASS_UNIVERSAL && ti.tag == 0 && !ti.is_constructed ) { *has_content = 0; /* this is optional - allow NUL tag */ } else /* neither [0] nor NULL */ { xfree (oid); return gpg_error (GPG_ERR_INV_CMS_OBJ); } if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (!ti.ndef && content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ } } *r_len = content_len; *r_ndef = content_ndef; *r_oid = oid; return 0; } /* Parse this structure and return the oid of the content as well as the algorithm identifier. The read position is then located at the value of the octect string. EncryptedContentInfo ::= SEQUENCE { contentType OBJECT IDENTIFIER, contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL } Returns: 0 on success or an error code. Other values are returned by the parameters. */ static gpg_error_t parse_encrypted_content_info (ksba_reader_t reader, unsigned long *r_len, int *r_ndef, char **r_cont_oid, char **r_algo_oid, char **r_algo_parm, size_t *r_algo_parmlen, int *has_content) { struct tag_info ti; gpg_error_t err; int content_ndef; unsigned long content_len; unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */ char *cont_oid = NULL; char *algo_oid = NULL; char *algo_parm = NULL; size_t algo_parmlen; size_t nread; /* Fixme: release oids in case of errors */ /* read the sequence triplet */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); content_len = ti.length; content_ndef = ti.ndef; if (!content_ndef && content_len < 3) return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */ /* read the OID */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID && !ti.is_constructed && ti.length) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ content_len -= ti.length; } if (ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); err = read_buffer (reader, tmpbuf, ti.length); if (err) return err; cont_oid = ksba_oid_to_str (tmpbuf, ti.length); if (!cont_oid) return gpg_error (GPG_ERR_ENOMEM); /* read the algorithmIdentifier */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ content_len -= ti.nhdr; if (content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ content_len -= ti.length; } if (ti.nhdr + ti.length >= DIM(tmpbuf)) return gpg_error (GPG_ERR_TOO_LARGE); memcpy (tmpbuf, ti.buf, ti.nhdr); err = read_buffer (reader, tmpbuf+ti.nhdr, ti.length); if (err) return err; err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length, &nread,&algo_oid, &algo_parm, &algo_parmlen); if (err) return err; assert (nread <= ti.nhdr + ti.length); if (nread < ti.nhdr + ti.length) return gpg_error (GPG_ERR_TOO_SHORT); /* the optional encryptedDataInfo */ *has_content = 0; if (content_ndef || content_len) { /* now read the implicit tag 0. Actually this is optional but in that case we don't expect to have a content_len - well, it may be the end tag */ err = _ksba_ber_read_tl (reader, &ti); if (err) { xfree (cont_oid); xfree (algo_oid); return err; } /* Note: the tag may either denote a constructed or a primitve object. Actually this should match the use of NDEF header but we don't ceck that */ if ( ti.class == CLASS_CONTEXT && ti.tag == 0 ) { *has_content = 1; if (!content_ndef) { if (content_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); content_len -= ti.nhdr; if (!ti.ndef && content_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); } } else /* not what we want - push it back */ { *has_content = 0; err = ksba_reader_unread (reader, ti.buf, ti.nhdr); if (err) return err; } } *r_len = content_len; *r_ndef = content_ndef; *r_cont_oid = cont_oid; *r_algo_oid = algo_oid; *r_algo_parm = algo_parm; *r_algo_parmlen = algo_parmlen; return 0; } /* Parse this structure and return the oid of the content. The read position is then located at the value of content. ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType } ContentType ::= OBJECT IDENTIFIER Returns: 0 on success or an error code. On success the OID and the length values are stored in the cms structure. */ gpg_error_t _ksba_cms_parse_content_info (ksba_cms_t cms) { gpg_error_t err; int has_content; int content_ndef; unsigned long content_len; char *oid; err = parse_content_info (cms->reader, &content_len, &content_ndef, &oid, &has_content); if (err) { /* return a more meaningful error message. This way the caller can pass arbitrary data to the function and get back an error that this is not CMS instead of the the not very detailed BER Error. */ if (gpg_err_code (err) == GPG_ERR_BAD_BER || gpg_err_code (err) == GPG_ERR_INV_CMS_OBJ || gpg_err_code (err) == GPG_ERR_TOO_SHORT) err = gpg_error (GPG_ERR_NO_CMS_OBJ); return err; } if (!has_content) return gpg_error (GPG_ERR_NO_CMS_OBJ); /* It is not optional here */ cms->content.length = content_len; cms->content.ndef = content_ndef; xfree (cms->content.oid); cms->content.oid = oid; return 0; } /* parse a SEQUENCE and the first element which is expected to be the CMS version. Return the version and the length info */ static gpg_error_t parse_cms_version (ksba_reader_t reader, int *r_version, unsigned long *r_len, int *r_ndef) { struct tag_info ti; gpg_error_t err; unsigned long data_len; int data_ndef; int c; /* read the sequence triplet */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); data_len = ti.length; data_ndef = ti.ndef; if (!data_ndef && data_len < 3) return gpg_error (GPG_ERR_TOO_SHORT); /*to encode the version*/ /* read the version integer */ err = _ksba_ber_read_tl (reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER && !ti.is_constructed && ti.length) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); if (!data_ndef) { if (data_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ data_len -= ti.nhdr; if (data_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ data_len -= ti.length; } if (ti.length != 1) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION); if ( (c=read_byte (reader)) == -1) { err = ksba_reader_error (reader); return err? err : gpg_error (GPG_ERR_GENERAL); } if ( !(c == 0 || c == 1 || c == 2 || c == 3 || c == 4) ) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION); *r_version = c; *r_len = data_len; *r_ndef = data_ndef; return 0; } /* Parse a structure: SignedData ::= SEQUENCE { version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }), digestAlgorithms SET OF AlgorithmIdentifier, encapContentInfo EncapsulatedContentInfo, certificates [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } */ gpg_error_t _ksba_cms_parse_signed_data_part_1 (ksba_cms_t cms) { struct tag_info ti; gpg_error_t err; int signed_data_ndef; unsigned long signed_data_len; int algo_set_ndef; unsigned long algo_set_len; int encap_cont_ndef; unsigned long encap_cont_len; int has_content; char *oid; char *p, *buffer; unsigned long off, len; err = parse_cms_version (cms->reader, &cms->cms_version, &signed_data_len, &signed_data_ndef); if (err) return err; /* read the SET OF algorithmIdentifiers */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET && ti.is_constructed) ) return gpg_error (GPG_ERR_INV_CMS_OBJ); /* not the expected SET tag */ if (!signed_data_ndef) { if (signed_data_len < ti.nhdr) return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */ signed_data_len -= ti.nhdr; if (!ti.ndef && signed_data_len < ti.length) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ signed_data_len -= ti.length; } algo_set_len = ti.length; algo_set_ndef = ti.ndef; /* fixme: we are not able to read ndef length algorithm indentifiers. */ if (algo_set_ndef) return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); /* read the entire sequence into a buffer (add one to avoid malloc(0)) */ buffer = xtrymalloc (algo_set_len + 1); if (!buffer) return gpg_error (GPG_ERR_ENOMEM); if (read_buffer (cms->reader, buffer, algo_set_len)) { xfree (buffer); err = ksba_reader_error (cms->reader); return err? err: gpg_error (GPG_ERR_GENERAL); } p = buffer; while (algo_set_len) { size_t nread; struct oidlist_s *ol; err = _ksba_parse_algorithm_identifier (p, algo_set_len, &nread, &oid); if (err) { xfree (buffer); return err; } assert (nread <= algo_set_len); algo_set_len -= nread; p += nread; /* store the oid */ ol = xtrymalloc (sizeof *ol); if (!ol) { xfree (oid); return gpg_error (GPG_ERR_ENOMEM); } ol->oid = oid; ol->next = cms->digest_algos; cms->digest_algos = ol; } xfree (buffer); buffer = NULL; /* Now for the encapsulatedContentInfo */ off = ksba_reader_tell (cms->reader); err = parse_content_info (cms->reader, &encap_cont_len, &encap_cont_ndef, &oid, &has_content); if (err) return err; cms->inner_cont_len = encap_cont_len; cms->inner_cont_ndef = encap_cont_ndef; cms->inner_cont_oid = oid; cms->detached_data = !has_content; if (!signed_data_ndef) { len = ksba_reader_tell (cms->reader) - off; if (signed_data_len < len) return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */ signed_data_len -= len; if (!encap_cont_ndef && signed_data_len < encap_cont_len) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ } /* We have to stop here so that the caller can set up the hashing etc. */ return 0; } /* Continue parsing of the structure we started to parse with the part_1 function. We expect to be right at the certificates tag. */ gpg_error_t _ksba_cms_parse_signed_data_part_2 (ksba_cms_t cms) { struct tag_info ti; gpg_error_t err; struct signer_info_s *si, **si_tail; /* read the next triplet which is either a [0], a [1] or a SET OF (signerInfo) */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) { /* well, there might be still an end tag pending; eat it - fixme: we should keep track of this to catch invalid encodings */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; } if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) { /* Implicit SET OF certificateSet with elements of CHOICE, but we assume the first choice which is a Certificate; all other choices are obsolete. We are now parsing a set of certificates which we do by utilizing the ksba_cert code. */ ksba_cert_t cert; int expect_endtag; expect_endtag = !!ti.ndef; for (;;) { struct certlist_s *cl; /* First see whether this is really a sequence */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (expect_endtag && !ti.class && !ti.tag) { /* This is an end tag. Read the next tag but don't fail if this is just an EOF. */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; return err; } break; } if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed)) break; /* not a sequence, so we are ready with the set */ /* We must unread so that the standard parser sees the sequence */ err = ksba_reader_unread (cms->reader, ti.buf, ti.nhdr); if (err) return err; /* Use the standard certificate parser */ err = ksba_cert_new (&cert); if (err) return err; err = ksba_cert_read_der (cert, cms->reader); if (err) { ksba_cert_release (cert); return err; } cl = xtrycalloc (1, sizeof *cl); if (!cl) { ksba_cert_release (cert); return gpg_error (GPG_ERR_ENOMEM); } cl->cert = cert; cl->next = cms->cert_list; cms->cert_list = cl; } } if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed) { /* implicit SET OF certificateList. We should delegate the parsing to a - not yet existing - ksba_crl module. CRLs are quite important for other applications too so we should provide a nice interface */ int expect_endtag; expect_endtag = !!ti.ndef; /* FIXME this is just dummy read code */ /* fprintf (stderr,"WARNING: Can't handle CRLs yet\n"); */ for (;;) { /* first see whether this is really a sequence */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (expect_endtag && !ti.class && !ti.tag) { /* This is an end tag. Read the next tag but don't fail if this is just an EOF. */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; return err; } break; } if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE && ti.is_constructed)) break; /* not a sequence, so we are ready with the set */ while (ti.length) { size_t n, nread; char dummy[256]; n = ti.length > DIM(dummy) ? DIM(dummy): ti.length; err = ksba_reader_read (cms->reader, dummy, n, &nread); if (err) return err; ti.length -= nread; } } } /* expect a SET OF signerInfo */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET && ti.is_constructed)) return gpg_error (GPG_ERR_INV_CMS_OBJ); si_tail = &cms->signer_info; while (ti.length) { size_t off1, off2; off1 = ksba_reader_tell (cms->reader); si = xtrycalloc (1, sizeof *si); if (!si) return gpg_error (GPG_ERR_ENOMEM); err = create_and_run_decoder (cms->reader, "CryptographicMessageSyntax.SignerInfo", 0, &si->root, &si->image, &si->imagelen); /* The signerInfo might be an empty set in the case of a certs-only signature. Thus we have to allow for EOF here */ if (gpg_err_code (err) == GPG_ERR_EOF) { xfree (si); err = 0; break; } if (err) { xfree (si); return err; } *si_tail = si; si_tail = &si->next; off2 = ksba_reader_tell (cms->reader); if ( (off2 - off1) > ti.length ) ti.length = 0; else ti.length -= off2 - off1; } return 0; } /* Parse the structure: EnvelopedData ::= SEQUENCE { version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }), originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, recipientInfos RecipientInfos, encryptedContentInfo EncryptedContentInfo, unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } OriginatorInfo ::= SEQUENCE { certs [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL } RecipientInfos ::= SET OF RecipientInfo EncryptedContentInfo ::= SEQUENCE { contentType ContentType, contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } EncryptedContent ::= OCTET STRING We stop parsing so that the next read will be the first byte of the encryptedContent or (if there is no content) the unprotectedAttrs. */ gpg_error_t _ksba_cms_parse_enveloped_data_part_1 (ksba_cms_t cms) { struct tag_info ti; gpg_error_t err; int env_data_ndef; unsigned long env_data_len; int encr_cont_ndef = 0; unsigned long encr_cont_len = 0; int has_content = 0; unsigned long off, len; char *cont_oid = NULL; char *algo_oid = NULL; char *algo_parm = NULL; size_t algo_parmlen = 0; struct value_tree_s *vt, **vtend; /* get the version */ err = parse_cms_version (cms->reader, &cms->cms_version, &env_data_len, &env_data_ndef); if (err) return err; /* read the next triplet which is either a [0] for originatorInfos or a SET_OF (recipientInfo) */ err = _ksba_ber_read_tl (cms->reader, &ti); if (err) return err; if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) { /* originatorInfo - but we skip it for now */ /* well, raise an error */ return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); } /* Next one is the SET OF RecipientInfo: * RecipientInfo ::= CHOICE { * ktri KeyTransRecipientInfo, * kari [1] KeyAgreeRecipientInfo, - * kekri [2] KEKRecipientInfo + * kekri [2] KEKRecipientInfo, + * pwri [3] PasswordRecipientInfo * } */ if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET && ti.is_constructed)) return gpg_error (GPG_ERR_INV_CMS_OBJ); vtend = &cms->recp_info; if (ti.ndef) { for (;;) { struct tag_info ti2; err = _ksba_ber_read_tl (cms->reader, &ti2); if (err) return err; if (!ti2.class && !ti2.tag) break; /* End tag found: ready. */ /* Not an end tag: Push it back and run the decoder. */ err = ksba_reader_unread (cms->reader, ti2.buf, ti2.nhdr); if (err) return err; vt = xtrycalloc (1, sizeof *vt); if (!vt) return gpg_error_from_syserror (); err = create_and_run_decoder (cms->reader, "CryptographicMessageSyntax.RecipientInfo", BER_DECODER_FLAG_FAST_STOP, &vt->root, &vt->image, &vt->imagelen); if (err) { xfree (vt); return err; } *vtend = vt; vtend = &vt->next; } } else { while (ti.length) { size_t off1, off2; off1 = ksba_reader_tell (cms->reader); vt = xtrycalloc (1, sizeof *vt); if (!vt) return gpg_error_from_syserror (); err = create_and_run_decoder (cms->reader, "CryptographicMessageSyntax.RecipientInfo", BER_DECODER_FLAG_FAST_STOP, &vt->root, &vt->image, &vt->imagelen); if (err) { xfree (vt); return err; } *vtend = vt; vtend = &vt->next; off2 = ksba_reader_tell (cms->reader); if ( (off2 - off1) > ti.length ) ti.length = 0; else ti.length -= off2 - off1; } } /* Now for the encryptedContentInfo */ off = ksba_reader_tell (cms->reader); err = parse_encrypted_content_info (cms->reader, &encr_cont_len, &encr_cont_ndef, &cont_oid, &algo_oid, &algo_parm, &algo_parmlen, &has_content); if (err) return err; cms->inner_cont_len = encr_cont_len; cms->inner_cont_ndef = encr_cont_ndef; cms->inner_cont_oid = cont_oid; cms->detached_data = !has_content; cms->encr_algo_oid = algo_oid; cms->encr_iv = algo_parm; algo_parm = NULL; cms->encr_ivlen = algo_parmlen; if (!env_data_ndef) { len = ksba_reader_tell (cms->reader) - off; if (env_data_len < len) return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */ env_data_len -= len; if (!encr_cont_ndef && env_data_len < encr_cont_len) return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */ } return 0; } /* handle the unprotected attributes */ gpg_error_t _ksba_cms_parse_enveloped_data_part_2 (ksba_cms_t cms) { (void)cms; /* FIXME */ return 0; } diff --git a/src/cms.asn b/src/cms.asn index f0f2b94..928e9b7 100644 --- a/src/cms.asn +++ b/src/cms.asn @@ -1,462 +1,472 @@ -- cms.asn - CryptographicMessageSyntax ASN.1 Module -- Copyright (C) 2001 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 either -- -- - the GNU Lesser General Public License as published by the Free -- Software Foundation; either version 3 of the License, or (at -- your option) any later version. -- -- or -- -- - the GNU General Public License as published by the Free -- Software Foundation; either version 2 of the License, or (at -- your option) any later version. -- -- or both in parallel, as here. -- -- 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 copies of the GNU General Public License -- and the GNU Lesser General Public License along with this program; -- if not, see . -- ----------------------------------------------------------------------- -- This module is based on the one given in appendix A of RFC2630 which -- exhibits this copyright notice: -- -- Copyright (C) The Internet Society (1999). All Rights Reserved. -- -- This document and translations of it may be copied and furnished to -- others, and derivative works that comment on or otherwise explain it -- or assist in its implementation may be prepared, copied, published -- and distributed, in whole or in part, without restriction of any -- kind, provided that the above copyright notice and this paragraph are -- included on all such copies and derivative works. However, this -- document itself may not be modified in any way, such as by removing -- the copyright notice or references to the Internet Society or other -- Internet organizations, except as needed for the purpose of -- developing Internet standards in which case the procedures for -- copyrights defined in the Internet Standards process must be -- followed, or as required to translate it into languages other than -- English. -- -- The limited permissions granted above are perpetual and will not be -- revoked by the Internet Society or its successors or assigns. -- -- This document and the information contained herein is provided on an -- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING -- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION -- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF -- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. ------------------------------------------------------------------------ CryptographicMessageSyntax { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) modules(0) cms(1) } DEFINITIONS IMPLICIT TAGS ::= BEGIN -- EXPORTS All -- The types and values defined in this module are exported for use in -- the other ASN.1 modules. Other applications may use them for their -- own purposes. -- Out parser does not support IMPORTS, instead we copy them verbatim -- at the end of this file --IMPORTS -- Directory Information Framework (X.501) -- Name -- FROM InformationFramework { joint-iso-itu-t ds(5) modules(1) -- informationFramework(1) 3 } -- Directory Authentication Framework (X.509) -- AlgorithmIdentifier, AttributeCertificate, Certificate, -- CertificateList, CertificateSerialNumber -- FROM AuthenticationFramework { joint-iso-itu-t ds(5) -- module(1) authenticationFramework(7) 3 } ; -- Cryptographic Message Syntax ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType } ContentType ::= OBJECT IDENTIFIER SignedData ::= SEQUENCE { version CMSVersion, digestAlgorithms DigestAlgorithmIdentifiers, encapContentInfo EncapsulatedContentInfo, certificates [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos } DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier SignerInfos ::= SET OF SignerInfo EncapsulatedContentInfo ::= SEQUENCE { eContentType ContentType, eContent [0] EXPLICIT OCTET STRING OPTIONAL } SignerInfo ::= SEQUENCE { version CMSVersion, sid SignerIdentifier, digestAlgorithm DigestAlgorithmIdentifier, signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, signatureAlgorithm SignatureAlgorithmIdentifier, signature SignatureValue, unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL } SignerIdentifier ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber, subjectKeyIdentifier [0] SubjectKeyIdentifier } SignedAttributes ::= SET SIZE (1..MAX) OF Attribute UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute Attribute ::= SEQUENCE { attrType OBJECT IDENTIFIER, attrValues SET OF AttributeValue } AttributeValue ::= ANY SignatureValue ::= OCTET STRING EnvelopedData ::= SEQUENCE { version CMSVersion, originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, recipientInfos RecipientInfos, encryptedContentInfo EncryptedContentInfo, unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } OriginatorInfo ::= SEQUENCE { certs [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL } RecipientInfos ::= SET OF RecipientInfo EncryptedContentInfo ::= SEQUENCE { contentType ContentType, contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } EncryptedContent ::= OCTET STRING UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute RecipientInfo ::= CHOICE { ktri KeyTransRecipientInfo, kari [1] KeyAgreeRecipientInfo, - kekri [2] KEKRecipientInfo } + kekri [2] KEKRecipientInfo, + pwri [3] PasswordRecipientInfo } EncryptedKey ::= OCTET STRING KeyTransRecipientInfo ::= SEQUENCE { version CMSVersion, -- always set to 0 or 2 rid RecipientIdentifier, keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, encryptedKey EncryptedKey } RecipientIdentifier ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber, subjectKeyIdentifier [0] SubjectKeyIdentifier } KeyAgreeRecipientInfo ::= SEQUENCE { version CMSVersion, -- always set to 3 originator [0] EXPLICIT OriginatorIdentifierOrKey, ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL, keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, recipientEncryptedKeys RecipientEncryptedKeys } OriginatorIdentifierOrKey ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber, subjectKeyIdentifier [0] SubjectKeyIdentifier, originatorKey [1] OriginatorPublicKey } OriginatorPublicKey ::= SEQUENCE { algorithm AlgorithmIdentifier, publicKey BIT STRING } RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey RecipientEncryptedKey ::= SEQUENCE { rid KeyAgreeRecipientIdentifier, encryptedKey EncryptedKey } KeyAgreeRecipientIdentifier ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber, rKeyId [0] IMPLICIT RecipientKeyIdentifier } RecipientKeyIdentifier ::= SEQUENCE { subjectKeyIdentifier SubjectKeyIdentifier, date GeneralizedTime OPTIONAL, other OtherKeyAttribute OPTIONAL } SubjectKeyIdentifier ::= OCTET STRING KEKRecipientInfo ::= SEQUENCE { version CMSVersion, -- always set to 4 kekid KEKIdentifier, keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, encryptedKey EncryptedKey } KEKIdentifier ::= SEQUENCE { keyIdentifier OCTET STRING, date GeneralizedTime OPTIONAL, other OtherKeyAttribute OPTIONAL } +PasswordRecipientInfo ::= SEQUENCE { + version CMSVersion, -- Always set to 0 + keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + DigestedData ::= SEQUENCE { version CMSVersion, digestAlgorithm DigestAlgorithmIdentifier, encapContentInfo EncapsulatedContentInfo, digest Digest } Digest ::= OCTET STRING EncryptedData ::= SEQUENCE { version CMSVersion, encryptedContentInfo EncryptedContentInfo, unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } AuthenticatedData ::= SEQUENCE { version CMSVersion, originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, recipientInfos RecipientInfos, macAlgorithm MessageAuthenticationCodeAlgorithm, digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL, encapContentInfo EncapsulatedContentInfo, authenticatedAttributes [2] IMPLICIT AuthAttributes OPTIONAL, mac MessageAuthenticationCode, unauthenticatedAttributes [3] IMPLICIT UnauthAttributes OPTIONAL } AuthAttributes ::= SET SIZE (1..MAX) OF Attribute UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute MessageAuthenticationCode ::= OCTET STRING DigestAlgorithmIdentifier ::= AlgorithmIdentifier SignatureAlgorithmIdentifier ::= AlgorithmIdentifier KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier +KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier + ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier CertificateRevocationLists ::= SET OF CertificateList CertificateChoices ::= CHOICE { certificate Certificate, -- See X.509 extendedCertificate [0] IMPLICIT ExtendedCertificate -- Obsolete -- FIXME attrCert [1] IMPLICIT AttributeCertificate -- See X.509 & X9.57 } CertificateSet ::= SET OF CertificateChoices IssuerAndSerialNumber ::= SEQUENCE { issuer Name, serialNumber CertificateSerialNumber } CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) } UserKeyingMaterial ::= OCTET STRING OtherKeyAttribute ::= SEQUENCE { keyAttrId OBJECT IDENTIFIER, keyAttr ANY DEFINED BY keyAttrId OPTIONAL } -- CMS Attributes MessageDigest ::= OCTET STRING SigningTime ::= Time Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } Countersignature ::= SignerInfo -- Algorithm Identifiers sha-1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithm(2) 26 } md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } id-dsa-with-sha1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) x9-57 (10040) x9cm(4) 3 } rsaEncryption OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 1 } dh-public-number OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 } id-alg-ESDH OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) alg(3) 5 } id-alg-CMS3DESwrap OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) alg(3) 6 } id-alg-CMSRC2wrap OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) alg(3) 7 } des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) encryptionAlgorithm(3) 7 } rc2-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) encryptionAlgorithm(3) 2 } hMAC-SHA1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) 8 1 2 } -- Algorithm Parameters KeyWrapAlgorithm ::= AlgorithmIdentifier RC2wrapParameter ::= RC2ParameterVersion RC2ParameterVersion ::= INTEGER CBCParameter ::= IV IV ::= OCTET STRING -- exactly 8 octets RC2CBCParameter ::= SEQUENCE { rc2ParameterVersion INTEGER, iv OCTET STRING } -- exactly 8 octets -- Content Type Object Identifiers id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 6 } id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 } id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 } id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 } id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 } id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 2 } -- Attribute Object Identifiers id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) 3 } id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) 4 } id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) 5 } id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) 6 } -- Obsolete Extended Certificate syntax from PKCS#6 ExtendedCertificate ::= SEQUENCE { extendedCertificateInfo ExtendedCertificateInfo, signatureAlgorithm SignatureAlgorithmIdentifier, signature Signature } ExtendedCertificateInfo ::= SEQUENCE { version CMSVersion, certificate Certificate, attributes UnauthAttributes } Signature ::= BIT STRING --******************************************* --*********** Included imports ************ --******************************************* -- Directory Information Framework (X.501) -- Name -- FROM InformationFramework { joint-iso-itu-t ds(5) modules(1) -- informationFramework(1) 3 } Name ::= CHOICE { rdnSequence RDNSequence } RDNSequence ::= SEQUENCE OF RelativeDistinguishedName RelativeDistinguishedName ::= SET OF AttributeTypeAndValue AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue } AttributeType ::= OBJECT IDENTIFIER AttributeValue ::= ANY -- Directory Authentication Framework (X.509) -- AlgorithmIdentifier, AttributeCertificate, Certificate, -- CertificateList, CertificateSerialNumber -- FROM AuthenticationFramework { joint-iso-itu-t ds(5) -- module(1) authenticationFramework(7) 3 } ; AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } -- Note: AttributeCertificate is not needed -- We handle a certificate by utilizing our ksba_cert_ functions Certificate ::= ANY -- Likewise CertificateList ::= ANY CertificateSerialNumber ::= INTEGER END -- of CryptographicMessageSyntax diff --git a/src/cms.c b/src/cms.c index 7f63311..17a74c8 100644 --- a/src/cms.c +++ b/src/cms.c @@ -1,3758 +1,3868 @@ /* cms.c - cryptographic message syntax main functions * Copyright (C) 2001, 2003, 2004, 2008, 2012, 2020 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 either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* References: * RFC-5652 := Cryptographic Message Syntax (CMS) (aka STD0070) * SPHINX := CMS profile developed by the German BSI. * (see also https://lwn.net/2001/1011/a/german-smime.php3) * PKCS#7 := Original specification of CMS */ #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" #include "der-builder.h" - +#include "stringbuf.h" 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 }, { "1.3.6.1.4.1.311.2.1.4", KSBA_CT_SPC_IND_DATA_CTX, ct_parse_data , ct_build_data }, { "1.3.6.1.4.1.11591.2.3.1", KSBA_CT_OPENPGP_KEYBLOCK, ct_parse_data , ct_build_data }, { NULL } }; static const 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 const char oidstr_messageDigest[] = "1.2.840.113549.1.9.4"; static const char oid_messageDigest[9] ="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x04"; static const char oidstr_signingTime[] = "1.2.840.113549.1.9.5"; static const char oid_signingTime[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x05"; static const char oidstr_smimeCapabilities[] = "1.2.840.113549.1.9.15"; #if 0 /* Set to 1 to use this debug helper. */ static void log_sexp (const char *text, ksba_const_sexp_t p) { int level = 0; gpgrt_log_debug ("%s: ", text); if (!p) gpgrt_log_printf ("[none]"); else { for (;;) { if (*p == '(') { gpgrt_log_printf ("%c", *p); p++; level++; } else if (*p == ')') { gpgrt_log_printf ("%c", *p); p++; if (--level <= 0 ) return; } else if (!digitp (p)) { gpgrt_log_printf ("[invalid s-exp]"); return; } else { char *endp; const unsigned char *s; unsigned long len, n; len = strtoul (p, &endp, 10); p = endp; if (*p != ':') { gpgrt_log_printf ("[invalid s-exp]"); return; } p++; for (s=p,n=0; n < len; n++, s++) if ( !((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '-' || *s == '.')) break; if (n < len) { gpgrt_log_printf ("#"); for (n=0; n < len; n++, p++) gpgrt_log_printf ("%02X", *p); gpgrt_log_printf ("#"); } else { for (n=0; n < len; n++, p++) gpgrt_log_printf ("%c", *p); } } } } gpgrt_log_printf ("\n"); } #endif /* debug helper */ /* Helper for read_and_hash_cont(). */ static gpg_error_t read_hash_block (ksba_cms_t cms, unsigned long nleft) { gpg_error_t err; char buffer[4096]; size_t n, nread; 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 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; 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; err = read_hash_block (cms, nleft); 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; err = read_hash_block (cms, nleft); 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 { /* This is basically the same as above but we allow for arbitrary types. Not sure whether it is really needed but right in the beginning of gnupg 1.9 we had at least one message with didn't used octet strings. Not ethat we don't do proper NLEFT checking but well why should we validate these things? Well, it might be nice to have such a feature but then we should write a more general mechanism to do that. */ 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; 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; err = read_hash_block (cms, nleft); 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 { err = read_hash_block (cms, nleft); 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 best effort. Because of the ubiquity of the stupid and insecure pkcs#12 format, the function will also identify those files and return KSBA_CT_PKCS12; there is and will be no other pkcs#12 support in this library. */ ksba_content_type_t ksba_cms_identify (ksba_reader_t reader) { struct tag_info ti; unsigned char buffer[24]; const unsigned char*p; size_t n, count; char *oid; int i; int maybe_p12 = 0; 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 a pkcs12 message we have this: 30 82 08 59 02 01 03 30 82 08 1F 06 09 2A 86 48 86 F7 0D 01 07 01 A0 82 ----------- ++++++++ ----------- ++++++++++++++++++++++++++++++++ SEQUENCE INTEGER SEQUENCE OID (data) This we need to read at least 22 bytes, we add 2 bytes to cope with length headers store with 4 bytes. */ 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_INTEGER && !ti.is_constructed && ti.length == 1 && n && *p == 3) { maybe_p12 = 1; p++; n--; 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; } ksba_free(oid); if (!content_handlers[i].oid) return KSBA_CT_NONE; /* unknown */ if (maybe_p12 && (content_handlers[i].ct == KSBA_CT_DATA || content_handlers[i].ct == KSBA_CT_SIGNED_DATA)) return KSBA_CT_PKCS12; 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->enc_val.ecdh.e); xfree (cms->cert_list->enc_val.ecdh.wrap_algo); xfree (cms->cert_list->enc_val.ecdh.encr_algo); 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); xfree (cms->signer_info); 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); xfree (cms->sig_val->ecc.r); xfree (cms->sig_val); cms->sig_val = tmp; } while (cms->capability_list) { struct oidparmlist_s *tmp = cms->capability_list->next; xfree (cms->capability_list->oid); xfree (cms->capability_list); cms->capability_list = 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, void *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 + * 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; root = si->root; image = si->image; } else if (cms->recp_info) { struct value_tree_s *tmp; 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 (cms->signer_info) { issuer_path = "SignerInfo.sid.issuerAndSerialNumber.issuer"; serial_path = "SignerInfo.sid.issuerAndSerialNumber.serialNumber"; } else if (cms->recp_info) { /* Find the choice to use. */ n = _ksba_asn_find_node (root, "RecipientInfo.+"); if (!n || !n->name) return gpg_error (GPG_ERR_NO_VALUE); if (!strcmp (n->name, "ktri")) { issuer_path = "ktri.rid.issuerAndSerialNumber.issuer"; serial_path = "ktri.rid.issuerAndSerialNumber.serialNumber"; } else if (!strcmp (n->name, "kari")) { issuer_path = ("kari..recipientEncryptedKeys" "..rid.issuerAndSerialNumber.issuer"); serial_path = ("kari..recipientEncryptedKeys" "..rid.issuerAndSerialNumber.serialNumber"); } else if (!strcmp (n->name, "kekri")) return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); + else if (!strcmp (n->name, "pwri")) + return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); else return gpg_error (GPG_ERR_INV_CMS_OBJ); root = n; } 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, n->type == TYPE_UTC_TIME, 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; } /* Helper to dump a S-expression. */ #if 0 static void dbg_print_sexp (ksba_const_sexp_t p) { int level = 0; if (!p) fputs ("[none]", stdout); else { for (;;) { if (*p == '(') { putchar (*p); p++; level++; } else if (*p == ')') { putchar (*p); p++; if (--level <= 0 ) { putchar ('\n'); return; } } else if (!digitp (p)) { fputs ("[invalid s-exp]\n", stdout); return; } else { const unsigned char *s; char *endp; unsigned long len, n; len = strtoul (p, &endp, 10); p = endp; if (*p != ':') { fputs ("[invalid s-exp]\n", stdout); return; } p++; for (s=p,n=0; n < len; n++, s++) if ( !((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '-' || *s == '.')) break; if (n < len) { putchar('#'); for (n=0; n < len; n++, p++) printf ("%02X", *p); putchar('#'); } else { for (n=0; n < len; n++, p++) putchar (*p); } } } } putchar ('\n'); } #endif /* 0 */ /** * 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 decryption * 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 root, n, n2; gpg_error_t err; - ksba_sexp_t string; + ksba_sexp_t string = NULL; struct value_tree_s *vt; char *keyencralgo = NULL; /* Key encryption algo. */ char *parm = NULL; /* Helper to get the parms of kencralgo. */ size_t parmlen; + char *parm2 = NULL; + size_t parm2len; + char *parm3 = NULL; + size_t parm3len; char *keywrapalgo = NULL; /* Key wrap algo. */ + char *keyderivealgo = NULL; /* Key derive algo. */ struct tag_info ti; const unsigned char *der; size_t derlen; 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 */ /* Find the choice to use. */ root = _ksba_asn_find_node (vt->root, "RecipientInfo.+"); if (!root || !root->name) return NULL; if (!strcmp (root->name, "ktri")) { n = _ksba_asn_find_node (root, "ktri.keyEncryptionAlgorithm"); if (!n || n->off == -1) 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); } else if (!strcmp (root->name, "kari")) { /* _ksba_asn_node_dump_all (root, stderr); */ /* Get the encrypted key. Result is in (DER,DERLEN) */ n = _ksba_asn_find_node (root, ("kari..recipientEncryptedKeys" "..encryptedKey")); if (!n || n->off == -1) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } der = vt->image + n->off; derlen = n->nhdr + n->len; err = parse_octet_string (&der, &derlen, &ti); if (err) goto leave; derlen = ti.length; /* gpgrt_log_printhex (der, derlen, "%s: encryptedKey", __func__); */ /* Get the KEK algos. */ n = _ksba_asn_find_node (root, "kari..keyEncryptionAlgorithm"); if (!n || n->off == -1) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } err = _ksba_parse_algorithm_identifier2 (vt->image + n->off, n->nhdr + n->len, NULL, &keyencralgo, &parm, &parmlen); if (err) goto leave; if (!parm) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } err = _ksba_parse_algorithm_identifier (parm, parmlen,NULL, &keywrapalgo); if (err) goto leave; /* gpgrt_log_debug ("%s: keyencralgo='%s'\n", __func__, keyencralgo); */ /* gpgrt_log_debug ("%s: keywrapalgo='%s'\n", __func__, keywrapalgo); */ /* Get the ephemeral public key. */ n = _ksba_asn_find_node (root, "kari..originator..originatorKey"); if (!n || n->off == -1) { err = gpg_error (GPG_ERR_INV_KEYINFO); goto leave; } err = _ksba_encval_kari_to_sexp (vt->image + n->off, n->nhdr + n->len, keyencralgo, keywrapalgo, der, derlen, &string); if (err) goto leave; /* gpgrt_log_debug ("%s: encryptedKey:\n", __func__); */ /* dbg_print_sexp (string); */ } else if (!strcmp (root->name, "kekri")) return NULL; /*GPG_ERR_UNSUPPORTED_CMS_OBJ*/ + else if (!strcmp (root->name, "pwri")) + { + /* _ksba_asn_node_dump_all (root, stderr); */ + + n = _ksba_asn_find_node (root, "pwri..keyEncryptionAlgorithm"); + if (!n || n->off == -1) + { + err = gpg_error (GPG_ERR_INV_KEYINFO); + goto leave; + } + err = _ksba_parse_algorithm_identifier2 (vt->image + n->off, + n->nhdr + n->len, NULL, + &keyencralgo, &parm, &parmlen); + if (err) + goto leave; + if (strcmp (keyencralgo, "1.2.840.113549.1.9.16.3.9")) + { + /* pwri requires this and only this OID. */ + err = gpg_error (GPG_ERR_INV_CMS_OBJ); + goto leave; + } + if (!parm) + { + err = gpg_error (GPG_ERR_INV_KEYINFO); + goto leave; + } + /* gpgrt_log_printhex (parm, parmlen, "parms"); */ + err = _ksba_parse_algorithm_identifier2 (parm, parmlen, NULL, + &keywrapalgo, &parm2, &parm2len); + if (err) + goto leave; + + /* gpgrt_log_debug ("%s: keywrapalgo='%s'\n", __func__, keywrapalgo); */ + /* gpgrt_log_printhex (parm2, parm2len, "parm:"); */ + + n = _ksba_asn_find_node (root, "pwri..keyDerivationAlgorithm"); + if (!n || n->off == -1) + { + /* Not found but that is okay becuase it is optional. */ + } + else + { + err = _ksba_parse_algorithm_identifier3 (vt->image + n->off, + n->nhdr + n->len, 0xa0, NULL, + &keyderivealgo, + &parm3, &parm3len, NULL); + if (err) + goto leave; + } + + n = _ksba_asn_find_node (root, "pwri..encryptedKey"); + if (!n || n->off == -1) + { + err = gpg_error (GPG_ERR_INV_KEYINFO); + goto leave; + } + der = vt->image + n->off; + derlen = n->nhdr + n->len; + err = parse_octet_string (&der, &derlen, &ti); + if (err) + goto leave; + derlen = ti.length; + /* gpgrt_log_printhex (der, derlen, "encryptedKey:"); */ + + /* Build the s-expression: + * (enc-val + * (pwri + * (derive-algo ) --| both are optional + * (derive-parm ) --| + * (encr-algo ) + * (encr-parm ) + * (encr-key ))) -- this is the encrypted session key + */ + { + struct stringbuf sb; + + init_stringbuf (&sb, 200); + put_stringbuf (&sb, "(7:enc-val(4:pwri"); + if (keyderivealgo && parm3) + { + put_stringbuf (&sb, "(11:derive-algo"); + put_stringbuf_sexp (&sb, keyderivealgo); + put_stringbuf (&sb, ")(11:derive-parm"); + put_stringbuf_mem_sexp (&sb, parm3, parm3len); + put_stringbuf (&sb, ")"); + } + put_stringbuf (&sb, "(9:encr-algo"); + put_stringbuf_sexp (&sb, keywrapalgo); + put_stringbuf (&sb, ")(9:encr-parm"); + put_stringbuf_mem_sexp (&sb, parm2, parm2len); + put_stringbuf (&sb, ")(8:encr-key"); + put_stringbuf_mem_sexp (&sb, der, derlen); + put_stringbuf (&sb, ")))"); + + string = get_stringbuf (&sb); + if (!string) + err = gpg_error_from_syserror (); + } + + } else return NULL; /*GPG_ERR_INV_CMS_OBJ*/ leave: xfree (keyencralgo); xfree (keywrapalgo); + xfree (keyderivealgo); xfree (parm); + xfree (parm2); + xfree (parm3); if (err) { /* gpgrt_log_debug ("%s: error: %s\n", __func__, gpg_strerror (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: Type 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: 0 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; } /* Add an S/MIME capability as an extended attribute to the message. This function is to be called for each capability in turn. The first capability added will receive the highest priority. CMS is the context, OID the object identifier of the capability and if DER is not NULL it is used as the DER-encoded parameters of the capability; the length of that DER object is given in DERLEN. DERLEN should be 0 if DER is NULL. The function returns 0 on success or an error code. */ gpg_error_t ksba_cms_add_smime_capability (ksba_cms_t cms, const char *oid, const unsigned char *der, size_t derlen) { gpg_error_t err; struct oidparmlist_s *opl, *opl2; if (!cms || !oid) return gpg_error (GPG_ERR_INV_VALUE); if (!der) derlen = 0; opl = xtrymalloc (sizeof *opl + derlen - 1); if (!opl) return gpg_error_from_errno (errno); opl->next = NULL; opl->oid = xtrystrdup (oid); if (!opl->oid) { err = gpg_error_from_errno (errno); xfree (opl); return err; } opl->parmlen = derlen; if (der) memcpy (opl->parm, der, derlen); /* Append it to maintain the desired order. */ if (!cms->capability_list) cms->capability_list = opl; else { for (opl2=cms->capability_list; opl2->next; opl2 = opl2->next) ; opl2->next = opl; } 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 unsigned 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 empty 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 passed time string. */ if (!*sigtime) _ksba_current_time (cl->signing_time); else _ksba_copy_time (cl->signing_time, sigtime); return 0; } /* Set the signature value as a canonical encoded s-expression. * * r_sig = (sig-val * ( * ( ) * ... * ( ) * )) * * must be given as a stringified OID or the special string * "rsa". For ECC must either be "ecdsa" or the OID matching the used * hash algorithm; the expected parameters are "r" and "s". * * 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) { gpg_error_t err; unsigned long n, namelen; struct sig_val_s *sv, **sv_tail; const unsigned char *s, *endp, *name; int ecc; /* True for ECC algos. */ 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 */ /* log_sexp ("sigval:", sigval); */ 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') { sv->algo = xtrystrdup ("1.2.840.113549.1.1.1"); /* rsa */ if (!sv->algo) { xfree (sv); return gpg_error (GPG_ERR_ENOMEM); } } else if (n==5 && !memcmp (s, "ecdsa", 5)) { /* Use a placeholder for later fixup. */ sv->algo = xtrystrdup ("ecdsa"); 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; ecc = (!strcmp (sv->algo, "ecdsa") /* placeholder */ || !strcmp (sv->algo, "1.2.840.10045.4.3.2") /* ecdsa-with-SHA256 */ || !strcmp (sv->algo, "1.2.840.10045.4.3.3") /* ecdsa-with-SHA384 */ || !strcmp (sv->algo, "1.2.840.10045.4.3.4") /* ecdsa-with-SHA512 */ ); xfree (sv->value); sv->value = NULL; xfree (sv->ecc.r); sv->ecc.r = NULL; while (*s == '(') { s++; n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } s++; name = s; namelen = n; s += n; if (!digitp(s)) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ goto leave; } n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } s++; if (namelen == 1 && *name == 's') { /* Store the "main" parameter into value. */ xfree (sv->value); sv->value = xtrymalloc (n); if (!sv->value) { err = gpg_error_from_syserror (); goto leave; } memcpy (sv->value, s, n); sv->valuelen = n; } else if (ecc && namelen == 1 && *name == 'r') { xfree (sv->ecc.r); sv->ecc.r = xtrymalloc (n); if (!sv->ecc.r) { err = gpg_error_from_syserror (); goto leave; } memcpy (sv->ecc.r, s, n); sv->ecc.rlen = n; } /* (We ignore all other parameter of the (key value) form.) */ s += n; if ( *s != ')') { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ goto leave; } s++; } /* Expect two closing parenthesis. */ if (*s != ')') { err = gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); goto leave; } s++; if ( *s != ')') { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } /* Check that we have all required data. */ if (!sv->value) { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } if (ecc && (!sv->ecc.r || !sv->ecc.rlen)) { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } *sv_tail = sv; return 0; /* Success. */ leave: /* Note: This is an error-only label. */ xfree (sv->value); xfree (sv->algo); xfree (sv->ecc.r); xfree (sv); return err; } /* 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 void *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 * ( * ( ) * ... * ( ) * (encr-algo ) * (wrap-algo ) * )) * * Note the must be given as a stringified OID or the special * string "rsa". For RSA there is just one parameter named "a"; * encr-algo and wrap-algo are also not used. For ECC must be * "ecdh", the parameter "s" gives the encrypted key, "e" specified * the ephemeral public key, and wrap-algo algo and encr-algo are the * stringified OIDs for the ECDH algorithm parameters. */ 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, *name; unsigned long n, namelen; int ecdh = 0; /* We expect ECC parameters. */ 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 cert to store the value. */ /* log_sexp ("encval", encval); */ 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 && !memcmp (s, "rsa", 3)) { /* 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 if (n==4 && !memcmp (s, "ecdh", 4)) { cl->enc_val.algo = xtrystrdup ("1.2.840.10045.2.1"); /* ecPublicKey */ 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; ecdh = !strcmp (cl->enc_val.algo, "1.2.840.10045.2.1"); xfree (cl->enc_val.value); cl->enc_val.value = NULL; xfree (cl->enc_val.ecdh.e); cl->enc_val.ecdh.e = NULL; xfree (cl->enc_val.ecdh.encr_algo); cl->enc_val.ecdh.encr_algo = NULL; xfree (cl->enc_val.ecdh.wrap_algo); cl->enc_val.ecdh.wrap_algo = NULL; while (*s == '(') { s++; n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; name = s; namelen = n; s += n; if (!digitp(s)) return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ n = strtoul (s, (char**)&endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; if (namelen == 1 && ((!ecdh && *name == 'a') || (ecdh && *name == 's'))) { /* Store the "main" parameter into value. */ 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; } else if (!ecdh) ; /* Ignore all other parameters for RSA. */ else if (namelen == 1 && *name == 'e') { xfree (cl->enc_val.ecdh.e); cl->enc_val.ecdh.e = xtrymalloc (n); if (!cl->enc_val.ecdh.e) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.ecdh.e, s, n); cl->enc_val.ecdh.elen = n; } else if (namelen == 9 && !memcmp (name, "encr-algo", 9)) { xfree (cl->enc_val.ecdh.encr_algo); cl->enc_val.ecdh.encr_algo = xtrymalloc (n+1); if (!cl->enc_val.ecdh.encr_algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.ecdh.encr_algo, s, n); cl->enc_val.ecdh.encr_algo[n] = 0; } else if (namelen == 9 && !memcmp (name, "wrap-algo", 9)) { xfree (cl->enc_val.ecdh.wrap_algo); cl->enc_val.ecdh.wrap_algo = xtrymalloc (n+1); if (!cl->enc_val.ecdh.wrap_algo) return gpg_error (GPG_ERR_ENOMEM); memcpy (cl->enc_val.ecdh.wrap_algo, s, n); cl->enc_val.ecdh.wrap_algo[n] = 0; } /* (We ignore all other parameter of the (key value) form.) */ s += n; if ( *s != ')') return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */ s++; } /* Expect two closing parenthesis. */ if (*s != ')') return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP); s++; if ( *s != ')') return gpg_error (GPG_ERR_INV_SEXP); /* Check that we have all required data. */ if (!cl->enc_val.value) return gpg_error (GPG_ERR_INV_SEXP); if (ecdh && (!cl->enc_val.ecdh.e || !cl->enc_val.ecdh.elen || !cl->enc_val.ecdh.encr_algo || !cl->enc_val.ecdh.wrap_algo)) 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) { (void)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) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_parse_encrypted_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } /* Content handlers for building messages */ static gpg_error_t ct_build_data (ksba_cms_t cms) { (void)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; } /* Store the sequence of capabilities at NODE */ static gpg_error_t store_smime_capability_sequence (AsnNode node, struct oidparmlist_s *capabilities) { gpg_error_t err; struct oidparmlist_s *cap, *cap2; 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 (cap=capabilities; cap; cap = cap->next) { /* (avoid writing duplicates) */ for (cap2=capabilities; cap2 != cap; cap2 = cap2->next) { if (!strcmp (cap->oid, cap2->oid) && cap->parmlen && cap->parmlen == cap2->parmlen && !memcmp (cap->parm, cap2->parm, cap->parmlen)) break; /* Duplicate found. */ } if (cap2 == cap) { /* RFC3851 requires that a missing parameter must not be encoded as NULL. This is in contrast to all other usages of the algorithm identifier where ist is allowed and in some profiles (e.g. tmttv2) even explicitly suggested to use NULL. */ err = _ksba_der_write_algorithm_identifier (tmpwrt, cap->oid, cap->parmlen?cap->parm:(const void*)"", cap->parmlen); if (err) { ksba_writer_release (tmpwrt); return err; } } } value = ksba_writer_snatch_mem (tmpwrt, &valuelen); if (!value) err = gpg_error (GPG_ERR_ENOMEM); if (!err) err = _ksba_der_store_sequence (node, value, valuelen); xfree (value); ksba_writer_release (tmpwrt); return err; } /* 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 = NULL; struct certlist_s *certlist; struct oidlist_s *digestlist; struct signer_info_s *si, **si_tail; AsnNode root = NULL; struct attrarray_s attrarray[4]; int attridx = 0; int i; memset (attrarray, 0, sizeof (attrarray)); /* 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. */ /* 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 */ 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; certlist = cms->cert_list; if (!certlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } digestlist = cms->digest_algos; if (!digestlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } si_tail = &cms->signer_info; for (signer=0; certlist; signer++, certlist = certlist->next, digestlist = digestlist->next) { AsnNode attr; AsnNode n; unsigned char *image; size_t imagelen; for (i = 0; i < attridx; i++) { _ksba_asn_release_nodes (attrarray[i].root); xfree (attrarray[i].image); } attridx = 0; memset (attrarray, 0, sizeof (attrarray)); if (!digestlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } if (!certlist->cert || !digestlist->oid) { err = gpg_error (GPG_ERR_BUG); goto leave; } /* Include the pretty important message digest. */ attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_messageDigest); if (err) goto leave; 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) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; 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) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_contentType); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = n->down; /* fixme: ugly hack */ err = _ksba_der_store_oid (n, cms->inner_cont_oid); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; 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"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_signingTime); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = n->down; /* fixme: ugly hack */ err = _ksba_der_store_time (n, certlist->signing_time); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; /* We will use the attributes again - so save them */ attrarray[attridx].root = attr; attrarray[attridx].image = image; attrarray[attridx].imagelen = imagelen; attridx++; } /* Include the S/MIME capabilities with the first signer. */ if (cms->capability_list && !signer) { attr = _ksba_asn_expand_tree (cms_tree->parse_tree, "CryptographicMessageSyntax.Attribute"); if (!attr) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = _ksba_asn_find_node (attr, "Attribute.attrType"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, oidstr_smimeCapabilities); if (err) goto leave; n = _ksba_asn_find_node (attr, "Attribute.attrValues"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } n = n->down; /* fixme: ugly hack */ err = store_smime_capability_sequence (n, cms->capability_list); if (err) goto leave; err = _ksba_der_encode_tree (attr, &image, &imagelen); if (err) goto leave; attrarray[attridx].root = attr; attrarray[attridx].image = image; attrarray[attridx].imagelen = imagelen; attridx++; } /* Arggh. That silly ASN.1 DER encoding rules: We need to sort the SET values. */ qsort (attrarray, attridx, 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) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } /* 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) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } assert (attridx <= DIM (attrarray)); for (i=0; i < attridx; i++) { if (i) { if ( !(n=_ksba_asn_insert_copy (n))) { err = gpg_error (GPG_ERR_ENOMEM); goto leave; } } err = _ksba_der_copy_tree (n, attrarray[i].root, attrarray[i].image); if (err) goto leave; _ksba_asn_release_nodes (attrarray[i].root); free (attrarray[i].image); attrarray[i].root = NULL; attrarray[i].image = NULL; } err = _ksba_der_encode_tree (root, &image, NULL); if (err) goto leave; si = xtrycalloc (1, sizeof *si); if (!si) return gpg_error (GPG_ERR_ENOMEM); si->root = root; root = NULL; si->image = image; /* Hmmm, we don't set the length of the image. */ *si_tail = si; si_tail = &si->next; } leave: _ksba_asn_release_nodes (root); ksba_asn_tree_release (cms_tree); for (i = 0; i < attridx; i++) { _ksba_asn_release_nodes (attrarray[i].root); xfree (attrarray[i].image); } return err; } /* 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 = NULL; struct certlist_s *certlist; struct oidlist_s *digestlist; struct signer_info_s *si; struct sig_val_s *sv; ksba_writer_t tmpwrt = NULL; AsnNode root = NULL; ksba_der_t dbld = NULL; /* Now we can really write the signer info */ err = ksba_asn_create_tree ("cms", &cms_tree); if (err) return err; certlist = cms->cert_list; if (!certlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ return err; } /* To construct the set we use a temporary writer object. */ err = ksba_writer_new (&tmpwrt); if (err) goto leave; err = ksba_writer_set_mem (tmpwrt, 2048); if (err) goto leave; 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 n, n2; unsigned char *image; size_t imagelen; const char *oid; if (!digestlist || !si || !sv) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } if (!certlist->cert || !digestlist->oid) { err = gpg_error (GPG_ERR_BUG); goto leave; } 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) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_integer (n, "\x00\x00\x00\x01\x01"); if (err) goto leave; /* Store the sid */ n = _ksba_asn_find_node (root, "SignerInfo.sid"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = set_issuer_serial (n, certlist->cert, 0); if (err) goto leave; /* store the digestAlgorithm */ n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.algorithm"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_oid (n, digestlist->oid); if (err) goto leave; n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.parameters"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_store_null (n); if (err) goto leave; /* and the signed attributes */ n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs"); if (!n || !n->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } assert (si->root); assert (si->image); n2 = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs"); if (!n2 || !n2->down) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } err = _ksba_der_copy_tree (n, n2, si->image); if (err) goto leave; image = NULL; /* store the signatureAlgorithm */ n = _ksba_asn_find_node (root, "SignerInfo.signatureAlgorithm.algorithm"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } if (!sv->algo) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } if (!strcmp (sv->algo, "ecdsa")) { /* Look at the digest algorithm and replace accordingly. */ if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.1")) oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-SHA256 */ else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.2")) oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-SHA384 */ else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.3")) oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-SHA512 */ else { err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } } else oid = sv->algo; err = _ksba_der_store_oid (n, oid); if (err) goto leave; n = _ksba_asn_find_node (root, "SignerInfo.signatureAlgorithm.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 signature */ if (!sv->value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } n = _ksba_asn_find_node (root, "SignerInfo.signature"); if (!n) { err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND); goto leave; } if (sv->ecc.r) /* ECDSA */ { unsigned char *tmpder; size_t tmpderlen; _ksba_der_release (dbld); dbld = _ksba_der_builder_new (0); if (!dbld) { err = gpg_error_from_syserror (); goto leave; } _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); _ksba_der_add_int (dbld, sv->ecc.r, sv->ecc.rlen, 1); _ksba_der_add_int (dbld, sv->value, sv->valuelen, 1); _ksba_der_add_end (dbld); err = _ksba_der_builder_get (dbld, &tmpder, &tmpderlen); if (err) goto leave; err = _ksba_der_store_octet_string (n, tmpder, tmpderlen); xfree (tmpder); if (err) goto leave; } else /* RSA */ { err = _ksba_der_store_octet_string (n, sv->value, sv->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); xfree (image); if (err) goto leave; } /* Write out the SET filled with all signer 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 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); leave: ksba_asn_tree_release (cms_tree); _ksba_asn_release_nodes (root); ksba_writer_release (tmpwrt); _ksba_der_release (dbld); 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; struct certlist_s *certlist; unsigned char *buf; const char *s; size_t len; ksba_der_t dbld = NULL; int any_ecdh = 0; /* See whether we have any ECDH recipients. */ for (certlist = cms->cert_list; certlist; certlist = certlist->next) if (certlist->enc_val.ecdh.e) { any_ecdh = 1; break; } /* 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 = any_ecdh? "\x02" :"\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 */ certlist = cms->cert_list; if (!certlist) { err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */ goto leave; } dbld = _ksba_der_builder_new (0); if (!dbld) { err = gpg_error_from_syserror (); goto leave; } _ksba_der_add_tag (dbld, 0, TYPE_SET); for (recpno=0; certlist; recpno++, certlist = certlist->next) { const unsigned char *der; size_t derlen; if (!certlist->cert) { err = gpg_error (GPG_ERR_BUG); goto leave; } if (!certlist->enc_val.ecdh.e) /* RSA (ktri) */ { _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* We store a version of 0 because we are only allowed to * use the issuerAndSerialNumber for SPHINX */ _ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "", 1); /* rid.issuerAndSerialNumber */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* rid.issuerAndSerialNumber.issuer */ err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); /* rid.issuerAndSerialNumber.serialNumber */ err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); _ksba_der_add_end (dbld); /* Store the keyEncryptionAlgorithm */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); if (!certlist->enc_val.algo || !certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } _ksba_der_add_oid (dbld, certlist->enc_val.algo); /* Now store NULL for the optional parameters. From Peter * Gutmann's X.509 style guide: * * Another pitfall to be aware of is that algorithms which * have no parameters have this specified as a NULL value * rather than omitting the parameters field entirely. The * reason for this is that when the 1988 syntax for * AlgorithmIdentifier was translated into the 1997 syntax, * the OPTIONAL associated with the AlgorithmIdentifier * parameters got lost. Later it was recovered via a defect * report, but by then everyone thought that algorithm * parameters were mandatory. Because of this the algorithm * parameters should be specified as NULL, regardless of what * you read elsewhere. * * The trouble is that things *never* get better, they just * stay the same, only more so * -- Terry Pratchett, "Eric" * * Although this is about signing, we always do it. Versions of * Libksba before 1.0.6 had a bug writing out the NULL tag here, * thus in reality we used to be correct according to the * standards despite we didn't intended so. */ _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0); _ksba_der_add_end (dbld); /* Store the encryptedKey */ if (!certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } _ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING, certlist->enc_val.value, certlist->enc_val.valuelen); } else /* ECDH */ { _ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* kari */ _ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "\x03", 1); _ksba_der_add_tag (dbld, CLASS_CONTEXT, 0); /* originator */ _ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* originatorKey */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* algorithm */ _ksba_der_add_oid (dbld, certlist->enc_val.algo); _ksba_der_add_end (dbld); _ksba_der_add_bts (dbld, certlist->enc_val.ecdh.e, certlist->enc_val.ecdh.elen, 0); _ksba_der_add_end (dbld); /* end originatorKey */ _ksba_der_add_end (dbld); /* end originator */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* keyEncrAlgo */ _ksba_der_add_oid (dbld, certlist->enc_val.ecdh.encr_algo); _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); _ksba_der_add_oid (dbld, certlist->enc_val.ecdh.wrap_algo); _ksba_der_add_end (dbld); _ksba_der_add_end (dbld); /* end keyEncrAlgo */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKeys */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKey */ /* rid.issuerAndSerialNumber */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen); if (err) goto leave; _ksba_der_add_der (dbld, der, derlen); _ksba_der_add_end (dbld); /* encryptedKey */ if (!certlist->enc_val.value) { err = gpg_error (GPG_ERR_MISSING_VALUE); goto leave; } _ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING, certlist->enc_val.value, certlist->enc_val.valuelen); _ksba_der_add_end (dbld); /* end recpEncrKey */ _ksba_der_add_end (dbld); /* end recpEncrKeys */ } _ksba_der_add_end (dbld); /* End SEQUENCE (ktri or kari) */ } _ksba_der_add_end (dbld); /* End SET */ /* Write out the SET filled with all recipient infos */ { unsigned char *image; size_t imagelen; err = _ksba_der_builder_get (dbld, &image, &imagelen); if (err) goto leave; err = ksba_writer_write (cms->writer, image, imagelen); xfree (image); 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_der_release (dbld); 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) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t ct_build_encrypted_data (ksba_cms_t cms) { (void)cms; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } diff --git a/src/keyinfo.c b/src/keyinfo.c index b97c3f1..d9e8f1c 100644 --- a/src/keyinfo.c +++ b/src/keyinfo.c @@ -1,1768 +1,1787 @@ /* keyinfo.c - Parse and build a keyInfo structure * Copyright (C) 2001, 2002, 2007, 2008, 2012, 2020 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 either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ /* Instead of using the ASN parser - which is easily possible - we use a simple handcoded one to speed up the operation and to make it more robust. */ #include #include #include #include #include #include "util.h" #include "asn1-func.h" #include "keyinfo.h" #include "shared.h" #include "convert.h" #include "ber-help.h" #include "sexp-parse.h" #include "stringbuf.h" #include "der-builder.h" /* Constants used for the public key algorithms. */ typedef enum { PKALGO_NONE, PKALGO_RSA, PKALGO_DSA, PKALGO_ECC, PKALGO_X25519, PKALGO_X448, PKALGO_ED25519, PKALGO_ED448 } pkalgo_t; struct algo_table_s { const char *oidstring; const unsigned char *oid; /* NULL indicattes end of table */ int oidlen; int supported; /* Values > 1 are also used to indicate hacks. */ pkalgo_t pkalgo; const char *algo_string; const char *elem_string; /* parameter names or '-', 'P' for plain ECDSA */ const char *ctrl_string; /* expected tag values (value > 127 are raw data)*/ const char *parmelem_string; /* parameter name or '-'. */ const char *parmctrl_string; /* expected tag values. */ const char *digest_string; /* The digest algo if included in the OID. */ }; /* Special values for the supported field. */ #define SUPPORTED_RSAPSS 2 static const struct algo_table_s pk_algo_table[] = { { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.7 */ "1.2.840.113549.1.1.7", /* RSAES-OAEP */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x07", 9, 0, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"}, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */ "1.2.840.113549.1.1.10", /* rsaPSS */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9, SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"}, { /* */ "2.5.8.1.1", /* rsa (ambiguous due to missing padding rules)*/ "\x55\x08\x01\x01", 4, 1, PKALGO_RSA, "ambiguous-rsa", "-ne", "\x30\x02\x02" }, { /* iso.member-body.us.x9-57.x9cm.1 */ "1.2.840.10040.4.1", /* dsa */ "\x2a\x86\x48\xce\x38\x04\x01", 7, 1, PKALGO_DSA, "dsa", "y", "\x02", "-pqg", "\x30\x02\x02\x02" }, { /* iso.member-body.us.ansi-x9-62.2.1 */ "1.2.840.10045.2.1", /* ecPublicKey */ "\x2a\x86\x48\xce\x3d\x02\x01", 7, 1, PKALGO_ECC, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.110 */ "1.3.101.110", /* X25519 */ "\x2b\x65\x6e", 3, 1, PKALGO_X25519, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.111 */ "1.3.101.111", /* X448 */ "\x2b\x65\x6f", 3, 1, PKALGO_X448, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.112 */ "1.3.101.112", /* Ed25519 */ "\x2b\x65\x70", 3, 1, PKALGO_ED25519, "ecc", "q", "\x80" }, { /* iso.identified-organization.thawte.113 */ "1.3.101.113", /* Ed448 */ "\x2b\x65\x71", 3, 1, PKALGO_ED448, "ecc", "q", "\x80" }, {NULL} }; static const struct algo_table_s sig_algo_table[] = { { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.5 */ "1.2.840.113549.1.1.5", /* sha1WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.4 */ "1.2.840.113549.1.1.4", /* md5WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x04", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md5" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.2 */ "1.2.840.113549.1.1.2", /* md2WithRSAEncryption */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x02", 9, 0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md2" }, { /* iso.member-body.us.x9-57.x9cm.1 */ "1.2.840.10040.4.3", /* dsa */ "\x2a\x86\x48\xce\x38\x04\x01", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02" }, { /* iso.member-body.us.x9-57.x9cm.3 */ "1.2.840.10040.4.3", /* dsaWithSha1 */ "\x2a\x86\x48\xce\x38\x04\x03", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" }, { /* Teletrust signature algorithm. */ "1.3.36.8.5.1.2.2", /* dsaWithRIPEMD160 */ "\x2b\x24\x08\x05\x01\x02\x02", 7, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "rmd160" }, { /* NIST Algorithm */ "2.16.840.1.101.3.4.3.1", /* dsaWithSha224 */ "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" }, { /* NIST Algorithm (the draft also used .1 but we better use .2) */ "2.16.840.1.101.3.4.3.2", /* dsaWithSha256 */ "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11, 1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha1 */ "1.2.840.10045.4.1", /* ecdsa */ "\x2a\x86\x48\xce\x3d\x04\x01", 7, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-specified */ "1.2.840.10045.4.3", "\x2a\x86\x48\xce\x3d\x04\x03", 7, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, NULL }, /* The digest algorithm is given by the parameter. */ { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha224 */ "1.2.840.10045.4.3.1", "\x2a\x86\x48\xce\x3d\x04\x03\x01", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha256 */ "1.2.840.10045.4.3.2", "\x2a\x86\x48\xce\x3d\x04\x03\x02", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha384 */ "1.2.840.10045.4.3.3", "\x2a\x86\x48\xce\x3d\x04\x03\x03", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha384" }, { /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha512 */ "1.2.840.10045.4.3.4", "\x2a\x86\x48\xce\x3d\x04\x03\x04", 8, 1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha512" }, { /* BSI TR-03111 bsiEcdsaWithSHA1 */ "0.4.0.127.0.7.1.1.4.1.1", "\x04\x00\x7f\x00\x07\x01\x01\x04\x01\x01", 10, 1, PKALGO_ECC, "ecdsa", "P", "", NULL, NULL, "sha1" }, { /* BSI TR-03111 bsiEcdsaWithSHA224 */ "0.4.0.127.0.7.1.1.4.1.2", "\x04\x00\x7f\x00\x07\x01\x01\x04\x01\x02", 10, 1, PKALGO_ECC, "ecdsa", "P", "", NULL, NULL, "sha224" }, { /* BSI TR-03111 bsiEcdsaWithSHA256 */ "0.4.0.127.0.7.1.1.4.1.3", "\x04\x00\x7f\x00\x07\x01\x01\x04\x01\x03", 10, 1, PKALGO_ECC, "ecdsa", "P", "", NULL, NULL, "sha256" }, { /* BSI TR-03111 bsiEcdsaWithSHA384 */ "0.4.0.127.0.7.1.1.4.1.4", "\x04\x00\x7f\x00\x07\x01\x01\x04\x01\x04", 10, 1, PKALGO_ECC, "ecdsa", "P", "", NULL, NULL, "sha384" }, { /* BSI TR-03111 bsiEcdsaWithSHA512 */ "0.4.0.127.0.7.1.1.4.1.5", "\x04\x00\x7f\x00\x07\x01\x01\x04\x01\x05", 10, 1, PKALGO_ECC, "ecdsa", "P", "", NULL, NULL, "sha512" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption used without hash algo*/ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "s", "\x82" }, { /* from NIST's OIW - actually belongs in a pure hash table */ "1.3.14.3.2.26", /* sha1 */ "\x2B\x0E\x03\x02\x1A", 5, 0, PKALGO_RSA, "sha-1", "", "", NULL, NULL, "sha1" }, { /* As used by telesec cards */ "1.3.36.3.3.1.2", /* rsaSignatureWithripemd160 */ "\x2b\x24\x03\x03\x01\x02", 6, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" }, { /* from NIST's OIW - used by TU Darmstadt */ "1.3.14.3.2.29", /* sha-1WithRSAEncryption */ "\x2B\x0E\x03\x02\x1D", 5, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.11", /* sha256WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha256" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.12", /* sha384WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0c", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha384" }, { /* from PKCS#1 */ "1.2.840.113549.1.1.13", /* sha512WithRSAEncryption */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0d", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha512" }, { /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */ "1.2.840.113549.1.1.10", /* rsaPSS */ "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9, SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, NULL}, { /* TeleTrust signature scheme with RSA signature and DSI according to ISO/IEC 9796-2 with random number and RIPEMD-160. I am not sure for what this is good; thus disabled. */ "1.3.36.3.4.3.2.2", /* sigS_ISO9796-2rndWithrsa_ripemd160 */ "\x2B\x24\x03\x04\x03\x02\x02", 7, 0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" }, { /* iso.identified-organization.thawte.112 */ "1.3.101.112", /* Ed25519 */ "\x2b\x65\x70", 3, 1, PKALGO_ED25519, "eddsa", "", "", NULL, NULL, NULL }, { /* iso.identified-organization.thawte.113 */ "1.3.101.113", /* Ed448 */ "\x2b\x65\x71", 3, 1, PKALGO_ED448, "eddsa", "", "", NULL, NULL, NULL }, {NULL} }; static const struct algo_table_s enc_algo_table[] = { {/* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */ "1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "a", "\x82" }, {/* iso.member-body.us.ansi-x9-62.2.1 */ "1.2.840.10045.2.1", /* ecPublicKey */ "\x2a\x86\x48\xce\x3d\x02\x01", 7, 1, PKALGO_ECC, "ecdh", "e", "\x80" }, {NULL} }; /* This tables maps names of ECC curves names to OIDs. A similar table is used by Libgcrypt. */ static const struct { const char *oid; const char *name; unsigned char pkalgo; /* If not 0 force the use of ALGO. */ } curve_names[] = { { "1.3.101.112", "Ed25519", PKALGO_ED25519}, { "1.3.101.110", "Curve25519", PKALGO_X25519}, { "1.3.101.110", "X25519", PKALGO_X25519}, { "1.3.101.113", "Ed448", PKALGO_ED448 }, { "1.3.101.111", "X448", PKALGO_X448 }, { "1.2.840.10045.3.1.1", "NIST P-192" }, { "1.2.840.10045.3.1.1", "nistp192" }, { "1.2.840.10045.3.1.1", "prime192v1" }, { "1.2.840.10045.3.1.1", "secp192r1" }, { "1.3.132.0.33", "NIST P-224" }, { "1.3.132.0.33", "nistp224" }, { "1.3.132.0.33", "secp224r1" }, { "1.2.840.10045.3.1.7", "NIST P-256" }, { "1.2.840.10045.3.1.7", "nistp256" }, { "1.2.840.10045.3.1.7", "prime256v1" }, { "1.2.840.10045.3.1.7", "secp256r1" }, { "1.3.132.0.34", "NIST P-384" }, { "1.3.132.0.34", "nistp384" }, { "1.3.132.0.34", "secp384r1" }, { "1.3.132.0.35", "NIST P-521" }, { "1.3.132.0.35", "nistp521" }, { "1.3.132.0.35", "secp521r1" }, { "1.3.36.3.3.2.8.1.1.1" , "brainpoolP160r1" }, { "1.3.36.3.3.2.8.1.1.3" , "brainpoolP192r1" }, { "1.3.36.3.3.2.8.1.1.5" , "brainpoolP224r1" }, { "1.3.36.3.3.2.8.1.1.7" , "brainpoolP256r1" }, { "1.3.36.3.3.2.8.1.1.9" , "brainpoolP320r1" }, { "1.3.36.3.3.2.8.1.1.11", "brainpoolP384r1" }, { "1.3.36.3.3.2.8.1.1.13", "brainpoolP512r1" }, { "1.2.643.2.2.35.1", "GOST2001-CryptoPro-A" }, { "1.2.643.2.2.35.2", "GOST2001-CryptoPro-B" }, { "1.2.643.2.2.35.3", "GOST2001-CryptoPro-C" }, { "1.2.643.7.1.2.1.2.1", "GOST2012-tc26-A" }, { "1.2.643.7.1.2.1.2.2", "GOST2012-tc26-B" }, { "1.3.132.0.10", "secp256k1" }, { NULL, NULL} }; /* Table to map well known curve parameters to their name. */ static const struct { const char *name; unsigned int derlen; const unsigned char *der; } ecdomainparm_to_name[] = { { "brainpoolP256r1", 227, "\x30\x81\xe0\x02\x01\x01\x30\x2c\x06\x07\x2a\x86\x48\xce\x3d\x01" "\x01\x02\x21\x00\xa9\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90" "\x9d\x83\x8d\x72\x6e\x3b\xf6\x23\xd5\x26\x20\x28\x20\x13\x48\x1d" "\x1f\x6e\x53\x77\x30\x44\x04\x20\x7d\x5a\x09\x75\xfc\x2c\x30\x57" "\xee\xf6\x75\x30\x41\x7a\xff\xe7\xfb\x80\x55\xc1\x26\xdc\x5c\x6c" "\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\x04\x20\x26\xdc\x5c\x6c\xe9\x4a" "\x4b\x44\xf3\x30\xb5\xd9\xbb\xd7\x7c\xbf\x95\x84\x16\x29\x5c\xf7" "\xe1\xce\x6b\xcc\xdc\x18\xff\x8c\x07\xb6\x04\x41\x04\x8b\xd2\xae" "\xb9\xcb\x7e\x57\xcb\x2c\x4b\x48\x2f\xfc\x81\xb7\xaf\xb9\xde\x27" "\xe1\xe3\xbd\x23\xc2\x3a\x44\x53\xbd\x9a\xce\x32\x62\x54\x7e\xf8" "\x35\xc3\xda\xc4\xfd\x97\xf8\x46\x1a\x14\x61\x1d\xc9\xc2\x77\x45" "\x13\x2d\xed\x8e\x54\x5c\x1d\x54\xc7\x2f\x04\x69\x97\x02\x21\x00" "\xa9\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90\x9d\x83\x8d\x71" "\x8c\x39\x7a\xa3\xb5\x61\xa6\xf7\x90\x1e\x0e\x82\x97\x48\x56\xa7" "\x02\x01\x01" }, { "brainpoolP384r1", 324, "\x30\x82\x01\x40\x02\x01\x01\x30\x3c\x06\x07\x2a\x86\x48\xce\x3d" "\x01\x01\x02\x31\x00\x8c\xb9\x1e\x82\xa3\x38\x6d\x28\x0f\x5d\x6f" "\x7e\x50\xe6\x41\xdf\x15\x2f\x71\x09\xed\x54\x56\xb4\x12\xb1\xda" "\x19\x7f\xb7\x11\x23\xac\xd3\xa7\x29\x90\x1d\x1a\x71\x87\x47\x00" "\x13\x31\x07\xec\x53\x30\x64\x04\x30\x7b\xc3\x82\xc6\x3d\x8c\x15" "\x0c\x3c\x72\x08\x0a\xce\x05\xaf\xa0\xc2\xbe\xa2\x8e\x4f\xb2\x27" "\x87\x13\x91\x65\xef\xba\x91\xf9\x0f\x8a\xa5\x81\x4a\x50\x3a\xd4" "\xeb\x04\xa8\xc7\xdd\x22\xce\x28\x26\x04\x30\x04\xa8\xc7\xdd\x22" "\xce\x28\x26\x8b\x39\xb5\x54\x16\xf0\x44\x7c\x2f\xb7\x7d\xe1\x07" "\xdc\xd2\xa6\x2e\x88\x0e\xa5\x3e\xeb\x62\xd5\x7c\xb4\x39\x02\x95" "\xdb\xc9\x94\x3a\xb7\x86\x96\xfa\x50\x4c\x11\x04\x61\x04\x1d\x1c" "\x64\xf0\x68\xcf\x45\xff\xa2\xa6\x3a\x81\xb7\xc1\x3f\x6b\x88\x47" "\xa3\xe7\x7e\xf1\x4f\xe3\xdb\x7f\xca\xfe\x0c\xbd\x10\xe8\xe8\x26" "\xe0\x34\x36\xd6\x46\xaa\xef\x87\xb2\xe2\x47\xd4\xaf\x1e\x8a\xbe" "\x1d\x75\x20\xf9\xc2\xa4\x5c\xb1\xeb\x8e\x95\xcf\xd5\x52\x62\xb7" "\x0b\x29\xfe\xec\x58\x64\xe1\x9c\x05\x4f\xf9\x91\x29\x28\x0e\x46" "\x46\x21\x77\x91\x81\x11\x42\x82\x03\x41\x26\x3c\x53\x15\x02\x31" "\x00\x8c\xb9\x1e\x82\xa3\x38\x6d\x28\x0f\x5d\x6f\x7e\x50\xe6\x41" "\xdf\x15\x2f\x71\x09\xed\x54\x56\xb3\x1f\x16\x6e\x6c\xac\x04\x25" "\xa7\xcf\x3a\xb6\xaf\x6b\x7f\xc3\x10\x3b\x88\x32\x02\xe9\x04\x65" "\x65\x02\x01\x01" }, { "brainpoolP512r1", 422, "\x30\x82\x01\xa2\x02\x01\x01\x30\x4c\x06\x07\x2a\x86\x48\xce\x3d" "\x01\x01\x02\x41\x00\xaa\xdd\x9d\xb8\xdb\xe9\xc4\x8b\x3f\xd4\xe6" "\xae\x33\xc9\xfc\x07\xcb\x30\x8d\xb3\xb3\xc9\xd2\x0e\xd6\x63\x9c" "\xca\x70\x33\x08\x71\x7d\x4d\x9b\x00\x9b\xc6\x68\x42\xae\xcd\xa1" "\x2a\xe6\xa3\x80\xe6\x28\x81\xff\x2f\x2d\x82\xc6\x85\x28\xaa\x60" "\x56\x58\x3a\x48\xf3\x30\x81\x84\x04\x40\x78\x30\xa3\x31\x8b\x60" "\x3b\x89\xe2\x32\x71\x45\xac\x23\x4c\xc5\x94\xcb\xdd\x8d\x3d\xf9" "\x16\x10\xa8\x34\x41\xca\xea\x98\x63\xbc\x2d\xed\x5d\x5a\xa8\x25" "\x3a\xa1\x0a\x2e\xf1\xc9\x8b\x9a\xc8\xb5\x7f\x11\x17\xa7\x2b\xf2" "\xc7\xb9\xe7\xc1\xac\x4d\x77\xfc\x94\xca\x04\x40\x3d\xf9\x16\x10" "\xa8\x34\x41\xca\xea\x98\x63\xbc\x2d\xed\x5d\x5a\xa8\x25\x3a\xa1" "\x0a\x2e\xf1\xc9\x8b\x9a\xc8\xb5\x7f\x11\x17\xa7\x2b\xf2\xc7\xb9" "\xe7\xc1\xac\x4d\x77\xfc\x94\xca\xdc\x08\x3e\x67\x98\x40\x50\xb7" "\x5e\xba\xe5\xdd\x28\x09\xbd\x63\x80\x16\xf7\x23\x04\x81\x81\x04" "\x81\xae\xe4\xbd\xd8\x2e\xd9\x64\x5a\x21\x32\x2e\x9c\x4c\x6a\x93" "\x85\xed\x9f\x70\xb5\xd9\x16\xc1\xb4\x3b\x62\xee\xf4\xd0\x09\x8e" "\xff\x3b\x1f\x78\xe2\xd0\xd4\x8d\x50\xd1\x68\x7b\x93\xb9\x7d\x5f" "\x7c\x6d\x50\x47\x40\x6a\x5e\x68\x8b\x35\x22\x09\xbc\xb9\xf8\x22" "\x7d\xde\x38\x5d\x56\x63\x32\xec\xc0\xea\xbf\xa9\xcf\x78\x22\xfd" "\xf2\x09\xf7\x00\x24\xa5\x7b\x1a\xa0\x00\xc5\x5b\x88\x1f\x81\x11" "\xb2\xdc\xde\x49\x4a\x5f\x48\x5e\x5b\xca\x4b\xd8\x8a\x27\x63\xae" "\xd1\xca\x2b\x2f\xa8\xf0\x54\x06\x78\xcd\x1e\x0f\x3a\xd8\x08\x92" "\x02\x41\x00\xaa\xdd\x9d\xb8\xdb\xe9\xc4\x8b\x3f\xd4\xe6\xae\x33" "\xc9\xfc\x07\xcb\x30\x8d\xb3\xb3\xc9\xd2\x0e\xd6\x63\x9c\xca\x70" "\x33\x08\x70\x55\x3e\x5c\x41\x4c\xa9\x26\x19\x41\x86\x61\x19\x7f" "\xac\x10\x47\x1d\xb1\xd3\x81\x08\x5d\xda\xdd\xb5\x87\x96\x82\x9c" "\xa9\x00\x69\x02\x01\x01" }, { NULL } }; #define TLV_LENGTH(prefix) do { \ if (!prefix ## len) \ return gpg_error (GPG_ERR_INV_KEYINFO); \ c = *(prefix)++; prefix ## len--; \ if (c == 0x80) \ return gpg_error (GPG_ERR_NOT_DER_ENCODED); \ if (c == 0xff) \ return gpg_error (GPG_ERR_BAD_BER); \ \ if ( !(c & 0x80) ) \ len = c; \ else \ { \ int count = c & 0x7f; \ \ for (len=0; count; count--) \ { \ len <<= 8; \ if (!prefix ## len) \ return gpg_error (GPG_ERR_BAD_BER);\ c = *(prefix)++; prefix ## len--; \ len |= c & 0xff; \ } \ } \ if (len > prefix ## len) \ return gpg_error (GPG_ERR_INV_KEYINFO); \ } while (0) /* Given a string BUF of length BUFLEN with either a curve name or its * OID in dotted form return a string in dotted form of the name. The * caller must free the result. On error NULL is returned. If a * curve requires the use of a certain algorithm, that algorithm is * stored at R_PKALGO. */ static char * get_ecc_curve_oid (const unsigned char *buf, size_t buflen, pkalgo_t *r_pkalgo) { unsigned char *result; int i, find_pkalgo; /* Skip an optional "oid." prefix. */ if (buflen > 4 && buf[3] == '.' && digitp (buf+4) && ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd') ||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D'))) { buf += 4; buflen -= 4; } /* If it does not look like an OID - map it through the table. */ if (buflen && !digitp (buf)) { for (i=0; curve_names[i].oid; i++) if (buflen == strlen (curve_names[i].name) && !memcmp (buf, curve_names[i].name, buflen)) break; if (!curve_names[i].oid) return NULL; /* Not found. */ buf = curve_names[i].oid; buflen = strlen (curve_names[i].oid); *r_pkalgo = curve_names[i].pkalgo; find_pkalgo = 0; } else find_pkalgo = 1; result = xtrymalloc (buflen + 1); if (!result) return NULL; /* Ooops */ memcpy (result, buf, buflen); result[buflen] = 0; if (find_pkalgo) { /* We still need to check whether the OID requires a certain ALGO. */ for (i=0; curve_names[i].oid; i++) if (!strcmp (curve_names[i].oid, result)) { *r_pkalgo = curve_names[i].pkalgo; break; } } return result; } /* Return the OFF and the LEN of algorithm within DER. Do some checks and return the number of bytes read in r_nread, adding this to der does point into the BIT STRING. mode 0: just get the algorithm identifier. FIXME: should be able to handle BER Encoding. mode 1: as described. */ static gpg_error_t -get_algorithm (int mode, const unsigned char *der, size_t derlen, +get_algorithm (int mode, const unsigned char *der, size_t derlen, int firsttag, size_t *r_nread, size_t *r_pos, size_t *r_len, int *r_bitstr, size_t *r_parm_pos, size_t *r_parm_len, int *r_parm_type) { int c; const unsigned char *start = der; const unsigned char *startseq; unsigned long seqlen, len; *r_bitstr = 0; if (r_parm_pos) *r_parm_pos = 0; if (r_parm_len) *r_parm_len = 0; if (r_parm_type) *r_parm_type = 0; /* get the inner sequence */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; - if ( c != 0x30 ) - return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */ + if ( c != firsttag ) + return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE or whatever */ TLV_LENGTH(der); seqlen = len; startseq = der; /* get the object identifier */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x06 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not an OBJECT IDENTIFIER */ TLV_LENGTH(der); /* der does now point to an oid of length LEN */ *r_pos = der - start; *r_len = len; der += len; derlen -= len; seqlen -= der - startseq;; /* Parse the parameter. */ if (seqlen) { const unsigned char *startparm = der; if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c == 0x05 ) { /* gpgrt_log_debug ("%s: parameter: NULL \n", __func__); */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c) return gpg_error (GPG_ERR_BAD_BER); /* NULL must have a length of 0 */ seqlen -= 2; } else if (r_parm_pos && r_parm_len && c == 0x04) { /* This is an octet string parameter and we need it. */ if (r_parm_type) *r_parm_type = TYPE_OCTET_STRING; TLV_LENGTH(der); *r_parm_pos = der - start; *r_parm_len = len; seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else if (r_parm_pos && r_parm_len && c == 0x06) { /* This is an object identifier. */ if (r_parm_type) *r_parm_type = TYPE_OBJECT_ID; TLV_LENGTH(der); *r_parm_pos = der - start; *r_parm_len = len; seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else if (r_parm_pos && r_parm_len && c == 0x30) { /* This is a sequence. */ if (r_parm_type) *r_parm_type = TYPE_SEQUENCE; TLV_LENGTH(der); *r_parm_pos = startparm - start; *r_parm_len = len + (der - startparm); seqlen -= der - startparm; der += len; derlen -= len; seqlen -= len; } else { /* printf ("parameter: with tag %02x - ignored\n", c); */ TLV_LENGTH(der); seqlen -= der - startparm; /* skip the value */ der += len; derlen -= len; seqlen -= len; } } if (seqlen) return gpg_error (GPG_ERR_INV_KEYINFO); if (mode) { /* move forward to the BIT_STR */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c == 0x03) *r_bitstr = 1; /* BIT STRING */ else if (c == 0x04) ; /* OCTECT STRING */ else return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a BIT STRING */ TLV_LENGTH(der); } *r_nread = der - start; return 0; } gpg_error_t _ksba_parse_algorithm_identifier (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid) { - return _ksba_parse_algorithm_identifier2 (der, derlen, - r_nread, r_oid, NULL, NULL); + return _ksba_parse_algorithm_identifier3 (der, derlen, 0x30, + r_nread, r_oid, NULL, NULL, NULL); } -/* Note that R_NREAD, R_PARM, and R_PARMLEN are optional. */ gpg_error_t _ksba_parse_algorithm_identifier2 (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid, char **r_parm, size_t *r_parmlen) +{ + return _ksba_parse_algorithm_identifier3 (der, derlen, 0x30, + r_nread, r_oid, + r_parm, r_parmlen, NULL); +} + + +/* Note that R_NREAD, R_PARM, and R_PARMLEN are optional. */ +gpg_error_t +_ksba_parse_algorithm_identifier3 (const unsigned char *der, size_t derlen, + int firsttag, + size_t *r_nread, char **r_oid, + char **r_parm, size_t *r_parmlen, + int *r_parmtype) { gpg_error_t err; int is_bitstr; size_t nread, off, len, off2, len2; int parm_type; /* fixme: get_algorithm might return the error invalid keyinfo - this should be invalid algorithm identifier */ *r_oid = NULL; if (r_nread) *r_nread = 0; off2 = len2 = 0; - err = get_algorithm (0, der, derlen, &nread, &off, &len, &is_bitstr, + err = get_algorithm (0, der, derlen, firsttag, + &nread, &off, &len, &is_bitstr, &off2, &len2, &parm_type); if (err) return err; if (r_nread) *r_nread = nread; *r_oid = ksba_oid_to_str (der+off, len); if (!*r_oid) return gpg_error (GPG_ERR_ENOMEM); /* Special hack for ecdsaWithSpecified. We replace the returned OID by the one in the parameter. */ - if (off2 && len2 && parm_type == TYPE_SEQUENCE + if (off2 && len2 && parm_type == TYPE_SEQUENCE && firsttag == 0x30 && !strcmp (*r_oid, "1.2.840.10045.4.3")) { xfree (*r_oid); *r_oid = NULL; - err = get_algorithm (0, der+off2, len2, &nread, &off, &len, &is_bitstr, + err = get_algorithm (0, der+off2, len2, 0x30, + &nread, &off, &len, &is_bitstr, NULL, NULL, NULL); if (err) { if (r_nread) *r_nread = 0; return err; } *r_oid = ksba_oid_to_str (der+off2+off, len); if (!*r_oid) { if (r_nread) *r_nread = 0; return gpg_error (GPG_ERR_ENOMEM); } off2 = len2 = 0; /* So that R_PARM is set to NULL. */ } if (r_parm && r_parmlen) { if (off2 && len2) { *r_parm = xtrymalloc (len2); if (!*r_parm) { xfree (*r_oid); *r_oid = NULL; return gpg_error (GPG_ERR_ENOMEM); } memcpy (*r_parm, der+off2, len2); *r_parmlen = len2; } else { *r_parm = NULL; *r_parmlen = 0; } } + if (r_parmtype) + *r_parmtype = parm_type; + return 0; } - /* Assume that DER is a buffer of length DERLEN with a DER encoded ASN.1 structure like this: keyInfo ::= SEQUENCE { SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } publicKey BIT STRING } The function parses this structure and create a SEXP suitable to be used as a public key in Libgcrypt. The S-Exp will be returned in a string which the caller must free. We don't pass an ASN.1 node here but a plain memory block. */ gpg_error_t _ksba_keyinfo_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { gpg_error_t err; int c, i; size_t nread, off, len, parm_off, parm_len; int parm_type; char *parm_oid = NULL; int algoidx; int is_bitstr; int got_curve = 0; const unsigned char *parmder = NULL; size_t parmderlen = 0; const unsigned char *ctrl; const char *elem; struct stringbuf sb; *r_string = NULL; /* check the outer sequence */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != 0x30 ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */ TLV_LENGTH(der); /* and now the inner part */ - err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr, + err = get_algorithm (1, der, derlen, 0x30, + &nread, &off, &len, &is_bitstr, &parm_off, &parm_len, &parm_type); if (err) return err; /* look into our table of supported algorithms */ for (algoidx=0; pk_algo_table[algoidx].oid; algoidx++) { if ( len == pk_algo_table[algoidx].oidlen && !memcmp (der+off, pk_algo_table[algoidx].oid, len)) break; } if (!pk_algo_table[algoidx].oid) return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); if (!pk_algo_table[algoidx].supported) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); if (parm_off && parm_len && parm_type == TYPE_OBJECT_ID) parm_oid = ksba_oid_to_str (der+parm_off, parm_len); else if (parm_off && parm_len) { parmder = der + parm_off; parmderlen = parm_len; } der += nread; derlen -= nread; if (is_bitstr) { /* Funny: X.509 defines the signature value as a bit string but CMS as an octet string - for ease of implementation we always allow both */ if (!derlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *der++; derlen--; if (c) fprintf (stderr, "warning: number of unused bits is not zero\n"); } /* fixme: we should calculate the initial length form the size of the sequence, so that we don't need a realloc later */ init_stringbuf (&sb, 100); put_stringbuf (&sb, "(10:public-key("); /* fixme: we can also use the oidstring here and prefix it with "oid." - this way we can pass more information into Libgcrypt or whatever library is used */ put_stringbuf_sexp (&sb, pk_algo_table[algoidx].algo_string); /* Insert the curve name for ECC. */ if (pk_algo_table[algoidx].pkalgo == PKALGO_ECC && parm_oid) { put_stringbuf (&sb, "("); put_stringbuf_sexp (&sb, "curve"); put_stringbuf_sexp (&sb, parm_oid); put_stringbuf (&sb, ")"); got_curve = 1; } else if (pk_algo_table[algoidx].pkalgo == PKALGO_ED25519 || pk_algo_table[algoidx].pkalgo == PKALGO_ED448 || pk_algo_table[algoidx].pkalgo == PKALGO_X25519 || pk_algo_table[algoidx].pkalgo == PKALGO_X448) { put_stringbuf (&sb, "("); put_stringbuf_sexp (&sb, "curve"); put_stringbuf_sexp (&sb, pk_algo_table[algoidx].oidstring); put_stringbuf (&sb, ")"); } /* If parameters are given and we have a description for them, parse them. */ if (parmder && parmderlen && pk_algo_table[algoidx].parmelem_string && pk_algo_table[algoidx].parmctrl_string) { elem = pk_algo_table[algoidx].parmelem_string; ctrl = pk_algo_table[algoidx].parmctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow reading a raw value. */ is_int = 1; len = parmderlen; } else { if (!parmderlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *parmder++; parmderlen--; if ( c != *ctrl ) { xfree (parm_oid); return gpg_error (GPG_ERR_UNEXPECTED_TAG); } is_int = c == 0x02; TLV_LENGTH (parmder); } if (is_int && *elem != '-') /* Take this integer. */ { char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, parmder, len); parmder += len; parmderlen -= len; put_stringbuf (&sb, ")"); } } } else if (!got_curve && parmder && parmderlen && pk_algo_table[algoidx].pkalgo == PKALGO_ECC) { /* This is ecPublicKey but has no named curve. This is not * allowed for PKIX but we try to figure the curve name out for * some well known curves by a simple parameter match. */ for (i=0; ecdomainparm_to_name[i].name; i++) if (ecdomainparm_to_name[i].derlen == parmderlen && !memcmp (ecdomainparm_to_name[i].der, parmder, parmderlen)) { put_stringbuf (&sb, "("); put_stringbuf_sexp (&sb, "curve"); put_stringbuf_sexp (&sb, ecdomainparm_to_name[i].name); put_stringbuf (&sb, ")"); got_curve = 1; break; } /* if (!got_curve) */ /* gpgrt_log_printhex (parmder, parmderlen, "ECDomainParm:"); */ } /* FIXME: We don't release the stringbuf in case of error better let the macro jump to a label */ elem = pk_algo_table[algoidx].elem_string; ctrl = pk_algo_table[algoidx].ctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow reading a raw value. */ is_int = 1; len = derlen; } else { if (!derlen) { xfree (parm_oid); return gpg_error (GPG_ERR_INV_KEYINFO); } c = *der++; derlen--; if ( c != *ctrl ) { xfree (parm_oid); return gpg_error (GPG_ERR_UNEXPECTED_TAG); } is_int = c == 0x02; TLV_LENGTH (der); } if (is_int && *elem != '-') /* Take this integer. */ { char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, der, len); der += len; derlen -= len; put_stringbuf (&sb, ")"); } } put_stringbuf (&sb, "))"); xfree (parm_oid); *r_string = get_stringbuf (&sb); if (!*r_string) return gpg_error (GPG_ERR_ENOMEM); return 0; } /* Match the algorithm string given in BUF which is of length BUFLEN * with the known algorithms from our table and return the table * entriy with the OID string. If WITH_SIG is true, the table of * signature algorithms is consulted first. */ static const char * oid_from_buffer (const unsigned char *buf, unsigned int buflen, pkalgo_t *r_pkalgo, int with_sig) { int i; /* Ignore an optional "oid." prefix. */ if (buflen > 4 && buf[3] == '.' && digitp (buf+4) && ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd') ||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D'))) { buf += 4; buflen -= 4; } if (with_sig) { /* Scan the signature table first. */ for (i=0; sig_algo_table[i].oid; i++) { if (!sig_algo_table[i].supported) continue; if (buflen == strlen (sig_algo_table[i].oidstring) && !memcmp (buf, sig_algo_table[i].oidstring, buflen)) break; if (buflen == strlen (sig_algo_table[i].algo_string) && !memcmp (buf, sig_algo_table[i].algo_string, buflen)) break; } if (sig_algo_table[i].oid) { *r_pkalgo = sig_algo_table[i].pkalgo; return sig_algo_table[i].oidstring; } } /* Scan the standard table. */ for (i=0; pk_algo_table[i].oid; i++) { if (!pk_algo_table[i].supported) continue; if (buflen == strlen (pk_algo_table[i].oidstring) && !memcmp (buf, pk_algo_table[i].oidstring, buflen)) break; if (buflen == strlen (pk_algo_table[i].algo_string) && !memcmp (buf, pk_algo_table[i].algo_string, buflen)) break; } if (!pk_algo_table[i].oid) return NULL; *r_pkalgo = pk_algo_table[i].pkalgo; return pk_algo_table[i].oidstring; } /* If ALGOINFOMODE is false: Take the "public-key" s-expression SEXP * and convert it into a DER encoded publicKeyInfo. * * If ALGOINFOMODE is true: Take the "sig-val" s-expression SEXP and * convert it into a DER encoded algorithmInfo. */ gpg_error_t _ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, int algoinfomode, unsigned char **r_der, size_t *r_derlen) { gpg_error_t err; const unsigned char *s; char *endp; unsigned long n; const char *algo_oid; char *curve_oid = NULL; pkalgo_t pkalgo, force_pkalgo; int i; struct { const char *name; int namelen; const unsigned char *value; int valuelen; } parm[10]; int parmidx; const char *parmdesc, *algoparmdesc; ksba_der_t dbld = NULL; ksba_der_t dbld2 = NULL; unsigned char *tmpder; size_t tmpderlen; if (!sexp) return gpg_error (GPG_ERR_INV_VALUE); s = sexp; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ s++; if (algoinfomode && n == 7 && !memcmp (s, "sig-val", 7)) s += 7; else if (n == 10 || !memcmp (s, "public-key", 10)) s += 10; else 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 */ n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */ s++; algo_oid = oid_from_buffer (s, n, &pkalgo, algoinfomode); if (!algo_oid) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); s += n; /* Collect all the values. */ force_pkalgo = 0; for (parmidx = 0; *s != ')' ; parmidx++) { if (parmidx >= DIM(parm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (*s != '(') { err = gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP); goto leave; } s++; n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } s++; parm[parmidx].name = s; parm[parmidx].namelen = n; s += n; if (!digitp(s)) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ goto leave; } n = strtoul (s, &endp, 10); s = endp; if (!n || *s != ':') return gpg_error (GPG_ERR_INV_SEXP); s++; parm[parmidx].value = s; parm[parmidx].valuelen = n; s += n; if ( *s != ')') { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */ goto leave; } s++; if (parm[parmidx].namelen == 5 && !memcmp (parm[parmidx].name, "curve", 5) && !curve_oid) { curve_oid = get_ecc_curve_oid (parm[parmidx].value, parm[parmidx].valuelen, &force_pkalgo); parmidx--; /* No need to store this parameter. */ } } s++; /* Allow for optional elements. */ if (*s == '(') { int depth = 1; err = sskip (&s, &depth); if (err) goto leave; } /* We need another closing parenthesis. */ if ( *s != ')' ) { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } if (force_pkalgo) pkalgo = force_pkalgo; /* Describe the parameters in the order we want them. For DSA wie * also set algoparmdesc so that we can later build the parameters * for the algorithmIdentifier. */ algoparmdesc = NULL; switch (pkalgo) { case PKALGO_RSA: parmdesc = algoinfomode? "" : "ne"; break; case PKALGO_DSA: parmdesc = algoinfomode? "" : "y"; algoparmdesc = "pqg"; break; case PKALGO_ECC: parmdesc = algoinfomode? "" : "q"; break; case PKALGO_ED25519: case PKALGO_X25519: case PKALGO_ED448: case PKALGO_X448: parmdesc = algoinfomode? "" : "q"; if (curve_oid) algo_oid = curve_oid; break; default: err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); goto leave; } /* Create a builder. */ dbld = _ksba_der_builder_new (0); if (!dbld) { err = gpg_error_from_syserror (); goto leave; } /* The outer sequence. */ if (!algoinfomode) _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* The sequence. */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* The object id. */ _ksba_der_add_oid (dbld, algo_oid); /* The parameter. */ if (algoparmdesc) { /* Write the sequence tag followed by the integers. */ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); for (s = algoparmdesc; *s; s++) for (i=0; i < parmidx; i++) if (parm[i].namelen == 1 && parm[i].name[0] == *s) { _ksba_der_add_int (dbld, parm[i].value, parm[i].valuelen, 1); break; /* inner loop */ } _ksba_der_add_end (dbld); } else if (pkalgo == PKALGO_ECC && !algoinfomode) { /* We only support the namedCurve choice for ECC parameters. */ if (!curve_oid) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); goto leave; } _ksba_der_add_oid (dbld, curve_oid); } else if (pkalgo == PKALGO_RSA) { _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0); } _ksba_der_add_end (dbld); /* sequence. */ /* Add the bit string if we are not in algoinfomode. */ if (!algoinfomode) { if (*parmdesc == 'q' && !parmdesc[1]) { /* This is ECC - Q is directly written as a bit string. */ for (i=0; i < parmidx; i++) if (parm[i].namelen == 1 && parm[i].name[0] == 'q') { if ((parm[i].valuelen & 1) && parm[i].valuelen > 32 && (parm[i].value[0] == 0x40 || parm[i].value[0] == 0x41 || parm[i].value[0] == 0x42)) { /* Odd length and prefixed with 0x40 - this is the * rfc4880bis indicator octet for extended point * formats - we may not emit that octet here. */ _ksba_der_add_bts (dbld, parm[i].value+1, parm[i].valuelen-1, 0); } else _ksba_der_add_bts (dbld, parm[i].value, parm[i].valuelen, 0); break; } } else /* Non-ECC - embed the values. */ { dbld2 = _ksba_der_builder_new (10); if (!dbld2) { err = gpg_error_from_syserror (); goto leave; } /* Note that no sequence is used if only one integer is written. */ if (parmdesc[0] && parmdesc[1]) _ksba_der_add_tag (dbld2, 0, TYPE_SEQUENCE); for (s = parmdesc; *s; s++) for (i=0; i < parmidx; i++) if (parm[i].namelen == 1 && parm[i].name[0] == *s) { _ksba_der_add_int (dbld2, parm[i].value, parm[i].valuelen, 1); break; /* inner loop */ } if (parmdesc[0] && parmdesc[1]) _ksba_der_add_end (dbld2); err = _ksba_der_builder_get (dbld2, &tmpder, &tmpderlen); if (err) goto leave; _ksba_der_add_bts (dbld, tmpder, tmpderlen, 0); xfree (tmpder); } _ksba_der_add_end (dbld); /* Outer sequence. */ } /* Get the result. */ err = _ksba_der_builder_get (dbld, r_der, r_derlen); leave: _ksba_der_release (dbld2); _ksba_der_release (dbld); xfree (curve_oid); return err; } /* Helper function to parse the parameters used for rsaPSS. * Given this sample DER object in (DER,DERLEN): * * SEQUENCE { * [0] { * SEQUENCE { * OBJECT IDENTIFIER sha-512 (2 16 840 1 101 3 4 2 3) * } * } * [1] { * SEQUENCE { * OBJECT IDENTIFIER pkcs1-MGF (1 2 840 113549 1 1 8) * SEQUENCE { * OBJECT IDENTIFIER sha-512 (2 16 840 1 101 3 4 2 3) * } * } * } * [2] { * INTEGER 64 * } * } * * The function returns the first OID at R_PSSHASH and the salt length * at R_SALTLEN. If the salt length is missing its default value is * returned. In case object does not resemble a the expected rsaPSS * parameters GPG_ERR_INV_OBJ is returned; other errors are returned * for an syntatically invalid object. On error NULL is stored at * R_PSSHASH. */ gpg_error_t _ksba_keyinfo_get_pss_info (const unsigned char *der, size_t derlen, char **r_psshash, unsigned int *r_saltlen) { gpg_error_t err; struct tag_info ti; char *psshash = NULL; char *tmpoid = NULL; unsigned int saltlen; *r_psshash = NULL; *r_saltlen = 0; err = parse_sequence (&der, &derlen, &ti); if (err) goto leave; /* Get the hash algo. */ err = parse_context_tag (&der, &derlen, &ti, 0); if (err) goto unknown_parms; err = parse_sequence (&der, &derlen, &ti); if (err) goto unknown_parms; err = parse_object_id_into_str (&der, &derlen, &psshash); if (err) goto unknown_parms; err = parse_optional_null (&der, &derlen, NULL); if (err) goto unknown_parms; /* Check the MGF OID and that its hash algo matches. */ err = parse_context_tag (&der, &derlen, &ti, 1); if (err) goto unknown_parms; err = parse_sequence (&der, &derlen, &ti); if (err) goto leave; err = parse_object_id_into_str (&der, &derlen, &tmpoid); if (err) goto unknown_parms; if (strcmp (tmpoid, "1.2.840.113549.1.1.8")) /* MGF1 */ goto unknown_parms; err = parse_sequence (&der, &derlen, &ti); if (err) goto leave; xfree (tmpoid); err = parse_object_id_into_str (&der, &derlen, &tmpoid); if (err) goto unknown_parms; if (strcmp (tmpoid, psshash)) goto unknown_parms; err = parse_optional_null (&der, &derlen, NULL); if (err) goto unknown_parms; /* Get the optional saltLength. */ err = parse_context_tag (&der, &derlen, &ti, 2); if (gpg_err_code (err) == GPG_ERR_INV_OBJ || gpg_err_code (err) == GPG_ERR_FALSE) saltlen = 20; /* Optional element - use default value */ else if (err) goto unknown_parms; else { err = parse_integer (&der, &derlen, &ti); if (err) goto leave; for (saltlen=0; ti.length; ti.length--) { saltlen <<= 8; saltlen |= (*der++) & 0xff; derlen--; } } /* All fine. */ *r_psshash = psshash; psshash = NULL; *r_saltlen = saltlen; err = 0; goto leave; unknown_parms: err = gpg_error (GPG_ERR_INV_OBJ); leave: xfree (psshash); xfree (tmpoid); return err; } /* Mode 0: work as described under _ksba_sigval_to_sexp * mode 1: work as described under _ksba_encval_to_sexp * mode 2: same as mode 1 but for ECDH; in this mode * KEYENCRYALO, KEYWRAPALGO, ENCRKEY, ENCRYKLEYLEN * are also required. */ static gpg_error_t cryptval_to_sexp (int mode, const unsigned char *der, size_t derlen, const char *keyencralgo, const char *keywrapalgo, const void *encrkey, size_t encrkeylen, ksba_sexp_t *r_string) { gpg_error_t err; const struct algo_table_s *algo_table; int c; size_t nread, off, len; int algoidx; int is_bitstr; const unsigned char *ctrl; const char *elem; struct stringbuf sb; size_t parm_off, parm_len; int parm_type; char *pss_hash = NULL; unsigned int salt_length = 0; /* FIXME: The entire function is very similar to keyinfo_to_sexp */ *r_string = NULL; if (!mode) algo_table = sig_algo_table; else algo_table = enc_algo_table; - err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr, + err = get_algorithm (1, der, derlen, 0x30, + &nread, &off, &len, &is_bitstr, &parm_off, &parm_len, &parm_type); if (err) return err; /* look into our table of supported algorithms */ for (algoidx=0; algo_table[algoidx].oid; algoidx++) { if ( len == algo_table[algoidx].oidlen && !memcmp (der+off, algo_table[algoidx].oid, len)) break; } if (!algo_table[algoidx].oid) return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); if (!algo_table[algoidx].supported) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); if (parm_type == TYPE_SEQUENCE && algo_table[algoidx].supported == SUPPORTED_RSAPSS) { /* This is rsaPSS and we collect the parameters. We simplify * this by assuming that pkcs1-MGF is used with an identical * hash algorithm. All other kinds of parameters are ignored. */ err = _ksba_keyinfo_get_pss_info (der + parm_off, parm_len, &pss_hash, &salt_length); if (gpg_err_code (err) == GPG_ERR_INV_OBJ) err = 0; if (err) return err; } der += nread; derlen -= nread; if (is_bitstr) { /* Funny: X.509 defines the signature value as a bit string but CMS as an octet string - for ease of implementation we always allow both */ if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if (c) fprintf (stderr, "warning: number of unused bits is not zero\n"); } /* fixme: we should calculate the initial length form the size of the sequence, so that we don't neen a realloc later */ init_stringbuf (&sb, 100); put_stringbuf (&sb, mode? "(7:enc-val(":"(7:sig-val("); put_stringbuf_sexp (&sb, algo_table[algoidx].algo_string); /* FIXME: We don't release the stringbuf in case of error better let the macro jump to a label */ if (!mode && (algo_table[algoidx].pkalgo == PKALGO_ED25519 ||algo_table[algoidx].pkalgo == PKALGO_ED448 || (algo_table[algoidx].pkalgo == PKALGO_ECC && *algo_table[algoidx].elem_string == 'P'))) { /* EdDSA is special: R and S are simply concatenated; see * rfc8410. The same code is used for Plain ECDSA format as * specified in BSI TR-03111; we indicate this with a 'P' in the * elem string. */ put_stringbuf (&sb, "(1:r"); put_stringbuf_mem_sexp (&sb, der, derlen/2); put_stringbuf (&sb, ")"); der += derlen/2; derlen /= 2; put_stringbuf (&sb, "(1:s"); put_stringbuf_mem_sexp (&sb, der, derlen); put_stringbuf (&sb, ")"); } else { elem = algo_table[algoidx].elem_string; ctrl = algo_table[algoidx].ctrl_string; for (; *elem; ctrl++, elem++) { int is_int; if ( (*ctrl & 0x80) && !elem[1] ) { /* Hack to allow a raw value */ is_int = 1; len = derlen; } else { if (!derlen) return gpg_error (GPG_ERR_INV_KEYINFO); c = *der++; derlen--; if ( c != *ctrl ) return gpg_error (GPG_ERR_UNEXPECTED_TAG); is_int = c == 0x02; TLV_LENGTH (der); } if (is_int && *elem != '-') { /* take this integer */ char tmp[2]; put_stringbuf (&sb, "("); tmp[0] = *elem; tmp[1] = 0; put_stringbuf_sexp (&sb, tmp); put_stringbuf_mem_sexp (&sb, der, len); der += len; derlen -= len; put_stringbuf (&sb, ")"); } } } if (mode == 2) /* ECDH */ { put_stringbuf (&sb, "(1:s"); put_stringbuf_mem_sexp (&sb, encrkey, encrkeylen); put_stringbuf (&sb, ")"); } put_stringbuf (&sb, ")"); if (!mode && algo_table[algoidx].digest_string) { /* Insert the hash algorithm if included in the OID. */ put_stringbuf (&sb, "(4:hash"); put_stringbuf_sexp (&sb, algo_table[algoidx].digest_string); put_stringbuf (&sb, ")"); } if (!mode && pss_hash) { put_stringbuf (&sb, "(5:flags3:pss)"); put_stringbuf (&sb, "(9:hash-algo"); put_stringbuf_sexp (&sb, pss_hash); put_stringbuf (&sb, ")"); put_stringbuf (&sb, "(11:salt-length"); put_stringbuf_uint (&sb, salt_length); put_stringbuf (&sb, ")"); } if (mode == 2) /* ECDH */ { put_stringbuf (&sb, "(9:encr-algo"); put_stringbuf_sexp (&sb, keyencralgo); put_stringbuf (&sb, ")(9:wrap-algo"); put_stringbuf_sexp (&sb, keywrapalgo); put_stringbuf (&sb, ")"); } put_stringbuf (&sb, ")"); *r_string = get_stringbuf (&sb); if (!*r_string) return gpg_error (GPG_ERR_ENOMEM); xfree (pss_hash); return 0; } /* Assume that DER is a buffer of length DERLEN with a DER encoded Asn.1 structure like this: SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } signature BIT STRING We only allow parameters == NULL. The function parses this structure and creates a S-Exp suitable to be used as signature value in Libgcrypt: (sig-val ( ( ) ... ( )) (hash algo)) The S-Exp will be returned in a string which the caller must free. We don't pass an ASN.1 node here but a plain memory block. */ gpg_error_t _ksba_sigval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { return cryptval_to_sexp (0, der, derlen, NULL, NULL, NULL, 0, r_string); } /* Assume that der is a buffer of length DERLEN with a DER encoded * ASN.1 structure like this: * * SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } * encryptedKey OCTET STRING * * The function parses this structure and creates a S-expression * suitable to be used as encrypted value in Libgcrypt's public key * functions: * * (enc-val * ( * ( ) * ... * ( ) * )) * * The S-expression will be returned in a string which the caller must * free. Note that the input buffer may not a proper ASN.1 object but * a plain memory block; this is becuase the SEQUENCE is followed by * an OCTET STRING or BIT STRING. */ gpg_error_t _ksba_encval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) { return cryptval_to_sexp (1, der, derlen, NULL, NULL, NULL, 0, r_string); } /* Assume that der is a buffer of length DERLEN with a DER encoded * ASN.1 structure like this: * * [1] { * SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } * encryptedKey BIT STRING * } * * The function parses this structure and creates an S-expression * conveying all parameters required for ECDH: * * (enc-val * (ecdh * (e ) * (s ) * (ukm ) * (encr-algo ) * (wrap-algo ))) * * E is the ephemeral public key and S is the encrypted key. The user * keying material (ukm) is optional. The S-expression will be * returned in a string which the caller must free. */ gpg_error_t _ksba_encval_kari_to_sexp (const unsigned char *der, size_t derlen, const char *keyencralgo, const char *keywrapalgo, const void *enckey, size_t enckeylen, ksba_sexp_t *r_string) { gpg_error_t err; struct tag_info ti; size_t save_derlen = derlen; err = parse_context_tag (&der, &derlen, &ti, 1); if (err) return err; if (save_derlen < ti.nhdr) return gpg_error (GPG_ERR_INV_BER); derlen = save_derlen - ti.nhdr; return cryptval_to_sexp (2, der, derlen, keyencralgo, keywrapalgo, enckey, enckeylen, r_string); } diff --git a/src/keyinfo.h b/src/keyinfo.h index 66f5805..0e59bbd 100644 --- a/src/keyinfo.h +++ b/src/keyinfo.h @@ -1,79 +1,86 @@ /* keyinfo.h - Parse and build a keyInfo structure * Copyright (C) 2001, 2012 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 either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef KEYINFO_H #define KEYINFO_H #include "asn1-func.h" gpg_error_t _ksba_parse_algorithm_identifier (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid); gpg_error_t _ksba_parse_algorithm_identifier2 (const unsigned char *der, size_t derlen, size_t *r_nread, char **r_oid, char **r_parm, size_t *r_parmlen); +gpg_error_t +_ksba_parse_algorithm_identifier3 (const unsigned char *der, size_t derlen, + int firsttag, + size_t *r_nread, char **r_oid, + char **r_parm, size_t *r_parmlen, + int *r_parmtype); + gpg_error_t _ksba_keyinfo_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string) _KSBA_VISIBILITY_DEFAULT; gpg_error_t _ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, int algoinfomode, unsigned char **r_der, size_t *r_derlen) _KSBA_VISIBILITY_DEFAULT; gpg_error_t _ksba_algoinfo_from_sexp (ksba_const_sexp_t sexp, unsigned char **r_der, size_t *r_derlen); gpg_error_t _ksba_keyinfo_get_pss_info (const unsigned char *der, size_t derlen, char **r_psshash, unsigned int *r_saltlen); gpg_error_t _ksba_sigval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string); gpg_error_t _ksba_encval_to_sexp (const unsigned char *der, size_t derlen, ksba_sexp_t *r_string); gpg_error_t _ksba_encval_kari_to_sexp (const unsigned char *der, size_t derlen, const char *keyencralgo, const char *keywrapalgo, const void *enckey, size_t enckeylen, ksba_sexp_t *r_string); int _ksba_node_with_oid_to_digest_algo (const unsigned char *image, AsnNode node); #endif /*KEYINFO_H*/ diff --git a/tests/t-cms-parser.c b/tests/t-cms-parser.c index a3e8531..f8a731c 100644 --- a/tests/t-cms-parser.c +++ b/tests/t-cms-parser.c @@ -1,327 +1,336 @@ /* t-cms-parser.c - basic test for the CMS parser. * Copyright (C) 2001 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 Foundation; either version 3 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, see . */ #include #include #include #include #include #include #include "../src/ksba.h" #include "t-common.h" static int quiet; static int verbose; void dummy_hash_fnc (void *arg, const void *buffer, size_t length) { (void)arg; (void)buffer; (void)length; } static int dummy_writer_cb (void *cb_value, const void *buffer, size_t count) { (void)cb_value; (void)buffer; (void)count; return 0; } static void one_file (const char *fname) { gpg_error_t err; FILE *fp; ksba_reader_t r; ksba_writer_t w; ksba_cms_t cms; int i; const char *algoid; ksba_stop_reason_t stopreason; const char *s; size_t n; ksba_sexp_t p; char *dn; int idx; if (!quiet) printf ("*** checking `%s' ***\n", fname); fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "%s:%d: can't open `%s': %s\n", __FILE__, __LINE__, fname, strerror (errno)); exit (1); } err = ksba_reader_new (&r); if (err) fail_if_err (err); err = ksba_reader_set_file (r, fp); fail_if_err (err); /* Also create a writer so that cms.c won't return an error when writing processed content. */ err = ksba_writer_new (&w); if (err) fail_if_err (err); err = ksba_writer_set_cb (w, dummy_writer_cb, NULL); fail_if_err (err); switch (ksba_cms_identify (r)) { case KSBA_CT_DATA: s = "data"; break; case KSBA_CT_SIGNED_DATA: s = "signed data"; break; case KSBA_CT_ENVELOPED_DATA: s = "enveloped data"; break; case KSBA_CT_DIGESTED_DATA: s = "digested data"; break; case KSBA_CT_ENCRYPTED_DATA: s = "encrypted data"; break; case KSBA_CT_AUTH_DATA: s = "auth data"; break; case KSBA_CT_SPC_IND_DATA_CTX:s = "spc indirect data context"; break; case KSBA_CT_OPENPGP_KEYBLOCK:s = "openpgp keyblock"; break; default: s = "unknown"; break; } if (!quiet) printf ("identified as: %s\n", s); err = ksba_cms_new (&cms); if (err) fail_if_err (err); err = ksba_cms_set_reader_writer (cms, r, w); fail_if_err (err); err = ksba_cms_parse (cms, &stopreason); fail_if_err2 (fname, err); if (!quiet) printf ("stop reason: %d\n", stopreason); s = ksba_cms_get_content_oid (cms, 0); if (!quiet) printf ("ContentType: %s\n", s?s:"none"); err = ksba_cms_parse (cms, &stopreason); fail_if_err2 (fname, err); if (!quiet) printf ("stop reason: %d\n", stopreason); s = ksba_cms_get_content_oid (cms, 1); if (!quiet) { printf ("EncapsulatedContentType: %s\n", s?s:"none"); printf ("DigestAlgorithms:"); } for (i=0; (algoid = ksba_cms_get_digest_algo_list (cms, i)); i++) if (!quiet) printf (" %s", algoid); if (!quiet) putchar('\n'); if (stopreason == KSBA_SR_NEED_HASH) if (!quiet) printf("Detached signature\n"); ksba_cms_set_hash_function (cms, dummy_hash_fnc, NULL); do { err = ksba_cms_parse (cms, &stopreason); fail_if_err2 (fname, err); if (!quiet) printf ("stop reason: %d\n", stopreason); } while (stopreason != KSBA_SR_READY); if (ksba_cms_get_content_type (cms, 0) == KSBA_CT_ENVELOPED_DATA) { for (idx=0; ; idx++) { err = ksba_cms_get_issuer_serial (cms, idx, &dn, &p); if (err == -1) break; /* ready */ - fail_if_err2 (fname, err); - if (!quiet) + if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_CMS_OBJ) { - printf ("recipient %d - issuer: ", idx); - print_dn (dn); + printf ("recipient %d" + " - kekri or pwri detected\n", idx); + err = 0; } - ksba_free (dn); - if (!quiet) + else { - putchar ('\n'); - printf ("recipient %d - serial: ", idx); - print_sexp_hex (p); - putchar ('\n'); + fail_if_err2 (fname, err); + if (!quiet) + { + printf ("recipient %d - issuer: ", idx); + print_dn (dn); + } + ksba_free (dn); + if (!quiet) + { + putchar ('\n'); + printf ("recipient %d - serial: ", idx); + print_sexp_hex (p); + putchar ('\n'); + } + ksba_free (p); } - ksba_free (p); dn = ksba_cms_get_enc_val (cms, idx); if (!quiet) { printf ("recipient %d - enc_val: ", idx); print_sexp (dn); putchar ('\n'); } ksba_free (dn); } } else { for (idx=0; idx < 1; idx++) { err = ksba_cms_get_issuer_serial (cms, idx, &dn, &p); if (gpg_err_code (err) == GPG_ERR_NO_DATA && !idx) { if (!quiet) printf ("this is a certs-only message\n"); break; } fail_if_err2 (fname, err); if (!quiet) { printf ("signer %d - issuer: ", idx); print_dn (dn); putchar ('\n'); } ksba_free (dn); if (!quiet) { printf ("signer %d - serial: ", idx); print_sexp_hex (p); putchar ('\n'); } ksba_free (p); err = ksba_cms_get_message_digest (cms, idx, &dn, &n); fail_if_err2 (fname, err); if (!quiet) { printf ("signer %d - messageDigest: ", idx); print_hex (dn, n); putchar ('\n'); } ksba_free (dn); err = ksba_cms_get_sigattr_oids (cms, idx, "1.2.840.113549.1.9.3",&dn); if (err && err != -1) fail_if_err2 (fname, err); if (err != -1) { char *tmp; for (tmp=dn; *tmp; tmp++) if (*tmp == '\n') *tmp = ' '; if (!quiet) printf ("signer %d - content-type: %s\n", idx, dn); ksba_free (dn); } algoid = ksba_cms_get_digest_algo (cms, idx); if (!quiet) printf ("signer %d - digest algo: %s\n", idx, algoid?algoid:"?"); dn = ksba_cms_get_sig_val (cms, idx); if (dn) { if (!quiet) { printf ("signer %d - signature: ", idx); print_sexp (dn); putchar ('\n'); } } else { if (!quiet) printf ("signer %d - signature not found\n", idx); } ksba_free (dn); } } ksba_cms_release (cms); ksba_writer_release (w); ksba_reader_release (r); fclose (fp); } int main (int argc, char **argv) { if (argc) { argc--; argv++; } if (argc && !strcmp (*argv, "--verbose")) { verbose = 1; argc--; argv++; } if (argc) { for (; argc; argc--, argv++) one_file (*argv); } else { static char *testfiles[] = { "samples/detached-sig.cms", "samples/ecdh-sample1.p7m", "samples/ecdsa-sample1.p7s", "samples/rsa-sample1.p7m", "samples/rsa-sample1.p7s", NULL }; char *fname; int idx; if (!verbose) quiet = 1; for (idx=0; testfiles[idx]; idx++) { fname = prepend_srcdir (testfiles[idx]); one_file (fname); free(fname); } } if (!quiet) printf ("*** all checks done\n"); return 0; }