Coverage Report

Created: 2026-03-07 07:02

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