Coverage Report

Created: 2024-05-20 06:11

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