1 /**
2 * ASN.1 string type
3 * 
4 * Copyright:
5 * (C) 1999-2010 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.asn1.asn1_str;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO):
15 
16 import botan.asn1.asn1_obj;
17 import botan.asn1.der_enc;
18 import botan.asn1.ber_dec;
19 import botan.utils.charset;
20 import botan.utils.parsing;
21 import botan.utils.types;
22 
23 alias ASN1String = RefCounted!ASN1StringImpl;
24 
25 /**
26 * Simple String
27 */
28 final class ASN1StringImpl : ASN1Object
29 {
30 public:
31 
32     /*
33     * DER encode an ASN1String
34     */
35     override void encodeInto(ref DEREncoder encoder) const
36     {
37         string value = iso8859();
38         if (tagging() == ASN1Tag.UTF8_STRING)
39             value = transcode(value, LATIN1_CHARSET, UTF8_CHARSET);
40         encoder.addObject(tagging(), ASN1Tag.UNIVERSAL, value);
41     }
42 
43     /*
44     * Decode a BER encoded ASN1String
45     */
46     override void decodeFrom(ref BERDecoder source)
47     {
48         BERObject obj = source.getNextObject();
49         CharacterSet charset_is;
50         
51         if (obj.type_tag == ASN1Tag.BMP_STRING)
52             charset_is = UCS2_CHARSET;
53         else if (obj.type_tag == ASN1Tag.UTF8_STRING)
54             charset_is = UTF8_CHARSET;
55         else
56             charset_is = LATIN1_CHARSET;
57         
58         initialize(transcode(obj.toString(), 
59                              charset_is, 
60                              LOCAL_CHARSET),
61                    obj.type_tag);
62     }
63 
64     /*
65     * Return this string in local encoding
66     */
67     string value() const
68     {
69         return transcode(m_iso_8859_str, LATIN1_CHARSET, LOCAL_CHARSET);
70     }
71 
72     bool opEquals(in RefCounted!(ASN1StringImpl) other) const
73     {
74         if (m_tag != other.tagging()) return false;
75         if (m_iso_8859_str != other.iso8859()) return false;
76         return true;
77     }
78 
79     /*
80     * Return this string in ISO 8859-1 encoding
81     */
82     string iso8859() const
83     {
84         return m_iso_8859_str;
85     }
86 
87     /*
88     * Return the type of this string object
89     */
90     ASN1Tag tagging() const
91     {
92         return m_tag;
93     }
94 
95     this(in string str, ASN1Tag t)
96     {
97         initialize(str, t);
98     }
99 
100     this(in string str = "")
101     {
102         m_iso_8859_str = transcode(str, LOCAL_CHARSET, LATIN1_CHARSET);
103         m_tag = chooseEncoding(m_iso_8859_str, "latin1");
104     }
105 
106 
107 private:
108     void initialize(in string str, ASN1Tag t) {
109         m_tag = t;
110         m_iso_8859_str = transcode(str, LOCAL_CHARSET, LATIN1_CHARSET);
111         
112         if (m_tag == ASN1Tag.DIRECTORY_STRING)
113             m_tag = chooseEncoding(m_iso_8859_str, "latin1");
114         
115         if (m_tag != ASN1Tag.NUMERIC_STRING &&
116             m_tag != ASN1Tag.PRINTABLE_STRING &&
117             m_tag != ASN1Tag.VISIBLE_STRING &&
118             m_tag != ASN1Tag.T61_STRING &&
119             m_tag != ASN1Tag.IA5_STRING &&
120             m_tag != ASN1Tag.UTF8_STRING &&
121             m_tag != ASN1Tag.BMP_STRING)
122             throw new InvalidArgument("ASN1String: Unknown string type " ~
123                                        to!string(m_tag));
124     }
125 
126     string m_iso_8859_str;
127     ASN1Tag m_tag;
128 }
129 
130 /*
131 * Choose an encoding for the string
132 */
133 ASN1Tag chooseEncoding(in string str,
134                          in string type)
135 {
136     __gshared immutable bool[256] IS_PRINTABLE = [
137         false, false, false, false, false, false, false, false, false, false, false, false,
138         false, false, false, false, false, false, false, false, false, false, false, false,
139         false, false, false, false, false, false, false, false, true, false, false, false,
140         false, false, false, false, true, true, false, true, true, true, true, true,
141         true, true, true, true, true, true, true, true, true, true, true, false,
142         false, true, false, true, false, true, true, true, true, true, true, true,
143         true, true, true, true, true, true, true, true, true, true, true, true,
144         true, true, true, true, true, true, true, false, false, false, false, false,
145         false, true, true, true, true, true, true, true, true, true, true, true,
146         true, true, true, true, true, true, true, true, true, true, true, true,
147         true, true, true, false, false, false, false, false, false, false, false, false,
148         false, false, false, false, false, false, false, false, false, false, false, false,
149         false, false, false, false, false, false, false, false, false, false, false, false,
150         false, false, false, false, false, false, false, false, false, false, false, false,
151         false, false, false, false, false, false, false, false, false, false, false, false,
152         false, false, false, false, false, false, false, false, false, false, false, false,
153         false, false, false, false, false, false, false, false, false, false, false, false,
154         false, false, false, false, false, false, false, false, false, false, false, false,
155         false, false, false, false, false, false, false, false, false, false, false, false,
156         false, false, false, false, false, false, false, false, false, false, false, false,
157         false, false, false, false, false, false, false, false, false, false, false, false,
158         false, false, false, false ];
159     
160     foreach (immutable(char) c; str)
161     {
162         if (!IS_PRINTABLE[cast(size_t) c])
163         {
164             if (type == "utf8")    return ASN1Tag.UTF8_STRING;
165             if (type == "latin1") return ASN1Tag.T61_STRING;
166             throw new InvalidArgument("chooseEncoding: Bad string type " ~ type);
167         }
168     }
169     return ASN1Tag.PRINTABLE_STRING;
170 }