Coverage Report

Created: 2023-09-25 06:56

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