Added SILC Thread Queue API
[silc.git] / lib / silcasn1 / silcasn1_encode.c
index a0504f55a8ca000e5b6642e899661561f6cc879c..58dfbf278bcdedbcf62961c4e7b56a2962661f85 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2003 - 2005 Pekka Riikonen
+  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
 
 */
 
-#include "silcincludes.h"
+#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
     goto fail;                                                         \
   }                                                                    \
   silc_stack_push(asn1->stack2, &frame);                               \
-  s = silc_smalloc_ua(stack2, s_len + 1);                              \
+  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(stack1, dest,                       \
+  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);                          \
    is TRUE if this encoder receives one primitive type as argument.  If
    it is a constructed type it must be FALSE value. */
 
-static bool
+static SilcBool
 silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
                  SilcAsn1Tag type, SilcAsn1Tag tag, SilcBerClass ber_class,
                  SilcAsn1Options opts, SilcBuffer dest, SilcUInt32 depth,
-                 bool primitive)
+                 SilcBool primitive)
 {
   unsigned char *ptr = dest->data;
   SilcAsn1Tag rtype, rtag;
   SilcAsn1Options ropts;
   SilcBerClass rclass;
   SilcUInt32 len = 0;
-  bool ret = FALSE, indef;
+  SilcBool ret = FALSE, indef;
   SilcBufferStruct buf;
   SilcStackFrame frame;
 
@@ -91,12 +94,19 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
     indef = (opts & SILC_ASN1_INDEFINITE ? TRUE : FALSE);
 
     /* By default UNIVERSAL is implied unless the following conditions
-       are met when CONTEXT will apply. */
+       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 (tag != type ||
-         opts & SILC_ASN1_IMPLICIT ||
-         opts & SILC_ASN1_EXPLICIT)
-       ber_class = SILC_BER_CLASS_CONTEXT;
+      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
@@ -129,19 +139,21 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
       ret = silc_asn1_encoder(asn1, stack2, stack1, type, type,
                              SILC_BER_CLASS_UNIVERSAL, opts,
                              &buf, depth + 1, primitive);
-      silc_stack_pop(stack2);
 
       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(stack1, dest,
+      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) {
@@ -181,7 +193,7 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
 
          /* Now encode with implicit tagging */
          len = silc_ber_encoded_len(tag, d_len, FALSE);
-         dest = silc_buffer_srealloc_size(stack1, dest,
+         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);
@@ -190,13 +202,33 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        } else {
          /* Copy the data directly into the tree. */
          len = silc_buffer_len(node);
-         dest = silc_buffer_srealloc_size(stack1, dest,
+         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:
       {
@@ -210,18 +242,20 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        silc_stack_push(stack2, &frame);
        ret = silc_asn1_encoder(asn1, stack2, stack1, rtype, rtag, rclass,
                                ropts, &buf, depth + 1, FALSE);
-       silc_stack_pop(stack2);
        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(stack1, dest,
+       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;
@@ -241,40 +275,84 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
             bytes in 1s complement */
        } else {
          /* Positive */
-         len = (silc_mp_sizeinbase(mpint, 2) + 7) / 8;
-         len += len & 7 ? 1: 0;
+         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(stack1, dest,
+       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, c = -1;
+       int i, k, c = 0;
        if (!oidstr)
          break;
 
        /* Get OID words from the string */
        cp = strchr(oidstr, '.');
        while (cp) {
-         c = sscanf(oidstr, "%lu", (unsigned long *)&oid);
-         if (c < 1) {
+         if (sscanf(oidstr, "%lu", &oid) != 1) {
            SILC_LOG_DEBUG(("Malformed OID string"));
            goto fail;
          }
@@ -283,6 +361,17 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
          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"));
@@ -308,7 +397,7 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        for (i = 2, len = 1; i < c; i++) {
          oid = words[i];
          if (oid) {
-           c = len;
+           k = len;
            mask = 0;
            while (oid) {
              buf.data[len++] = (oid & 0x7f) | mask;
@@ -316,11 +405,11 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
              mask |= 0x80;
            }
            mask = len - 1;
-           while (c < mask) {
-             oid = buf.data[c];
-             buf.data[c] = buf.data[mask];
+           while (k < mask) {
+             oid = buf.data[k];
+             buf.data[k] = buf.data[mask];
              buf.data[mask] = oid;
-             c++;
+             k++;
              mask--;
            }
 
@@ -329,11 +418,12 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
          buf.data[len++] = 0x00;
        }
 
-       len = silc_ber_encoded_len(tag, len, indef);
-       dest = silc_buffer_srealloc_size(stack1, dest,
+       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;
@@ -348,7 +438,7 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
 
        assert(indef == FALSE);
        len = silc_ber_encoded_len(tag, 1, FALSE);
-       dest = silc_buffer_srealloc_size(stack1, dest,
+       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);
@@ -379,10 +469,11 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        silc_buffer_push(&buf, 1);
 
        len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), indef);
-       dest = silc_buffer_srealloc_size(stack1, dest,
+       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;
@@ -392,14 +483,21 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
     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(stack1, dest,
+       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;
       }
 
@@ -417,7 +515,7 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        }
 
        len = silc_ber_encoded_len(tag, strlen(timestr), indef);
-       dest = silc_buffer_srealloc_size(stack1, dest,
+       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);
@@ -440,7 +538,7 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        }
 
        len = silc_ber_encoded_len(tag, strlen(timestr), indef);
-       dest = silc_buffer_srealloc_size(stack1, dest,
+       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);
@@ -464,7 +562,7 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
        }
 
        len = silc_ber_encoded_len(tag, d_len, indef);
-       dest = silc_buffer_srealloc_size(stack1, dest,
+       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);
@@ -475,8 +573,17 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
 
     case SILC_ASN1_TAG_OCTET_STRING:
       {
-       /* Octet string.  We put it in as 8-bit ASCII */
-       SILC_ASN1_ENCODE_STRING(SILC_STRING_ASCII);
+       /* 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;
       }
 
@@ -588,25 +695,27 @@ silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
   return ret;
 }
 
-bool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...)
+SilcBool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...)
 {
   SilcAsn1Tag type, tag;
   SilcAsn1Options opts;
   SilcBerClass ber_class;
   SilcStackFrame frame1, frame2;
-  SilcStack stack1 = NULL;
-  bool ret;
+  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);
-    asn1->ap = NULL;
     return FALSE;
   }
 
@@ -619,6 +728,7 @@ bool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...)
         that stack aware calls revert to normal allocation routines. */
       stack1 = asn1->stack1;
       asn1->stack1 = NULL;
+      asn1->orig_stack = orig;
     }
 
     if (o & SILC_ASN1_ACCUMUL) {
@@ -660,8 +770,9 @@ bool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...)
   if (stack1 && !asn1->stack1)
     asn1->stack1 = stack1;
 
+  asn1->orig_stack = orig;
+
   va_end(asn1->ap);
-  asn1->ap = NULL;
 
   return ret;
 }