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