FreeRDP
TestASN1.c
1 #include <winpr/asn1.h>
2 #include <winpr/print.h>
3 
4 static const BYTE boolContent[] = { 0x01, 0x01, 0xFF };
5 static const BYTE badBoolContent[] = { 0x01, 0x04, 0xFF };
6 
7 static const BYTE integerContent[] = { 0x02, 0x01, 0x02 };
8 static const BYTE badIntegerContent[] = { 0x02, 0x04, 0x02 };
9 static const BYTE positiveIntegerContent[] = { 0x02, 0x02, 0x00, 0xff };
10 static const BYTE negativeIntegerContent[] = { 0x02, 0x01, 0xff };
11 
12 static const BYTE seqContent[] = { 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1B, 0x44,
13  0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x53, 0x69, 0x67,
14  0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75,
15  0x73, 0x74, 0x20, 0x43, 0x6F, 0x2E, 0x31 };
16 
17 static const BYTE contextualInteger[] = { 0xA0, 0x03, 0x02, 0x01, 0x02 };
18 
19 static const BYTE oidContent[] = { 0x06, 0x03, 0x55, 0x04, 0x0A };
20 static const BYTE badOidContent[] = { 0x06, 0x89, 0x55, 0x04, 0x0A };
21 static const BYTE oidValue[] = { 0x55, 0x04, 0x0A };
22 
23 static const BYTE ia5stringContent[] = { 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
24  0x63, 0x70, 0x73, 0x2E, 0x72, 0x6F, 0x6F, 0x74, 0x2D,
25  0x78, 0x31, 0x2E, 0x6C, 0x65, 0x74, 0x73, 0x65, 0x6E,
26  0x63, 0x72, 0x79, 0x70, 0x74, 0x2E, 0x6F, 0x72, 0x67 };
27 
28 static const BYTE utctimeContent[] = { 0x17, 0x0D, 0x32, 0x31, 0x30, 0x33, 0x31, 0x37,
29  0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5A };
30 
31 int TestASN1Read(int argc, char* argv[])
32 {
33  WinPrAsn1Decoder decoder;
34  WinPrAsn1Decoder seqDecoder;
35  wStream staticS;
36  WinPrAsn1_BOOL boolV = 0;
37  WinPrAsn1_INTEGER integerV = 0;
38  WinPrAsn1_OID oidV;
39  WinPrAsn1_IA5STRING ia5stringV = NULL;
40  WinPrAsn1_UTCTIME utctimeV;
41  WinPrAsn1_tag tag = 0;
42  size_t len = 0;
43  BOOL error = 0;
44 
45  /* ============== Test INTEGERs ================ */
46  Stream_StaticConstInit(&staticS, integerContent, sizeof(integerContent));
47  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
48  if (!WinPrAsn1DecReadInteger(&decoder, &integerV))
49  return -1;
50 
51  Stream_StaticConstInit(&staticS, positiveIntegerContent, sizeof(positiveIntegerContent));
52  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
53  if (!WinPrAsn1DecReadInteger(&decoder, &integerV) && integerV != 0xff)
54  return -1;
55 
56  Stream_StaticConstInit(&staticS, negativeIntegerContent, sizeof(negativeIntegerContent));
57  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
58  if (!WinPrAsn1DecReadInteger(&decoder, &integerV) && integerV != -1)
59  return -1;
60 
61  Stream_StaticConstInit(&staticS, badIntegerContent, sizeof(badIntegerContent));
62  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
63  if (WinPrAsn1DecReadInteger(&decoder, &integerV))
64  return -1;
65 
66  /* ================ Test BOOL ================*/
67  Stream_StaticConstInit(&staticS, boolContent, sizeof(boolContent));
68  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
69  if (!WinPrAsn1DecReadBoolean(&decoder, &boolV))
70  return -10;
71 
72  Stream_StaticConstInit(&staticS, badBoolContent, sizeof(badBoolContent));
73  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
74  if (WinPrAsn1DecReadBoolean(&decoder, &boolV))
75  return -11;
76 
77  /* ================ Test OID ================*/
78  Stream_StaticConstInit(&staticS, oidContent, sizeof(oidContent));
79  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
80  if (!WinPrAsn1DecReadOID(&decoder, &oidV, TRUE) || oidV.len != 3 ||
81  (memcmp(oidV.data, oidValue, oidV.len) != 0))
82  return -15;
83  WinPrAsn1FreeOID(&oidV);
84 
85  Stream_StaticConstInit(&staticS, badOidContent, sizeof(badOidContent));
86  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
87  if (WinPrAsn1DecReadOID(&decoder, &oidV, TRUE))
88  return -15;
89  WinPrAsn1FreeOID(&oidV);
90 
91  Stream_StaticConstInit(&staticS, ia5stringContent, sizeof(ia5stringContent));
92  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
93  if (!WinPrAsn1DecReadIA5String(&decoder, &ia5stringV) ||
94  (strcmp(ia5stringV, "http://cps.root-x1.letsencrypt.org") != 0))
95  return -16;
96  free(ia5stringV);
97 
98  /* ================ Test utc time ================*/
99  Stream_StaticConstInit(&staticS, utctimeContent, sizeof(utctimeContent));
100  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
101  if (!WinPrAsn1DecReadUtcTime(&decoder, &utctimeV) || utctimeV.year != 2021 ||
102  utctimeV.month != 3 || utctimeV.day != 17 || utctimeV.minute != 40 || utctimeV.tz != 'Z')
103  return -17;
104 
105  /* ================ Test sequence ================*/
106  Stream_StaticConstInit(&staticS, seqContent, sizeof(seqContent));
107  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
108  if (!WinPrAsn1DecReadSequence(&decoder, &seqDecoder))
109  return -20;
110 
111  Stream_StaticConstInit(&staticS, seqContent, sizeof(seqContent));
112  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
113  if (!WinPrAsn1DecReadTagLenValue(&decoder, &tag, &len, &seqDecoder))
114  return -21;
115 
116  if (tag != ER_TAG_SEQUENCE)
117  return -22;
118 
119  if (!WinPrAsn1DecPeekTag(&seqDecoder, &tag) || tag != ER_TAG_OBJECT_IDENTIFIER)
120  return -23;
121 
122  /* ================ Test contextual ================*/
123  Stream_StaticConstInit(&staticS, contextualInteger, sizeof(contextualInteger));
124  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
125  error = TRUE;
126  if (!WinPrAsn1DecReadContextualInteger(&decoder, 0, &error, &integerV) || error)
127  return -25;
128 
129  /* test reading a contextual integer that is not there (index 1).
130  * that should not touch the decoder read head and we shall be able to extract contextual tag 0
131  * after that
132  */
133  WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
134  error = FALSE;
135  if (WinPrAsn1DecReadContextualInteger(&decoder, 1, &error, &integerV) || error)
136  return -26;
137 
138  error = FALSE;
139  if (!WinPrAsn1DecReadContextualInteger(&decoder, 0, &error, &integerV) || error)
140  return -27;
141 
142  return 0;
143 }
144 
145 static BYTE oid1_val[] = { 1 };
146 static const WinPrAsn1_OID oid1 = { sizeof(oid1_val), oid1_val };
147 static BYTE oid2_val[] = { 2, 2 };
148 static WinPrAsn1_OID oid2 = { sizeof(oid2_val), oid2_val };
149 static BYTE oid3_val[] = { 3, 3, 3 };
150 static WinPrAsn1_OID oid3 = { sizeof(oid3_val), oid3_val };
151 static BYTE oid4_val[] = { 4, 4, 4, 4 };
152 static WinPrAsn1_OID oid4 = { sizeof(oid4_val), oid4_val };
153 
154 int TestASN1Write(int argc, char* argv[])
155 {
156  wStream* s = NULL;
157  size_t expectedOuputSz = 0;
158  int retCode = 100;
159  WinPrAsn1_UTCTIME utcTime;
160  WinPrAsn1_IA5STRING ia5string = NULL;
161  WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
162  if (!enc)
163  goto out;
164 
165  /* Let's encode something like:
166  * APP(3)
167  * SEQ2
168  * OID1
169  * OID2
170  * SEQ3
171  * OID3
172  * OID4
173  *
174  * [5] integer(200)
175  * [6] SEQ (empty)
176  * [7] UTC time (2016-03-17 16:40:41 UTC)
177  * [8] IA5String(test)
178  * [9] OctetString
179  * SEQ(empty)
180  *
181  */
182 
183  /* APP(3) */
184  retCode = 101;
185  if (!WinPrAsn1EncAppContainer(enc, 3))
186  goto out;
187 
188  /* SEQ2 */
189  retCode = 102;
190  if (!WinPrAsn1EncSeqContainer(enc))
191  goto out;
192 
193  retCode = 103;
194  if (WinPrAsn1EncOID(enc, &oid1) != 3)
195  goto out;
196 
197  retCode = 104;
198  if (WinPrAsn1EncOID(enc, &oid2) != 4)
199  goto out;
200 
201  retCode = 105;
202  if (WinPrAsn1EncEndContainer(enc) != 9)
203  goto out;
204 
205  /* SEQ3 */
206  retCode = 110;
207  if (!WinPrAsn1EncSeqContainer(enc))
208  goto out;
209 
210  retCode = 111;
211  if (WinPrAsn1EncOID(enc, &oid3) != 5)
212  goto out;
213 
214  retCode = 112;
215  if (WinPrAsn1EncOID(enc, &oid4) != 6)
216  goto out;
217 
218  retCode = 113;
219  if (WinPrAsn1EncEndContainer(enc) != 13)
220  goto out;
221 
222  /* [5] integer(200) */
223  retCode = 114;
224  if (WinPrAsn1EncContextualInteger(enc, 5, 200) != 6)
225  goto out;
226 
227  /* [6] SEQ (empty) */
228  retCode = 115;
229  if (!WinPrAsn1EncContextualSeqContainer(enc, 6))
230  goto out;
231 
232  retCode = 116;
233  if (WinPrAsn1EncEndContainer(enc) != 4)
234  goto out;
235 
236  /* [7] UTC time (2016-03-17 16:40:41 UTC) */
237  retCode = 117;
238  utcTime.year = 2016;
239  utcTime.month = 3;
240  utcTime.day = 17;
241  utcTime.hour = 16;
242  utcTime.minute = 40;
243  utcTime.second = 41;
244  utcTime.tz = 'Z';
245  if (WinPrAsn1EncContextualUtcTime(enc, 7, &utcTime) != 17)
246  goto out;
247 
248  /* [8] IA5String(test) */
249  retCode = 118;
250  ia5string = "test";
251  if (!WinPrAsn1EncContextualContainer(enc, 8))
252  goto out;
253 
254  retCode = 119;
255  if (WinPrAsn1EncIA5String(enc, ia5string) != 6)
256  goto out;
257 
258  retCode = 120;
259  if (WinPrAsn1EncEndContainer(enc) != 8)
260  goto out;
261 
262  /* [9] OctetString
263  * SEQ(empty)
264  */
265  retCode = 121;
266  if (!WinPrAsn1EncContextualOctetStringContainer(enc, 9))
267  goto out;
268 
269  retCode = 122;
270  if (!WinPrAsn1EncSeqContainer(enc))
271  goto out;
272 
273  retCode = 123;
274  if (WinPrAsn1EncEndContainer(enc) != 2)
275  goto out;
276 
277  retCode = 124;
278  if (WinPrAsn1EncEndContainer(enc) != 6)
279  goto out;
280 
281  /* close APP */
282  expectedOuputSz = 24 + 6 + 4 + 17 + 8 + 6;
283  retCode = 200;
284  if (WinPrAsn1EncEndContainer(enc) != expectedOuputSz)
285  goto out;
286 
287  /* let's output the result */
288  retCode = 201;
289  s = Stream_New(NULL, 1024);
290  if (!s)
291  goto out;
292 
293  retCode = 202;
294  if (!WinPrAsn1EncToStream(enc, s) || Stream_GetPosition(s) != expectedOuputSz)
295  goto out;
296  /* winpr_HexDump("", WLOG_ERROR, Stream_Buffer(s), Stream_GetPosition(s));*/
297 
298  /*
299  * let's perform a mini-performance test, where we encode an ASN1 message with a big depth,
300  * so that we trigger reallocation routines in the encoder. We're gonna encode something like
301  * SEQ1
302  * SEQ2
303  * SEQ3
304  * ...
305  * SEQ1000
306  * INTEGER(2)
307  *
308  * As static chunks and containers are 50, a depth of 1000 should be enough
309  *
310  */
311  WinPrAsn1Encoder_Reset(enc);
312 
313  retCode = 203;
314  for (size_t i = 0; i < 1000; i++)
315  {
316  if (!WinPrAsn1EncSeqContainer(enc))
317  goto out;
318  }
319 
320  retCode = 204;
321  if (WinPrAsn1EncInteger(enc, 2) != 3)
322  goto out;
323 
324  retCode = 205;
325  for (size_t i = 0; i < 1000; i++)
326  {
327  if (!WinPrAsn1EncEndContainer(enc))
328  goto out;
329  }
330 
331  retCode = 0;
332 
333 out:
334  if (s)
335  Stream_Free(s, TRUE);
336  WinPrAsn1Encoder_Free(&enc);
337  return retCode;
338 }
339 
340 int TestASN1(int argc, char* argv[])
341 {
342  int ret = TestASN1Read(argc, argv);
343  if (ret)
344  return ret;
345 
346  return TestASN1Write(argc, argv);
347 }