Coverage Report

Created: 2024-05-20 06:11

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