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 %u", Stream_GetRemainingLength(s), 1u);
255 return FALSE;
256 }
257
258 Stream_Read_UINT8(s, byte);
259
260 if (byte != expect)
261 {
262 WLog_VRB(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
263 Stream_Rewind(s, 1);
264 return FALSE;
265 }
266
267 return ber_read_length(s, length);
268}
269
270size_t ber_write_contextual_tag(wStream* s, BYTE tag, size_t length, BOOL pc)
271{
272 WINPR_ASSERT(s);
273 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
274 Stream_Write_UINT8(s, (BER_CLASS_CTXT | BER_PC(pc)) | (BER_TAG_MASK & tag));
275 return 1 + ber_write_length(s, length);
276}
277
278size_t ber_sizeof_contextual_tag(size_t length)
279{
280 return 1 + _ber_sizeof_length(length);
281}
282
283BOOL ber_read_sequence_tag(wStream* s, size_t* length)
284{
285 const BYTE expect = ((BER_CLASS_UNIV | BER_CONSTRUCT) | (BER_TAG_SEQUENCE_OF));
286 BYTE byte = 0;
287
288 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
289 return FALSE;
290
291 Stream_Read_UINT8(s, byte);
292
293 if (byte != expect)
294 {
295 WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
296 return FALSE;
297 }
298
299 return ber_read_length(s, length);
300}
301
308size_t ber_write_sequence_tag(wStream* s, size_t length)
309{
310 Stream_Write_UINT8(s, (BER_CLASS_UNIV | BER_CONSTRUCT) | (BER_TAG_MASK & BER_TAG_SEQUENCE));
311 return 1 + ber_write_length(s, length);
312}
313
314size_t ber_sizeof_sequence(size_t length)
315{
316 return 1 + _ber_sizeof_length(length) + length;
317}
318
319size_t ber_sizeof_sequence_tag(size_t length)
320{
321 return 1 + _ber_sizeof_length(length);
322}
323
324BOOL ber_read_enumerated(wStream* s, BYTE* enumerated, BYTE count)
325{
326 size_t length = 0;
327
328 WINPR_ASSERT(enumerated);
329
330 if (!ber_read_universal_tag(s, BER_TAG_ENUMERATED, FALSE) || !ber_read_length(s, &length))
331 return FALSE;
332
333 if (length != 1)
334 {
335 WLog_WARN(TAG, "short data, got %" PRIuz ", expected %u", length, 1u);
336 return FALSE;
337 }
338 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
339 return FALSE;
340
341 Stream_Read_UINT8(s, *enumerated);
342
343 /* check that enumerated value falls within expected range */
344 if (*enumerated + 1 > count)
345 {
346 WLog_WARN(TAG, "invalid data, expected %" PRIu8 " < %" PRIu8, *enumerated, count);
347 return FALSE;
348 }
349
350 return TRUE;
351}
352
353void ber_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count)
354{
355 ber_write_universal_tag(s, BER_TAG_ENUMERATED, FALSE);
356 ber_write_length(s, 1);
357 Stream_Write_UINT8(s, enumerated);
358}
359
360BOOL ber_read_bit_string(wStream* s, size_t* length, BYTE* padding)
361{
362 if (!ber_read_universal_tag(s, BER_TAG_BIT_STRING, FALSE) || !ber_read_length(s, length))
363 return FALSE;
364
365 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
366 return FALSE;
367
368 Stream_Read_UINT8(s, *padding);
369 return TRUE;
370}
371
379size_t ber_write_octet_string(wStream* s, const BYTE* oct_str, size_t length)
380{
381 size_t size = 0;
382
383 WINPR_ASSERT(oct_str || (length == 0));
384 size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
385 size += ber_write_length(s, length);
386 Stream_Write(s, oct_str, length);
387 size += length;
388 return size;
389}
390
391size_t ber_write_contextual_octet_string(wStream* s, BYTE tag, const BYTE* oct_str, size_t length)
392{
393 size_t inner = ber_sizeof_octet_string(length);
394 size_t ret = 0;
395 size_t r = 0;
396
397 ret = ber_write_contextual_tag(s, tag, inner, TRUE);
398 if (!ret)
399 return 0;
400
401 r = ber_write_octet_string(s, oct_str, length);
402 if (!r)
403 return 0;
404 return ret + r;
405}
406
407size_t ber_write_char_to_unicode_octet_string(wStream* s, const char* str)
408{
409 WINPR_ASSERT(str);
410 size_t size = 0;
411 size_t length = strlen(str) + 1;
412 size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
413 size += ber_write_length(s, length * sizeof(WCHAR));
414
415 if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
416 return 0;
417 return size + length * sizeof(WCHAR);
418}
419
420size_t ber_write_contextual_unicode_octet_string(wStream* s, BYTE tag, LPWSTR str)
421{
422 WINPR_ASSERT(str);
423 size_t len = _wcslen(str) * sizeof(WCHAR);
424 size_t inner_len = ber_sizeof_octet_string(len);
425 size_t ret = 0;
426
427 ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
428 return ret + ber_write_octet_string(s, (const BYTE*)str, len);
429}
430
431size_t ber_write_contextual_char_to_unicode_octet_string(wStream* s, BYTE tag, const char* str)
432{
433 size_t ret = 0;
434 size_t len = strlen(str);
435 size_t inner_len = ber_sizeof_octet_string(len * 2);
436
437 WINPR_ASSERT(Stream_GetRemainingCapacity(s) < ber_sizeof_contextual_tag(inner_len) + inner_len);
438
439 ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
440 ret += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
441 ret += ber_write_length(s, len * sizeof(WCHAR));
442
443 if (Stream_Write_UTF16_String_From_UTF8(s, len, str, len, TRUE) < 0)
444 return 0;
445
446 return ret + len;
447}
448
449BOOL ber_read_unicode_octet_string(wStream* s, LPWSTR* str)
450{
451 LPWSTR ret = nullptr;
452 size_t length = 0;
453
454 if (!ber_read_octet_string_tag(s, &length))
455 return FALSE;
456
457 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
458 return FALSE;
459
460 ret = calloc(1, length + 2);
461 if (!ret)
462 return FALSE;
463
464 memcpy(ret, Stream_ConstPointer(s), length);
465 ret[length / 2] = 0;
466 Stream_Seek(s, length);
467 *str = ret;
468 return TRUE;
469}
470
471BOOL ber_read_char_from_unicode_octet_string(wStream* s, char** str)
472{
473 size_t length = 0;
474 char* ptr = nullptr;
475
476 *str = nullptr;
477 if (!ber_read_octet_string_tag(s, &length))
478 return FALSE;
479
480 ptr = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), nullptr);
481 if (!ptr)
482 return FALSE;
483 *str = ptr;
484 return TRUE;
485}
486
487BOOL ber_read_octet_string_tag(wStream* s, size_t* length)
488{
489 return ber_read_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) && ber_read_length(s, length);
490}
491
492BOOL ber_read_octet_string(wStream* s, BYTE** content, size_t* length)
493{
494 BYTE* ret = nullptr;
495
496 WINPR_ASSERT(s);
497 WINPR_ASSERT(content);
498 WINPR_ASSERT(length);
499
500 if (!ber_read_octet_string_tag(s, length))
501 return FALSE;
502 if (!Stream_CheckAndLogRequiredLength(TAG, s, *length))
503 return FALSE;
504
505 ret = malloc(*length);
506 if (!ret)
507 return FALSE;
508
509 Stream_Read(s, ret, *length);
510 *content = ret;
511 return TRUE;
512}
513
514size_t ber_write_octet_string_tag(wStream* s, size_t length)
515{
516 if (ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) == 0)
517 return 0;
518 if (ber_write_length(s, length) == 0)
519 return 0;
520 return 1 + _ber_sizeof_length(length);
521}
522
523size_t ber_sizeof_octet_string(size_t length)
524{
525 return 1 + _ber_sizeof_length(length) + length;
526}
527
528size_t ber_sizeof_contextual_octet_string(size_t length)
529{
530 size_t ret = ber_sizeof_octet_string(length);
531 return ber_sizeof_contextual_tag(ret) + ret;
532}
533
542BOOL ber_read_BOOL(wStream* s, BOOL* value)
543{
544 size_t length = 0;
545 BYTE v = 0;
546
547 WINPR_ASSERT(value);
548 if (!ber_read_universal_tag(s, BER_TAG_BOOLEAN, FALSE) || !ber_read_length(s, &length))
549 return FALSE;
550
551 if (length != 1)
552 {
553 WLog_WARN(TAG, "short data, got %" PRIuz ", expected %u", length, 1u);
554 return FALSE;
555 }
556 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
557 return FALSE;
558
559 Stream_Read_UINT8(s, v);
560 *value = (v != 0);
561 return TRUE;
562}
563
571void ber_write_BOOL(wStream* s, BOOL value)
572{
573 ber_write_universal_tag(s, BER_TAG_BOOLEAN, FALSE);
574 ber_write_length(s, 1);
575 Stream_Write_UINT8(s, (value == TRUE) ? 0xFF : 0);
576}
577
578BOOL ber_read_integer(wStream* s, UINT32* value)
579{
580 size_t length = 0;
581
582 WINPR_ASSERT(s);
583
584 if (!ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE))
585 return FALSE;
586 if (!ber_read_length(s, &length))
587 return FALSE;
588 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
589 return FALSE;
590
591 if (value == nullptr)
592 {
593 // even if we don't care the integer value, check the announced size
594 return Stream_SafeSeek(s, length);
595 }
596
597 if (length == 1)
598 {
599 Stream_Read_UINT8(s, *value);
600 }
601 else if (length == 2)
602 {
603 Stream_Read_UINT16_BE(s, *value);
604 }
605 else if (length == 3)
606 {
607 BYTE byte = 0;
608 Stream_Read_UINT8(s, byte);
609 Stream_Read_UINT16_BE(s, *value);
610 *value += (byte << 16) & 0xFF0000;
611 }
612 else if (length == 4)
613 {
614 Stream_Read_UINT32_BE(s, *value);
615 }
616 else if (length == 8)
617 {
618 WLog_ERR(TAG, "should implement reading an 8 bytes integer");
619 return FALSE;
620 }
621 else
622 {
623 WLog_ERR(TAG, "should implement reading an integer with length=%" PRIuz, length);
624 return FALSE;
625 }
626
627 return TRUE;
628}
629
639size_t ber_write_integer(wStream* s, UINT32 value)
640{
641 WINPR_ASSERT(s);
642
643 if (value < 0x80)
644 {
645 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
646 return 0;
647 if (ber_write_length(s, 1) == 0)
648 return 0;
649
650 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value));
651 return 3;
652 }
653 else if (value < 0x8000)
654 {
655 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
656 return 0;
657 if (ber_write_length(s, 2) == 0)
658 return 0;
659
660 Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
661 return 4;
662 }
663 else if (value < 0x800000)
664 {
665 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
666 return 0;
667 if (ber_write_length(s, 3) == 0)
668 return 0;
669
670 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value >> 16));
671 Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
672 return 5;
673 }
674 else if (value < 0x80000000)
675 {
676 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
677 return 0;
678 if (ber_write_length(s, 4) == 0)
679 return 0;
680
681 Stream_Write_UINT32_BE(s, value);
682 return 6;
683 }
684 else
685 {
686 /* treat as signed integer i.e. NT/HRESULT error codes */
687 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
688 return 0;
689 if (ber_write_length(s, 4) == 0)
690 return 0;
691
692 Stream_Write_UINT32_BE(s, value);
693 return 6;
694 }
695}
696
697size_t ber_write_contextual_integer(wStream* s, BYTE tag, UINT32 value)
698{
699 size_t len = ber_sizeof_integer(value);
700
701 WINPR_ASSERT(s);
702
703 WINPR_ASSERT(Stream_EnsureRemainingCapacity(s, len + 5));
704
705 len += ber_write_contextual_tag(s, tag, len, TRUE);
706 if (ber_write_integer(s, value) == 0)
707 return 0;
708 return len;
709}
710
711size_t ber_sizeof_integer(UINT32 value)
712{
713 if (value < 0x80)
714 {
715 return 3;
716 }
717 else if (value < 0x8000)
718 {
719 return 4;
720 }
721 else if (value < 0x800000)
722 {
723 return 5;
724 }
725 else if (value < 0x80000000)
726 {
727 return 6;
728 }
729 else
730 {
731 /* treat as signed integer i.e. NT/HRESULT error codes */
732 return 6;
733 }
734}
735
736size_t ber_sizeof_contextual_integer(UINT32 value)
737{
738 size_t intSize = ber_sizeof_integer(value);
739 return ber_sizeof_contextual_tag(intSize) + intSize;
740}
741
742BOOL ber_read_integer_length(wStream* s, size_t* length)
743{
744 return ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE) && ber_read_length(s, length);
745}