1 | //======================================================================== |
2 | // |
3 | // check_distinguished_name_parser.h |
4 | // |
5 | // This file is licensed under the GPLv2 or later |
6 | // |
7 | // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
8 | //======================================================================== |
9 | #include "DistinguishedNameParser.h" |
10 | |
11 | #include <QtTest/QTest> |
12 | #include <iostream> |
13 | |
14 | class TestDistinguishedNameParser : public QObject |
15 | { |
16 | Q_OBJECT |
17 | public: |
18 | explicit TestDistinguishedNameParser(QObject *parent = nullptr) : QObject(parent) { } |
19 | private slots: |
20 | // The big set of input/output. Several of the helper functions can be tested independently |
21 | void testParser(); |
22 | void testParser_data(); |
23 | |
24 | void testRemoveLeadingSpaces(); |
25 | void testRemoveLeadingSpaces_data(); |
26 | |
27 | void testRemoveTrailingSpaces(); |
28 | void testRemoveTrailingSpaces_data(); |
29 | |
30 | void testParseHexString(); |
31 | void testParseHexString_data(); |
32 | }; |
33 | |
34 | void TestDistinguishedNameParser::testParser() |
35 | { |
36 | QFETCH(std::string, inputData); |
37 | QFETCH(DN::Result, expectedResult); |
38 | |
39 | auto result = DN::parseString(string: inputData); |
40 | QCOMPARE(result, expectedResult); |
41 | } |
42 | |
43 | void TestDistinguishedNameParser::testParser_data() |
44 | { |
45 | QTest::addColumn<std::string>(name: "inputData" ); |
46 | QTest::addColumn<DN::Result>(name: "expectedResult" ); |
47 | |
48 | QTest::newRow(dataTag: "empty" ) << std::string {} << DN::Result {}; |
49 | QTest::newRow(dataTag: "CN=Simple" ) << std::string { "CN=Simple" } << DN::Result { { "CN" , "Simple" } }; |
50 | QTest::newRow(dataTag: "CN=Name with spaces" ) << std::string { "CN=Name with spaces" } << DN::Result { { "CN" , "Name with spaces" } }; |
51 | QTest::newRow(dataTag: "CN=Simple,O=Silly" ) << std::string { "CN=Simple,O=Silly" } << DN::Result { { "CN" , "Simple" }, { "O" , "Silly" } }; |
52 | QTest::newRow(dataTag: "CN=Steve Kille,O=Isode Limited,C=GB" ) << std::string { "CN=Steve Kille,O=Isode Limited,C=GB" } << DN::Result { { "CN" , "Steve Kille" }, { "O" , "Isode Limited" }, { "C" , "GB" } }; |
53 | QTest::newRow(dataTag: "CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US" ) |
54 | << std::string { "CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US" } << DN::Result { { "CN" , "some.user@example.com" }, { "O" , "MyCompany" }, { "L" , "San Diego" }, { "ST" , "California" }, { "C" , "US" } }; |
55 | QTest::newRow(dataTag: "Multi valued" ) << std::string { "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US" } |
56 | << DN::Result { { "OU" , "Sales" }, { "CN" , "J. Smith" }, { "O" , "Widget Inc." }, { "C" , "US" } }; // This is technically wrong, but probably good enough for now |
57 | QTest::newRow(dataTag: "Escaping comma" ) << std::string { "CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB" } << DN::Result { { "CN" , "L. Eagle" }, { "O" , "Sue, Grabbit and Runn" }, { "C" , "GB" } }; |
58 | QTest::newRow(dataTag: "Escaped trailing space" ) << std::string { "CN=Trailing space\\ " } << DN::Result { { "CN" , "Trailing space " } }; |
59 | QTest::newRow(dataTag: "Escaped quote" ) << std::string { "CN=Quotation \\\" Mark" } << DN::Result { { "CN" , "Quotation \" Mark" } }; |
60 | |
61 | QTest::newRow(dataTag: "CN=Simple with escaping" ) << std::string { "CN=S\\69mpl\\65\\7A" } << DN::Result { { "CN" , "Simplez" } }; |
62 | QTest::newRow(dataTag: "SN=Lu\\C4\\8Di\\C4\\87" ) << std::string { "SN=Lu\\C4\\8Di\\C4\\87" } << DN::Result { { "SN" , "Lučić" } }; |
63 | QTest::newRow(dataTag: "CN=\"Quoted name\"" ) << std::string { "CN=\"Quoted name\"" } << DN::Result { { "CN" , "Quoted name" } }; |
64 | QTest::newRow(dataTag: "CN=\" Leading and trailing spacees \"" ) << std::string { "CN=\" Leading and trailing spaces \"" } << DN::Result { { "CN" , " Leading and trailing spaces " } }; |
65 | QTest::newRow(dataTag: "Comma in quotes" ) << std::string { "CN=\"Comma, inside\"" } << DN::Result { { "CN" , "Comma, inside" } }; |
66 | QTest::newRow(dataTag: "forbidden chars in quotes" ) << std::string { "CN=\"Forbidden !@#$%&*()<>[]{},.?/\\| chars\"" } << DN::Result { { "CN" , "Forbidden !@#$%&*()<>[]{},.?/\\| chars" } }; |
67 | QTest::newRow(dataTag: "Quoted quotation" ) << std::string { "CN=\"Quotation \\\" Mark\"" } << DN::Result { { "CN" , "Quotation \" Mark" } }; |
68 | QTest::newRow(dataTag: "Quoted quotation" ) << std::string { "CN=\"Quotation \\\" Mark\\\" Multiples\"" } << DN::Result { { "CN" , "Quotation \" Mark\" Multiples" } }; |
69 | |
70 | QTest::newRow(dataTag: "frompdf1" ) << std::string { "2.5.4.97=#5553742D49644E722E20444520313233343735323233,CN=TeleSec PKS eIDAS QES CA 5,O=Deutsche Telekom AG,C=DE" } |
71 | << DN::Result { { "2.5.4.97" , "USt-IdNr. DE 123475223" }, { "CN" , "TeleSec PKS eIDAS QES CA 5" }, { "O" , "Deutsche Telekom AG" }, { "C" , "DE" } }; |
72 | QTest::newRow(dataTag: "frompdf2" ) << std::string { "2.5.4.5=#34,CN=Koch\\, Werner,2.5.4.42=#5765726E6572,2.5.4.4=#4B6F6368,C=DE" } |
73 | << DN::Result { { "SerialNumber" , "4" }, { "CN" , "Koch, Werner" }, { "GN" , "Werner" }, { "SN" , "Koch" }, { "C" , "DE" } }; |
74 | QTest::newRow(dataTag: "frompdf2a" ) << std::string { "2.5.4.5=#34,CN=Koch\\, Werner,oid.2.5.4.42=#5765726E6572,OID.2.5.4.4=#4B6F6368,C=DE" } |
75 | << DN::Result { { "SerialNumber" , "4" }, { "CN" , "Koch, Werner" }, { "GN" , "Werner" }, { "SN" , "Koch" }, { "C" , "DE" } }; |
76 | |
77 | // weird spacing |
78 | QTest::newRow(dataTag: "CN =Simple" ) << std::string { "CN =Simple" } << DN::Result { { "CN" , "Simple" } }; |
79 | QTest::newRow(dataTag: "CN= Simple" ) << std::string { "CN= Simple" } << DN::Result { { "CN" , "Simple" } }; |
80 | QTest::newRow(dataTag: "CN=Simple " ) << std::string { "CN=Simple " } << DN::Result { { "CN" , "Simple" } }; |
81 | QTest::newRow(dataTag: "CN=Simple," ) << std::string { "CN=Simple," } << DN::Result { { "CN" , "Simple" } }; |
82 | QTest::newRow(dataTag: "CN=Simple, O=Silly" ) << std::string { "CN=Simple, O=Silly" } << DN::Result { { "CN" , "Simple" }, { "O" , "Silly" } }; |
83 | |
84 | // various malformed |
85 | QTest::newRow(dataTag: "CN=Simple\\" ) << std::string { "CN=Simple\\" } << DN::Result {}; |
86 | QTest::newRow(dataTag: "CN=" ) << std::string { "CN=" } << DN::Result {}; |
87 | QTest::newRow(dataTag: "CN=Simple\\X" ) << std::string { "CN=Simple\\X" } << DN::Result {}; |
88 | QTest::newRow(dataTag: "CN=Simple, O" ) << std::string { "CN=Simple, O" } << DN::Result {}; |
89 | QTest::newRow(dataTag: "CN=Sim\"ple" ) << std::string { "CN=Sim\"ple, O" } << DN::Result {}; |
90 | QTest::newRow(dataTag: "CN=Simple\\a" ) << std::string { "CN=Simple\\a" } << DN::Result {}; |
91 | QTest::newRow(dataTag: "=Simple" ) << std::string { "=Simple" } << DN::Result {}; |
92 | QTest::newRow(dataTag: "CN=\"Simple" ) << std::string { "CN=\"Simple" } << DN::Result {}; |
93 | QTest::newRow(dataTag: "CN=\"Simple" ) << std::string { "CN=\"Simple\\" } << DN::Result {}; |
94 | QTest::newRow(dataTag: "unquoted quotation in quotation" ) << std::string { "CN=\"Quotation \" Mark\"" } << DN::Result {}; |
95 | } |
96 | |
97 | void TestDistinguishedNameParser::testRemoveLeadingSpaces() |
98 | { |
99 | QFETCH(std::string, input); |
100 | QFETCH(std::string, expectedOutput); |
101 | |
102 | auto result = DN::detail::removeLeadingSpaces(view: input); |
103 | QCOMPARE(result, expectedOutput); |
104 | } |
105 | void TestDistinguishedNameParser::testRemoveLeadingSpaces_data() |
106 | { |
107 | QTest::addColumn<std::string>(name: "input" ); |
108 | QTest::addColumn<std::string>(name: "expectedOutput" ); |
109 | |
110 | QTest::newRow(dataTag: "Empty" ) << std::string {} << std::string {}; |
111 | QTest::newRow(dataTag: "No leading spaces" ) << std::string { "horse" } << std::string { "horse" }; |
112 | QTest::newRow(dataTag: "Some spaces" ) << std::string { " horse" } << std::string { "horse" }; |
113 | QTest::newRow(dataTag: "Some leading and trailing" ) << std::string { " horse " } << std::string { "horse " }; |
114 | } |
115 | |
116 | void TestDistinguishedNameParser::testRemoveTrailingSpaces() |
117 | { |
118 | QFETCH(std::string, input); |
119 | QFETCH(std::string, expectedOutput); |
120 | |
121 | auto result = DN::detail::removeTrailingSpaces(view: input); |
122 | QCOMPARE(result, expectedOutput); |
123 | } |
124 | void TestDistinguishedNameParser::testRemoveTrailingSpaces_data() |
125 | { |
126 | QTest::addColumn<std::string>(name: "input" ); |
127 | QTest::addColumn<std::string>(name: "expectedOutput" ); |
128 | |
129 | QTest::newRow(dataTag: "Empty" ) << std::string {} << std::string {}; |
130 | QTest::newRow(dataTag: "No leading spaces" ) << std::string { "horse" } << std::string { "horse" }; |
131 | QTest::newRow(dataTag: "Some spaces" ) << std::string { "horse " } << std::string { "horse" }; |
132 | QTest::newRow(dataTag: "Some leading and trailing" ) << std::string { " horse " } << std::string { " horse" }; |
133 | } |
134 | |
135 | void TestDistinguishedNameParser::testParseHexString() |
136 | { |
137 | QFETCH(std::string, input); |
138 | QFETCH(std::optional<std::string>, expectedOutput); |
139 | |
140 | auto result = DN::detail::parseHexString(view: input); |
141 | QCOMPARE(result, expectedOutput); |
142 | } |
143 | |
144 | void TestDistinguishedNameParser::testParseHexString_data() |
145 | { |
146 | QTest::addColumn<std::string>(name: "input" ); |
147 | QTest::addColumn<std::optional<std::string>>(name: "expectedOutput" ); |
148 | |
149 | QTest::newRow(dataTag: "4" ) << std::string { "34" } << std::optional<std::string>("4" ); |
150 | QTest::newRow(dataTag: "Koch" ) << std::string { "4B6F6368" } << std::optional<std::string>("Koch" ); |
151 | QTest::newRow(dataTag: "USt-IdNr. DE 123475223" ) << std::string { "5553742D49644E722E20444520313233343735323233" } << std::optional<std::string>("USt-IdNr. DE 123475223" ); |
152 | |
153 | // various baddies |
154 | QTest::newRow(dataTag: "empty" ) << std::string {} << std::optional<std::string> {}; |
155 | QTest::newRow(dataTag: "FFF" ) << std::string { "FFF" } << std::optional<std::string> {}; |
156 | QTest::newRow(dataTag: "F" ) << std::string { "F" } << std::optional<std::string> {}; |
157 | QTest::newRow(dataTag: "XX" ) << std::string { "XX" } << std::optional<std::string> {}; |
158 | } |
159 | |
160 | QTEST_GUILESS_MAIN(TestDistinguishedNameParser); |
161 | #include "check_distinguished_name_parser.moc" |
162 | |