/src/botan/src/lib/asn1/asn1_str.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Simple ASN.1 String Types |
3 | | * (C) 1999-2007,2020 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/asn1_obj.h> |
9 | | |
10 | | #include <botan/ber_dec.h> |
11 | | #include <botan/der_enc.h> |
12 | | #include <botan/internal/charset.h> |
13 | | #include <botan/internal/ct_utils.h> |
14 | | #include <botan/internal/fmt.h> |
15 | | |
16 | | namespace Botan { |
17 | | |
18 | | namespace { |
19 | | |
20 | | /* |
21 | | * Choose an encoding for the string |
22 | | */ |
23 | 148k | ASN1_Type choose_encoding(std::string_view str) { |
24 | 148k | auto all_printable = CT::Mask<uint8_t>::set(); |
25 | | |
26 | 148k | for(size_t i = 0; i != str.size(); ++i) { |
27 | 0 | const uint8_t c = static_cast<uint8_t>(str[i]); |
28 | |
|
29 | 0 | auto is_alpha_lower = CT::Mask<uint8_t>::is_within_range(c, 'a', 'z'); |
30 | 0 | auto is_alpha_upper = CT::Mask<uint8_t>::is_within_range(c, 'A', 'Z'); |
31 | 0 | auto is_decimal = CT::Mask<uint8_t>::is_within_range(c, '0', '9'); |
32 | |
|
33 | 0 | auto is_print_punc = CT::Mask<uint8_t>::is_any_of(c, {' ', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'}); |
34 | |
|
35 | 0 | auto is_printable = is_alpha_lower | is_alpha_upper | is_decimal | is_print_punc; |
36 | |
|
37 | 0 | all_printable &= is_printable; |
38 | 0 | } |
39 | | |
40 | 148k | if(all_printable.as_bool()) { |
41 | 148k | return ASN1_Type::PrintableString; |
42 | 148k | } else { |
43 | 0 | return ASN1_Type::Utf8String; |
44 | 0 | } |
45 | 148k | } |
46 | | |
47 | 346k | bool is_utf8_subset_string_type(ASN1_Type tag) { |
48 | 346k | return (tag == ASN1_Type::NumericString || tag == ASN1_Type::PrintableString || tag == ASN1_Type::VisibleString || |
49 | 346k | tag == ASN1_Type::Ia5String || tag == ASN1_Type::Utf8String); |
50 | 346k | } |
51 | | |
52 | 190k | bool is_asn1_string_type(ASN1_Type tag) { |
53 | 190k | return (is_utf8_subset_string_type(tag) || tag == ASN1_Type::TeletexString || tag == ASN1_Type::BmpString || |
54 | 190k | tag == ASN1_Type::UniversalString); |
55 | 190k | } |
56 | | |
57 | | } // namespace |
58 | | |
59 | | //static |
60 | 44.7k | bool ASN1_String::is_string_type(ASN1_Type tag) { |
61 | 44.7k | return is_asn1_string_type(tag); |
62 | 44.7k | } |
63 | | |
64 | 155k | ASN1_String::ASN1_String(std::string_view str, ASN1_Type t) : m_utf8_str(str), m_tag(t) { |
65 | 155k | if(!is_utf8_subset_string_type(m_tag)) { |
66 | 22 | throw Invalid_Argument("ASN1_String only supports encoding to UTF-8 or a UTF-8 subset"); |
67 | 22 | } |
68 | 155k | } |
69 | | |
70 | 148k | ASN1_String::ASN1_String(std::string_view str) : ASN1_String(str, choose_encoding(str)) {} |
71 | | |
72 | | /* |
73 | | * DER encode an ASN1_String |
74 | | */ |
75 | 0 | void ASN1_String::encode_into(DER_Encoder& encoder) const { |
76 | 0 | if(m_data.empty()) { |
77 | 0 | BOTAN_ASSERT_NOMSG(is_utf8_subset_string_type(tagging())); |
78 | 0 | encoder.add_object(tagging(), ASN1_Class::Universal, m_utf8_str); |
79 | 0 | } else { |
80 | | // If this string was decoded, reserialize using original encoding |
81 | 0 | encoder.add_object(tagging(), ASN1_Class::Universal, m_data.data(), m_data.size()); |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | /* |
86 | | * Decode a BER encoded ASN1_String |
87 | | */ |
88 | 146k | void ASN1_String::decode_from(BER_Decoder& source) { |
89 | 146k | BER_Object obj = source.get_next_object(); |
90 | | |
91 | 146k | if(!is_asn1_string_type(obj.type())) { |
92 | 1.11k | auto typ = static_cast<uint32_t>(obj.type()); |
93 | 1.11k | throw Decoding_Error(fmt("ASN1_String: Unknown string type {}", typ)); |
94 | 1.11k | } |
95 | | |
96 | 145k | m_tag = obj.type(); |
97 | 145k | m_data.assign(obj.bits(), obj.bits() + obj.length()); |
98 | | |
99 | 145k | if(m_tag == ASN1_Type::BmpString) { |
100 | 8.54k | m_utf8_str = ucs2_to_utf8(m_data.data(), m_data.size()); |
101 | 136k | } else if(m_tag == ASN1_Type::UniversalString) { |
102 | 4.06k | m_utf8_str = ucs4_to_utf8(m_data.data(), m_data.size()); |
103 | 132k | } else if(m_tag == ASN1_Type::TeletexString) { |
104 | | /* |
105 | | TeletexString is nominally ITU T.61 not ISO-8859-1 but it seems |
106 | | the majority of implementations actually used that charset here. |
107 | | */ |
108 | 16.4k | m_utf8_str = latin1_to_utf8(m_data.data(), m_data.size()); |
109 | 116k | } else { |
110 | | // All other supported string types are UTF-8 or some subset thereof |
111 | 116k | m_utf8_str = ASN1::to_string(obj); |
112 | 116k | } |
113 | 145k | } |
114 | | |
115 | | } // namespace Botan |