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 size_t length = strlen(str) + 1;
411 size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
412 size += ber_write_length(s, length * sizeof(WCHAR));
413
414 if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
415 return 0;
416 return size + length * sizeof(WCHAR);
417}
418
419size_t ber_write_contextual_unicode_octet_string(wStream* s, BYTE tag, LPWSTR str)
420{
421 WINPR_ASSERT(str);
422 size_t len = _wcslen(str) * sizeof(WCHAR);
423 size_t inner_len = ber_sizeof_octet_string(len);
424 size_t ret = 0;
425
426 ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
427 return ret + ber_write_octet_string(s, (const BYTE*)str, len);
428}
429
430size_t ber_write_contextual_char_to_unicode_octet_string(wStream* s, BYTE tag, const char* str)
431{
432 size_t ret = 0;
433 size_t len = strlen(str);
434 size_t inner_len = ber_sizeof_octet_string(len * 2);
435
436 WINPR_ASSERT(Stream_GetRemainingCapacity(s) < ber_sizeof_contextual_tag(inner_len) + inner_len);
437
438 ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
439 ret += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
440 ret += ber_write_length(s, len * sizeof(WCHAR));
441
442 if (Stream_Write_UTF16_String_From_UTF8(s, len, str, len, TRUE) < 0)
443 return 0;
444
445 return ret + len;
446}
447
448BOOL ber_read_unicode_octet_string(wStream* s, LPWSTR* str)
449{
450 LPWSTR ret = nullptr;
451 size_t length = 0;
452
453 if (!ber_read_octet_string_tag(s, &length))
454 return FALSE;
455
456 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
457 return FALSE;
458
459 ret = calloc(1, length + 2);
460 if (!ret)
461 return FALSE;
462
463 memcpy(ret, Stream_ConstPointer(s), length);
464 ret[length / 2] = 0;
465 Stream_Seek(s, length);
466 *str = ret;
467 return TRUE;
468}
469
470BOOL ber_read_char_from_unicode_octet_string(wStream* s, char** str)
471{
472 size_t length = 0;
473 char* ptr = nullptr;
474
475 *str = nullptr;
476 if (!ber_read_octet_string_tag(s, &length))
477 return FALSE;
478
479 ptr = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), nullptr);
480 if (!ptr)
481 return FALSE;
482 *str = ptr;
483 return TRUE;
484}
485
486BOOL ber_read_octet_string_tag(wStream* s, size_t* length)
487{
488 return ber_read_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) && ber_read_length(s, length);
489}
490
491BOOL ber_read_octet_string(wStream* s, BYTE** content, size_t* length)
492{
493 BYTE* ret = nullptr;
494
495 WINPR_ASSERT(s);
496 WINPR_ASSERT(content);
497 WINPR_ASSERT(length);
498
499 if (!ber_read_octet_string_tag(s, length))
500 return FALSE;
501 if (!Stream_CheckAndLogRequiredLength(TAG, s, *length))
502 return FALSE;
503
504 ret = malloc(*length);
505 if (!ret)
506 return FALSE;
507
508 Stream_Read(s, ret, *length);
509 *content = ret;
510 return TRUE;
511}
512
513size_t ber_write_octet_string_tag(wStream* s, size_t length)
514{
515 if (ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) == 0)
516 return 0;
517 if (ber_write_length(s, length) == 0)
518 return 0;
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 %u", length, 1u);
553 return FALSE;
554 }
555 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
556 return FALSE;
557
558 Stream_Read_UINT8(s, v);
559 *value = (v != 0);
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 == nullptr)
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 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
645 return 0;
646 if (ber_write_length(s, 1) == 0)
647 return 0;
648
649 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value));
650 return 3;
651 }
652 else if (value < 0x8000)
653 {
654 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
655 return 0;
656 if (ber_write_length(s, 2) == 0)
657 return 0;
658
659 Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
660 return 4;
661 }
662 else if (value < 0x800000)
663 {
664 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
665 return 0;
666 if (ber_write_length(s, 3) == 0)
667 return 0;
668
669 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value >> 16));
670 Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
671 return 5;
672 }
673 else if (value < 0x80000000)
674 {
675 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
676 return 0;
677 if (ber_write_length(s, 4) == 0)
678 return 0;
679
680 Stream_Write_UINT32_BE(s, value);
681 return 6;
682 }
683 else
684 {
685 /* treat as signed integer i.e. NT/HRESULT error codes */
686 if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
687 return 0;
688 if (ber_write_length(s, 4) == 0)
689 return 0;
690
691 Stream_Write_UINT32_BE(s, value);
692 return 6;
693 }
694}
695
696size_t ber_write_contextual_integer(wStream* s, BYTE tag, UINT32 value)
697{
698 size_t len = ber_sizeof_integer(value);
699
700 WINPR_ASSERT(s);
701
702 WINPR_ASSERT(Stream_EnsureRemainingCapacity(s, len + 5));
703
704 len += ber_write_contextual_tag(s, tag, len, TRUE);
705 if (ber_write_integer(s, value) == 0)
706 return 0;
707 return len;
708}
709
710size_t ber_sizeof_integer(UINT32 value)
711{
712 if (value < 0x80)
713 {
714 return 3;
715 }
716 else if (value < 0x8000)
717 {
718 return 4;
719 }
720 else if (value < 0x800000)
721 {
722 return 5;
723 }
724 else if (value < 0x80000000)
725 {
726 return 6;
727 }
728 else
729 {
730 /* treat as signed integer i.e. NT/HRESULT error codes */
731 return 6;
732 }
733}
734
735size_t ber_sizeof_contextual_integer(UINT32 value)
736{
737 size_t intSize = ber_sizeof_integer(value);
738 return ber_sizeof_contextual_tag(intSize) + intSize;
739}
740
741BOOL ber_read_integer_length(wStream* s, size_t* length)
742{
743 return ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE) && ber_read_length(s, length);
744}