FreeRDP
Loading...
Searching...
No Matches
ber.c
1
20#include <freerdp/config.h>
21
22#include <stdio.h>
23#include <winpr/assert.h>
24#include <winpr/cast.h>
25#include <winpr/crt.h>
26#include <winpr/string.h>
27
28#include <freerdp/log.h>
29#include <freerdp/crypto/ber.h>
30
31#define TAG FREERDP_TAG("crypto")
32
33BOOL ber_read_length(wStream* s, size_t* length)
34{
35 BYTE byte = 0;
36
37 WINPR_ASSERT(s);
38 WINPR_ASSERT(length);
39
40 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
41 return FALSE;
42
43 Stream_Read_UINT8(s, byte);
44
45 if (byte & 0x80)
46 {
47 byte &= ~(0x80);
48
49 if (!Stream_CheckAndLogRequiredLength(TAG, s, byte))
50 return FALSE;
51
52 if (byte == 1)
53 Stream_Read_UINT8(s, *length);
54 else if (byte == 2)
55 Stream_Read_UINT16_BE(s, *length);
56 else
57 {
58 WLog_ERR(TAG, "ber: unexpected byte 0x%02" PRIx8 ", expected [1,2]", byte);
59 return FALSE;
60 }
61 }
62 else
63 {
64 *length = byte;
65 }
66
67 return TRUE;
68}
69
76size_t ber_write_length(wStream* s, size_t length)
77{
78 WINPR_ASSERT(s);
79
80 if (length > 0xFF)
81 {
82 WINPR_ASSERT(length <= UINT16_MAX);
83 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 3);
84 Stream_Write_UINT8(s, 0x80 ^ 2);
85 Stream_Write_UINT16_BE(s, (UINT16)length);
86 return 3;
87 }
88
89 WINPR_ASSERT(length <= UINT8_MAX);
90 if (length > 0x7F)
91 {
92 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 2);
93 Stream_Write_UINT8(s, 0x80 ^ 1);
94 Stream_Write_UINT8(s, (UINT8)length);
95 return 2;
96 }
97
98 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
99 Stream_Write_UINT8(s, (UINT8)length);
100 return 1;
101}
102
103size_t _ber_sizeof_length(size_t length)
104{
105 if (length > 0xFF)
106 return 3;
107
108 if (length > 0x7F)
109 return 2;
110
111 return 1;
112}
113
123BOOL ber_read_universal_tag(wStream* s, BYTE tag, BOOL pc)
124{
125 BYTE byte = 0;
126 const BYTE expect = (BER_CLASS_UNIV | BER_PC(pc) | (BER_TAG_MASK & tag));
127
128 WINPR_ASSERT(s);
129
130 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
131 return FALSE;
132
133 Stream_Read_UINT8(s, byte);
134
135 if (byte != expect)
136 {
137 WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
138 return FALSE;
139 }
140
141 return TRUE;
142}
143
151size_t ber_write_universal_tag(wStream* s, BYTE tag, BOOL pc)
152{
153 WINPR_ASSERT(s);
154 Stream_Write_UINT8(s, (BER_CLASS_UNIV | BER_PC(pc)) | (BER_TAG_MASK & tag));
155 return 1;
156}
157
165BOOL ber_read_application_tag(wStream* s, BYTE tag, size_t* length)
166{
167 BYTE byte = 0;
168
169 WINPR_ASSERT(s);
170 WINPR_ASSERT(length);
171
172 if (tag > 30)
173 {
174 const BYTE expect = ((BER_CLASS_APPL | BER_CONSTRUCT) | BER_TAG_MASK);
175
176 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
177 return FALSE;
178
179 Stream_Read_UINT8(s, byte);
180
181 if (byte != expect)
182 {
183 WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
184 return FALSE;
185 }
186
187 Stream_Read_UINT8(s, byte);
188
189 if (byte != tag)
190 {
191 WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, tag);
192 return FALSE;
193 }
194
195 return ber_read_length(s, length);
196 }
197 else
198 {
199 const BYTE expect = ((BER_CLASS_APPL | BER_CONSTRUCT) | (BER_TAG_MASK & tag));
200
201 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
202 return FALSE;
203
204 Stream_Read_UINT8(s, byte);
205
206 if (byte != expect)
207 {
208 WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
209 return FALSE;
210 }
211
212 return ber_read_length(s, length);
213 }
214
215 return TRUE;
216}
217
225void ber_write_application_tag(wStream* s, BYTE tag, size_t length)
226{
227 WINPR_ASSERT(s);
228
229 if (tag > 30)
230 {
231 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 2);
232 Stream_Write_UINT8(s, (BER_CLASS_APPL | BER_CONSTRUCT) | BER_TAG_MASK);
233 Stream_Write_UINT8(s, tag);
234 ber_write_length(s, length);
235 }
236 else
237 {
238 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
239 Stream_Write_UINT8(s, (BER_CLASS_APPL | BER_CONSTRUCT) | (BER_TAG_MASK & tag));
240 ber_write_length(s, length);
241 }
242}
243
244BOOL ber_read_contextual_tag(wStream* s, BYTE tag, size_t* length, BOOL pc)
245{
246 const BYTE expect = ((BER_CLASS_CTXT | BER_PC(pc)) | (BER_TAG_MASK & tag));
247 BYTE byte = 0;
248
249 WINPR_ASSERT(s);
250 WINPR_ASSERT(length);
251
252 if (Stream_GetRemainingLength(s) < 1)
253 {
254 WLog_VRB(TAG, "short data, got %" PRIuz ", expected %" PRIuz, Stream_GetRemainingLength(s),
255 1);
256 return FALSE;
257 }
258
259 Stream_Read_UINT8(s, byte);
260
261 if (byte != expect)
262 {
263 WLog_VRB(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
264 Stream_Rewind(s, 1);
265 return FALSE;
266 }
267
268 return ber_read_length(s, length);
269}
270
271size_t ber_write_contextual_tag(wStream* s, BYTE tag, size_t length, BOOL pc)
272{
273 WINPR_ASSERT(s);
274 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
275 Stream_Write_UINT8(s, (BER_CLASS_CTXT | BER_PC(pc)) | (BER_TAG_MASK & tag));
276 return 1 + ber_write_length(s, length);
277}
278
279size_t ber_sizeof_contextual_tag(size_t length)
280{
281 return 1 + _ber_sizeof_length(length);
282}
283
284BOOL ber_read_sequence_tag(wStream* s, size_t* length)
285{
286 const BYTE expect = ((BER_CLASS_UNIV | BER_CONSTRUCT) | (BER_TAG_SEQUENCE_OF));
287 BYTE byte = 0;
288
289 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
290 return FALSE;
291
292 Stream_Read_UINT8(s, byte);
293
294 if (byte != expect)
295 {
296 WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
297 return FALSE;
298 }
299
300 return ber_read_length(s, length);
301}
302
309size_t ber_write_sequence_tag(wStream* s, size_t length)
310{
311 Stream_Write_UINT8(s, (BER_CLASS_UNIV | BER_CONSTRUCT) | (BER_TAG_MASK & BER_TAG_SEQUENCE));
312 return 1 + ber_write_length(s, length);
313}
314
315size_t ber_sizeof_sequence(size_t length)
316{
317 return 1 + _ber_sizeof_length(length) + length;
318}
319
320size_t ber_sizeof_sequence_tag(size_t length)
321{
322 return 1 + _ber_sizeof_length(length);
323}
324
325BOOL ber_read_enumerated(wStream* s, BYTE* enumerated, BYTE count)
326{
327 size_t length = 0;
328
329 WINPR_ASSERT(enumerated);
330
331 if (!ber_read_universal_tag(s, BER_TAG_ENUMERATED, FALSE) || !ber_read_length(s, &length))
332 return FALSE;
333
334 if (length != 1)
335 {
336 WLog_WARN(TAG, "short data, got %" PRIuz ", expected %" PRIuz, length, 1);
337 return FALSE;
338 }
339 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
340 return FALSE;
341
342 Stream_Read_UINT8(s, *enumerated);
343
344 /* check that enumerated value falls within expected range */
345 if (*enumerated + 1 > count)
346 {
347 WLog_WARN(TAG, "invalid data, expected %" PRIu8 " < %" PRIu8, *enumerated, count);
348 return FALSE;
349 }
350
351 return TRUE;
352}
353
354void ber_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count)
355{
356 ber_write_universal_tag(s, BER_TAG_ENUMERATED, FALSE);
357 ber_write_length(s, 1);
358 Stream_Write_UINT8(s, enumerated);
359}
360
361BOOL ber_read_bit_string(wStream* s, size_t* length, BYTE* padding)
362{
363 if (!ber_read_universal_tag(s, BER_TAG_BIT_STRING, FALSE) || !ber_read_length(s, length))
364 return FALSE;
365
366 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
367 return FALSE;
368
369 Stream_Read_UINT8(s, *padding);
370 return TRUE;
371}
372
380size_t ber_write_octet_string(wStream* s, const BYTE* oct_str, size_t length)
381{
382 size_t size = 0;
383
384 WINPR_ASSERT(oct_str || (length == 0));
385 size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
386 size += ber_write_length(s, length);
387 Stream_Write(s, oct_str, length);
388 size += length;
389 return size;
390}
391
392size_t ber_write_contextual_octet_string(wStream* s, BYTE tag, const BYTE* oct_str, size_t length)
393{
394 size_t inner = ber_sizeof_octet_string(length);
395 size_t ret = 0;
396 size_t r = 0;
397
398 ret = ber_write_contextual_tag(s, tag, inner, TRUE);
399 if (!ret)
400 return 0;
401
402 r = ber_write_octet_string(s, oct_str, length);
403 if (!r)
404 return 0;
405 return ret + r;
406}
407
408size_t ber_write_char_to_unicode_octet_string(wStream* s, const char* str)
409{
410 WINPR_ASSERT(str);
411 size_t size = 0;
412 size_t length = strlen(str) + 1;
413 size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
414 size += ber_write_length(s, length * sizeof(WCHAR));
415
416 if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
417 return 0;
418 return size + length * sizeof(WCHAR);
419}
420
421size_t ber_write_contextual_unicode_octet_string(wStream* s, BYTE tag, LPWSTR str)
422{
423 WINPR_ASSERT(str);
424 size_t len = _wcslen(str) * sizeof(WCHAR);
425 size_t inner_len = ber_sizeof_octet_string(len);
426 size_t ret = 0;
427
428 ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
429 return ret + ber_write_octet_string(s, (const BYTE*)str, len);
430}
431
432size_t ber_write_contextual_char_to_unicode_octet_string(wStream* s, BYTE tag, const char* str)
433{
434 size_t ret = 0;
435 size_t len = strlen(str);
436 size_t inner_len = ber_sizeof_octet_string(len * 2);
437
438 WINPR_ASSERT(Stream_GetRemainingCapacity(s) < ber_sizeof_contextual_tag(inner_len) + inner_len);
439
440 ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
441 ret += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
442 ret += ber_write_length(s, len * sizeof(WCHAR));
443
444 if (Stream_Write_UTF16_String_From_UTF8(s, len, str, len, TRUE) < 0)
445 return 0;
446
447 return ret + len;
448}
449
450BOOL ber_read_unicode_octet_string(wStream* s, LPWSTR* str)
451{
452 LPWSTR ret = NULL;
453 size_t length = 0;
454
455 if (!ber_read_octet_string_tag(s, &length))
456 return FALSE;
457
458 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
459 return FALSE;
460
461 ret = calloc(1, length + 2);
462 if (!ret)
463 return FALSE;
464
465 memcpy(ret, Stream_ConstPointer(s), length);
466 ret[length / 2] = 0;
467 Stream_Seek(s, length);
468 *str = ret;
469 return TRUE;
470}
471
472BOOL ber_read_char_from_unicode_octet_string(wStream* s, char** str)
473{
474 size_t length = 0;
475 char* ptr = NULL;
476
477 *str = NULL;
478 if (!ber_read_octet_string_tag(s, &length))
479 return FALSE;
480
481 ptr = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), NULL);
482 if (!ptr)
483 return FALSE;
484 *str = ptr;
485 return TRUE;
486}
487
488BOOL ber_read_octet_string_tag(wStream* s, size_t* length)
489{
490 return ber_read_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) && ber_read_length(s, length);
491}
492
493BOOL ber_read_octet_string(wStream* s, BYTE** content, size_t* length)
494{
495 BYTE* ret = NULL;
496
497 WINPR_ASSERT(s);
498 WINPR_ASSERT(content);
499 WINPR_ASSERT(length);
500
501 if (!ber_read_octet_string_tag(s, length))
502 return FALSE;
503 if (!Stream_CheckAndLogRequiredLength(TAG, s, *length))
504 return FALSE;
505
506 ret = malloc(*length);
507 if (!ret)
508 return FALSE;
509
510 Stream_Read(s, ret, *length);
511 *content = ret;
512 return TRUE;
513}
514
515size_t ber_write_octet_string_tag(wStream* s, size_t length)
516{
517 ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
518 ber_write_length(s, length);
519 return 1 + _ber_sizeof_length(length);
520}
521
522size_t ber_sizeof_octet_string(size_t length)
523{
524 return 1 + _ber_sizeof_length(length) + length;
525}
526
527size_t ber_sizeof_contextual_octet_string(size_t length)
528{
529 size_t ret = ber_sizeof_octet_string(length);
530 return ber_sizeof_contextual_tag(ret) + ret;
531}
532
541BOOL ber_read_BOOL(wStream* s, BOOL* value)
542{
543 size_t length = 0;
544 BYTE v = 0;
545
546 WINPR_ASSERT(value);
547 if (!ber_read_universal_tag(s, BER_TAG_BOOLEAN, FALSE) || !ber_read_length(s, &length))
548 return FALSE;
549
550 if (length != 1)
551 {
552 WLog_WARN(TAG, "short data, got %" PRIuz ", expected %" PRIuz, length, 1);
553 return FALSE;
554 }
555 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
556 return FALSE;
557
558 Stream_Read_UINT8(s, v);
559 *value = (v ? TRUE : FALSE);
560 return TRUE;
561}
562
570void ber_write_BOOL(wStream* s, BOOL value)
571{
572 ber_write_universal_tag(s, BER_TAG_BOOLEAN, FALSE);
573 ber_write_length(s, 1);
574 Stream_Write_UINT8(s, (value == TRUE) ? 0xFF : 0);
575}
576
577BOOL ber_read_integer(wStream* s, UINT32* value)
578{
579 size_t length = 0;
580
581 WINPR_ASSERT(s);
582
583 if (!ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE))
584 return FALSE;
585 if (!ber_read_length(s, &length))
586 return FALSE;
587 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
588 return FALSE;
589
590 if (value == NULL)
591 {
592 // even if we don't care the integer value, check the announced size
593 return Stream_SafeSeek(s, length);
594 }
595
596 if (length == 1)
597 {
598 Stream_Read_UINT8(s, *value);
599 }
600 else if (length == 2)
601 {
602 Stream_Read_UINT16_BE(s, *value);
603 }
604 else if (length == 3)
605 {
606 BYTE byte = 0;
607 Stream_Read_UINT8(s, byte);
608 Stream_Read_UINT16_BE(s, *value);
609 *value += (byte << 16) & 0xFF0000;
610 }
611 else if (length == 4)
612 {
613 Stream_Read_UINT32_BE(s, *value);
614 }
615 else if (length == 8)
616 {
617 WLog_ERR(TAG, "should implement reading an 8 bytes integer");
618 return FALSE;
619 }
620 else
621 {
622 WLog_ERR(TAG, "should implement reading an integer with length=%" PRIuz, length);
623 return FALSE;
624 }
625
626 return TRUE;
627}
628
638size_t ber_write_integer(wStream* s, UINT32 value)
639{
640 WINPR_ASSERT(s);
641
642 if (value < 0x80)
643 {
644 ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE);
645 ber_write_length(s, 1);
646
647 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value));
648 return 3;
649 }
650 else if (value < 0x8000)
651 {
652 ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE);
653 ber_write_length(s, 2);
654
655 Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
656 return 4;
657 }
658 else if (value < 0x800000)
659 {
660 ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE);
661 ber_write_length(s, 3);
662
663 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value >> 16));
664 Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
665 return 5;
666 }
667 else if (value < 0x80000000)
668 {
669 ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE);
670 ber_write_length(s, 4);
671
672 Stream_Write_UINT32_BE(s, value);
673 return 6;
674 }
675 else
676 {
677 /* treat as signed integer i.e. NT/HRESULT error codes */
678 ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE);
679 ber_write_length(s, 4);
680
681 Stream_Write_UINT32_BE(s, value);
682 return 6;
683 }
684}
685
686size_t ber_write_contextual_integer(wStream* s, BYTE tag, UINT32 value)
687{
688 size_t len = ber_sizeof_integer(value);
689
690 WINPR_ASSERT(s);
691
692 WINPR_ASSERT(Stream_EnsureRemainingCapacity(s, len + 5));
693
694 len += ber_write_contextual_tag(s, tag, len, TRUE);
695 ber_write_integer(s, value);
696 return len;
697}
698
699size_t ber_sizeof_integer(UINT32 value)
700{
701 if (value < 0x80)
702 {
703 return 3;
704 }
705 else if (value < 0x8000)
706 {
707 return 4;
708 }
709 else if (value < 0x800000)
710 {
711 return 5;
712 }
713 else if (value < 0x80000000)
714 {
715 return 6;
716 }
717 else
718 {
719 /* treat as signed integer i.e. NT/HRESULT error codes */
720 return 6;
721 }
722}
723
724size_t ber_sizeof_contextual_integer(UINT32 value)
725{
726 size_t intSize = ber_sizeof_integer(value);
727 return ber_sizeof_contextual_tag(intSize) + intSize;
728}
729
730BOOL ber_read_integer_length(wStream* s, size_t* length)
731{
732 return ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE) && ber_read_length(s, length);
733}