From e54b4ae18261d7d54ec5aa9d0e9cc5944bdaf5eb Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 13 Jan 2023 17:20:09 +0800 Subject: [PATCH] Add checking to ASN.1 IA5, UTF8, Printable strings --- include/gmssl/asn1.h | 7 ++- src/asn1.c | 131 +++++++++++++++++++++++++++++++++++++++++-- src/x509_cer.c | 2 +- src/x509_ext.c | 4 +- tests/asn1test.c | 6 +- 5 files changed, 135 insertions(+), 15 deletions(-) diff --git a/include/gmssl/asn1.h b/include/gmssl/asn1.h index 1cbec66d..470b9442 100644 --- a/include/gmssl/asn1.h +++ b/include/gmssl/asn1.h @@ -169,7 +169,6 @@ int asn1_object_identifier_print(FILE *fp, int fmt, int ind, const char *label, #define asn1_implicit_enumerated_to_der(i,val,out,outlen) asn1_int_to_der_ex(ASN1_TAG_IMPLICIT(i),val,out,outlen) #define asn1_implicit_enumerated_from_der(i,val,in,inlen) asn1_int_from_der_ex(ASN1_TAG_IMPLICIT(i),val,in,inlen) -int asn1_utf8_string_check(const char *d, size_t dlen); int asn1_utf8_string_to_der_ex(int tag, const char *d, size_t dlen, uint8_t **out, size_t *outlen); int asn1_utf8_string_from_der_ex(int tag, const char **d, size_t *dlen, const uint8_t **in, size_t *inlen); #define asn1_utf8_string_to_der(d,dlen,out,outlen) asn1_utf8_string_to_der_ex(ASN1_TAG_UTF8String,d,dlen,out,outlen) @@ -177,7 +176,6 @@ int asn1_utf8_string_from_der_ex(int tag, const char **d, size_t *dlen, const ui #define asn1_implicit_utf8_string_to_der(i,d,dlen,out,outlen) asn1_utf8_string_to_der_ex(ASN1_TAG_IMPLICIT(i),d,dlen,out,outlen) #define asn1_implicit_utf8_string_from_der(i,d,dlen,in,inlen) asn1_utf8_string_from_der_ex(ASN1_TAG_IMPLICIT(i),d,dlen,in,inlen) -int asn1_printable_string_check(const char *d, size_t dlen); int asn1_printable_string_case_ignore_match(const char *a, size_t alen, const char *b, size_t blen); int asn1_printable_string_to_der_ex(int tag, const char *d, size_t dlen, uint8_t **out, size_t *outlen); int asn1_printable_string_from_der_ex(int tag, const char **d, size_t *dlen, const uint8_t **in, size_t *inlen); @@ -186,7 +184,6 @@ int asn1_printable_string_from_der_ex(int tag, const char **d, size_t *dlen, con #define asn1_implicit_printable_string_to_der(i,d,dlen,out,outlen) asn1_printable_string_to_der_ex(ASN1_TAG_IMPLICIT(i),d,dlen,out,outlen) #define asn1_implicit_printable_string_from_der(i,d,dlen,in,inlen) asn1_printable_string_from_der_ex(ASN1_TAG_IMPLICIT(i),d,dlen,in,inlen) -int asn1_ia5_string_check(const char *d, size_t dlen); int asn1_ia5_string_to_der_ex(int tag, const char *d, size_t dlen, uint8_t **out, size_t *outlen); int asn1_ia5_string_from_der_ex(int tag, const char **d, size_t *dlen, const uint8_t **in, size_t *inlen); #define asn1_ia5_string_to_der(d,dlen,out,outlen) asn1_ia5_string_to_der_ex(ASN1_TAG_IA5String,d,dlen,out,outlen) @@ -194,6 +191,10 @@ int asn1_ia5_string_from_der_ex(int tag, const char **d, size_t *dlen, const uin #define asn1_implicit_ia5_string_to_der(i,d,dlen,out,outlen) asn1_ia5_string_to_der_ex(ASN1_TAG_IMPLICIT(i),d,dlen,out,outlen) #define asn1_implicit_ia5_string_from_der(i,d,dlen,in,inlen) asn1_ia5_string_from_der_ex(ASN1_TAG_IMPLICIT(i),d,dlen,in,inlen) + +int asn1_string_is_printable_string(const char *d, size_t dlen); +int asn1_string_is_ia5_string(const char *d, size_t dlen); +int asn1_string_is_utf8_string(const char *d, size_t dlen); int asn1_string_print(FILE *fp, int fmt, int ind, const char *label, int tag, const uint8_t *d, size_t dlen); #define ASN1_UTC_TIME_LEN (sizeof("YYMMDDHHMMSSZ")-1) diff --git a/src/asn1.c b/src/asn1.c index c35ed2ee..d60c3af1 100644 --- a/src/asn1.c +++ b/src/asn1.c @@ -113,12 +113,73 @@ int asn1_tag_is_cstring(int tag) return 0; } -int asn1_utf8_string_check(const char *a, size_t alen) +/* +utf-8 character encoding + 1-byte: 0xxxxxxx + 2-byte: 110xxxxx 10xxxxxx + 3-byte: 1110xxxx 10xxxxxx 10xxxxxx + 4-byte: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +*/ +int asn1_utf8char_from_bytes(uint32_t *c, const uint8_t **pin, size_t *pinlen) { + uint32_t utf8char; + const uint8_t *in = *pin; + size_t inlen = *pinlen; + uint32_t utf8char_len, i; + + if (!inlen) { + return 0; + } + + if ((in[0] & 0x80) == 0x00) { + utf8char_len = 1; + } else if ((in[0] & 0xe0) == 0xc0) { + utf8char_len = 2; + } else if ((in[0] & 0xf0) == 0xe0) { + utf8char_len = 3; + } else if ((in[0] & 0xf8) == 0xf0) { + utf8char_len = 4; + } else { + //error_print(); + return -1; + } + + if (inlen < utf8char_len) { + //error_print(); + return -1; + } + + utf8char = in[0]; + for (i = 1; i < utf8char_len; i++) { + if ((in[i] & 0x60) != 0x80) { + //error_print(); + return -1; + } + utf8char = (utf8char << 8) | in[i]; + } + + *c = utf8char; + (*pin) += utf8char_len; + (*pinlen) -= utf8char_len; return 1; } -static int asn1_char_is_printable(int a) +int asn1_string_is_utf8_string(const char *a, size_t alen) +{ + uint32_t utf8char; + + if (!a || !alen) { + return 0; + } + while (alen) { + if (asn1_utf8char_from_bytes(&utf8char, (const uint8_t **)&a, &alen) != 1) { + return 0; + } + } + return 1; +} + +int asn1_char_is_printable(int a) { if (isalpha(a) || isdigit(a)) { return 1; @@ -132,6 +193,7 @@ static int asn1_char_is_printable(int a) return 0; } +/* int asn1_printable_string_check(const char *a, size_t alen) { size_t i; @@ -143,6 +205,18 @@ int asn1_printable_string_check(const char *a, size_t alen) } return 1; } +*/ + +int asn1_string_is_printable_string(const char *a, size_t alen) +{ + size_t i; + for (i = 0; i < alen; i++) { + if (asn1_char_is_printable(a[i]) != 1) { + return 0; + } + } + return 1; +} int asn1_printable_string_case_ignore_match(const char *a, size_t alen, const char *b, size_t blen) @@ -177,8 +251,14 @@ int asn1_printable_string_case_ignore_match(const char *a, size_t alen, return 1; } -int asn1_ia5_string_check(const char *a, size_t alen) +int asn1_string_is_ia5_string(const char *a, size_t alen) { + size_t i; + for (i = 0; i < alen; i++) { + if (!isascii(a[i])) { + return 0; + } + } return 1; } @@ -1314,17 +1394,56 @@ int asn1_string_from_der(int tag, const char **a, size_t *alen, const uint8_t ** int asn1_utf8_string_from_der_ex(int tag, const char **a, size_t *alen, const uint8_t **in, size_t *inlen) { - return asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen); + int ret; + if ((ret = asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (!(*a) || !(*alen)) { + error_print(); + return -1; + } + if (asn1_string_is_utf8_string(*a, *alen) != 1) { + error_print(); + return -1; + } + return 1; } int asn1_printable_string_from_der_ex(int tag, const char **a, size_t *alen, const uint8_t **in, size_t *inlen) { - return asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen); + int ret; + if ((ret = asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (!(*a) || !(*alen)) { + error_print(); + return -1; + } + if (asn1_string_is_printable_string(*a, *alen) != 1) { + error_print(); + return -1; + } + return 1; } int asn1_ia5_string_from_der_ex(int tag, const char **a, size_t *alen, const uint8_t **in, size_t *inlen) { - return asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen); + int ret; + if ((ret = asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (!(*a) || !(*alen)) { + error_print(); + return -1; + } + if (asn1_string_is_ia5_string(*a, *alen) != 1) { + error_print(); + return -1; + } + return 1; } int asn1_utc_time_from_der_ex(int tag, time_t *t, const uint8_t **pin, size_t *pinlen) diff --git a/src/x509_cer.c b/src/x509_cer.c index af86cfe9..02ad43ac 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -528,7 +528,7 @@ static size_t _strlen(const char *s) { return s ? strlen(s) : 0; } static int x509_name_tag(const char *str) { if (str) { - if (asn1_printable_string_check(str, strlen(str)) == 1) + if (asn1_string_is_printable_string(str, strlen(str)) == 1) return ASN1_TAG_PrintableString; else return ASN1_TAG_UTF8String; } diff --git a/src/x509_ext.c b/src/x509_ext.c index 4c579243..5c6cefcc 100644 --- a/src/x509_ext.c +++ b/src/x509_ext.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. @@ -527,7 +527,7 @@ int x509_general_names_add_general_name(uint8_t *gns, size_t *gnslen, size_t max case X509_gn_rfc822_name: case X509_gn_dns_name: case X509_gn_uniform_resource_identifier: - if (asn1_ia5_string_check((char *)d, dlen) != 1) { + if (asn1_string_is_ia5_string((char *)d, dlen) != 1) { error_print(); return -1; } diff --git a/tests/asn1test.c b/tests/asn1test.c index a55b383f..2d5fb036 100644 --- a/tests/asn1test.c +++ b/tests/asn1test.c @@ -370,7 +370,7 @@ static int test_asn1_printable_string(void) char *tests[] = { "hello", "world", - "Just do it!", + "Just do it", // string "Just do it!" include invalid '!' }; uint8_t buf[256]; uint8_t *p = buf; @@ -408,12 +408,12 @@ static int test_asn1_printable_string(void) static int test_asn1_printable_string_check(void) { char *printable_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?"; - if (asn1_printable_string_check(printable_str, strlen(printable_str)) != 1) { + if (asn1_string_is_printable_string(printable_str, strlen(printable_str)) != 1) { error_print(); return -1; } - if (asn1_printable_string_check("a*b", 3) == 1) { + if (asn1_string_is_printable_string("a*b", 3) == 1) { error_print(); return -1; }