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