FreeRDP
Loading...
Searching...
No Matches
asn1.c
1
20#include <winpr/config.h>
21
22#include <winpr/asn1.h>
23#include <winpr/wlog.h>
24#include <winpr/crt.h>
25
26#include "../../log.h"
27#define TAG WINPR_TAG("asn1")
28
29typedef struct
30{
31 size_t poolOffset;
32 size_t capacity;
33 size_t used;
34} Asn1Chunk;
35
36#define MAX_STATIC_ITEMS 50
37
39typedef enum
40{
41 ASN1_CONTAINER_SEQ,
42 ASN1_CONTAINER_SET,
43 ASN1_CONTAINER_APP,
44 ASN1_CONTAINER_CONTEXT_ONLY,
45 ASN1_CONTAINER_OCTETSTRING,
46} ContainerType;
47
48typedef struct WinPrAsn1EncContainer WinPrAsn1EncContainer;
50struct WinPrAsn1EncContainer
51{
52 size_t headerChunkId;
53 BOOL contextual;
54 WinPrAsn1_tag tag;
55 ContainerType containerType;
56};
57
59struct WinPrAsn1Encoder
60{
61 WinPrAsn1EncodingRule encoding;
62 wStream* pool;
63
64 Asn1Chunk* chunks;
65 Asn1Chunk staticChunks[MAX_STATIC_ITEMS];
66 size_t freeChunkId;
67 size_t chunksCapacity;
68
69 WinPrAsn1EncContainer* containers;
70 WinPrAsn1EncContainer staticContainers[MAX_STATIC_ITEMS];
71 size_t freeContainerIndex;
72 size_t containerCapacity;
73};
74
75#define WINPR_ASSERT_VALID_TAG(t) WINPR_ASSERT((t) < 64)
76
77void WinPrAsn1FreeOID(WinPrAsn1_OID* poid)
78{
79 WINPR_ASSERT(poid);
80 free(poid->data);
81 poid->data = nullptr;
82 poid->len = 0;
83}
84
85void WinPrAsn1FreeOctetString(WinPrAsn1_OctetString* octets)
86{
87 WINPR_ASSERT(octets);
88 /* Zero sensitive data (e.g. credential UTF-16 copies) before freeing */
89 if (octets->data && octets->len > 0)
90 memset(octets->data, 0, octets->len);
91 WinPrAsn1FreeOID(octets);
92}
93
150WinPrAsn1Encoder* WinPrAsn1Encoder_New(WinPrAsn1EncodingRule encoding)
151{
152 WinPrAsn1Encoder* enc = calloc(1, sizeof(*enc));
153 if (!enc)
154 return nullptr;
155
156 enc->encoding = encoding;
157 enc->pool = Stream_New(nullptr, 1024);
158 if (!enc->pool)
159 {
160 free(enc);
161 return nullptr;
162 }
163
164 enc->containers = &enc->staticContainers[0];
165 enc->chunks = &enc->staticChunks[0];
166 enc->chunksCapacity = MAX_STATIC_ITEMS;
167 enc->freeContainerIndex = 0;
168 return enc;
169}
170
171void WinPrAsn1Encoder_Reset(WinPrAsn1Encoder* enc)
172{
173 WINPR_ASSERT(enc);
174
175 enc->freeContainerIndex = 0;
176 enc->freeChunkId = 0;
177
178 ZeroMemory(enc->chunks, sizeof(*enc->chunks) * enc->chunksCapacity);
179}
180
181void WinPrAsn1Encoder_Free(WinPrAsn1Encoder** penc)
182{
183 WINPR_ASSERT(penc);
184 WinPrAsn1Encoder_FreeNoNull(*penc);
185 *penc = nullptr;
186}
187
188void WinPrAsn1Encoder_FreeNoNull(WinPrAsn1Encoder* enc)
189{
190 if (enc)
191 {
192 if (enc->containers != &enc->staticContainers[0])
193 free(enc->containers);
194
195 if (enc->chunks != &enc->staticChunks[0])
196 free(enc->chunks);
197
198 Stream_Free(enc->pool, TRUE);
199 free(enc);
200 }
201}
202
203static Asn1Chunk* asn1enc_get_free_chunk(WinPrAsn1Encoder* enc, size_t chunkSz, BOOL commit,
204 size_t* id)
205{
206 Asn1Chunk* ret = nullptr;
207 WINPR_ASSERT(enc);
208 WINPR_ASSERT(chunkSz);
209
210 if (commit)
211 {
212 /* if it's not a reservation let's see if the last chunk is not a reservation and can be
213 * expanded */
214 size_t lastChunk = enc->freeChunkId ? enc->freeChunkId - 1 : 0;
215 ret = &enc->chunks[lastChunk];
216 if (ret->capacity && ret->capacity == ret->used)
217 {
218 if (!Stream_EnsureRemainingCapacity(enc->pool, chunkSz))
219 return nullptr;
220
221 Stream_Seek(enc->pool, chunkSz);
222 ret->capacity += chunkSz;
223 ret->used += chunkSz;
224 if (id)
225 *id = lastChunk;
226 return ret;
227 }
228 }
229
230 if (enc->freeChunkId == enc->chunksCapacity)
231 {
232 /* chunks need a resize */
233 Asn1Chunk* src = (enc->chunks != &enc->staticChunks[0]) ? enc->chunks : nullptr;
234 Asn1Chunk* tmp = realloc(src, (enc->chunksCapacity + 10) * sizeof(*src));
235 if (!tmp)
236 return nullptr;
237
238 if (enc->chunks == &enc->staticChunks[0])
239 memcpy(tmp, &enc->staticChunks[0], enc->chunksCapacity * sizeof(*src));
240 else
241 memset(tmp + enc->freeChunkId, 0, sizeof(*tmp) * 10);
242
243 enc->chunks = tmp;
244 enc->chunksCapacity += 10;
245 }
246 if (enc->freeChunkId == enc->chunksCapacity)
247 return nullptr;
248
249 if (!Stream_EnsureRemainingCapacity(enc->pool, chunkSz))
250 return nullptr;
251
252 ret = &enc->chunks[enc->freeChunkId];
253 ret->poolOffset = Stream_GetPosition(enc->pool);
254 ret->capacity = chunkSz;
255 ret->used = commit ? chunkSz : 0;
256 if (id)
257 *id = enc->freeChunkId;
258
259 enc->freeChunkId++;
260 Stream_Seek(enc->pool, chunkSz);
261 return ret;
262}
263
264static WinPrAsn1EncContainer* asn1enc_get_free_container(WinPrAsn1Encoder* enc, size_t* id)
265{
266 WinPrAsn1EncContainer* ret = nullptr;
267 WINPR_ASSERT(enc);
268
269 if (enc->freeContainerIndex == enc->containerCapacity)
270 {
271 /* containers need a resize (or switch from static to dynamic) */
272 WinPrAsn1EncContainer* src =
273 (enc->containers != &enc->staticContainers[0]) ? enc->containers : nullptr;
274 WinPrAsn1EncContainer* tmp = realloc(src, (enc->containerCapacity + 10) * sizeof(*src));
275 if (!tmp)
276 return nullptr;
277
278 if (enc->containers == &enc->staticContainers[0])
279 memcpy(tmp, &enc->staticContainers[0], enc->containerCapacity * sizeof(*src));
280
281 enc->containers = tmp;
282 enc->containerCapacity += 10;
283 }
284 if (enc->freeContainerIndex == enc->containerCapacity)
285 return nullptr;
286
287 ret = &enc->containers[enc->freeContainerIndex];
288 *id = enc->freeContainerIndex;
289
290 enc->freeContainerIndex++;
291 return ret;
292}
293
294static size_t lenBytes(size_t len)
295{
296 if (len < 128)
297 return 1;
298 if (len < (1u << 8))
299 return 2;
300 if (len < (1u << 16))
301 return 3;
302 if (len < (1u << 24))
303 return 4;
304
305 return 5;
306}
307
308static void asn1WriteLen(wStream* s, size_t len)
309{
310 if (len < 128)
311 {
312 Stream_Write_UINT8(s, (UINT8)len);
313 }
314 else if (len < (1u << 8))
315 {
316 Stream_Write_UINT8(s, 0x81);
317 Stream_Write_UINT8(s, (UINT8)len);
318 }
319 else if (len < (1u << 16))
320 {
321 Stream_Write_UINT8(s, 0x82);
322 Stream_Write_UINT16_BE(s, (UINT16)len);
323 }
324 else if (len < (1u << 24))
325 {
326 Stream_Write_UINT8(s, 0x83);
327 Stream_Write_UINT24_BE(s, (UINT32)len);
328 }
329 else
330 {
331 WINPR_ASSERT(len <= UINT32_MAX);
332 Stream_Write_UINT8(s, 0x84);
333 Stream_Write_UINT32_BE(s, (UINT32)len);
334 }
335}
336
337static WinPrAsn1EncContainer* getAsn1Container(WinPrAsn1Encoder* enc, ContainerType ctype,
338 WinPrAsn1_tag tag, BOOL contextual, size_t maxLen)
339{
340 size_t ret = 0;
341 size_t chunkId = 0;
342 WinPrAsn1EncContainer* container = nullptr;
343
344 Asn1Chunk* chunk = asn1enc_get_free_chunk(enc, maxLen, FALSE, &chunkId);
345 if (!chunk)
346 return nullptr;
347
348 container = asn1enc_get_free_container(enc, &ret);
349 container->containerType = ctype;
350 container->tag = tag;
351 container->contextual = contextual;
352 container->headerChunkId = chunkId;
353 return container;
354}
355
356BOOL WinPrAsn1EncAppContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId)
357{
358 WINPR_ASSERT_VALID_TAG(tagId);
359 return getAsn1Container(enc, ASN1_CONTAINER_APP, tagId, FALSE, 6) != nullptr;
360}
361
362BOOL WinPrAsn1EncSeqContainer(WinPrAsn1Encoder* enc)
363{
364 return getAsn1Container(enc, ASN1_CONTAINER_SEQ, 0, FALSE, 6) != nullptr;
365}
366
367BOOL WinPrAsn1EncSetContainer(WinPrAsn1Encoder* enc)
368{
369 return getAsn1Container(enc, ASN1_CONTAINER_SET, 0, FALSE, 6) != nullptr;
370}
371
372BOOL WinPrAsn1EncContextualSeqContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId)
373{
374 return getAsn1Container(enc, ASN1_CONTAINER_SEQ, tagId, TRUE, 6 + 6) != nullptr;
375}
376
377BOOL WinPrAsn1EncContextualSetContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId)
378{
379 return getAsn1Container(enc, ASN1_CONTAINER_SET, tagId, TRUE, 6 + 6) != nullptr;
380}
381
382BOOL WinPrAsn1EncContextualContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId)
383{
384 return getAsn1Container(enc, ASN1_CONTAINER_CONTEXT_ONLY, tagId, TRUE, 6) != nullptr;
385}
386
387BOOL WinPrAsn1EncOctetStringContainer(WinPrAsn1Encoder* enc)
388{
389 return getAsn1Container(enc, ASN1_CONTAINER_OCTETSTRING, 0, FALSE, 6) != nullptr;
390}
391
392BOOL WinPrAsn1EncContextualOctetStringContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId)
393{
394 return getAsn1Container(enc, ASN1_CONTAINER_OCTETSTRING, tagId, TRUE, 6 + 6) != nullptr;
395}
396
397size_t WinPrAsn1EncEndContainer(WinPrAsn1Encoder* enc)
398{
399 size_t innerLen = 0;
400 size_t unused = 0;
401 size_t innerHeaderBytes = 0;
402 size_t outerHeaderBytes = 0;
403 BYTE containerByte = 0;
404 WinPrAsn1EncContainer* container = nullptr;
405 Asn1Chunk* chunk = nullptr;
406 wStream staticS;
407 wStream* s = &staticS;
408
409 WINPR_ASSERT(enc);
410 WINPR_ASSERT(enc->freeContainerIndex);
411
412 /* compute inner length */
413 container = &enc->containers[enc->freeContainerIndex - 1];
414 innerLen = 0;
415 for (size_t i = container->headerChunkId + 1; i < enc->freeChunkId; i++)
416 innerLen += enc->chunks[i].used;
417
418 /* compute effective headerLength */
419 switch (container->containerType)
420 {
421 case ASN1_CONTAINER_SEQ:
422 containerByte = ER_TAG_SEQUENCE;
423 innerHeaderBytes = 1 + lenBytes(innerLen);
424 break;
425 case ASN1_CONTAINER_SET:
426 containerByte = ER_TAG_SET;
427 innerHeaderBytes = 1 + lenBytes(innerLen);
428 break;
429 case ASN1_CONTAINER_OCTETSTRING:
430 containerByte = ER_TAG_OCTET_STRING;
431 innerHeaderBytes = 1 + lenBytes(innerLen);
432 break;
433 case ASN1_CONTAINER_APP:
434 containerByte = ER_TAG_APP | container->tag;
435 innerHeaderBytes = 1 + lenBytes(innerLen);
436 break;
437 case ASN1_CONTAINER_CONTEXT_ONLY:
438 innerHeaderBytes = 0;
439 break;
440 default:
441 WLog_ERR(TAG, "invalid containerType");
442 return 0;
443 }
444
445 outerHeaderBytes = innerHeaderBytes;
446 if (container->contextual)
447 {
448 outerHeaderBytes = 1 + lenBytes(innerHeaderBytes + innerLen) + innerHeaderBytes;
449 }
450
451 /* we write the headers at the end of the reserved space and we adjust
452 * the chunk to be a non reserved chunk */
453 chunk = &enc->chunks[container->headerChunkId];
454 unused = chunk->capacity - outerHeaderBytes;
455 chunk->poolOffset += unused;
456 chunk->capacity = chunk->used = outerHeaderBytes;
457
458 Stream_StaticInit(s, Stream_Buffer(enc->pool) + chunk->poolOffset, outerHeaderBytes);
459 if (container->contextual)
460 {
461 Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | container->tag);
462 asn1WriteLen(s, innerHeaderBytes + innerLen);
463 }
464
465 switch (container->containerType)
466 {
467 case ASN1_CONTAINER_SEQ:
468 case ASN1_CONTAINER_SET:
469 case ASN1_CONTAINER_OCTETSTRING:
470 case ASN1_CONTAINER_APP:
471 Stream_Write_UINT8(s, containerByte);
472 asn1WriteLen(s, innerLen);
473 break;
474 case ASN1_CONTAINER_CONTEXT_ONLY:
475 break;
476 default:
477 WLog_ERR(TAG, "invalid containerType");
478 return 0;
479 }
480
481 /* TODO: here there is place for packing chunks */
482 enc->freeContainerIndex--;
483 return outerHeaderBytes + innerLen;
484}
485
486static BOOL asn1_getWriteStream(WinPrAsn1Encoder* enc, size_t len, wStream* s)
487{
488 BYTE* dest = nullptr;
489 Asn1Chunk* chunk = asn1enc_get_free_chunk(enc, len, TRUE, nullptr);
490 if (!chunk)
491 return FALSE;
492
493 dest = Stream_Buffer(enc->pool) + chunk->poolOffset + chunk->capacity - len;
494 Stream_StaticInit(s, dest, len);
495 return TRUE;
496}
497
498size_t WinPrAsn1EncRawContent(WinPrAsn1Encoder* enc, const WinPrAsn1_MemoryChunk* c)
499{
500 wStream staticS;
501 wStream* s = &staticS;
502
503 WINPR_ASSERT(enc);
504 WINPR_ASSERT(c);
505
506 if (!asn1_getWriteStream(enc, c->len, s))
507 return 0;
508
509 Stream_Write(s, c->data, c->len);
510 return c->len;
511}
512
513size_t WinPrAsn1EncContextualRawContent(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
514 const WinPrAsn1_MemoryChunk* c)
515{
516 wStream staticS;
517 wStream* s = &staticS;
518
519 WINPR_ASSERT(enc);
520 WINPR_ASSERT(c);
521 WINPR_ASSERT_VALID_TAG(tagId);
522
523 size_t len = 1 + lenBytes(c->len) + c->len;
524 if (!asn1_getWriteStream(enc, len, s))
525 return 0;
526
527 Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId);
528 asn1WriteLen(s, c->len);
529
530 Stream_Write(s, c->data, c->len);
531 return len;
532}
533
534static size_t asn1IntegerLen(WinPrAsn1_INTEGER value)
535{
536 if (value <= 127 && value >= -128)
537 return 2;
538 else if (value <= 32767 && value >= -32768)
539 return 3;
540 else
541 return 5;
542}
543
544static size_t WinPrAsn1EncIntegerLike(WinPrAsn1Encoder* enc, WinPrAsn1_tag b,
545 WinPrAsn1_INTEGER value)
546{
547 wStream staticS = WINPR_C_ARRAY_INIT;
548 wStream* s = &staticS;
549
550 const size_t len = asn1IntegerLen(value);
551 if (!asn1_getWriteStream(enc, 1 + len, s))
552 return 0;
553
554 Stream_Write_UINT8(s, b);
555 switch (len)
556 {
557 case 2:
558 Stream_Write_UINT8(s, 1);
559 Stream_Write_INT8(s, (INT8)value);
560 break;
561 case 3:
562 Stream_Write_UINT8(s, 2);
563 Stream_Write_INT16_BE(s, (INT16)value);
564 break;
565 case 5:
566 Stream_Write_UINT8(s, 4);
567 Stream_Write_INT32_BE(s, (INT32)value);
568 break;
569 default:
570 return 0;
571 }
572 return 1 + len;
573}
574
575size_t WinPrAsn1EncInteger(WinPrAsn1Encoder* enc, WinPrAsn1_INTEGER integer)
576{
577 return WinPrAsn1EncIntegerLike(enc, ER_TAG_INTEGER, integer);
578}
579
580size_t WinPrAsn1EncEnumerated(WinPrAsn1Encoder* enc, WinPrAsn1_ENUMERATED value)
581{
582 return WinPrAsn1EncIntegerLike(enc, ER_TAG_ENUMERATED, value);
583}
584
585static size_t WinPrAsn1EncContextualIntegerLike(WinPrAsn1Encoder* enc, WinPrAsn1_tag tag,
586 WinPrAsn1_tagId tagId, WinPrAsn1_INTEGER value)
587{
588 wStream staticS = WINPR_C_ARRAY_INIT;
589 wStream* s = &staticS;
590
591 WINPR_ASSERT(enc);
592 WINPR_ASSERT_VALID_TAG(tagId);
593
594 const size_t len = asn1IntegerLen(value);
595 const size_t outLen = 1 + lenBytes(1 + len) + (1 + len);
596 if (!asn1_getWriteStream(enc, outLen, s))
597 return 0;
598
599 Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId);
600 asn1WriteLen(s, 1 + len);
601
602 Stream_Write_UINT8(s, tag);
603 switch (len)
604 {
605 case 2:
606 Stream_Write_UINT8(s, 1);
607 Stream_Write_INT8(s, (INT8)value);
608 break;
609 case 3:
610 Stream_Write_UINT8(s, 2);
611 Stream_Write_INT16_BE(s, (INT16)value);
612 break;
613 case 5:
614 Stream_Write_UINT8(s, 4);
615 Stream_Write_INT32_BE(s, value);
616 break;
617 default:
618 return 0;
619 }
620 return outLen;
621}
622
623size_t WinPrAsn1EncContextualInteger(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
624 WinPrAsn1_INTEGER integer)
625{
626 return WinPrAsn1EncContextualIntegerLike(enc, ER_TAG_INTEGER, tagId, integer);
627}
628
629size_t WinPrAsn1EncContextualEnumerated(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
630 WinPrAsn1_ENUMERATED value)
631{
632 return WinPrAsn1EncContextualIntegerLike(enc, ER_TAG_ENUMERATED, tagId, value);
633}
634
635size_t WinPrAsn1EncBoolean(WinPrAsn1Encoder* enc, WinPrAsn1_BOOL b)
636{
637 wStream staticS;
638 wStream* s = &staticS;
639
640 if (!asn1_getWriteStream(enc, 3, s))
641 return 0;
642
643 Stream_Write_UINT8(s, ER_TAG_BOOLEAN);
644 Stream_Write_UINT8(s, 1);
645 Stream_Write_UINT8(s, b ? 0xff : 0);
646
647 return 3;
648}
649
650size_t WinPrAsn1EncContextualBoolean(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, WinPrAsn1_BOOL b)
651{
652 wStream staticS;
653 wStream* s = &staticS;
654
655 WINPR_ASSERT(enc);
656 WINPR_ASSERT_VALID_TAG(tagId);
657
658 if (!asn1_getWriteStream(enc, 5, s))
659 return 0;
660
661 Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId);
662 Stream_Write_UINT8(s, 3);
663
664 Stream_Write_UINT8(s, ER_TAG_BOOLEAN);
665 Stream_Write_UINT8(s, 1);
666 Stream_Write_UINT8(s, b ? 0xff : 0);
667
668 return 5;
669}
670
671static size_t WinPrAsn1EncMemoryChunk(WinPrAsn1Encoder* enc, BYTE wireType,
672 const WinPrAsn1_MemoryChunk* mchunk)
673{
674 wStream s;
675 size_t len = 0;
676
677 WINPR_ASSERT(enc);
678 WINPR_ASSERT(mchunk);
679 len = 1 + lenBytes(mchunk->len) + mchunk->len;
680
681 if (!asn1_getWriteStream(enc, len, &s))
682 return 0;
683
684 Stream_Write_UINT8(&s, wireType);
685 asn1WriteLen(&s, mchunk->len);
686 Stream_Write(&s, mchunk->data, mchunk->len);
687 return len;
688}
689
690size_t WinPrAsn1EncOID(WinPrAsn1Encoder* enc, const WinPrAsn1_OID* oid)
691{
692 return WinPrAsn1EncMemoryChunk(enc, ER_TAG_OBJECT_IDENTIFIER, oid);
693}
694
695size_t WinPrAsn1EncOctetString(WinPrAsn1Encoder* enc, const WinPrAsn1_OctetString* octetstring)
696{
697 return WinPrAsn1EncMemoryChunk(enc, ER_TAG_OCTET_STRING, octetstring);
698}
699
700size_t WinPrAsn1EncIA5String(WinPrAsn1Encoder* enc, WinPrAsn1_IA5STRING ia5)
701{
703 WINPR_ASSERT(ia5);
704 chunk.data = (BYTE*)ia5;
705 chunk.len = strlen(ia5);
706 return WinPrAsn1EncMemoryChunk(enc, ER_TAG_IA5STRING, &chunk);
707}
708
709size_t WinPrAsn1EncGeneralString(WinPrAsn1Encoder* enc, WinPrAsn1_STRING str)
710{
712 WINPR_ASSERT(str);
713 chunk.data = (BYTE*)str;
714 chunk.len = strlen(str);
715 return WinPrAsn1EncMemoryChunk(enc, ER_TAG_GENERAL_STRING, &chunk);
716}
717
718static size_t WinPrAsn1EncContextualMemoryChunk(WinPrAsn1Encoder* enc, BYTE wireType,
719 WinPrAsn1_tagId tagId,
720 const WinPrAsn1_MemoryChunk* mchunk)
721{
722 wStream s;
723 size_t len = 0;
724 size_t outLen = 0;
725
726 WINPR_ASSERT(enc);
727 WINPR_ASSERT_VALID_TAG(tagId);
728 WINPR_ASSERT(mchunk);
729 len = 1 + lenBytes(mchunk->len) + mchunk->len;
730
731 outLen = 1 + lenBytes(len) + len;
732 if (!asn1_getWriteStream(enc, outLen, &s))
733 return 0;
734
735 Stream_Write_UINT8(&s, ER_TAG_CONTEXTUAL | tagId);
736 asn1WriteLen(&s, len);
737
738 Stream_Write_UINT8(&s, wireType);
739 asn1WriteLen(&s, mchunk->len);
740 Stream_Write(&s, mchunk->data, mchunk->len);
741 return outLen;
742}
743
744size_t WinPrAsn1EncContextualOID(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
745 const WinPrAsn1_OID* oid)
746{
747 return WinPrAsn1EncContextualMemoryChunk(enc, ER_TAG_OBJECT_IDENTIFIER, tagId, oid);
748}
749
750size_t WinPrAsn1EncContextualOctetString(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
751 const WinPrAsn1_OctetString* octetstring)
752{
753 return WinPrAsn1EncContextualMemoryChunk(enc, ER_TAG_OCTET_STRING, tagId, octetstring);
754}
755
756size_t WinPrAsn1EncContextualIA5String(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
757 WinPrAsn1_IA5STRING ia5)
758{
760 WINPR_ASSERT(ia5);
761 chunk.data = (BYTE*)ia5;
762 chunk.len = strlen(ia5);
763
764 return WinPrAsn1EncContextualMemoryChunk(enc, ER_TAG_IA5STRING, tagId, &chunk);
765}
766
767static void write2digit(wStream* s, UINT8 v)
768{
769 Stream_Write_UINT8(s, '0' + (v / 10));
770 Stream_Write_UINT8(s, '0' + (v % 10));
771}
772
773size_t WinPrAsn1EncUtcTime(WinPrAsn1Encoder* enc, const WinPrAsn1_UTCTIME* utc)
774{
775 wStream staticS = WINPR_C_ARRAY_INIT;
776 wStream* s = &staticS;
777
778 WINPR_ASSERT(enc);
779 WINPR_ASSERT(utc);
780 WINPR_ASSERT(utc->year >= 2000);
781
782 if (!asn1_getWriteStream(enc, 15, s))
783 return 0;
784
785 Stream_Write_UINT8(s, ER_TAG_UTCTIME);
786 Stream_Write_UINT8(s, 13);
787
788 write2digit(s, (UINT8)(utc->year - 2000));
789 write2digit(s, utc->month);
790 write2digit(s, utc->day);
791 write2digit(s, utc->hour);
792 write2digit(s, utc->minute);
793 write2digit(s, utc->second);
794 Stream_Write_INT8(s, utc->tz);
795 return 15;
796}
797
798size_t WinPrAsn1EncContextualUtcTime(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId,
799 const WinPrAsn1_UTCTIME* utc)
800{
801 wStream staticS;
802 wStream* s = &staticS;
803
804 WINPR_ASSERT(enc);
805 WINPR_ASSERT_VALID_TAG(tagId);
806 WINPR_ASSERT(utc);
807 WINPR_ASSERT(utc->year >= 2000);
808 WINPR_ASSERT(utc->year < 2256);
809
810 if (!asn1_getWriteStream(enc, 17, s))
811 return 0;
812
813 Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId);
814 Stream_Write_UINT8(s, 15);
815
816 Stream_Write_UINT8(s, ER_TAG_UTCTIME);
817 Stream_Write_UINT8(s, 13);
818
819 write2digit(s, (UINT8)(utc->year - 2000));
820 write2digit(s, utc->month);
821 write2digit(s, utc->day);
822 write2digit(s, utc->hour);
823 write2digit(s, utc->minute);
824 write2digit(s, utc->second);
825 Stream_Write_INT8(s, utc->tz);
826
827 return 17;
828}
829
830BOOL WinPrAsn1EncStreamSize(WinPrAsn1Encoder* enc, size_t* s)
831{
832 size_t finalSize = 0;
833
834 WINPR_ASSERT(enc);
835 WINPR_ASSERT(s);
836
837 if (enc->freeContainerIndex != 0)
838 {
839 WLog_ERR(TAG, "some container have not been closed");
840 return FALSE;
841 }
842
843 for (size_t i = 0; i < enc->freeChunkId; i++)
844 finalSize += enc->chunks[i].used;
845 *s = finalSize;
846 return TRUE;
847}
848
849BOOL WinPrAsn1EncToStream(WinPrAsn1Encoder* enc, wStream* s)
850{
851 size_t finalSize = 0;
852
853 WINPR_ASSERT(enc);
854 WINPR_ASSERT(s);
855
856 if (!WinPrAsn1EncStreamSize(enc, &finalSize))
857 return FALSE;
858
859 if (!Stream_EnsureRemainingCapacity(s, finalSize))
860 return FALSE;
861
862 for (size_t i = 0; i < enc->freeChunkId; i++)
863 {
864 BYTE* src = Stream_Buffer(enc->pool) + enc->chunks[i].poolOffset;
865 Stream_Write(s, src, enc->chunks[i].used);
866 }
867
868 return TRUE;
869}
870
871void WinPrAsn1Decoder_Init(WinPrAsn1Decoder* decoder, WinPrAsn1EncodingRule encoding,
872 wStream* source)
873{
874 WINPR_ASSERT(decoder);
875 WINPR_ASSERT(source);
876
877 decoder->encoding = encoding;
878 memcpy(&decoder->source, source, sizeof(*source));
879}
880
881void WinPrAsn1Decoder_InitMem(WinPrAsn1Decoder* decoder, WinPrAsn1EncodingRule encoding,
882 const BYTE* source, size_t len)
883{
884 WINPR_ASSERT(decoder);
885 WINPR_ASSERT(source);
886
887 decoder->encoding = encoding;
888 Stream_StaticConstInit(&decoder->source, source, len);
889}
890
891BOOL WinPrAsn1DecPeekTag(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag)
892{
893 WINPR_ASSERT(dec);
894 WINPR_ASSERT(tag);
895
896 if (Stream_GetRemainingLength(&dec->source) < 1)
897 return FALSE;
898 Stream_Peek(&dec->source, tag, 1);
899 return TRUE;
900}
901
902static size_t readLen(wStream* s, size_t* len, BOOL derCheck)
903{
904 size_t retLen = 0;
905 size_t ret = 0;
906
907 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
908 return 0;
909
910 Stream_Read_UINT8(s, retLen);
911 ret++;
912 if (retLen & 0x80)
913 {
914 BYTE tmp = 0;
915 size_t nBytes = (retLen & 0x7f);
916
917 if (!Stream_CheckAndLogRequiredLength(TAG, s, nBytes))
918 return 0;
919
920 ret += nBytes;
921 for (retLen = 0; nBytes; nBytes--)
922 {
923 Stream_Read_UINT8(s, tmp);
924 retLen = (retLen << 8) + tmp;
925 }
926
927 if (derCheck)
928 {
929 /* check that the DER rule is respected, and that length encoding is optimal */
930 if (ret > 1 && retLen < 128)
931 return 0;
932 }
933 }
934
935 *len = retLen;
936 return ret;
937}
938
939static size_t readTagAndLen(WinPrAsn1Decoder* dec, wStream* s, WinPrAsn1_tag* tag, size_t* len)
940{
941 size_t lenBytes = 0;
942
943 if (Stream_GetRemainingLength(s) < 1)
944 return 0;
945
946 Stream_Read(s, tag, 1);
947 lenBytes = readLen(s, len, (dec->encoding == WINPR_ASN1_DER));
948 if (lenBytes == 0)
949 return 0;
950
951 return 1 + lenBytes;
952}
953
954size_t WinPrAsn1DecReadTagAndLen(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, size_t* len)
955{
956 WINPR_ASSERT(dec);
957 WINPR_ASSERT(tag);
958 WINPR_ASSERT(len);
959
960 return readTagAndLen(dec, &dec->source, tag, len);
961}
962
963size_t WinPrAsn1DecPeekTagAndLen(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, size_t* len)
964{
965 wStream staticS = WINPR_C_ARRAY_INIT;
966
967 WINPR_ASSERT(dec);
968
969 wStream* s = Stream_StaticConstInit(&staticS, Stream_ConstPointer(&dec->source),
970 Stream_GetRemainingLength(&dec->source));
971 return readTagAndLen(dec, s, tag, len);
972}
973
974size_t WinPrAsn1DecReadTagLenValue(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, size_t* len,
975 WinPrAsn1Decoder* value)
976{
977 size_t ret = 0;
978 WINPR_ASSERT(dec);
979 WINPR_ASSERT(tag);
980 WINPR_ASSERT(len);
981 WINPR_ASSERT(value);
982
983 ret = readTagAndLen(dec, &dec->source, tag, len);
984 if (!ret)
985 return 0;
986
987 if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, *len))
988 return 0;
989
990 value->encoding = dec->encoding;
991 Stream_StaticInit(&value->source, Stream_Pointer(&dec->source), *len);
992 Stream_Seek(&dec->source, *len);
993 return ret + *len;
994}
995
996size_t WinPrAsn1DecReadBoolean(WinPrAsn1Decoder* dec, WinPrAsn1_BOOL* target)
997{
998 BYTE v = 0;
999 WinPrAsn1_tag tag = 0;
1000 size_t len = 0;
1001 size_t ret = 0;
1002
1003 WINPR_ASSERT(dec);
1004 WINPR_ASSERT(target);
1005
1006 ret = readTagAndLen(dec, &dec->source, &tag, &len);
1007 if (!ret || tag != ER_TAG_BOOLEAN)
1008 return 0;
1009 if (Stream_GetRemainingLength(&dec->source) < len || len != 1)
1010 return 0;
1011
1012 Stream_Read_UINT8(&dec->source, v);
1013 *target = !!v;
1014 return ret;
1015}
1016
1017static size_t WinPrAsn1DecReadIntegerLike(WinPrAsn1Decoder* dec, WinPrAsn1_tag expectedTag,
1018 WinPrAsn1_INTEGER* target)
1019{
1020 WinPrAsn1_tag tag = 0;
1021 size_t len = 0;
1022
1023 WINPR_ASSERT(dec);
1024 WINPR_ASSERT(target);
1025
1026 size_t ret = readTagAndLen(dec, &dec->source, &tag, &len);
1027 if (!ret || (tag != expectedTag))
1028 return 0;
1029 if (len == 0 || Stream_GetRemainingLength(&dec->source) < len || (len > 4))
1030 return 0;
1031
1032 UINT32 uval = 0;
1033 UINT8 v = 0;
1034
1035 Stream_Read_UINT8(&dec->source, v);
1036
1037 /* extract sign from first byte.
1038 * the ASN integer might be smaller than 32bit so we need to set the initial
1039 * value to FF for all unused bytes (e.g. all except the lowest one we just read)
1040 */
1041 BOOL isNegative = (v & 0x80);
1042 if (isNegative)
1043 uval = 0xFFFFFF00;
1044 uval |= v;
1045
1046 for (size_t x = 1; x < len; x++)
1047 {
1048 Stream_Read_UINT8(&dec->source, v);
1049 uval <<= 8;
1050 uval |= v;
1051 }
1052
1053 *target = (WinPrAsn1_INTEGER)uval;
1054 ret += len;
1055
1056 /* TODO: check ber/der rules */
1057 return ret;
1058}
1059
1060size_t WinPrAsn1DecReadInteger(WinPrAsn1Decoder* dec, WinPrAsn1_INTEGER* target)
1061{
1062 return WinPrAsn1DecReadIntegerLike(dec, ER_TAG_INTEGER, target);
1063}
1064
1065size_t WinPrAsn1DecReadEnumerated(WinPrAsn1Decoder* dec, WinPrAsn1_ENUMERATED* target)
1066{
1067 return WinPrAsn1DecReadIntegerLike(dec, ER_TAG_ENUMERATED, target);
1068}
1069
1070static size_t WinPrAsn1DecReadMemoryChunkLike(WinPrAsn1Decoder* dec, WinPrAsn1_tag expectedTag,
1071 WinPrAsn1_MemoryChunk* target, BOOL allocate)
1072{
1073 WinPrAsn1_tag tag = 0;
1074 size_t len = 0;
1075 size_t ret = 0;
1076
1077 WINPR_ASSERT(dec);
1078 WINPR_ASSERT(target);
1079
1080 ret = readTagAndLen(dec, &dec->source, &tag, &len);
1081 if (!ret || tag != expectedTag)
1082 return 0;
1083 if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len))
1084 return 0;
1085
1086 ret += len;
1087
1088 target->len = len;
1089 if (allocate && (len > 0))
1090 {
1091 target->data = malloc(len);
1092 if (!target->data)
1093 return 0;
1094 Stream_Read(&dec->source, target->data, len);
1095 }
1096 else
1097 {
1098 target->data = Stream_Pointer(&dec->source);
1099 Stream_Seek(&dec->source, len);
1100 }
1101
1102 return ret;
1103}
1104
1105size_t WinPrAsn1DecReadOID(WinPrAsn1Decoder* dec, WinPrAsn1_OID* target, BOOL allocate)
1106{
1107 return WinPrAsn1DecReadMemoryChunkLike(dec, ER_TAG_OBJECT_IDENTIFIER,
1108 (WinPrAsn1_MemoryChunk*)target, allocate);
1109}
1110
1111size_t WinPrAsn1DecReadOctetString(WinPrAsn1Decoder* dec, WinPrAsn1_OctetString* target,
1112 BOOL allocate)
1113{
1114 return WinPrAsn1DecReadMemoryChunkLike(dec, ER_TAG_OCTET_STRING, target, allocate);
1115}
1116
1117size_t WinPrAsn1DecReadIA5String(WinPrAsn1Decoder* dec, WinPrAsn1_IA5STRING* target)
1118{
1119 WinPrAsn1_tag tag = 0;
1120 size_t len = 0;
1121 size_t ret = 0;
1122 WinPrAsn1_IA5STRING s = nullptr;
1123
1124 WINPR_ASSERT(dec);
1125 WINPR_ASSERT(target);
1126
1127 ret = readTagAndLen(dec, &dec->source, &tag, &len);
1128 if (!ret || tag != ER_TAG_IA5STRING)
1129 return 0;
1130 if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len))
1131 return 0;
1132
1133 ret += len;
1134
1135 s = malloc(len + 1);
1136 if (!s)
1137 return 0;
1138 Stream_Read(&dec->source, s, len);
1139 s[len] = 0;
1140 *target = s;
1141 return ret;
1142}
1143
1144size_t WinPrAsn1DecReadGeneralString(WinPrAsn1Decoder* dec, WinPrAsn1_STRING* target)
1145{
1146 WinPrAsn1_tag tag = 0;
1147 size_t len = 0;
1148 size_t ret = 0;
1149 WinPrAsn1_IA5STRING s = nullptr;
1150
1151 WINPR_ASSERT(dec);
1152 WINPR_ASSERT(target);
1153
1154 ret = readTagAndLen(dec, &dec->source, &tag, &len);
1155 if (!ret || tag != ER_TAG_GENERAL_STRING)
1156 return 0;
1157 if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len))
1158 return 0;
1159
1160 ret += len;
1161
1162 s = malloc(len + 1);
1163 if (!s)
1164 return 0;
1165 Stream_Read(&dec->source, s, len);
1166 s[len] = 0;
1167 *target = s;
1168 return ret;
1169}
1170
1171static int read2digits(wStream* s)
1172{
1173 int ret = 0;
1174 char c = 0;
1175
1176 Stream_Read_INT8(s, c);
1177 if (c < '0' || c > '9')
1178 return -1;
1179
1180 ret = (c - '0') * 10;
1181
1182 Stream_Read_INT8(s, c);
1183 if (c < '0' || c > '9')
1184 return -1;
1185
1186 ret += (c - '0');
1187 return ret;
1188}
1189
1190size_t WinPrAsn1DecReadUtcTime(WinPrAsn1Decoder* dec, WinPrAsn1_UTCTIME* target)
1191{
1192 WinPrAsn1_tag tag = 0;
1193 size_t len = 0;
1194 size_t ret = 0;
1195 int v = 0;
1196 wStream sub;
1197 wStream* s = &sub;
1198
1199 WINPR_ASSERT(dec);
1200 WINPR_ASSERT(target);
1201
1202 ret = readTagAndLen(dec, &dec->source, &tag, &len);
1203 if (!ret || tag != ER_TAG_UTCTIME)
1204 return 0;
1205 if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len) || len < 12)
1206 return 0;
1207
1208 Stream_StaticConstInit(s, Stream_ConstPointer(&dec->source), len);
1209
1210 v = read2digits(s);
1211 if ((v <= 0) || (v >= UINT16_MAX - 2000))
1212 return 0;
1213 target->year = (UINT16)(2000 + v);
1214
1215 v = read2digits(s);
1216 if ((v <= 0) || (v > UINT8_MAX))
1217 return 0;
1218 target->month = (UINT8)v;
1219
1220 v = read2digits(s);
1221 if ((v <= 0) || (v > UINT8_MAX))
1222 return 0;
1223 target->day = (UINT8)v;
1224
1225 v = read2digits(s);
1226 if ((v <= 0) || (v > UINT8_MAX))
1227 return 0;
1228 target->hour = (UINT8)v;
1229
1230 v = read2digits(s);
1231 if ((v <= 0) || (v > UINT8_MAX))
1232 return 0;
1233 target->minute = (UINT8)v;
1234
1235 v = read2digits(s);
1236 if ((v <= 0) || (v > UINT8_MAX))
1237 return 0;
1238 target->second = (UINT8)v;
1239
1240 if (Stream_GetRemainingLength(s) >= 1)
1241 {
1242 Stream_Read_INT8(s, target->tz);
1243 }
1244
1245 Stream_Seek(&dec->source, len);
1246 ret += len;
1247
1248 return ret;
1249}
1250
1251size_t WinPrAsn1DecReadNull(WinPrAsn1Decoder* dec)
1252{
1253 WinPrAsn1_tag tag = 0;
1254 size_t len = 0;
1255 size_t ret = 0;
1256
1257 WINPR_ASSERT(dec);
1258
1259 ret = readTagAndLen(dec, &dec->source, &tag, &len);
1260 if (!ret || tag != ER_TAG_NULL || len)
1261 return 0;
1262
1263 return ret;
1264}
1265
1266static size_t readConstructed(WinPrAsn1Decoder* dec, wStream* s, WinPrAsn1_tag* tag,
1267 WinPrAsn1Decoder* target)
1268{
1269 size_t len = 0;
1270 size_t ret = 0;
1271
1272 ret = readTagAndLen(dec, s, tag, &len);
1273 if (!ret || !Stream_CheckAndLogRequiredLength(TAG, s, len))
1274 return 0;
1275
1276 target->encoding = dec->encoding;
1277 Stream_StaticConstInit(&target->source, Stream_ConstPointer(s), len);
1278 Stream_Seek(s, len);
1279 return ret + len;
1280}
1281
1282size_t WinPrAsn1DecReadApp(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, WinPrAsn1Decoder* setDec)
1283{
1284 WinPrAsn1_tag tag = 0;
1285 size_t ret = 0;
1286
1287 WINPR_ASSERT(dec);
1288 WINPR_ASSERT(setDec);
1289
1290 ret = readConstructed(dec, &dec->source, &tag, setDec);
1291 if ((tag & ER_TAG_APP) != ER_TAG_APP)
1292 return 0;
1293
1294 *tagId = (tag & ER_TAG_MASK);
1295 return ret;
1296}
1297
1298size_t WinPrAsn1DecReadSequence(WinPrAsn1Decoder* dec, WinPrAsn1Decoder* seqDec)
1299{
1300 WinPrAsn1_tag tag = 0;
1301 size_t ret = 0;
1302
1303 WINPR_ASSERT(dec);
1304 WINPR_ASSERT(seqDec);
1305
1306 ret = readConstructed(dec, &dec->source, &tag, seqDec);
1307 if (tag != ER_TAG_SEQUENCE)
1308 return 0;
1309
1310 return ret;
1311}
1312
1313size_t WinPrAsn1DecReadSet(WinPrAsn1Decoder* dec, WinPrAsn1Decoder* setDec)
1314{
1315 WinPrAsn1_tag tag = 0;
1316 size_t ret = 0;
1317
1318 WINPR_ASSERT(dec);
1319 WINPR_ASSERT(setDec);
1320
1321 ret = readConstructed(dec, &dec->source, &tag, setDec);
1322 if (tag != ER_TAG_SET)
1323 return 0;
1324
1325 return ret;
1326}
1327
1328static size_t readContextualTag(WinPrAsn1Decoder* dec, wStream* s, WinPrAsn1_tagId* tagId,
1329 WinPrAsn1Decoder* ctxtDec)
1330{
1331 size_t ret = 0;
1332 WinPrAsn1_tag ftag = 0;
1333
1334 ret = readConstructed(dec, s, &ftag, ctxtDec);
1335 if (!ret)
1336 return 0;
1337
1338 if ((ftag & ER_TAG_CONTEXTUAL) != ER_TAG_CONTEXTUAL)
1339 return 0;
1340
1341 *tagId = (ftag & ER_TAG_MASK);
1342 return ret;
1343}
1344
1345size_t WinPrAsn1DecReadContextualTag(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId,
1346 WinPrAsn1Decoder* ctxtDec)
1347{
1348 WINPR_ASSERT(dec);
1349 WINPR_ASSERT(tagId);
1350 WINPR_ASSERT(ctxtDec);
1351
1352 return readContextualTag(dec, &dec->source, tagId, ctxtDec);
1353}
1354
1355size_t WinPrAsn1DecPeekContextualTag(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId,
1356 WinPrAsn1Decoder* ctxtDec)
1357{
1358 wStream staticS;
1359 WINPR_ASSERT(dec);
1360
1361 Stream_StaticConstInit(&staticS, Stream_ConstPointer(&dec->source),
1362 Stream_GetRemainingLength(&dec->source));
1363 return readContextualTag(dec, &staticS, tagId, ctxtDec);
1364}
1365
1366static size_t readContextualHeader(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error,
1367 WinPrAsn1Decoder* content)
1368{
1369 WinPrAsn1_tag ftag = 0;
1370 size_t ret = 0;
1371
1372 WINPR_ASSERT(dec);
1373 WINPR_ASSERT(error);
1374 WINPR_ASSERT(content);
1375
1376 *error = TRUE;
1377 ret = WinPrAsn1DecPeekContextualTag(dec, &ftag, content);
1378 if (!ret)
1379 return 0;
1380
1381 if (ftag != tagId)
1382 {
1383 *error = FALSE;
1384 return 0;
1385 }
1386
1387 *error = FALSE;
1388 return ret;
1389}
1390
1391size_t WinPrAsn1DecReadContextualBool(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error,
1392 WinPrAsn1_BOOL* target)
1393{
1394 size_t ret = 0;
1395 size_t ret2 = 0;
1396 WinPrAsn1Decoder content = WinPrAsn1Decoder_init();
1397
1398 ret = readContextualHeader(dec, tagId, error, &content);
1399 if (!ret)
1400 return 0;
1401
1402 ret2 = WinPrAsn1DecReadBoolean(&content, target);
1403 if (!ret2)
1404 {
1405 *error = TRUE;
1406 return 0;
1407 }
1408
1409 Stream_Seek(&dec->source, ret);
1410 return ret;
1411}
1412
1413size_t WinPrAsn1DecReadContextualInteger(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error,
1414 WinPrAsn1_INTEGER* target)
1415{
1416 size_t ret = 0;
1417 size_t ret2 = 0;
1418 WinPrAsn1Decoder content = WinPrAsn1Decoder_init();
1419
1420 ret = readContextualHeader(dec, tagId, error, &content);
1421 if (!ret)
1422 return 0;
1423
1424 ret2 = WinPrAsn1DecReadInteger(&content, target);
1425 if (!ret2)
1426 {
1427 *error = TRUE;
1428 return 0;
1429 }
1430
1431 Stream_Seek(&dec->source, ret);
1432 return ret;
1433}
1434
1435size_t WinPrAsn1DecReadContextualOID(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error,
1436 WinPrAsn1_OID* target, BOOL allocate)
1437{
1438 size_t ret = 0;
1439 size_t ret2 = 0;
1440 WinPrAsn1Decoder content = WinPrAsn1Decoder_init();
1441
1442 ret = readContextualHeader(dec, tagId, error, &content);
1443 if (!ret)
1444 return 0;
1445
1446 ret2 = WinPrAsn1DecReadOID(&content, target, allocate);
1447 if (!ret2)
1448 {
1449 *error = TRUE;
1450 return 0;
1451 }
1452
1453 Stream_Seek(&dec->source, ret);
1454 return ret;
1455}
1456
1457size_t WinPrAsn1DecReadContextualOctetString(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId,
1458 BOOL* error, WinPrAsn1_OctetString* target,
1459 BOOL allocate)
1460{
1461 size_t ret = 0;
1462 size_t ret2 = 0;
1463 WinPrAsn1Decoder content = WinPrAsn1Decoder_init();
1464
1465 ret = readContextualHeader(dec, tagId, error, &content);
1466 if (!ret)
1467 return 0;
1468
1469 ret2 = WinPrAsn1DecReadOctetString(&content, target, allocate);
1470 if (!ret2)
1471 {
1472 *error = TRUE;
1473 return 0;
1474 }
1475
1476 Stream_Seek(&dec->source, ret);
1477 return ret;
1478}
1479
1480size_t WinPrAsn1DecReadContextualSequence(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error,
1481 WinPrAsn1Decoder* target)
1482{
1483 size_t ret = 0;
1484 size_t ret2 = 0;
1485 WinPrAsn1Decoder content = WinPrAsn1Decoder_init();
1486
1487 ret = readContextualHeader(dec, tagId, error, &content);
1488 if (!ret)
1489 return 0;
1490
1491 ret2 = WinPrAsn1DecReadSequence(&content, target);
1492 if (!ret2)
1493 {
1494 *error = TRUE;
1495 return 0;
1496 }
1497
1498 Stream_Seek(&dec->source, ret);
1499 return ret;
1500}
1501
1502wStream WinPrAsn1DecGetStream(WinPrAsn1Decoder* dec)
1503{
1504 wStream s = WINPR_C_ARRAY_INIT;
1505 WINPR_ASSERT(dec);
1506
1507 Stream_StaticConstInit(&s, Stream_ConstPointer(&dec->source),
1508 Stream_GetRemainingLength(&dec->source));
1509 return s;
1510}