diff options
author | jsing <> | 2022-04-28 18:30:57 +0000 |
---|---|---|
committer | jsing <> | 2022-04-28 18:30:57 +0000 |
commit | 308c1defb0063d1aa0ab378d1a88c5ce12e650d9 (patch) | |
tree | 897dde441b9fb1700461ec4356623e41d0139e0a | |
parent | 7e4a5a70be4f9de72ee5cafeda9f8966ced355f5 (diff) | |
download | openbsd-308c1defb0063d1aa0ab378d1a88c5ce12e650d9.tar.gz openbsd-308c1defb0063d1aa0ab378d1a88c5ce12e650d9.tar.bz2 openbsd-308c1defb0063d1aa0ab378d1a88c5ce12e650d9.zip |
Rewrite asn1_check_tlen() using CBS.
Rather than calling asn1_get_object_cbs(), call asn1_get_identifier_cbs(),
then immediately proceed with the tag number and tag class check. Only if
that succeeds (or it is not required) do we call asn1_get_length_cbs().
This avoids incurring the overhead of decoding the length in the case where
the tag number and tag class do not match.
While here rename asn1_check_tlen() to asn1_check_tag() - while we decode
the length, what we are normally checking is the tag number and tag class.
Also rename the arguments for readability. For now the argument types
and encoding remain unchanged.
ok inoguchi@ tb@
-rw-r--r-- | src/lib/libcrypto/asn1/asn1_lib.c | 6 | ||||
-rw-r--r-- | src/lib/libcrypto/asn1/asn1_locl.h | 6 | ||||
-rw-r--r-- | src/lib/libcrypto/asn1/tasn_dec.c | 164 |
3 files changed, 113 insertions, 63 deletions
diff --git a/src/lib/libcrypto/asn1/asn1_lib.c b/src/lib/libcrypto/asn1/asn1_lib.c index 6a29c327fe..97ce6caeef 100644 --- a/src/lib/libcrypto/asn1/asn1_lib.c +++ b/src/lib/libcrypto/asn1/asn1_lib.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: asn1_lib.c,v 1.52 2022/03/26 14:47:58 jsing Exp $ */ | 1 | /* $OpenBSD: asn1_lib.c,v 1.53 2022/04/28 18:30:57 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2021 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2021 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | #include "bytestring.h" | 21 | #include "bytestring.h" |
22 | 22 | ||
23 | static int | 23 | int |
24 | asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class, | 24 | asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class, |
25 | int *out_constructed, uint32_t *out_tag_number) | 25 | int *out_constructed, uint32_t *out_tag_number) |
26 | { | 26 | { |
@@ -76,7 +76,7 @@ asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class, | |||
76 | return 1; | 76 | return 1; |
77 | } | 77 | } |
78 | 78 | ||
79 | static int | 79 | int |
80 | asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite, | 80 | asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite, |
81 | uint32_t *out_length) | 81 | uint32_t *out_length) |
82 | { | 82 | { |
diff --git a/src/lib/libcrypto/asn1/asn1_locl.h b/src/lib/libcrypto/asn1/asn1_locl.h index 4d4c7a348d..86907aa8f0 100644 --- a/src/lib/libcrypto/asn1/asn1_locl.h +++ b/src/lib/libcrypto/asn1/asn1_locl.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: asn1_locl.h,v 1.27 2022/04/27 17:56:13 jsing Exp $ */ | 1 | /* $OpenBSD: asn1_locl.h,v 1.28 2022/04/28 18:30:57 jsing Exp $ */ |
2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | 2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL |
3 | * project 2006. | 3 | * project 2006. |
4 | */ | 4 | */ |
@@ -193,6 +193,10 @@ int UTF8_putc(unsigned char *str, int len, unsigned long value); | |||
193 | 193 | ||
194 | int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb); | 194 | int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb); |
195 | 195 | ||
196 | int asn1_get_identifier_cbs(CBS *cbs, int der_mode, uint8_t *out_class, | ||
197 | int *out_constructed, uint32_t *out_tag_number); | ||
198 | int asn1_get_length_cbs(CBS *cbs, int der_mode, int *out_indefinite, | ||
199 | uint32_t *out_length); | ||
196 | int asn1_get_object_cbs(CBS *cbs, int der_mode, uint8_t *out_class, | 200 | int asn1_get_object_cbs(CBS *cbs, int der_mode, uint8_t *out_class, |
197 | int *out_constructed, uint32_t *out_tag_number, int *out_indefinite, | 201 | int *out_constructed, uint32_t *out_tag_number, int *out_indefinite, |
198 | uint32_t *out_length); | 202 | uint32_t *out_length); |
diff --git a/src/lib/libcrypto/asn1/tasn_dec.c b/src/lib/libcrypto/asn1/tasn_dec.c index b88c5f2bc1..8b02c13e6b 100644 --- a/src/lib/libcrypto/asn1/tasn_dec.c +++ b/src/lib/libcrypto/asn1/tasn_dec.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tasn_dec.c,v 1.53 2022/04/27 17:56:13 jsing Exp $ */ | 1 | /* $OpenBSD: tasn_dec.c,v 1.54 2022/04/28 18:30:57 jsing Exp $ */ |
2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | 2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL |
3 | * project 2000. | 3 | * project 2000. |
4 | */ | 4 | */ |
@@ -81,10 +81,6 @@ static int asn1_find_end(const unsigned char **in, long len, char inf); | |||
81 | static int asn1_collect(CBB *cbb, const unsigned char **in, long len, | 81 | static int asn1_collect(CBB *cbb, const unsigned char **in, long len, |
82 | char inf, int tag, int aclass, int depth); | 82 | char inf, int tag, int aclass, int depth); |
83 | 83 | ||
84 | static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass, | ||
85 | char *inf, char *cst, const unsigned char **in, long len, int exptag, | ||
86 | int expclass, char opt); | ||
87 | |||
88 | static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, | 84 | static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, |
89 | long len, const ASN1_ITEM *it, int tag, int aclass, char opt, int depth); | 85 | long len, const ASN1_ITEM *it, int tag, int aclass, char opt, int depth); |
90 | static int asn1_template_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, | 86 | static int asn1_template_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, |
@@ -96,6 +92,13 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, | |||
96 | static int asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, | 92 | static int asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, |
97 | const ASN1_ITEM *it); | 93 | const ASN1_ITEM *it); |
98 | 94 | ||
95 | static int asn1_check_tag_cbs(CBS *cbs, long *out_len, int *out_tag, | ||
96 | uint8_t *out_class, char *out_indefinite, char *out_constructed, | ||
97 | int expected_tag, int expected_class, char optional); | ||
98 | static int asn1_check_tag(long *out_len, int *out_tag, uint8_t *out_class, | ||
99 | char *out_indefinite, char *out_constructed, const unsigned char **in, | ||
100 | long len, int expected_tag, int expected_class, char optional); | ||
101 | |||
99 | ASN1_VALUE * | 102 | ASN1_VALUE * |
100 | ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, | 103 | ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, |
101 | const ASN1_ITEM *it) | 104 | const ASN1_ITEM *it) |
@@ -187,8 +190,8 @@ asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, | |||
187 | 190 | ||
188 | p = *in; | 191 | p = *in; |
189 | /* Just read in tag and class */ | 192 | /* Just read in tag and class */ |
190 | ret = asn1_check_tlen(NULL, &otag, &oclass, NULL, NULL, | 193 | ret = asn1_check_tag(NULL, &otag, &oclass, NULL, NULL, &p, len, |
191 | &p, len, -1, 0, 1); | 194 | -1, 0, 1); |
192 | if (!ret) { | 195 | if (!ret) { |
193 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 196 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
194 | goto err; | 197 | goto err; |
@@ -295,8 +298,8 @@ asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, | |||
295 | aclass = V_ASN1_UNIVERSAL; | 298 | aclass = V_ASN1_UNIVERSAL; |
296 | } | 299 | } |
297 | /* Get SEQUENCE length and update len, p */ | 300 | /* Get SEQUENCE length and update len, p */ |
298 | ret = asn1_check_tlen(&len, NULL, NULL, &seq_eoc, &cst, | 301 | ret = asn1_check_tag(&len, NULL, NULL, &seq_eoc, &cst, &p, len, |
299 | &p, len, tag, aclass, opt); | 302 | tag, aclass, opt); |
300 | if (!ret) { | 303 | if (!ret) { |
301 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 304 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
302 | goto err; | 305 | goto err; |
@@ -478,8 +481,8 @@ asn1_template_ex_d2i(ASN1_VALUE **val, const unsigned char **in, long inlen, | |||
478 | * content and where it starts: so read in EXPLICIT header to | 481 | * content and where it starts: so read in EXPLICIT header to |
479 | * get the info. | 482 | * get the info. |
480 | */ | 483 | */ |
481 | ret = asn1_check_tlen(&len, NULL, NULL, &exp_eoc, &cst, | 484 | ret = asn1_check_tag(&len, NULL, NULL, &exp_eoc, &cst, &p, |
482 | &p, inlen, tt->tag, aclass, opt); | 485 | inlen, tt->tag, aclass, opt); |
483 | q = p; | 486 | q = p; |
484 | if (!ret) { | 487 | if (!ret) { |
485 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 488 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
@@ -555,8 +558,8 @@ asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in, long len, | |||
555 | sktag = V_ASN1_SEQUENCE; | 558 | sktag = V_ASN1_SEQUENCE; |
556 | } | 559 | } |
557 | /* Get the tag */ | 560 | /* Get the tag */ |
558 | ret = asn1_check_tlen(&len, NULL, NULL, &sk_eoc, NULL, | 561 | ret = asn1_check_tag(&len, NULL, NULL, &sk_eoc, NULL, &p, len, |
559 | &p, len, sktag, skaclass, opt); | 562 | sktag, skaclass, opt); |
560 | if (!ret) { | 563 | if (!ret) { |
561 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 564 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
562 | return 0; | 565 | return 0; |
@@ -680,8 +683,8 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, long inlen, | |||
680 | return 0; | 683 | return 0; |
681 | } | 684 | } |
682 | p = *in; | 685 | p = *in; |
683 | ret = asn1_check_tlen(NULL, &utype, &oclass, NULL, NULL, | 686 | ret = asn1_check_tag(NULL, &utype, &oclass, NULL, NULL, &p, |
684 | &p, inlen, -1, 0, 0); | 687 | inlen, -1, 0, 0); |
685 | if (!ret) { | 688 | if (!ret) { |
686 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 689 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
687 | return 0; | 690 | return 0; |
@@ -695,8 +698,8 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, long inlen, | |||
695 | } | 698 | } |
696 | p = *in; | 699 | p = *in; |
697 | /* Check header */ | 700 | /* Check header */ |
698 | ret = asn1_check_tlen(&plen, NULL, NULL, &inf, &cst, | 701 | ret = asn1_check_tag(&plen, NULL, NULL, &inf, &cst, &p, inlen, tag, |
699 | &p, inlen, tag, aclass, opt); | 702 | aclass, opt); |
700 | if (!ret) { | 703 | if (!ret) { |
701 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 704 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
702 | return 0; | 705 | return 0; |
@@ -938,7 +941,7 @@ asn1_find_end(const unsigned char **in, long len, char inf) | |||
938 | } | 941 | } |
939 | q = p; | 942 | q = p; |
940 | /* Just read in a header: only care about the length */ | 943 | /* Just read in a header: only care about the length */ |
941 | if (!asn1_check_tlen(&plen, NULL, NULL, &inf, NULL, &p, len, | 944 | if (!asn1_check_tag(&plen, NULL, NULL, &inf, NULL, &p, len, |
942 | -1, 0, 0)) { | 945 | -1, 0, 0)) { |
943 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 946 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
944 | return 0; | 947 | return 0; |
@@ -1001,8 +1004,8 @@ asn1_collect(CBB *cbb, const unsigned char **in, long len, char inf, | |||
1001 | break; | 1004 | break; |
1002 | } | 1005 | } |
1003 | 1006 | ||
1004 | if (!asn1_check_tlen(&plen, NULL, NULL, &ininf, &cst, &p, | 1007 | if (!asn1_check_tag(&plen, NULL, NULL, &ininf, &cst, &p, len, |
1005 | len, tag, aclass, 0)) { | 1008 | tag, aclass, 0)) { |
1006 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 1009 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
1007 | return 0; | 1010 | return 0; |
1008 | } | 1011 | } |
@@ -1044,56 +1047,99 @@ asn1_check_eoc(const unsigned char **in, long len) | |||
1044 | return 0; | 1047 | return 0; |
1045 | } | 1048 | } |
1046 | 1049 | ||
1047 | /* Check an ASN1 tag and length: a bit like ASN1_get_object | ||
1048 | * but it sets the length for indefinite length constructed | ||
1049 | * form, we don't know the exact length but we can set an | ||
1050 | * upper bound to the amount of data available minus the | ||
1051 | * header length just read. | ||
1052 | */ | ||
1053 | |||
1054 | static int | 1050 | static int |
1055 | asn1_check_tlen(long *olen, int *otag, unsigned char *oclass, char *inf, | 1051 | asn1_check_tag_cbs(CBS *cbs, long *out_len, int *out_tag, uint8_t *out_class, |
1056 | char *cst, const unsigned char **in, long len, int exptag, int expclass, | 1052 | char *out_indefinite, char *out_constructed, int expected_tag, |
1057 | char opt) | 1053 | int expected_class, char optional) |
1058 | { | 1054 | { |
1059 | int i; | 1055 | uint32_t tag_number, length; |
1060 | int ptag, pclass; | 1056 | int constructed, indefinite; |
1061 | long plen; | 1057 | uint8_t tag_class; |
1062 | const unsigned char *p, *q; | 1058 | |
1063 | 1059 | if (out_len != NULL) | |
1064 | p = *in; | 1060 | *out_len = 0; |
1065 | q = p; | 1061 | if (out_tag != NULL) |
1066 | 1062 | *out_tag = 0; | |
1067 | i = ASN1_get_object(&p, &plen, &ptag, &pclass, len); | 1063 | if (out_class != NULL) |
1068 | if (i & 0x80) { | 1064 | *out_class = 0; |
1065 | if (out_indefinite != NULL) | ||
1066 | *out_indefinite = 0; | ||
1067 | if (out_constructed != NULL) | ||
1068 | *out_constructed = 0; | ||
1069 | |||
1070 | if (!asn1_get_identifier_cbs(cbs, 0, &tag_class, &constructed, | ||
1071 | &tag_number)) { | ||
1069 | ASN1error(ASN1_R_BAD_OBJECT_HEADER); | 1072 | ASN1error(ASN1_R_BAD_OBJECT_HEADER); |
1070 | return 0; | 1073 | return 0; |
1071 | } | 1074 | } |
1072 | if (exptag >= 0) { | 1075 | if (expected_tag >= 0) { |
1073 | if ((exptag != ptag) || (expclass != pclass)) { | 1076 | if (expected_tag != tag_number || |
1074 | /* If type is OPTIONAL, not an error: | 1077 | expected_class != tag_class << 6) { |
1075 | * indicate missing type. | 1078 | /* Indicate missing type if this is OPTIONAL. */ |
1076 | */ | 1079 | if (optional) |
1077 | if (opt) | ||
1078 | return -1; | 1080 | return -1; |
1081 | |||
1079 | ASN1error(ASN1_R_WRONG_TAG); | 1082 | ASN1error(ASN1_R_WRONG_TAG); |
1080 | return 0; | 1083 | return 0; |
1081 | } | 1084 | } |
1082 | } | 1085 | } |
1086 | if (!asn1_get_length_cbs(cbs, 0, &indefinite, &length)) { | ||
1087 | ASN1error(ASN1_R_BAD_OBJECT_HEADER); | ||
1088 | return 0; | ||
1089 | } | ||
1083 | 1090 | ||
1084 | if (i & 1) | 1091 | /* Indefinite length can only be used with constructed encoding. */ |
1085 | plen = len - (p - q); | 1092 | if (indefinite && !constructed) { |
1086 | if (inf) | 1093 | ASN1error(ASN1_R_BAD_OBJECT_HEADER); |
1087 | *inf = i & 1; | 1094 | return 0; |
1088 | if (cst) | 1095 | } |
1089 | *cst = i & V_ASN1_CONSTRUCTED; | 1096 | |
1090 | if (olen) | 1097 | if (!indefinite && CBS_len(cbs) < length) { |
1091 | *olen = plen; | 1098 | ASN1error(ASN1_R_TOO_LONG); |
1092 | if (oclass) | 1099 | return 0; |
1093 | *oclass = pclass; | 1100 | } |
1094 | if (otag) | 1101 | |
1095 | *otag = ptag; | 1102 | if (tag_number > INT_MAX || CBS_len(cbs) > INT_MAX) { |
1103 | ASN1error(ASN1_R_TOO_LONG); | ||
1104 | return 0; | ||
1105 | } | ||
1106 | |||
1107 | if (indefinite) | ||
1108 | length = CBS_len(cbs); | ||
1109 | |||
1110 | if (out_len != NULL) | ||
1111 | *out_len = length; | ||
1112 | if (out_tag != NULL) | ||
1113 | *out_tag = tag_number; | ||
1114 | if (out_class != NULL) | ||
1115 | *out_class = tag_class << 6; | ||
1116 | if (out_indefinite != NULL && indefinite) | ||
1117 | *out_indefinite = 1 << 0; | ||
1118 | if (out_constructed != NULL && constructed) | ||
1119 | *out_constructed = 1 << 5; | ||
1096 | 1120 | ||
1097 | *in = p; | ||
1098 | return 1; | 1121 | return 1; |
1099 | } | 1122 | } |
1123 | |||
1124 | static int | ||
1125 | asn1_check_tag(long *out_len, int *out_tag, unsigned char *out_class, | ||
1126 | char *out_indefinite, char *out_constructed, const unsigned char **in, | ||
1127 | long len, int expected_tag, int expected_class, char optional) | ||
1128 | { | ||
1129 | CBS cbs; | ||
1130 | int ret; | ||
1131 | |||
1132 | if (len < 0) | ||
1133 | return 0; | ||
1134 | |||
1135 | CBS_init(&cbs, *in, len); | ||
1136 | |||
1137 | ret = asn1_check_tag_cbs(&cbs, out_len, out_tag, out_class, | ||
1138 | out_indefinite, out_constructed, expected_tag, expected_class, | ||
1139 | optional); | ||
1140 | |||
1141 | if (ret == 1) | ||
1142 | *in = CBS_data(&cbs); | ||
1143 | |||
1144 | return ret; | ||
1145 | } | ||