Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/crypto/per.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * ASN.1 Packed Encoding Rules (BER)
4
 *
5
 * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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/assert.h>
21
#include <winpr/cast.h>
22
#include <winpr/print.h>
23
24
#include <freerdp/config.h>
25
#include <freerdp/crypto/per.h>
26
27
#include <freerdp/log.h>
28
#define TAG FREERDP_TAG("crypto.per")
29
30
/**
31
 * Read PER length.
32
 *
33
 * @param s stream to read from
34
 * @param length A pointer to return the length read, must not be NULL
35
 *
36
 * @return \b TRUE for success, \b FALSE otherwise.
37
 */
38
39
BOOL per_read_length(wStream* s, UINT16* length)
40
0
{
41
0
  BYTE byte = 0;
42
43
0
  WINPR_ASSERT(length);
44
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
45
0
    return FALSE;
46
47
0
  Stream_Read_UINT8(s, byte);
48
49
0
  if (byte & 0x80)
50
0
  {
51
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
52
0
      return FALSE;
53
54
0
    byte &= ~(0x80);
55
0
    *length = WINPR_ASSERTING_INT_CAST(UINT16, byte << 8);
56
0
    Stream_Read_UINT8(s, byte);
57
0
    *length += byte;
58
0
  }
59
0
  else
60
0
  {
61
0
    *length = byte;
62
0
  }
63
64
0
  return TRUE;
65
0
}
66
67
/**
68
 * Write PER length.
69
 * @param s stream
70
 * @param length length
71
 *
72
 * @return \b TRUE for success, \b FALSE otherwise.
73
 */
74
75
BOOL per_write_length(wStream* s, UINT16 length)
76
0
{
77
0
  if (length > 0x7F)
78
0
  {
79
0
    if (!Stream_EnsureRemainingCapacity(s, 2))
80
0
      return FALSE;
81
0
    Stream_Write_UINT16_BE(s, (length | 0x8000));
82
0
  }
83
0
  else
84
0
  {
85
0
    if (!Stream_EnsureRemainingCapacity(s, 1))
86
0
      return FALSE;
87
0
    Stream_Write_UINT8(s, (UINT8)length);
88
0
  }
89
0
  return TRUE;
90
0
}
91
92
/**
93
 * Read PER choice.
94
 * @param s stream
95
 * @param choice choice
96
 *
97
 * @return \b TRUE for success, \b FALSE otherwise.
98
 */
99
100
BOOL per_read_choice(wStream* s, BYTE* choice)
101
0
{
102
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
103
0
    return FALSE;
104
105
0
  Stream_Read_UINT8(s, *choice);
106
0
  return TRUE;
107
0
}
108
109
/**
110
 * Write PER CHOICE.
111
 * @param s stream
112
 * @param choice index of chosen field
113
 *
114
 * @return \b TRUE for success, \b FALSE otherwise.
115
 */
116
117
BOOL per_write_choice(wStream* s, BYTE choice)
118
0
{
119
0
  if (!Stream_EnsureRemainingCapacity(s, 1))
120
0
    return FALSE;
121
0
  Stream_Write_UINT8(s, choice);
122
0
  return TRUE;
123
0
}
124
125
/**
126
 * Read PER selection.
127
 * @param s stream
128
 * @param selection selection
129
 *
130
 * @return \b TRUE for success, \b FALSE otherwise.
131
 */
132
133
BOOL per_read_selection(wStream* s, BYTE* selection)
134
0
{
135
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
136
0
    return FALSE;
137
138
0
  WINPR_ASSERT(selection);
139
0
  Stream_Read_UINT8(s, *selection);
140
0
  return TRUE;
141
0
}
142
143
/**
144
 * Write PER selection for OPTIONAL fields.
145
 * @param s stream
146
 * @param selection bit map of selected fields
147
 *
148
 * @return \b TRUE for success, \b FALSE otherwise.
149
 */
150
151
BOOL per_write_selection(wStream* s, BYTE selection)
152
0
{
153
0
  if (!Stream_EnsureRemainingCapacity(s, 1))
154
0
    return FALSE;
155
0
  Stream_Write_UINT8(s, selection);
156
0
  return TRUE;
157
0
}
158
159
/**
160
 * Read PER number of sets.
161
 * @param s stream
162
 * @param number number of sets
163
 *
164
 * @return \b TRUE for success, \b FALSE otherwise.
165
 */
166
167
BOOL per_read_number_of_sets(wStream* s, BYTE* number)
168
0
{
169
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
170
0
    return FALSE;
171
172
0
  WINPR_ASSERT(number);
173
0
  Stream_Read_UINT8(s, *number);
174
0
  return TRUE;
175
0
}
176
177
/**
178
 * Write PER number of sets for SET OF.
179
 *
180
 * @param s stream
181
 * @param number number of sets
182
 *
183
 * @return \b TRUE for success, \b FALSE otherwise.
184
 */
185
186
BOOL per_write_number_of_sets(wStream* s, BYTE number)
187
0
{
188
0
  if (!Stream_EnsureRemainingCapacity(s, 1))
189
0
    return FALSE;
190
0
  Stream_Write_UINT8(s, number);
191
0
  return TRUE;
192
0
}
193
194
/**
195
 * Read PER padding with zeros.
196
 *
197
 * @param s A stream to read from
198
 * @param length the data to write
199
 *
200
 * @return \b TRUE for success, \b FALSE otherwise.
201
 */
202
203
BOOL per_read_padding(wStream* s, UINT16 length)
204
0
{
205
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
206
0
    return FALSE;
207
208
0
  Stream_Seek(s, length);
209
0
  return TRUE;
210
0
}
211
212
/**
213
 * Write PER padding with zeros.
214
 * @param s A stream to write to
215
 * @param length the data to write
216
 *
217
 * @return \b TRUE for success, \b FALSE otherwise.
218
 */
219
220
BOOL per_write_padding(wStream* s, UINT16 length)
221
0
{
222
0
  if (!Stream_EnsureRemainingCapacity(s, length))
223
0
    return FALSE;
224
0
  Stream_Zero(s, length);
225
0
  return TRUE;
226
0
}
227
228
/**
229
 * Read PER INTEGER.
230
 * @param s stream
231
 * @param integer integer
232
 *
233
 * @return \b TRUE for success, \b FALSE otherwise.
234
 */
235
236
BOOL per_read_integer(wStream* s, UINT32* integer)
237
0
{
238
0
  UINT16 length = 0;
239
240
0
  WINPR_ASSERT(integer);
241
242
0
  if (!per_read_length(s, &length))
243
0
    return FALSE;
244
245
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
246
0
    return FALSE;
247
248
0
  if (length == 0)
249
0
    *integer = 0;
250
0
  else if (length == 1)
251
0
    Stream_Read_UINT8(s, *integer);
252
0
  else if (length == 2)
253
0
    Stream_Read_UINT16_BE(s, *integer);
254
0
  else
255
0
    return FALSE;
256
257
0
  return TRUE;
258
0
}
259
260
/**
261
 * Write PER INTEGER.
262
 * @param s stream
263
 * @param integer integer
264
 *
265
 * @return \b TRUE for success, \b FALSE otherwise.
266
 */
267
268
BOOL per_write_integer(wStream* s, UINT32 integer)
269
0
{
270
0
  if (integer <= UINT8_MAX)
271
0
  {
272
0
    if (!per_write_length(s, 1))
273
0
      return FALSE;
274
0
    if (!Stream_EnsureRemainingCapacity(s, 1))
275
0
      return FALSE;
276
0
    Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, integer));
277
0
  }
278
0
  else if (integer <= UINT16_MAX)
279
0
  {
280
0
    if (!per_write_length(s, 2))
281
0
      return FALSE;
282
0
    if (!Stream_EnsureRemainingCapacity(s, 2))
283
0
      return FALSE;
284
0
    Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, integer));
285
0
  }
286
0
  else if (integer <= UINT32_MAX)
287
0
  {
288
0
    if (!per_write_length(s, 4))
289
0
      return FALSE;
290
0
    if (!Stream_EnsureRemainingCapacity(s, 4))
291
0
      return FALSE;
292
0
    Stream_Write_UINT32_BE(s, integer);
293
0
  }
294
0
  return TRUE;
295
0
}
296
297
/**
298
 * Read PER INTEGER (UINT16).
299
 *
300
 * @param s The stream to read from
301
 * @param integer The integer result variable pointer, must not be NULL
302
 * @param min minimum value
303
 *
304
 * @return \b TRUE for success, \b FALSE otherwise
305
 */
306
307
BOOL per_read_integer16(wStream* s, UINT16* integer, UINT16 min)
308
0
{
309
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
310
0
    return FALSE;
311
312
0
  Stream_Read_UINT16_BE(s, *integer);
313
314
0
  if (*integer > UINT16_MAX - min)
315
0
  {
316
0
    WLog_WARN(TAG, "PER uint16 invalid value %" PRIu16 " > %" PRIu16, *integer,
317
0
              UINT16_MAX - min);
318
0
    return FALSE;
319
0
  }
320
321
0
  *integer += min;
322
323
0
  return TRUE;
324
0
}
325
326
/**
327
 * Write PER INTEGER (UINT16).
328
 * @param s stream
329
 * @param integer integer
330
 * @param min minimum value
331
 *
332
 * @return \b TRUE for success, \b FALSE otherwise.
333
 */
334
335
BOOL per_write_integer16(wStream* s, UINT16 integer, UINT16 min)
336
0
{
337
0
  if (min > integer)
338
0
    return FALSE;
339
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
340
0
    return FALSE;
341
0
  Stream_Write_UINT16_BE(s, integer - min);
342
0
  return TRUE;
343
0
}
344
345
/**
346
 * Read PER ENUMERATED.
347
 *
348
 * @param s The stream to read from
349
 * @param enumerated enumerated result variable, must not be NULL
350
 * @param count enumeration count
351
 *
352
 * @return \b TRUE for success, \b FALSE otherwise
353
 */
354
355
BOOL per_read_enumerated(wStream* s, BYTE* enumerated, BYTE count)
356
0
{
357
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
358
0
    return FALSE;
359
360
0
  WINPR_ASSERT(enumerated);
361
0
  Stream_Read_UINT8(s, *enumerated);
362
363
  /* check that enumerated value falls within expected range */
364
0
  if (*enumerated + 1 > count)
365
0
  {
366
0
    WLog_WARN(TAG, "PER invalid data, expected %" PRIu8 " < %" PRIu8, *enumerated, count);
367
0
    return FALSE;
368
0
  }
369
370
0
  return TRUE;
371
0
}
372
373
/**
374
 * Write PER ENUMERATED.
375
 *
376
 * @param s The stream to write to
377
 * @param enumerated enumerated
378
 * @param count enumeration count
379
 *
380
 * @return \b TRUE for success, \b FALSE otherwise
381
 */
382
383
BOOL per_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count)
384
0
{
385
0
  if (!Stream_EnsureRemainingCapacity(s, 1))
386
0
    return FALSE;
387
0
  Stream_Write_UINT8(s, enumerated);
388
0
  return TRUE;
389
0
}
390
391
static BOOL per_check_oid_and_log_mismatch(const BYTE* got, const BYTE* expect, size_t length)
392
0
{
393
0
  if (memcmp(got, expect, length) == 0)
394
0
  {
395
0
    return TRUE;
396
0
  }
397
0
  else
398
0
  {
399
0
    char* got_str = winpr_BinToHexString(got, length, TRUE);
400
0
    char* expect_str = winpr_BinToHexString(expect, length, TRUE);
401
402
0
    WLog_WARN(TAG, "PER OID mismatch, got %s, expected %s", got_str, expect_str);
403
0
    free(got_str);
404
0
    free(expect_str);
405
0
    return FALSE;
406
0
  }
407
0
}
408
409
/**
410
 * Read PER OBJECT_IDENTIFIER (OID).
411
 *
412
 * @param s The stream to read from
413
 * @param oid object identifier (OID)
414
 * @warning It works correctly only for limited set of OIDs.
415
 *
416
 * @return \b TRUE for success, \b FALSE otherwise
417
 */
418
419
BOOL per_read_object_identifier(wStream* s, const BYTE oid[6])
420
0
{
421
0
  BYTE t12 = 0;
422
0
  UINT16 length = 0;
423
0
  BYTE a_oid[6] = { 0 };
424
425
0
  if (!per_read_length(s, &length))
426
0
    return FALSE;
427
428
0
  if (length != 5)
429
0
  {
430
0
    WLog_WARN(TAG, "PER length, got %" PRIu16 ", expected 5", length);
431
0
    return FALSE;
432
0
  }
433
434
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
435
0
    return FALSE;
436
437
0
  Stream_Read_UINT8(s, t12); /* first two tuples */
438
0
  a_oid[0] = t12 / 40;
439
0
  a_oid[1] = t12 % 40;
440
441
0
  Stream_Read_UINT8(s, a_oid[2]); /* tuple 3 */
442
0
  Stream_Read_UINT8(s, a_oid[3]); /* tuple 4 */
443
0
  Stream_Read_UINT8(s, a_oid[4]); /* tuple 5 */
444
0
  Stream_Read_UINT8(s, a_oid[5]); /* tuple 6 */
445
446
0
  return per_check_oid_and_log_mismatch(a_oid, oid, sizeof(a_oid));
447
0
}
448
449
/**
450
 * Write PER OBJECT_IDENTIFIER (OID)
451
 * @param s stream
452
 * @param oid object identifier (oid)
453
 * @warning It works correctly only for limited set of OIDs.
454
 *
455
 * @return \b TRUE for success, \b FALSE otherwise.
456
 */
457
458
BOOL per_write_object_identifier(wStream* s, const BYTE oid[6])
459
0
{
460
0
  BYTE t12 = oid[0] * 40 + oid[1];
461
0
  if (!Stream_EnsureRemainingCapacity(s, 6))
462
0
    return FALSE;
463
0
  Stream_Write_UINT8(s, 5);      /* length */
464
0
  Stream_Write_UINT8(s, t12);    /* first two tuples */
465
0
  Stream_Write_UINT8(s, oid[2]); /* tuple 3 */
466
0
  Stream_Write_UINT8(s, oid[3]); /* tuple 4 */
467
0
  Stream_Write_UINT8(s, oid[4]); /* tuple 5 */
468
0
  Stream_Write_UINT8(s, oid[5]); /* tuple 6 */
469
0
  return TRUE;
470
0
}
471
472
/**
473
 * Read PER OCTET_STRING.
474
 *
475
 * @param s The stream to read from
476
 * @param oct_str octet string
477
 * @param length string length
478
 * @param min minimum length
479
 *
480
 * @return \b TRUE for success, \b FALSE otherwise.
481
 */
482
483
BOOL per_read_octet_string(wStream* s, const BYTE* oct_str, UINT16 length, UINT16 min)
484
0
{
485
0
  UINT16 mlength = 0;
486
487
0
  if (!per_read_length(s, &mlength))
488
0
    return FALSE;
489
490
0
  if (mlength + min != length)
491
0
  {
492
0
    WLog_ERR(TAG, "length mismatch: %" PRIu16 "!= %" PRIu16, mlength + min, length);
493
0
    return FALSE;
494
0
  }
495
496
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
497
0
    return FALSE;
498
499
0
  const BYTE* a_oct_str = Stream_ConstPointer(s);
500
0
  Stream_Seek(s, length);
501
502
0
  return per_check_oid_and_log_mismatch(a_oct_str, oct_str, length);
503
0
}
504
505
/**
506
 * Write PER OCTET_STRING
507
 * @param s stream
508
 * @param oct_str octet string
509
 * @param length string length
510
 * @param min minimum string length
511
 *
512
 * @return \b TRUE for success, \b FALSE otherwise.
513
 */
514
515
BOOL per_write_octet_string(wStream* s, const BYTE* oct_str, UINT16 length, UINT16 min)
516
0
{
517
0
  UINT16 mlength = 0;
518
519
0
  mlength = (length >= min) ? length - min : min;
520
521
0
  if (!per_write_length(s, mlength))
522
0
    return FALSE;
523
524
0
  if (!Stream_EnsureRemainingCapacity(s, length))
525
0
    return FALSE;
526
0
  for (UINT16 i = 0; i < length; i++)
527
0
    Stream_Write_UINT8(s, oct_str[i]);
528
0
  return TRUE;
529
0
}
530
531
/**
532
 * Read PER NumericString.
533
 * @param s stream
534
 * @param min minimum string length
535
 *
536
 * @return \b TRUE for success, \b FALSE otherwise.
537
 */
538
539
BOOL per_read_numeric_string(wStream* s, UINT16 min)
540
0
{
541
0
  size_t length = 0;
542
0
  UINT16 mlength = 0;
543
544
0
  if (!per_read_length(s, &mlength))
545
0
    return FALSE;
546
547
0
  length = (mlength + min + 1) / 2;
548
549
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
550
0
    return FALSE;
551
552
0
  Stream_Seek(s, length);
553
0
  return TRUE;
554
0
}
555
556
/**
557
 * Write PER NumericString.
558
 * @param s stream
559
 * @param num_str numeric string
560
 * @param length string length
561
 * @param min minimum string length
562
 *
563
 * @return \b TRUE for success, \b FALSE otherwise.
564
 */
565
566
BOOL per_write_numeric_string(wStream* s, const BYTE* num_str, UINT16 length, UINT16 min)
567
0
{
568
0
  WINPR_ASSERT(num_str || (length == 0));
569
570
0
  const UINT16 mlength = (length >= min) ? length - min : min;
571
572
0
  if (!per_write_length(s, mlength))
573
0
    return FALSE;
574
575
0
  if (!Stream_EnsureRemainingCapacity(s, length))
576
0
    return FALSE;
577
0
  for (UINT16 i = 0; i < length; i += 2)
578
0
  {
579
0
    BYTE c1 = num_str[i];
580
0
    BYTE c2 = ((i + 1) < length) ? num_str[i + 1] : 0x30;
581
582
0
    if ((c1 < 0x30) || (c2 < 0x30))
583
0
      return FALSE;
584
585
0
    c1 = (c1 - 0x30) % 10;
586
0
    c2 = (c2 - 0x30) % 10;
587
0
    const BYTE num = WINPR_ASSERTING_INT_CAST(BYTE, (c1 << 4) | c2);
588
589
0
    Stream_Write_UINT8(s, num); /* string */
590
0
  }
591
0
  return TRUE;
592
0
}