Rewrite ASN1_OBJECT content to ascii/text conversion.
authorjsing <jsing@openbsd.org>
Wed, 2 Mar 2022 11:28:00 +0000 (11:28 +0000)
committerjsing <jsing@openbsd.org>
Wed, 2 Mar 2022 11:28:00 +0000 (11:28 +0000)
Rewrite the ASN1_OBJECT content to ascii/text conversion code using CBB and
CBS. Currently there is a strange split with i2t_ASN1_OBJECT() calling
OBJ_obj2txt() which implements the conversion, while OBJ_txt2obj() calls
back into the misnamed a2d_ASN1_OBJECT() function. Move the conversion
code into asn1/a_object.c and have OBJ_txt2obj() call that instead.

ok inoguchi@ tb@

lib/libcrypto/asn1/a_object.c
lib/libcrypto/asn1/asn1_locl.h
lib/libcrypto/objects/obj_dat.c

index 1407f7d..f26026e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: a_object.c,v 1.37 2022/01/07 11:13:54 tb Exp $ */
+/* $OpenBSD: a_object.c,v 1.38 2022/03/02 11:28:00 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -270,37 +270,190 @@ a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
        return (0);
 }
 
+static int
+oid_parse_arc(CBS *cbs, uint64_t *out_arc)
+{
+       uint64_t arc = 0;
+       uint8_t val;
+
+       do {
+               if (!CBS_get_u8(cbs, &val))
+                       return 0;
+               if (arc == 0 && val == 0x80)
+                       return 0;
+               if (arc > (UINT64_MAX >> 7))
+                       return 0;
+               arc = (arc << 7) | (val & 0x7f);
+       } while (val & 0x80);
+
+       *out_arc = arc;
+
+       return 1;
+}
+
+static int
+oid_add_arc_txt(CBB *cbb, uint64_t arc, int first)
+{
+       const char *fmt = ".%llu";
+       char s[22]; /* Digits in decimal representation of 2^64-1, plus '.' and NUL. */
+       int n;
+
+       if (first)
+               fmt = "%llu";
+       n = snprintf(s, sizeof(s), fmt, (unsigned long long)arc);
+       if (n < 0 || (size_t)n >= sizeof(s))
+               return 0;
+       if (!CBB_add_bytes(cbb, s, n))
+               return 0;
+
+       return 1;
+}
+
+static int
+c2a_ASN1_OBJECT(CBS *cbs, CBB *cbb)
+{
+       uint64_t arc, si1, si2;
+
+       /*
+        * X.690 section 8.19 - the first two subidentifiers are encoded as
+        * (x * 40) + y, with x being limited to [0,1,2].
+        */
+       if (!oid_parse_arc(cbs, &arc))
+               return 0;
+       if ((si1 = arc / 40) > 2)
+               si1 = 2;
+       si2 = arc - si1 * 40;
+
+       if (!oid_add_arc_txt(cbb, si1, 1))
+               return 0;
+       if (!oid_add_arc_txt(cbb, si2, 0))
+               return 0;
+
+       while (CBS_len(cbs) > 0) {
+               if (!oid_parse_arc(cbs, &arc))
+                       return 0;
+               if (!oid_add_arc_txt(cbb, arc, 0))
+                       return 0;
+       }
+
+       /* NUL terminate. */
+       if (!CBB_add_u8(cbb, 0))
+               return 0;
+
+       return 1;
+}
+
+static int
+i2t_ASN1_OBJECT_oid(const ASN1_OBJECT *aobj, CBB *cbb)
+{
+       CBS cbs;
+
+       CBS_init(&cbs, aobj->data, aobj->length);
+
+       return c2a_ASN1_OBJECT(&cbs, cbb);
+}
+
+static int
+i2t_ASN1_OBJECT_name(const ASN1_OBJECT *aobj, CBB *cbb, const char **out_name)
+{
+       const char *name;
+       int nid;
+
+       if ((nid = OBJ_obj2nid(aobj)) == NID_undef)
+               return 0;
+
+       if ((name = OBJ_nid2ln(nid)) == NULL)
+               name = OBJ_nid2sn(nid);
+       if (name == NULL)
+               return 0;
+
+       *out_name = name;
+
+       if (!CBB_add_bytes(cbb, name, strlen(name)))
+               return 0;
+
+       /* NUL terminate. */
+       if (!CBB_add_u8(cbb, 0))
+               return 0;
+
+       return 1;
+}
+
+static int
+i2t_ASN1_OBJECT_cbb(const ASN1_OBJECT *aobj, CBB *cbb, int no_name)
+{
+       const char *name;
+
+       if (!no_name) {
+               if (i2t_ASN1_OBJECT_name(aobj, cbb, &name))
+                       return 1;
+               if (name != NULL)
+                       return 0;
+       }
+       return i2t_ASN1_OBJECT_oid(aobj, cbb);
+}
+
+int
+i2t_ASN1_OBJECT_internal(const ASN1_OBJECT *aobj, char *buf, int buf_len, int no_name)
+{
+       uint8_t *data = NULL;
+       size_t data_len;
+       CBB cbb;
+       int ret = 0;
+
+       if (buf_len < 0)
+               return 0;
+       if (buf_len > 0)
+               buf[0] = '\0';
+
+       if (!CBB_init(&cbb, 0))
+               goto err;
+       if (!i2t_ASN1_OBJECT_cbb(aobj, &cbb, no_name))
+               goto err;
+       if (!CBB_finish(&cbb, &data, &data_len))
+               goto err;
+
+       ret = strlcpy(buf, data, buf_len);
+ err:
+       CBB_cleanup(&cbb);
+       free(data);
+
+       return ret;
+}
+
 int
-i2t_ASN1_OBJECT(char *buf, int buf_len, const ASN1_OBJECT *a)
+i2t_ASN1_OBJECT(char *buf, int buf_len, const ASN1_OBJECT *aobj)
 {
-       return OBJ_obj2txt(buf, buf_len, a, 0);
+       return i2t_ASN1_OBJECT_internal(aobj, buf, buf_len, 0);
 }
 
 int
-i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a)
+i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *aobj)
 {
-       char *tmp = NULL;
-       size_t tlen = 256;
-       int i = -1;
+       uint8_t *data = NULL;
+       size_t data_len;
+       CBB cbb;
+       int ret = -1;
 
-       if ((a == NULL) || (a->data == NULL))
-               return(BIO_write(bp, "NULL", 4));
-       if ((tmp = malloc(tlen)) == NULL)
-               return -1;
-       i = i2t_ASN1_OBJECT(tmp, tlen, a);
-       if (i > (int)(tlen - 1)) {
-               freezero(tmp, tlen);
-               if ((tmp = malloc(i + 1)) == NULL)
-                       return -1;
-               tlen = i + 1;
-               i = i2t_ASN1_OBJECT(tmp, tlen, a);
+       if (aobj == NULL || aobj->data == NULL)
+               return BIO_write(bp, "NULL", 4);
+
+       if (!CBB_init(&cbb, 0))
+               goto err;
+       if (!i2t_ASN1_OBJECT_cbb(aobj, &cbb, 0)) {
+               ret = BIO_write(bp, "<INVALID>", 9);
+               goto err;
        }
-       if (i <= 0)
-               i = BIO_write(bp, "<INVALID>", 9);
-       else
-               i = BIO_write(bp, tmp, i);
-       freezero(tmp, tlen);
-       return (i);
+       if (!CBB_finish(&cbb, &data, &data_len))
+               goto err;
+               
+       ret = BIO_write(bp, data, data_len);
+
+ err:
+       CBB_cleanup(&cbb);
+       free(data);
+
+       return ret;
 }
 
 ASN1_OBJECT *
index 7d34d7c..bb6a9fc 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: asn1_locl.h,v 1.20 2022/01/14 08:53:53 tb Exp $ */
+/* $OpenBSD: asn1_locl.h,v 1.21 2022/03/02 11:28:00 jsing Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2006.
  */
@@ -201,4 +201,7 @@ int asn1_get_object_cbs(CBS *cbs, int der_mode, uint8_t *out_class,
 
 int asn1_tag2charwidth(int tag);
 
+int i2t_ASN1_OBJECT_internal(const ASN1_OBJECT *aobj, char *buf, int buf_len,
+    int no_name);
+
 __END_HIDDEN_DECLS
index 03e65f1..786bed6 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: obj_dat.c,v 1.47 2022/02/12 03:01:59 jsing Exp $ */
+/* $OpenBSD: obj_dat.c,v 1.48 2022/03/02 11:28:00 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -524,83 +524,12 @@ OBJ_txt2obj(const char *s, int no_name)
 }
 
 int
-OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name)
+OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *aobj, int no_name)
 {
-       int i, ret = 0, len, nid, first = 1;
-       const unsigned char *p;
-       uint64_t l;
-
-       /* Ensure that, at every state, |buf| is NUL-terminated. */
-       if (buf_len > 0)
-               buf[0] = '\0';
-
-       if ((a == NULL) || (a->data == NULL))
-               goto err;
-
-       if (!no_name && (nid = OBJ_obj2nid(a)) != NID_undef) {
-               const char *s;
-               s = OBJ_nid2ln(nid);
-               if (s == NULL)
-                       s = OBJ_nid2sn(nid);
-               if (s) {
-                       ret = strlcpy(buf, s, buf_len);
-                       goto out;
-               }
-       }
-
-       len = a->length;
-       p = a->data;
-
-       while (len > 0) {
-               l = 0;
-               for (;;) {
-                       unsigned char c = *p++;
-                       len--;
-                       if ((len == 0) && (c & 0x80))
-                               goto err;
-                       l |= c & 0x7f;
-                       if (!(c & 0x80))
-                               break;
-                       if (l > (UINT64_MAX >> 7L))
-                               goto err;
-                       l <<= 7L;
-               }
-
-               if (first) {
-                       first = 0;
-                       if (l >= 80) {
-                               i = 2;
-                               l -= 80;
-                       } else {
-                               i = (int)(l / 40);
-                               l -= (long)(i * 40);
-                       }
-                       if (buf_len > 1) {
-                               *buf++ = i + '0';
-                               *buf = '\0';
-                               buf_len--;
-                       }
-                       ret++;
-               }
-
-               i = snprintf(buf, buf_len, ".%llu", l);
-               if (i < 0)
-                       goto err;
-               if (i >= buf_len) {
-                       buf_len = 0;
-               } else {
-                       buf += i;
-                       buf_len -= i;
-               }
-               ret += i;
-       }
-
- out:
-       return ret;
+       if (aobj == NULL || aobj->data == NULL)
+               return 0;
 
- err:
-       ret = 0;
-       goto out;
+       return i2t_ASN1_OBJECT_internal(aobj, buf, buf_len, no_name);
 }
 
 int