/* der-decoder.c - Distinguished Encoding Rules Encoder * Copyright (C) 2001, 2004, 2008, 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 . */ #include #include #include #include #include #include "util.h" #include "ksba.h" #include "asn1-func.h" #include "ber-help.h" #include "der-encoder.h" #include "convert.h" struct der_encoder_s { AsnNode module; /* the ASN.1 structure */ ksba_writer_t writer; const char *last_errdesc; /* string with the error description */ AsnNode root; /* of the expanded parse tree */ int debug; }; /* To be useful for the DER encoder we store all data direct as the binary image, so we use the VALTYPE_MEM */ static gpg_error_t store_value (AsnNode node, const void *buffer, size_t length) { _ksba_asn_set_value (node, VALTYPE_MEM, buffer, length); return 0; } static void clear_value (AsnNode node) { _ksba_asn_set_value (node, VALTYPE_NULL, NULL, 0); } DerEncoder _ksba_der_encoder_new (void) { DerEncoder d; d = xtrycalloc (1, sizeof *d); if (!d) return NULL; return d; } void _ksba_der_encoder_release (DerEncoder d) { xfree (d); } /** * _ksba_der_encoder_set_module: * @d: Decoder object * @module: ASN.1 Parse tree * * Initialize the decoder with the ASN.1 module. Note, that this is a * shallow copy of the module. Fixme: What about ref-counting of * AsnNodes? * * Return value: 0 on success or an error code **/ gpg_error_t _ksba_der_encoder_set_module (DerEncoder d, ksba_asn_tree_t module) { if (!d || !module) return gpg_error (GPG_ERR_INV_VALUE); if (d->module) return gpg_error (GPG_ERR_CONFLICT); /* module already set */ d->module = module->parse_tree; return 0; } gpg_error_t _ksba_der_encoder_set_writer (DerEncoder d, ksba_writer_t w) { if (!d || !w) return gpg_error (GPG_ERR_INV_VALUE); if (d->writer) return gpg_error (GPG_ERR_CONFLICT); /* reader already set */ d->writer = w; return 0; } /* Helpers to construct and write out objects */ /* Create and write a AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } where parameters will be set to NULL if parm is NULL or to an octet string with the given parm. As a special hack parameter will not be written if PARM is given but parmlen is 0. */ gpg_error_t _ksba_der_write_algorithm_identifier (ksba_writer_t w, const char *oid, const void *parm, size_t parmlen) { gpg_error_t err; unsigned char *buf; size_t len; int no_null = (parm && !parmlen); err = ksba_oid_from_str (oid, &buf, &len); if (err) return err; /* write the sequence */ /* fixme: the the length to encode the TLV values are actually not just 2 byte each but depend on the length of the values - for our purposes the static values do work. */ err = _ksba_ber_write_tl (w, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, (no_null? 2:4) + len + (parm? parmlen:0)); if (err) goto leave; /* the OBJECT ID header and the value */ err = _ksba_ber_write_tl (w, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len); if (!err) err = ksba_writer_write (w, buf, len); if (err) goto leave; /* Write the parameter */ if (no_null) ; else if (parm) { err = _ksba_ber_write_tl (w, TYPE_OCTET_STRING, CLASS_UNIVERSAL, 0, parmlen); if (!err) err = ksba_writer_write (w, parm, parmlen); } else { err = _ksba_ber_write_tl (w, TYPE_NULL, CLASS_UNIVERSAL, 0, 0); } leave: xfree (buf); return err; } /************************************************* *** Copy data from a tree image to the tree *** *************************************************/ /* Copy all values from the tree SRC (with values store in SRCIMAGE) to the tree DST */ gpg_error_t _ksba_der_copy_tree (AsnNode dst_root, AsnNode src_root, const unsigned char *src_image) { AsnNode s, d; s = src_root; d = dst_root; /* note: we use the is_any flags becuase an inserted copy may have already changed the any tag to the actual type */ while (s && d && (s->type == d->type || d->flags.is_any)) { if (d->flags.is_any) d->type = s->type; if (s->flags.in_array && s->right) { if (!_ksba_asn_insert_copy (d)) return gpg_error (GPG_ERR_ENOMEM); } if ( !_ksba_asn_is_primitive (s->type) ) ; else if (s->off == -1) clear_value (d); else store_value (d, src_image + s->off + s->nhdr, s->len); s = _ksba_asn_walk_tree (src_root, s); d = _ksba_asn_walk_tree (dst_root, d); } if (s || d) { /* fputs ("ksba_der_copy_tree: trees don't match\nSOURCE TREE:\n", stderr); */ /* _ksba_asn_node_dump_all (src_root, stderr); */ /* fputs ("DESTINATION TREE:\n", stderr); */ /* _ksba_asn_node_dump_all (dst_root, stderr); */ return gpg_error (GPG_ERR_ENCODING_PROBLEM); } return 0; } /********************************************* ********** Store data in a tree ************* *********************************************/ gpg_error_t _ksba_der_store_time (AsnNode node, const ksba_isotime_t atime) { char buf[50], *p; int need_gen; gpg_error_t err; /* First check that ATIME is indeed as formatted as expected. */ err = _ksba_assert_time_format (atime); if (err) return err; memcpy (buf, atime, 8); memcpy (buf+8, atime+9, 6); strcpy (buf+14, "Z"); /* We need to use generalized time beginning with the year 2050. */ need_gen = (_ksba_cmp_time (atime, "20500101T000000") >= 0); if (node->type == TYPE_ANY) node->type = need_gen? TYPE_GENERALIZED_TIME : TYPE_UTC_TIME; else if (node->type == TYPE_CHOICE) { /* find a suitable choice to store the value */ AsnNode n; for (n=node->down; n; n=n->right) { if ( (need_gen && n->type == TYPE_GENERALIZED_TIME) || (!need_gen && n->type == TYPE_UTC_TIME)) { node = n; break; } } } if (node->type == TYPE_GENERALIZED_TIME || node->type == TYPE_UTC_TIME) { p = node->type == TYPE_UTC_TIME? (buf+2):buf; return store_value (node, p, strlen (p)); } else return gpg_error (GPG_ERR_INV_VALUE); } /* Store the utf-8 STRING in NODE. */ gpg_error_t _ksba_der_store_string (AsnNode node, const char *string) { if (node->type == TYPE_CHOICE) { /* find a suitable choice to store the value */ } if (node->type == TYPE_PRINTABLE_STRING) { return store_value (node, string, strlen (string)); } else return gpg_error (GPG_ERR_INV_VALUE); } /* Store the integer VALUE in NODE. VALUE is assumed to be a DER encoded integer prefixed with 4 bytes given its length in network byte order. */ gpg_error_t _ksba_der_store_integer (AsnNode node, const unsigned char *value) { if (node->type == TYPE_INTEGER) { size_t len; len = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; return store_value (node, value+4, len); } else return gpg_error (GPG_ERR_INV_VALUE); } gpg_error_t _ksba_der_store_oid (AsnNode node, const char *oid) { gpg_error_t err; if (node->type == TYPE_ANY) node->type = TYPE_OBJECT_ID; if (node->type == TYPE_OBJECT_ID) { unsigned char *buf; size_t len; err = ksba_oid_from_str (oid, &buf, &len); if (err) return err; err = store_value (node, buf, len); xfree (buf); return err; } else return gpg_error (GPG_ERR_INV_VALUE); } gpg_error_t _ksba_der_store_octet_string (AsnNode node, const char *buf, size_t len) { if (node->type == TYPE_ANY) node->type = TYPE_OCTET_STRING; if (node->type == TYPE_OCTET_STRING) { return store_value (node, buf, len); } else return gpg_error (GPG_ERR_INV_VALUE); } gpg_error_t _ksba_der_store_sequence (AsnNode node, const unsigned char *buf, size_t len) { if (node->type == TYPE_ANY) node->type = TYPE_PRE_SEQUENCE; if (node->type == TYPE_SEQUENCE || node->type == TYPE_PRE_SEQUENCE) { return store_value (node, buf, len); } else return gpg_error (GPG_ERR_INV_VALUE); } gpg_error_t _ksba_der_store_null (AsnNode node) { if (node->type == TYPE_ANY) node->type = TYPE_NULL; if (node->type == TYPE_NULL) { clear_value (node); return 0; } else return gpg_error (GPG_ERR_INV_VALUE); } /* Actual DER encoder */ /* We have a value for this node. Calculate the length of the header and store it in node->nhdr and store the length of the value in node->value. We assume that this is a primitive node and has a value of type VALTYPE_MEM. */ static void set_nhdr_and_len (AsnNode node, unsigned long length) { int buflen = 0; if (node->type == TYPE_SET_OF || node->type == TYPE_SEQUENCE_OF) buflen++; else if (node->type == TYPE_TAG) buflen++; else if (node->type < 0x1f || node->type == TYPE_PRE_SEQUENCE) buflen++; else { never_reached (); /* Fixme: tags with values above 31 are not yet implemented */ } if (!node->type /*&& !class*/) buflen++; /* end tag */ else if (node->type == TYPE_NULL /*&& !class*/) buflen++; /* NULL tag */ else if (!length) buflen++; /* indefinite length */ else if (length < 128) buflen++; else { buflen += (length <= 0xff ? 2: length <= 0xffff ? 3: length <= 0xffffff ? 4: 5); } node->len = length; node->nhdr = buflen; } /* Like above but put now put it into buffer. return the number of bytes copied. There is no need to do length checking here */ static size_t copy_nhdr_and_len (unsigned char *buffer, AsnNode node) { unsigned char *p = buffer; int tag, class; unsigned long length; tag = node->type; class = CLASS_UNIVERSAL; length = node->len; if (tag == TYPE_SET_OF) tag = TYPE_SET; else if (tag == TYPE_SEQUENCE_OF) tag = TYPE_SEQUENCE; else if (tag == TYPE_PRE_SEQUENCE) tag = TYPE_SEQUENCE; else if (tag == TYPE_TAG) { class = CLASS_CONTEXT; /* Hmmm: we no way to handle other classes */ tag = node->value.v_ulong; } if (tag < 0x1f) { *p = (class << 6) | tag; if (!_ksba_asn_is_primitive (tag)) *p |= 0x20; p++; } else { /* fixme: Not_Implemented*/ } if (!tag && !class) *p++ = 0; /* end tag */ else if (tag == TYPE_NULL && !class) *p++ = 0; /* NULL tag */ else if (!length) *p++ = 0x80; /* indefinite length - can't happen! */ else if (length < 128) *p++ = length; else { int i; /* fixme: if we know the sizeof an ulong we could support larger objects - however this is pretty ridiculous */ i = (length <= 0xff ? 1: length <= 0xffff ? 2: length <= 0xffffff ? 3: 4); *p++ = (0x80 | i); if (i > 3) *p++ = length >> 24; if (i > 2) *p++ = length >> 16; if (i > 1) *p++ = length >> 8; *p++ = length; } return p - buffer; } static unsigned long sum_up_lengths (AsnNode root) { AsnNode n; unsigned long len = 0; if (root->type == TYPE_NULL) return root->nhdr; if (!(n=root->down) || _ksba_asn_is_primitive (root->type)) len = root->len; else { for (; n; n = n->right) len += sum_up_lengths (n); } if ( !_ksba_asn_is_primitive (root->type) && root->type != TYPE_CHOICE && len && !root->flags.is_implicit) { /* this is a constructed one */ set_nhdr_and_len (root, len); } return len? (len + root->nhdr):0; } /* Create a DER encoding from the value tree ROOT and return an allocated image of appropriate length in r_image and r_imagelen. The value tree is modified so that it can be used the same way as a parsed one, i.e the elements off, and len are set to point into image. */ gpg_error_t _ksba_der_encode_tree (AsnNode root, unsigned char **r_image, size_t *r_imagelen) { AsnNode n; unsigned char *image; size_t imagelen, len; /* clear out all fields */ for (n=root; n ; n = _ksba_asn_walk_tree (root, n)) { n->off = -1; n->len = 0; n->nhdr = 0; } /* Set default values */ /* FIXME */ /* calculate the length of the headers. These are the tag and length fields of all primitive elements */ for (n=root; n ; n = _ksba_asn_walk_tree (root, n)) { if (_ksba_asn_is_primitive (n->type) && !n->flags.is_implicit && ((n->valuetype == VALTYPE_MEM && n->value.v_mem.len) || n->type == TYPE_NULL)) set_nhdr_and_len (n, n->value.v_mem.len); } /* Now calculate the length of all constructed types */ imagelen = sum_up_lengths (root); #if 0 /* set off to zero, so that it can be dumped */ for (n=root; n ; n = _ksba_asn_walk_tree (root, n)) n->off = 0; fputs ("DER encoded value Tree:\n", stderr); _ksba_asn_node_dump_all (root, stderr); for (n=root; n ; n = _ksba_asn_walk_tree (root, n)) n->off = -1; #endif /* now we can create an encoding in image */ image = xtrymalloc (imagelen); if (!image) return gpg_error (GPG_ERR_ENOMEM); len = 0; for (n=root; n ; n = _ksba_asn_walk_tree (root, n)) { size_t nbytes; if (!n->nhdr) continue; assert (n->off == -1); assert (len < imagelen); n->off = len; nbytes = copy_nhdr_and_len (image+len, n); len += nbytes; if ( _ksba_asn_is_primitive (n->type) && n->valuetype == VALTYPE_MEM && n->value.v_mem.len ) { nbytes = n->value.v_mem.len; assert (len + nbytes <= imagelen); memcpy (image+len, n->value.v_mem.buf, nbytes); len += nbytes; } } assert (len == imagelen); *r_image = image; if (r_imagelen) *r_imagelen = imagelen; return 0; }