--- /dev/null
+/* $OpenBSD: asn1_lib.c,v 1.51 2021/12/25 07:04:03 jsing Exp $ */
+/*
+ * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <limits.h>
+
+#include "bytestring.h"
+
+static int
+asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class,
+ int *out_constructed, uint32_t *out_tag_number)
+{
+ uint8_t tag_class, tag_val;
+ int tag_constructed;
+ uint32_t tag_number;
+
+ /*
+ * Decode ASN.1 identifier octets - see ITU-T X.690 section 8.1.2.
+ */
+
+ *out_class = 0;
+ *out_constructed = 0;
+ *out_tag_number = 0;
+
+ if (!CBS_get_u8(cbs, &tag_val))
+ return 0;
+
+ /*
+ * ASN.1 tag class, encoding (primitive or constructed) and tag number
+ * are encoded in one or more identifier octets - the first octet
+ * contains the 2 bit tag class, the 1 bit encoding type and 5 bits
+ * of tag number.
+ *
+ * For tag numbers larger than 30 (0x1e) the 5 bit tag number in the
+ * first octet is set to all ones (0x1f) - the tag number is then
+ * encoded in subsequent octets - each of which have a one bit
+ * continuation flag and 7 bits of tag number in big-endian form.
+ * The encoding should not contain leading zeros but can for BER.
+ */
+ tag_class = (tag_val >> 6) & 0x3;
+ tag_constructed = (tag_val >> 5) & 0x1;
+ tag_number = tag_val & 0x1f;
+
+ /* Long form. */
+ if (tag_number == 0x1f) {
+ tag_number = 0;
+ do {
+ if (!CBS_get_u8(cbs, &tag_val))
+ return 0;
+ if (der_mode && tag_number == 0 && tag_val == 0x80)
+ return 0;
+ if (tag_number > (UINT32_MAX >> 7))
+ return 0;
+ tag_number = tag_number << 7 | (tag_val & 0x7f);
+ } while ((tag_val & 0x80) != 0);
+ }
+
+ *out_class = tag_class;
+ *out_constructed = tag_constructed;
+ *out_tag_number = tag_number;
+
+ return 1;
+}
+
+static int
+asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite,
+ uint32_t *out_length)
+{
+ uint8_t len_bytes;
+ uint32_t length;
+ uint8_t val;
+
+ /*
+ * Decode ASN.1 length octets - see ITU-T X.690 section 8.1.3.
+ */
+
+ *out_length = 0;
+ *out_indefinite = 0;
+
+ if (!CBS_get_u8(cbs, &val))
+ return 0;
+
+ /*
+ * Short form - length is encoded in the lower 7 bits of a single byte.
+ */
+ if (val < 0x80) {
+ *out_length = val;
+ return 1;
+ }
+
+ /*
+ * Indefinite length - content continues until an End of Content (EOC)
+ * marker is reached. Must be used with constructed encoding.
+ */
+ if (val == 0x80) {
+ *out_indefinite = 1;
+ return 1;
+ }
+
+ /*
+ * Long form - the lower 7 bits of the first byte specifies the number
+ * of bytes used to encode the length, the following bytes specify the
+ * length in big-endian form. The encoding should not contain leading
+ * zeros but can for BER. A length value of 0x7f is invalid.
+ */
+ if ((len_bytes = val & 0x7f) == 0x7f)
+ return 0;
+
+ length = 0;
+
+ while (len_bytes-- > 0) {
+ if (!CBS_get_u8(cbs, &val))
+ return 0;
+ if (der_mode && length == 0 && val == 0)
+ return 0;
+ if (length > (UINT32_MAX >> 8))
+ return 0;
+ length = (length << 8) | val;
+ }
+
+ *out_length = length;
+
+ return 1;
+}
+
+int
+asn1_get_object_cbs(CBS *cbs, int der_mode, uint8_t *out_tag_class,
+ int *out_constructed, uint32_t *out_tag_number, int *out_indefinite,
+ uint32_t *out_length)
+{
+ int constructed, indefinite;
+ uint32_t tag_number, length;
+ uint8_t tag_class;
+
+ *out_tag_class = 0;
+ *out_constructed = 0;
+ *out_tag_number = 0;
+ *out_indefinite = 0;
+ *out_length = 0;
+
+ if (!asn1_get_identifier_cbs(cbs, der_mode, &tag_class, &constructed,
+ &tag_number))
+ return 0;
+ if (!asn1_get_length_cbs(cbs, der_mode, &indefinite, &length))
+ return 0;
+
+ /* Indefinite length can only be used with constructed encoding. */
+ if (indefinite && !constructed)
+ return 0;
+
+ *out_tag_class = tag_class;
+ *out_constructed = constructed;
+ *out_tag_number = tag_number;
+ *out_indefinite = indefinite;
+ *out_length = length;
+
+ return 1;
+}
-/* $OpenBSD: asn1_old_lib.c,v 1.1 2021/12/15 18:12:10 jsing Exp $ */
+/* $OpenBSD: asn1_old_lib.c,v 1.2 2021/12/25 07:04:03 jsing Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
#include <openssl/asn1.h>
#include <openssl/err.h>
-static int asn1_get_length(const unsigned char **pp, int *inf, long *rl, int max);
+#include "asn1_locl.h"
+
static void asn1_put_length(unsigned char **pp, int length);
static int
ASN1_get_object(const unsigned char **pp, long *plength, int *ptag,
int *pclass, long omax)
{
- int i, ret;
- long l;
- const unsigned char *p = *pp;
- int tag, xclass, inf;
- long max = omax;
-
- if (!max)
- goto err;
- ret = (*p & V_ASN1_CONSTRUCTED);
- xclass = (*p & V_ASN1_PRIVATE);
- i = *p & V_ASN1_PRIMITIVE_TAG;
- if (i == V_ASN1_PRIMITIVE_TAG) { /* high-tag */
- p++;
- if (--max == 0)
- goto err;
- l = 0;
- while (*p & 0x80) {
- l <<= 7L;
- l |= *(p++) & 0x7f;
- if (--max == 0)
- goto err;
- if (l > (INT_MAX >> 7L))
- goto err;
- }
- l <<= 7L;
- l |= *(p++) & 0x7f;
- tag = (int)l;
- if (--max == 0)
- goto err;
- } else {
- tag = i;
- p++;
- if (--max == 0)
- goto err;
+ int constructed, indefinite;
+ uint32_t tag_number, length;
+ uint8_t tag_class;
+ CBS cbs;
+ int ret = 0;
+
+ *pclass = 0;
+ *ptag = 0;
+ *plength = 0;
+
+ CBS_init(&cbs, *pp, omax);
+
+ if (!asn1_get_object_cbs(&cbs, 0, &tag_class, &constructed, &tag_number,
+ &indefinite, &length)) {
+ ASN1error(ASN1_R_HEADER_TOO_LONG);
+ return 0x80;
}
- *ptag = tag;
- *pclass = xclass;
- if (!asn1_get_length(&p, &inf, plength, (int)max))
- goto err;
- if (inf && !(ret & V_ASN1_CONSTRUCTED))
- goto err;
+ if (tag_number > INT_MAX) {
+ ASN1error(ASN1_R_HEADER_TOO_LONG);
+ return 0x80;
+ }
- if (*plength > (omax - (p - *pp))) {
+ /*
+ * API insanity ahead... in this case we add an error to the stack and
+ * signal an error by setting the 8th bit in the return value... but we
+ * still provide all of the decoded data.
+ */
+ if (length > CBS_len(&cbs)) {
ASN1error(ASN1_R_TOO_LONG);
- /* Set this so that even if things are not long enough
- * the values are set correctly */
- ret |= 0x80;
+ ret = 0x80;
}
- *pp = p;
- return (ret | inf);
-err:
- ASN1error(ASN1_R_HEADER_TOO_LONG);
- return (0x80);
-}
+ *pclass = tag_class << 6;
+ *ptag = tag_number;
+ *plength = length;
-static int
-asn1_get_length(const unsigned char **pp, int *inf, long *rl, int max)
-{
- const unsigned char *p = *pp;
- unsigned long ret = 0;
- unsigned int i;
+ *pp = CBS_data(&cbs);
- if (max-- < 1)
- return (0);
- if (*p == 0x80) {
- *inf = 1;
- ret = 0;
- p++;
- } else {
- *inf = 0;
- i = *p & 0x7f;
- if (*(p++) & 0x80) {
- if (max < (int)i)
- return (0);
- /* skip leading zeroes */
- while (i && *p == 0) {
- p++;
- i--;
- }
- if (i > sizeof(long))
- return 0;
- while (i-- > 0) {
- ret <<= 8L;
- ret |= *(p++);
- }
- } else
- ret = i;
- }
- if (ret > LONG_MAX)
- return 0;
- *pp = p;
- *rl = (long)ret;
- return (1);
+ if (constructed)
+ ret |= 1 << 5;
+ if (indefinite)
+ ret |= 1;
+
+ return ret;
}
/* class 0 is constructed