+++ /dev/null
-/*
-
- silcasn1_encode.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2003 - 2007 Pekka Riikonen
-
- This program 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; version 2 of the License.
-
- This program 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.
-
-*/
-
-#include "silc.h"
-#include "silcasn1.h"
-#include "silcber.h"
-
-#define SILC_ASN1_BUFFER_FREE(b, stack) if (!stack) silc_buffer_purge(b);
-#define SILC_ASN1_STACK(stack, asn1) stack ? stack : asn1->orig_stack
-
-/************************** ASN.1 Encoder routines **************************/
-
-/* Encode string from UTF-8 string to other string encodings. Encodes
- diretly to BER blob. */
-#define SILC_ASN1_ENCODE_STRING(enc) \
- unsigned char *s, *d = va_arg(asn1->ap, unsigned char *); \
- SilcUInt32 s_len, d_len = va_arg(asn1->ap, SilcUInt32); \
- if (!d) \
- break; \
- s_len = silc_utf8_decoded_len(d, d_len, (enc)); \
- if (s_len == 0) { \
- SILC_LOG_DEBUG(("Malformed %d string value", (enc))); \
- goto fail; \
- } \
- silc_stack_push(asn1->stack2, &frame); \
- s = silc_smalloc(stack2, s_len + 1); \
- if (s) { \
- silc_utf8_decode(d, d_len, (enc), s, s_len); \
- s[s_len] = '\0'; \
- } \
- len = silc_ber_encoded_len(tag, s_len, indef); \
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest, \
- silc_buffer_truelen(dest) + len); \
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE, \
- tag, s, s_len, indef); \
- silc_stack_pop(asn1->stack2); \
- if (!ret) \
- goto fail;
-
-/* The internal ASN.1 encoder. The `type', `tag' and `opts' are the
- first arguments (either very first or first for recursion) for a type.
- The `depth' includes the current depth of recursion. The `primitive'
- is TRUE if this encoder receives one primitive type as argument. If
- it is a constructed type it must be FALSE value. */
-
-static SilcBool
-silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
- SilcAsn1Tag type, SilcAsn1Tag tag, SilcBerClass ber_class,
- SilcAsn1Options opts, SilcBuffer dest, SilcUInt32 depth,
- SilcBool primitive)
-{
- unsigned char *ptr = dest->data;
- SilcAsn1Tag rtype, rtag;
- SilcAsn1Options ropts;
- SilcBerClass rclass;
- SilcUInt32 len = 0;
- SilcBool ret = FALSE, indef;
- SilcBufferStruct buf;
- SilcStackFrame frame;
-
-#ifdef SILC_DEBUG
- char sp[SILC_ASN1_RECURSION_DEPTH + 1];
- memset(sp, 0, sizeof(sp));
- if (depth)
- memset(sp, 32, depth);
-#endif /* SILC_DEBUG */
-
- if (depth >= SILC_ASN1_RECURSION_DEPTH) {
- SILC_LOG_DEBUG(("Maximum recursion depth reached"));
- return FALSE;
- }
-
- while (1) {
- /* These options cannot be used in encoding */
- opts &= ~SILC_ASN1_OPTIONAL;
-
- /* Get length encoding */
- indef = (opts & SILC_ASN1_INDEFINITE ? TRUE : FALSE);
-
- /* By default UNIVERSAL is implied unless the following conditions
- are met when CONTEXT will apply. For SILC_ASN1_TAG_ANY_PRIMITIVE
- the class is changed only if flags dictate it. */
- if (ber_class == SILC_BER_CLASS_UNIVERSAL) {
- if (type == SILC_ASN1_TAG_ANY_PRIMITIVE) {
- if (opts & SILC_ASN1_IMPLICIT ||
- opts & SILC_ASN1_EXPLICIT)
- ber_class = SILC_BER_CLASS_CONTEXT;
- } else {
- if (tag != type ||
- opts & SILC_ASN1_IMPLICIT ||
- opts & SILC_ASN1_EXPLICIT)
- ber_class = SILC_BER_CLASS_CONTEXT;
- }
- }
-
-#ifdef SILC_DEBUG
- SILC_LOG_DEBUG(
- ("%04d: %sEncode %s [%d] %s %s %s %s", depth, sp[0] ? sp : "",
- silc_asn1_tag_name(type), tag,
- ber_class == SILC_BER_CLASS_UNIVERSAL ? "univ" :
- ber_class == SILC_BER_CLASS_APPLICATION ? "appl" :
- ber_class == SILC_BER_CLASS_CONTEXT ? "cont" : "priv",
- (type != SILC_ASN1_TAG_SEQUENCE && type != SILC_ASN1_TAG_SET) ?
- opts & SILC_ASN1_EXPLICIT ? "constr" :
- type == SILC_ASN1_TAG_ANY &&
- !(opts & SILC_ASN1_EXPLICIT) ? "constr" : "primit" : "constr",
- indef ? opts & SILC_ASN1_EXPLICIT ? "defin" : "indef" : "defin",
- opts & SILC_ASN1_IMPLICIT ? "implicit" :
- opts & SILC_ASN1_EXPLICIT ? "explicit" : ""));
-#endif /* SILC_DEBUG */
-
- /* If tagging is explicit we add constructed type before the underlaying
- types. The underlaying types are encoded recursively with this
- encoder. */
- if (opts & SILC_ASN1_EXPLICIT) {
- memset(&buf, 0, sizeof(buf));
-
- primitive = (type != SILC_ASN1_TAG_SEQUENCE &&
- type != SILC_ASN1_TAG_SET);
- opts &= ~SILC_ASN1_EXPLICIT;
-
- silc_stack_push(stack2, &frame);
- ret = silc_asn1_encoder(asn1, stack2, stack1, type, type,
- SILC_BER_CLASS_UNIVERSAL, opts,
- &buf, depth + 1, primitive);
-
- if (!ret) {
- SILC_LOG_DEBUG(("Error encoding explicit tag"));
- silc_stack_pop(stack2);
- goto fail;
- }
-
- /* Encode the explicit tag */
- len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), FALSE);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_CONSTRUCTED,
- tag, buf.data, silc_buffer_len(&buf), FALSE);
- SILC_ASN1_BUFFER_FREE(&buf, stack2);
- silc_stack_pop(stack2);
- if (!ret)
- goto fail;
- if (primitive) {
- primitive = FALSE;
- goto cont;
- }
- goto ok;
- }
-
- /* Encode by the type */
- switch (type) {
-
- case SILC_ASN1_TAG_ANY:
- {
- /* ANY is another ASN.1 node which is added to this tree */
- SilcBuffer node = va_arg(asn1->ap, SilcBuffer);
- if (!node)
- break;
-
- /* Encode ASN.1 node into the tree. */
- if (opts & SILC_ASN1_IMPLICIT || type != tag) {
- /* We are tagging implicitly so we need to change the identifier
- of the underlaying type. Only constructed type is allowed with
- ANY when tagging implicitly. */
- const unsigned char *d;
- SilcUInt32 d_len;
- SilcBerEncoding enc;
-
- /* Get the underlaying data */
- ret = silc_ber_decode(node, NULL, &enc, NULL, &d, &d_len,
- NULL, NULL);
- if (!ret) {
- SILC_LOG_DEBUG(("Error decoding underlaying node for ANY"));
- goto fail;
- }
- assert(enc == SILC_BER_ENC_CONSTRUCTED);
-
- /* Now encode with implicit tagging */
- len = silc_ber_encoded_len(tag, d_len, FALSE);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_CONSTRUCTED,
- tag, d, d_len, FALSE);
- if (!ret)
- goto fail;
- } else {
- /* Copy the data directly into the tree. */
- len = silc_buffer_len(node);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- if (!dest)
- goto fail;
- silc_buffer_put(dest, node->data, len);
- }
- break;
- }
-
- case SILC_ASN1_TAG_ANY_PRIMITIVE:
- {
- /* ANY_PRIMITIVE is any primitive in encoded format. */
- SilcBuffer prim = va_arg(asn1->ap, SilcBuffer);
- if (!prim)
- break;
-
- /* Encode the primitive data */
- len = silc_ber_encoded_len(tag, silc_buffer_len(prim), FALSE);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, prim->data, silc_buffer_len(prim), FALSE);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_SEQUENCE:
- case SILC_ASN1_TAG_SET:
- {
- /* SEQUENCE/SET is a sequence of types. Sequences are opened and
- encoded recursively by calling this same encoder. */
- memset(&buf, 0, sizeof(buf));
-
- /* Get type, tag and options for the first argument in recursion */
- SILC_ASN1_ARGS(asn1, rtype, rtag, rclass, ropts);
-
- silc_stack_push(stack2, &frame);
- ret = silc_asn1_encoder(asn1, stack2, stack1, rtype, rtag, rclass,
- ropts, &buf, depth + 1, FALSE);
- if (!ret) {
- SILC_LOG_DEBUG(("Error traversing a SEQUENCE/SET"));
- silc_stack_pop(stack2);
- goto fail;
- }
-
- /* Encode the sequence */
- len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_CONSTRUCTED,
- tag, buf.data, silc_buffer_len(&buf), indef);
- SILC_ASN1_BUFFER_FREE(&buf, stack2);
- silc_stack_pop(stack2);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_INTEGER:
- case SILC_ASN1_TAG_ENUM:
- {
- /* Integer */
- SilcMPInt *mpint = va_arg(asn1->ap, SilcMPInt *);
- if (!mpint)
- break;
-
- memset(&buf, 0, sizeof(buf));
- if (silc_mp_cmp_ui(mpint, 0) < 0) {
- /* XXX TODO, negative integer. Take 2s complement, then store
- bytes in 1s complement */
- } else {
- /* Positive */
- len = silc_mp_sizeinbase(mpint, 2);
- if (!(len & 7))
- len = ((len + 7) / 8) + 1;
- else
- len = (len + 7) / 8;
- silc_stack_push(stack2, &frame);
- silc_buffer_srealloc_size(stack2, &buf,
- silc_buffer_truelen(&buf) + len);
- buf.data[0] = 0x00;
- silc_mp_mp2bin_noalloc(mpint, buf.data, silc_buffer_len(&buf));
- }
-
- /* Encode the integer */
- len = silc_ber_encoded_len(tag, len, indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, buf.data, silc_buffer_len(&buf), FALSE);
- SILC_ASN1_BUFFER_FREE(&buf, stack2);
- silc_stack_pop(stack2);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_SHORT_INTEGER:
- {
- /* Short Integer */
- SilcUInt32 sint = va_arg(asn1->ap, SilcUInt32);
- SilcMPInt z;
-
- if (tag == SILC_ASN1_TAG_SHORT_INTEGER)
- tag = SILC_ASN1_TAG_INTEGER;
-
- memset(&buf, 0, sizeof(buf));
-
- silc_stack_push(stack2, &frame);
- silc_mp_sinit(stack2, &z);
- silc_mp_set_ui(&z, sint);
-
- len = silc_mp_sizeinbase(&z, 2);
- if (!(len & 7))
- len = ((len + 7) / 8) + 1;
- else
- len = (len + 7) / 8;
- silc_buffer_srealloc_size(stack2, &buf,
- silc_buffer_truelen(&buf) + len);
- buf.data[0] = 0x00;
- silc_mp_mp2bin_noalloc(&z, buf.data, silc_buffer_len(&buf));
- silc_mp_uninit(&z);
-
- /* Encode the integer */
- len = silc_ber_encoded_len(tag, len, indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, buf.data, silc_buffer_len(&buf), FALSE);
- SILC_ASN1_BUFFER_FREE(&buf, stack2);
- silc_stack_pop(stack2);
- if (!ret)
- goto fail;
- break;
- }
- break;
-
- case SILC_ASN1_TAG_OID:
- {
- /* Object identifier */
- char *cp, *oidstr = va_arg(asn1->ap, char *);
- SilcUInt32 words[24], oid, mask;
- int i, k, c = 0;
- if (!oidstr)
- break;
-
- /* Get OID words from the string */
- cp = strchr(oidstr, '.');
- while (cp) {
- if (sscanf(oidstr, "%lu", &oid) != 1) {
- SILC_LOG_DEBUG(("Malformed OID string"));
- goto fail;
- }
- if (c + 1 > sizeof(words) / sizeof(words[0]))
- goto fail;
- words[c++] = oid;
- oidstr = cp + 1;
- cp = strchr(oidstr, '.');
-
- if (!cp) {
- if (sscanf(oidstr, "%lu", &oid) != 1) {
- SILC_LOG_DEBUG(("Malformed OID string"));
- goto fail;
- }
- if (c + 1 > sizeof(words) / sizeof(words[0]))
- goto fail;
- words[c++] = oid;
- break;
- }
- }
- if (c < 2) {
- SILC_LOG_DEBUG(("Malfromed OID string"));
- goto fail;
- }
-
- /* Get OID data length */
- for (i = 2, len = 1; i < c; i++) {
- if (words[i]) {
- for (oid = words[i]; oid; oid >>= 7)
- len++;
- continue;
- }
- len++;
- }
-
- /* Encode the OID */
- memset(&buf, 0, sizeof(buf));
- silc_stack_push(stack2, &frame);
- silc_buffer_srealloc_size(stack2, &buf,
- silc_buffer_truelen(&buf) + len);
- buf.data[0] = words[0] * 40 + words[1];
- for (i = 2, len = 1; i < c; i++) {
- oid = words[i];
- if (oid) {
- k = len;
- mask = 0;
- while (oid) {
- buf.data[len++] = (oid & 0x7f) | mask;
- oid >>= 7;
- mask |= 0x80;
- }
- mask = len - 1;
- while (k < mask) {
- oid = buf.data[k];
- buf.data[k] = buf.data[mask];
- buf.data[mask] = oid;
- k++;
- mask--;
- }
-
- continue;
- }
- buf.data[len++] = 0x00;
- }
-
- len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, buf.data, silc_buffer_len(&buf), FALSE);
- SILC_ASN1_BUFFER_FREE(&buf, stack2);
- silc_stack_pop(stack2);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_BOOLEAN:
- {
- /* Encodes boolean (TRUE/FALSE) value */
- unsigned char val[1];
- val[0] = (va_arg(asn1->ap, SilcUInt32) ? 0xff : 0x00);
-
- assert(indef == FALSE);
- len = silc_ber_encoded_len(tag, 1, FALSE);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, val, 1, FALSE);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_BIT_STRING:
- {
- /* Encode the data as is, with the bit padding. d_len is in bits. */
- unsigned char *d = va_arg(asn1->ap, unsigned char *);
- SilcUInt32 d_len = va_arg(asn1->ap, SilcUInt32);
- unsigned char pad[1];
- if (!d)
- break;
-
- pad[0] = (8 - (d_len & 7)) & 7;
- d_len = ((d_len + 7) / 8) + 1;
-
- memset(&buf, 0, sizeof(buf));
- silc_stack_push(stack2, &frame);
- silc_buffer_srealloc_size(stack2, &buf,
- silc_buffer_truelen(&buf) + d_len);
- silc_buffer_put(&buf, pad, 1);
- silc_buffer_pull(&buf, 1);
- silc_buffer_put(&buf, d, d_len - 1);
- silc_buffer_push(&buf, 1);
-
- len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, buf.data, silc_buffer_len(&buf), indef);
- SILC_ASN1_BUFFER_FREE(&buf, stack2);
- silc_stack_pop(stack2);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_NULL:
- {
- /* Encode empty BER block */
- SilcBool val = va_arg(asn1->ap, SilcUInt32);
-
- assert(indef == FALSE);
-
- if (!val)
- break;
-
- len = silc_ber_encoded_len(tag, 0, FALSE);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, NULL, 0, FALSE);
- if (!ret)
- goto fail;
-
- break;
- }
-
- case SILC_ASN1_TAG_UTC_TIME:
- {
- /* Universal encoded time string */
- SilcTime timeval = va_arg(asn1->ap, SilcTime);
- char timestr[32];
- if (!timeval)
- break;
-
- if (!silc_time_universal_string(timeval, timestr, sizeof(timestr))) {
- SILC_LOG_DEBUG(("Could not encode universal time string"));
- goto fail;
- }
-
- len = silc_ber_encoded_len(tag, strlen(timestr), indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, timestr, strlen(timestr), indef);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_GENERALIZED_TIME:
- {
- /* Generalized encoded time string */
- SilcTime timeval = va_arg(asn1->ap, SilcTime);
- char timestr[32];
- if (!timeval)
- break;
-
- if (!silc_time_generalized_string(timeval, timestr, sizeof(timestr))) {
- SILC_LOG_DEBUG(("Could not encode generalized time string"));
- goto fail;
- }
-
- len = silc_ber_encoded_len(tag, strlen(timestr), indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, timestr, strlen(timestr), indef);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_UTF8_STRING:
- {
- /* UTF-8 string */
- unsigned char *d = va_arg(asn1->ap, unsigned char *);
- SilcUInt32 d_len = va_arg(asn1->ap, SilcUInt32);
- if (!d)
- break;
-
- /* By default all strings that get here should already be UTF-8 */
- if (!silc_utf8_valid(d, d_len)) {
- SILC_LOG_DEBUG(("Malformed UTF-8 string"));
- goto fail;
- }
-
- len = silc_ber_encoded_len(tag, d_len, indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, d, d_len, indef);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_OCTET_STRING:
- {
- /* Octet string. Put data as is. */
- unsigned char *d = va_arg(asn1->ap, unsigned char *);
- SilcUInt32 d_len = va_arg(asn1->ap, SilcUInt32);
-
- len = silc_ber_encoded_len(tag, d_len, indef);
- dest = silc_buffer_srealloc_size(SILC_ASN1_STACK(stack1, asn1), dest,
- silc_buffer_truelen(dest) + len);
- ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
- tag, d, d_len, indef);
- if (!ret)
- goto fail;
- break;
- }
-
- case SILC_ASN1_TAG_NUMERIC_STRING:
- {
- /* Numerical (digit) string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_NUMERICAL);
- break;
- }
-
- case SILC_ASN1_TAG_PRINTABLE_STRING:
- {
- /* Printable string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_PRINTABLE);
- break;
- }
-
- case SILC_ASN1_TAG_TELETEX_STRING:
- {
- /* Teletex (T61) string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_TELETEX);
- break;
- }
-
- case SILC_ASN1_TAG_IA5_STRING:
- {
- /* US ASCII string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_ASCII);
- break;
- }
-
- case SILC_ASN1_TAG_VISIBLE_STRING:
- {
- /* Visible string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_VISIBLE);
- break;
- }
-
- case SILC_ASN1_TAG_UNIVERSAL_STRING:
- {
- /* Universal (UCS-4) string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_UNIVERSAL);
- break;
- }
-
- case SILC_ASN1_TAG_UNRESTRICTED_STRING:
- case SILC_ASN1_TAG_GENERAL_STRING:
- {
- /* Handle now unrestricted and general as 8-bit ascii, which
- probably isn't correct. */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_ASCII);
- break;
- }
-
- case SILC_ASN1_TAG_BMP_STRING:
- {
- /* BMP (UCS-2) string */
- SILC_ASN1_ENCODE_STRING(SILC_STRING_UNIVERSAL);
- break;
- }
-
- case SILC_ASN1_TAG_ODE:
- case SILC_ASN1_TAG_ETI:
- case SILC_ASN1_TAG_REAL:
- case SILC_ASN1_TAG_EMBEDDED:
- case SILC_ASN1_TAG_ROI:
- case SILC_ASN1_TAG_VIDEOTEX_STRING:
- case SILC_ASN1_TAG_GRAPHIC_STRING:
- {
- SILC_NOT_IMPLEMENTED("Unsupported ASN.1 tag");
- ret = FALSE;
- goto fail;
- break;
- }
-
- default:
- SILC_LOG_DEBUG(("Invalid ASN.1 tag `%d'. Cannot encode ASN.1.", type));
- ret = FALSE;
- goto fail;
- break;
- }
-
- cont:
- if (len)
- silc_buffer_pull(dest, len);
- if (primitive) {
- ret = TRUE;
- goto ok;
- }
-
- /* Get next type, tag and options */
- SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
- if (type == SILC_ASN1_END) {
- ret = TRUE;
- goto ok;
- }
- }
-
- fail:
- SILC_LOG_DEBUG(("Error encoding type %d (depth %d)", type, depth));
-
- ok:
- if (ptr)
- len = dest->data - ptr;
- else
- len = dest->data - dest->head;
- silc_buffer_push(dest, len);
-
- return ret;
-}
-
-SilcBool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...)
-{
- SilcAsn1Tag type, tag;
- SilcAsn1Options opts;
- SilcBerClass ber_class;
- SilcStackFrame frame1, frame2;
- SilcStack stack1 = NULL, orig;
- SilcBool ret;
-
- if (!asn1)
- return FALSE;
-
- va_start(asn1->ap, dest);
-
- orig = asn1->orig_stack;
- asn1->orig_stack = NULL;
-
- /* Get the first arguments and call the encoder. */
- SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
- if (!type) {
- va_end(asn1->ap);
- return FALSE;
- }
-
- /* Handle internal options for encoder. */
- if (type == SILC_ASN1_TAG_OPTS) {
- SilcUInt32 o = va_arg(asn1->ap, SilcUInt32);
-
- if (o & SILC_ASN1_ALLOC) {
- /* User wants to alloate everything. Set the stack to NULL so
- that stack aware calls revert to normal allocation routines. */
- stack1 = asn1->stack1;
- asn1->stack1 = NULL;
- asn1->orig_stack = orig;
- }
-
- if (o & SILC_ASN1_ACCUMUL) {
- /* If accumul flag is not set yet, then push the stack. */
- if (!asn1->accumul) {
- silc_stack_push(asn1->stack1, NULL);
- asn1->accumul = 1;
- }
- }
-
- /* Take again the arguments */
- SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
- } else {
- /* No flags set, all flags will be reset. */
-
- /* If accumul flag is set now pop the stack so that all accumulated
- memory becomes free again. */
- if (asn1->accumul) {
- silc_stack_pop(asn1->stack1);
- asn1->accumul = 0;
- }
- }
-
- /* Push the stack for normal allocation from stack. */
- if (!asn1->accumul)
- silc_stack_push(asn1->stack1, &frame1);
-
- /* Start encoding */
- silc_stack_push(asn1->stack2, &frame2);
- ret = silc_asn1_encoder(asn1, asn1->stack1, asn1->stack2,
- type, tag, ber_class, opts, dest, 0, FALSE);
- silc_stack_pop(asn1->stack2);
-
- /* Pop the stack to free normal allocations from stack. */
- if (!asn1->accumul)
- silc_stack_pop(asn1->stack1);
-
- /* If SILC_ASN1_ALLOC flag was set, restore the stack. */
- if (stack1 && !asn1->stack1)
- asn1->stack1 = stack1;
-
- asn1->orig_stack = orig;
-
- va_end(asn1->ap);
-
- return ret;
-}