asn1_object_id_der: introduced

This introduces a function to encode from a textual object
identifier to a DER encoding. This complements asn1_get_object_id_der().

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
diff --git a/NEWS b/NEWS
index 934f655..737f2d0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,13 @@
 GNU Libtasn1 NEWS                                     -*- outline -*-
 
-* Noteworthy changes in release 4.15.1 (unreleased) [stable]
+* Noteworthy changes in release 4.16.0 (unreleased) [stable]
 - asn1_decode_simple_ber: added support for constructed definite
   octet strings. This allows this function decode the whole set of
   BER encodings for OCTET STRINGs.
 - asn1_get_object_id_der: enhance the range of decoded OIDs (#25).
+  This also makes OID encoding and decoding more strict on invalid
+  input.
+- asn1_object_id_der: introduced.
 
 
 * Noteworthy changes in release 4.15.0 (released 2019-11-21) [stable]
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 6fe5ab1..787c304 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -71,6 +71,7 @@
 gdoc_MANS += man/asn1_length_der.3
 gdoc_MANS += man/asn1_octet_der.3
 gdoc_MANS += man/asn1_encode_simple_der.3
+gdoc_MANS += man/asn1_object_id_der.3
 gdoc_MANS += man/asn1_bit_der.3
 gdoc_MANS += man/asn1_der_coding.3
 gdoc_MANS += man/asn1_write_value.3
@@ -121,6 +122,7 @@
 gdoc_TEXINFOS += texi/asn1_length_der.texi
 gdoc_TEXINFOS += texi/asn1_octet_der.texi
 gdoc_TEXINFOS += texi/asn1_encode_simple_der.texi
+gdoc_TEXINFOS += texi/asn1_object_id_der.texi
 gdoc_TEXINFOS += texi/asn1_bit_der.texi
 gdoc_TEXINFOS += texi/asn1_der_coding.texi
 gdoc_TEXINFOS += texi/asn1_write_value.texi
diff --git a/lib/coding.c b/lib/coding.c
index 08aca3d..f7e15bb 100644
--- a/lib/coding.c
+++ b/lib/coding.c
@@ -33,10 +33,6 @@
 #include "minmax.h"
 #include <structure.h>
 
-/* for unit testing */
-extern ASN1_API int
-_asn1_object_id_der (const char *str, unsigned char *der, int *der_len);
-
 #define MAX_TAG_LEN 16
 
 /******************************************************/
@@ -125,7 +121,7 @@
 /******************************************************/
 static void
 _asn1_tag_der (unsigned char class, unsigned int tag_value,
-	       unsigned char *ans, int *ans_len)
+	       unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len)
 {
   int k;
   unsigned char temp[ASN1_MAX_TAG_SIZE];
@@ -361,7 +357,7 @@
 /*   ASN1_SUCCESS if succesful                        */
 /*   or an error value.                               */
 /******************************************************/
-int
+static int
 _asn1_object_id_der (const char *str, unsigned char *der, int *der_len)
 {
   int len_len, counter, max_len;
@@ -436,6 +432,47 @@
   return ASN1_SUCCESS;
 }
 
+/**
+ * asn1_object_id_der:
+ * @str: An object identifier in numeric, dot format.
+ * @der: buffer to hold the returned encoding (may be %NULL).
+ * @der_len: initially the size of @der; will hold the final size.
+ * @flags: must be zero
+ *
+ * Creates the DER encoding of the provided object identifier.
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID
+ *   if @str is not a valid OID, %ASN1_MEM_ERROR if the @der
+ *   vector isn't big enough and in this case @der_len will contain the
+ *   length needed.
+ **/
+int
+asn1_object_id_der (const char *str, unsigned char *der, int *der_len, unsigned flags)
+{
+  unsigned char tag_der[MAX_TAG_LEN];
+  int tag_len = 0, r;
+  int max_len = *der_len;
+
+  *der_len = 0;
+
+  _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), ETYPE_TAG (ASN1_ETYPE_OBJECT_ID),
+                 tag_der, &tag_len);
+
+  if (max_len > tag_len)
+    {
+      memcpy(der, tag_der, tag_len);
+    }
+  max_len -= tag_len;
+  der += tag_len;
+
+  r = _asn1_object_id_der (str, der, &max_len);
+  if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS)
+    {
+      *der_len = max_len + tag_len;
+    }
+
+  return r;
+}
 
 static const unsigned char bit_mask[] =
   { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };
diff --git a/lib/includes/libtasn1.h.in b/lib/includes/libtasn1.h.in
index 7e4baaf..8664fee 100644
--- a/lib/includes/libtasn1.h.in
+++ b/lib/includes/libtasn1.h.in
@@ -485,6 +485,10 @@
                           int der_len, int *ret_len,
                           char *str, int str_size);
 
+extern ASN1_API int
+  asn1_object_id_der (const char *str, unsigned char *der, int *der_len,
+                      unsigned flags);
+
 /* Compatibility types */
 
 /**
diff --git a/lib/libtasn1.map b/lib/libtasn1.map
index 3ce75d6..1f76486 100644
--- a/lib/libtasn1.map
+++ b/lib/libtasn1.map
@@ -62,7 +62,7 @@
     *;
 };
 
-LIBTASN1_4_15_1 {
+LIBTASN1_4_16_0 {
   global:
-    _asn1_object_id_der;
+    asn1_object_id_der;
 } LIBTASN1_0_3;
diff --git a/tests/object-id-encoding.c b/tests/object-id-encoding.c
index 7c39448..ee2271d 100644
--- a/tests/object-id-encoding.c
+++ b/tests/object-id-encoding.c
@@ -24,10 +24,6 @@
 
 #include "libtasn1.h"
 
-extern ASN1_API int
-_asn1_object_id_der (const char *str, unsigned char *der, int *der_len);
-
-
 struct tv
 {
   int der_len;
@@ -89,13 +85,12 @@
 main (int argc, char *argv[])
 {
   unsigned char der[128];
-  int ret, der_len, i, j, exp_der_len;
-  const unsigned char *exp_der;
+  int ret, der_len, i, j;
 
   for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++)
     {
       der_len = sizeof(der);
-      ret = _asn1_object_id_der(tv[i].oid, der, &der_len);
+      ret = asn1_object_id_der(tv[i].oid, der, &der_len);
       if (ret != ASN1_SUCCESS)
 	{
 	  if (ret == tv[i].expected_error)
@@ -113,21 +108,18 @@
           return 1;
         }
 
-      /* the internal function does not insert the tag */
-      exp_der = tv[i].der + 1;
-      exp_der_len = tv[i].der_len - 1;
-      if (der_len != exp_der_len || memcmp(der, exp_der, der_len-1) != 0)
+      if (der_len != tv[i].der_len || memcmp(der, tv[i].der, der_len) != 0)
 	{
 	  fprintf (stderr,
 		   "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n",
-		   __LINE__, (unsigned long) i, tv[i].oid, der_len, exp_der_len);
+		   __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len);
           fprintf(stderr, "\nGot:\t\t");
           for (j=0;j<der_len;j++)
             fprintf(stderr, "%.2x", der[j]);
 
           fprintf(stderr, "\nExpected:\t");
-          for (j=0;j<exp_der_len;j++)
-            fprintf(stderr, "%.2x", exp_der[j]);
+          for (j=0;j<tv[i].der_len;j++)
+            fprintf(stderr, "%.2x", tv[i].der[j]);
           fprintf(stderr, "\n");
 
 	  return 1;