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