Coverage Report

Created: 2026-04-12 06:58

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