Coverage Report

Created: 2024-02-11 06:28

/src/strongswan/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2013 Tobias Brunner
3
 *
4
 * Copyright (C) secunet Security Networks AG
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License as published by the
8
 * Free Software Foundation; either version 2 of the License, or (at your
9
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10
 *
11
 * This program is distributed in the hope that it will be useful, but
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14
 * for more details.
15
 */
16
17
#include "pkcs12_decode.h"
18
19
#include <utils/debug.h>
20
#include <asn1/oid.h>
21
#include <asn1/asn1.h>
22
#include <asn1/asn1_parser.h>
23
#include <credentials/sets/mem_cred.h>
24
25
typedef struct private_pkcs12_t private_pkcs12_t;
26
27
/**
28
 * Private data of a pkcs12_t object
29
 */
30
struct private_pkcs12_t {
31
32
  /**
33
   * Public interface
34
   */
35
  pkcs12_t public;
36
37
  /**
38
   * Contained credentials
39
   */
40
  mem_cred_t *creds;
41
};
42
43
METHOD(container_t, get_type, container_type_t,
44
  private_pkcs12_t *this)
45
0
{
46
0
  return CONTAINER_PKCS12;
47
0
}
48
49
METHOD(container_t, get_data, bool,
50
  private_pkcs12_t *this, chunk_t *data)
51
0
{
52
  /* we could return the content of the outer-most PKCS#7 container (authSafe)
53
   * don't really see the point though */
54
0
  return FALSE;
55
0
}
56
57
METHOD(container_t, get_encoding, bool,
58
  private_pkcs12_t *this, chunk_t *encoding)
59
0
{
60
  /* similar to get_data() we don't have any use for it at the moment */
61
0
  return FALSE;
62
0
}
63
64
METHOD(pkcs12_t, create_cert_enumerator, enumerator_t*,
65
  private_pkcs12_t *this)
66
0
{
67
0
  return this->creds->set.create_cert_enumerator(&this->creds->set, CERT_ANY,
68
0
                           KEY_ANY, NULL, FALSE);
69
0
}
70
71
METHOD(pkcs12_t, create_key_enumerator, enumerator_t*,
72
  private_pkcs12_t *this)
73
0
{
74
0
  return this->creds->set.create_private_enumerator(&this->creds->set,
75
0
                            KEY_ANY, NULL);
76
0
}
77
78
METHOD(container_t, destroy, void,
79
  private_pkcs12_t *this)
80
0
{
81
0
  this->creds->destroy(this->creds);
82
0
  free(this);
83
0
}
84
85
static private_pkcs12_t *pkcs12_create()
86
0
{
87
0
  private_pkcs12_t *this;
88
89
0
  INIT(this,
90
0
    .public = {
91
0
      .container = {
92
0
        .get_type = _get_type,
93
0
        .create_signature_enumerator = (void*)enumerator_create_empty,
94
0
        .get_data = _get_data,
95
0
        .get_encoding = _get_encoding,
96
0
        .destroy = _destroy,
97
0
      },
98
0
      .create_cert_enumerator = _create_cert_enumerator,
99
0
      .create_key_enumerator = _create_key_enumerator,
100
0
    },
101
0
    .creds = mem_cred_create(),
102
0
  );
103
0
  return this;
104
0
}
105
106
/**
107
 * ASN.1 definition of an CertBag structure
108
 */
109
static const asn1Object_t certBagObjects[] = {
110
  { 0, "CertBag",     ASN1_SEQUENCE,    ASN1_BODY     }, /* 0 */
111
  { 1,   "certId",    ASN1_OID,     ASN1_BODY     }, /* 1 */
112
  { 1,   "certValue",   ASN1_CONTEXT_C_0, ASN1_BODY       }, /* 2 */
113
  { 0, "exit",      ASN1_EOC,     ASN1_EXIT     }
114
};
115
0
#define CERT_BAG_ID   1
116
0
#define CERT_BAG_VALUE  2
117
118
/**
119
 * Parse a CertBag structure and extract certificate
120
 */
121
static bool add_certificate(private_pkcs12_t *this, int level0, chunk_t blob)
122
0
{
123
0
  asn1_parser_t *parser;
124
0
  chunk_t object;
125
0
  int objectID;
126
0
  int oid = OID_UNKNOWN;
127
0
  bool success = FALSE;
128
129
0
  parser = asn1_parser_create(certBagObjects, blob);
130
0
  parser->set_top_level(parser, level0);
131
132
0
  while (parser->iterate(parser, &objectID, &object))
133
0
  {
134
0
    switch (objectID)
135
0
    {
136
0
      case CERT_BAG_ID:
137
0
        oid = asn1_known_oid(object);
138
0
        break;
139
0
      case CERT_BAG_VALUE:
140
0
      {
141
0
        if (oid == OID_X509_CERTIFICATE &&
142
0
          asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
143
0
                parser->get_level(parser)+1, "x509Certificate"))
144
0
        {
145
0
          certificate_t *cert;
146
147
0
          DBG2(DBG_ASN, "-- > parsing certificate from PKCS#12");
148
0
          cert = lib->creds->create(lib->creds,
149
0
                        CRED_CERTIFICATE, CERT_X509,
150
0
                        BUILD_BLOB_ASN1_DER, object,
151
0
                        BUILD_END);
152
0
          if (cert)
153
0
          {
154
0
            this->creds->add_cert(this->creds, FALSE, cert);
155
0
            DBG2(DBG_ASN, "-- < --");
156
0
          }
157
0
          else
158
0
          {
159
0
            DBG2(DBG_ASN, "-- < failed parsing certificate from "
160
0
               "PKCS#12");
161
0
          }
162
0
        }
163
0
        break;
164
0
      }
165
0
    }
166
0
  }
167
0
  success = parser->success(parser);
168
0
  parser->destroy(parser);
169
0
  return success;
170
0
}
171
172
/**
173
 * ASN.1 definition of an AuthenticatedSafe structure
174
 */
175
static const asn1Object_t safeContentsObjects[] = {
176
  { 0, "SafeContents",  ASN1_SEQUENCE,    ASN1_LOOP     }, /* 0 */
177
  { 1,   "SafeBag",   ASN1_SEQUENCE,    ASN1_BODY     }, /* 1 */
178
  { 2,     "bagId",   ASN1_OID,     ASN1_BODY     }, /* 2 */
179
  { 2,     "bagValue",  ASN1_CONTEXT_C_0, ASN1_BODY       }, /* 3 */
180
  { 2,     "bagAttr",   ASN1_SET,     ASN1_OPT|ASN1_RAW   }, /* 4 */
181
  { 2,     "end opt",   ASN1_EOC,     ASN1_END      }, /* 5 */
182
  { 0, "end loop",    ASN1_EOC,     ASN1_END      }, /* 6 */
183
  { 0, "exit",      ASN1_EOC,     ASN1_EXIT     }
184
};
185
0
#define SAFE_BAG_ID   2
186
0
#define SAFE_BAG_VALUE  3
187
188
/**
189
 * Parse a SafeContents structure and extract credentials
190
 */
191
static bool parse_safe_contents(private_pkcs12_t *this, int level0,
192
                chunk_t blob)
193
0
{
194
0
  asn1_parser_t *parser;
195
0
  chunk_t object;
196
0
  int objectID;
197
0
  int oid = OID_UNKNOWN;
198
0
  bool success = FALSE;
199
200
0
  parser = asn1_parser_create(safeContentsObjects, blob);
201
0
  parser->set_top_level(parser, level0);
202
203
0
  while (parser->iterate(parser, &objectID, &object))
204
0
  {
205
0
    switch (objectID)
206
0
    {
207
0
      case SAFE_BAG_ID:
208
0
        oid = asn1_known_oid(object);
209
0
        break;
210
0
      case SAFE_BAG_VALUE:
211
0
      {
212
0
        switch (oid)
213
0
        {
214
0
          case OID_P12_CERT_BAG:
215
0
          {
216
0
            add_certificate(this, parser->get_level(parser)+1,
217
0
                    object);
218
0
            break;
219
0
          }
220
0
          case OID_P12_KEY_BAG:
221
0
          case OID_P12_PKCS8_KEY_BAG:
222
0
          {
223
0
            private_key_t *key;
224
225
0
            DBG2(DBG_ASN, "-- > parsing private key from PKCS#12");
226
0
            key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
227
0
                    KEY_ANY, BUILD_BLOB_ASN1_DER, object,
228
0
                    BUILD_END);
229
0
            if (key)
230
0
            {
231
0
              this->creds->add_key(this->creds, key);
232
0
              DBG2(DBG_ASN, "-- < --");
233
0
            }
234
0
            else
235
0
            {
236
0
              DBG2(DBG_ASN, "-- < failed parsing private key "
237
0
                 "from PKCS#12");
238
0
            }
239
0
          }
240
0
          default:
241
0
            break;
242
0
        }
243
0
        break;
244
0
      }
245
0
    }
246
0
  }
247
0
  success = parser->success(parser);
248
0
  parser->destroy(parser);
249
0
  return success;
250
0
}
251
252
/**
253
 * ASN.1 definition of an AuthenticatedSafe structure
254
 */
255
static const asn1Object_t authenticatedSafeObjects[] = {
256
  { 0, "AuthenticatedSafe", ASN1_SEQUENCE,  ASN1_LOOP }, /* 0 */
257
  { 1,   "ContentInfo",   ASN1_SEQUENCE,  ASN1_OBJ  }, /* 1 */
258
  { 0, "end loop",      ASN1_EOC,   ASN1_END  }, /* 2 */
259
  { 0, "exit",        ASN1_EOC,   ASN1_EXIT }
260
};
261
0
#define AUTHENTICATED_SAFE_DATA   1
262
263
/**
264
 * Parse an AuthenticatedSafe structure
265
 */
266
static bool parse_authenticated_safe(private_pkcs12_t *this, chunk_t blob)
267
0
{
268
0
  asn1_parser_t *parser;
269
0
  chunk_t object;
270
0
  int objectID;
271
0
  bool success = FALSE;
272
273
0
  parser = asn1_parser_create(authenticatedSafeObjects, blob);
274
275
0
  while (parser->iterate(parser, &objectID, &object))
276
0
  {
277
0
    switch (objectID)
278
0
    {
279
0
      case AUTHENTICATED_SAFE_DATA:
280
0
      {
281
0
        container_t *container;
282
0
        chunk_t data;
283
284
0
        container = lib->creds->create(lib->creds, CRED_CONTAINER,
285
0
                    CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER,
286
0
                    object, BUILD_END);
287
0
        if (!container)
288
0
        {
289
0
          goto end;
290
0
        }
291
0
        switch (container->get_type(container))
292
0
        {
293
0
          case CONTAINER_PKCS7_DATA:
294
0
          case CONTAINER_PKCS7_ENCRYPTED_DATA:
295
0
          case CONTAINER_PKCS7_ENVELOPED_DATA:
296
0
            if (container->get_data(container, &data))
297
0
            {
298
0
              break;
299
0
            }
300
            /* fall-through */
301
0
          default:
302
0
            container->destroy(container);
303
0
            goto end;
304
0
        }
305
0
        container->destroy(container);
306
307
0
        if (!parse_safe_contents(this, parser->get_level(parser)+1,
308
0
                     data))
309
0
        {
310
0
          chunk_clear(&data);
311
0
          goto end;
312
0
        }
313
0
        chunk_clear(&data);
314
0
        break;
315
0
      }
316
0
    }
317
0
  }
318
0
  success = parser->success(parser);
319
0
end:
320
0
  parser->destroy(parser);
321
0
  return success;
322
0
}
323
324
/**
325
 * Verify the given MAC using the given password.
326
 */
327
static bool verify_mac_pw(signer_t *signer, hash_algorithm_t hash, chunk_t salt,
328
              uint64_t iterations, chunk_t data, chunk_t mac,
329
              chunk_t pw)
330
0
{
331
0
  chunk_t key, calculated;
332
0
  bool success = FALSE;
333
334
0
  key = chunk_alloca(signer->get_key_size(signer));
335
0
  calculated = chunk_alloca(signer->get_block_size(signer));
336
337
0
  if (pkcs12_derive_key(hash, pw, salt, iterations, PKCS12_KEY_MAC, key) &&
338
0
    signer->set_key(signer, key) &&
339
0
    signer->get_signature(signer, data, calculated.ptr) &&
340
0
    chunk_equals_const(mac, calculated))
341
0
  {
342
0
    success = TRUE;
343
0
  }
344
0
  memwipe(key.ptr, key.len);
345
0
  return success;
346
0
}
347
348
/**
349
 * Verify the given MAC with available passwords.
350
 */
351
static bool verify_mac(hash_algorithm_t hash, chunk_t salt,
352
             uint64_t iterations, chunk_t data, chunk_t mac)
353
0
{
354
0
  enumerator_t *enumerator;
355
0
  shared_key_t *shared;
356
0
  signer_t *signer;
357
0
  bool success = FALSE;
358
359
0
  signer = lib->crypto->create_signer(lib->crypto,
360
0
                hasher_algorithm_to_integrity(hash, mac.len));
361
0
  if (!signer)
362
0
  {
363
0
    return FALSE;
364
0
  }
365
366
  /* try without and with an empty password, which is not the same thing */
367
0
  if (verify_mac_pw(signer, hash, salt, iterations, data, mac, chunk_empty) ||
368
0
    verify_mac_pw(signer, hash, salt, iterations, data, mac, chunk_from_str("")))
369
0
  {
370
0
    signer->destroy(signer);
371
0
    return TRUE;
372
0
  }
373
374
0
  enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
375
0
                    SHARED_PRIVATE_KEY_PASS, NULL, NULL);
376
0
  while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
377
0
  {
378
0
    if (verify_mac_pw(signer, hash, salt, iterations, data, mac,
379
0
              shared->get_key(shared)))
380
0
    {
381
0
      success = TRUE;
382
0
      break;
383
0
    }
384
0
  }
385
0
  enumerator->destroy(enumerator);
386
0
  signer->destroy(signer);
387
0
  return success;
388
0
}
389
390
/**
391
 * ASN.1 definition of digestInfo
392
 */
393
static const asn1Object_t digestInfoObjects[] = {
394
  { 0, "digestInfo",      ASN1_SEQUENCE,    ASN1_OBJ  }, /*  0 */
395
  { 1,   "digestAlgorithm", ASN1_EOC,     ASN1_RAW  }, /*  1 */
396
  { 1,   "digest",      ASN1_OCTET_STRING,  ASN1_BODY }, /*  2 */
397
  { 0, "exit",        ASN1_EOC,     ASN1_EXIT }
398
};
399
0
#define DIGEST_INFO_ALGORITHM   1
400
0
#define DIGEST_INFO_DIGEST      2
401
402
/**
403
 * Parse a digestInfo structure
404
 */
405
static bool parse_digest_info(chunk_t blob, int level0, hash_algorithm_t *hash,
406
                chunk_t *digest)
407
0
{
408
0
  asn1_parser_t *parser;
409
0
  chunk_t object;
410
0
  int objectID;
411
0
  bool success;
412
413
0
  parser = asn1_parser_create(digestInfoObjects, blob);
414
0
  parser->set_top_level(parser, level0);
415
416
0
  while (parser->iterate(parser, &objectID, &object))
417
0
  {
418
0
    switch (objectID)
419
420
0
    {
421
0
      case DIGEST_INFO_ALGORITHM:
422
0
      {
423
0
        int oid = asn1_parse_algorithmIdentifier(object,
424
0
                   parser->get_level(parser)+1, NULL);
425
426
0
        *hash = hasher_algorithm_from_oid(oid);
427
0
        break;
428
0
      }
429
0
      case DIGEST_INFO_DIGEST:
430
0
      {
431
0
        *digest = object;
432
0
        break;
433
0
      }
434
0
      default:
435
0
        break;
436
0
    }
437
0
  }
438
0
  success = parser->success(parser);
439
0
  parser->destroy(parser);
440
0
  return success;
441
0
}
442
443
/**
444
 * ASN.1 definition of a PFX structure
445
 */
446
static const asn1Object_t PFXObjects[] = {
447
  { 0, "PFX",       ASN1_SEQUENCE,    ASN1_NONE     }, /* 0 */
448
  { 1,   "version",   ASN1_INTEGER,   ASN1_BODY     }, /* 1 */
449
  { 1,   "authSafe",    ASN1_SEQUENCE,    ASN1_OBJ      }, /* 2 */
450
  { 1,   "macData",   ASN1_SEQUENCE,    ASN1_OPT|ASN1_BODY  }, /* 3 */
451
  { 2,     "mac",     ASN1_SEQUENCE,    ASN1_RAW      }, /* 4 */
452
  { 2,     "macSalt",   ASN1_OCTET_STRING,  ASN1_BODY     }, /* 5 */
453
  { 2,     "iterations",  ASN1_INTEGER,   ASN1_DEF|ASN1_BODY  }, /* 6 */
454
  { 1,   "end opt",   ASN1_EOC,     ASN1_END      }, /* 7 */
455
  { 0, "exit",      ASN1_EOC,     ASN1_EXIT     }
456
};
457
0
#define PFX_AUTH_SAFE 2
458
0
#define PFX_MAC     4
459
0
#define PFX_SALT    5
460
0
#define PFX_ITERATIONS  6
461
462
/**
463
 * Parse an ASN.1 encoded PFX structure
464
 */
465
static bool parse_PFX(private_pkcs12_t *this, chunk_t blob)
466
0
{
467
0
  asn1_parser_t *parser;
468
0
  int objectID;
469
0
  chunk_t object, auth_safe, digest = chunk_empty, salt = chunk_empty,
470
0
      data = chunk_empty;
471
0
  hash_algorithm_t hash = HASH_UNKNOWN;
472
0
  container_t *container = NULL;
473
0
  uint64_t iterations = 0;
474
0
  bool success = FALSE;
475
476
0
  parser = asn1_parser_create(PFXObjects, blob);
477
478
0
  while (parser->iterate(parser, &objectID, &object))
479
0
  {
480
0
    switch (objectID)
481
0
    {
482
0
      case PFX_AUTH_SAFE:
483
0
      {
484
0
        auth_safe = object;
485
0
        break;
486
0
      }
487
0
      case PFX_MAC:
488
0
      {
489
0
        if (!parse_digest_info(object, parser->get_level(parser)+1,
490
0
                     &hash, &digest))
491
0
        {
492
0
          goto end_parse;
493
0
        }
494
0
        break;
495
0
      }
496
0
      case PFX_SALT:
497
0
      {
498
0
        salt = object;
499
0
        break;
500
0
      }
501
0
      case PFX_ITERATIONS:
502
0
      {
503
0
        iterations = object.len ? asn1_parse_integer_uint64(object) : 1;
504
0
        break;
505
0
      }
506
0
    }
507
0
  }
508
0
  success = parser->success(parser);
509
510
0
end_parse:
511
0
  parser->destroy(parser);
512
0
  if (!success)
513
0
  {
514
0
    return FALSE;
515
0
  }
516
517
0
  success = FALSE;
518
0
  DBG2(DBG_ASN, "-- > --");
519
0
  container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
520
0
                   BUILD_BLOB_ASN1_DER, auth_safe, BUILD_END);
521
0
  if (container && container->get_data(container, &data))
522
0
  {
523
0
    if (hash != HASH_UNKNOWN)
524
0
    {
525
0
      if (container->get_type(container) != CONTAINER_PKCS7_DATA)
526
0
      {
527
0
        goto end;
528
0
      }
529
0
      if (!verify_mac(hash, salt, iterations, data, digest))
530
0
      {
531
0
        DBG1(DBG_ASN, "  MAC verification of PKCS#12 container failed");
532
0
        goto end;
533
0
      }
534
0
    }
535
0
    else
536
0
    {
537
0
      enumerator_t *enumerator;
538
0
      auth_cfg_t *auth;
539
540
0
      if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
541
0
      {
542
0
        goto end;
543
0
      }
544
0
      enumerator = container->create_signature_enumerator(container);
545
0
      if (!enumerator->enumerate(enumerator, &auth))
546
0
      {
547
0
        DBG1(DBG_ASN, "  signature verification of PKCS#12 container "
548
0
           "failed");
549
0
        enumerator->destroy(enumerator);
550
0
        goto end;
551
0
      }
552
0
      enumerator->destroy(enumerator);
553
0
    }
554
0
    success = parse_authenticated_safe(this, data);
555
0
  }
556
0
end:
557
0
  DBG2(DBG_ASN, "-- < --");
558
0
  DESTROY_IF(container);
559
0
  chunk_free(&data);
560
0
  return success;
561
0
}
562
563
/**
564
 * See header.
565
 */
566
pkcs12_t *pkcs12_decode(container_type_t type, va_list args)
567
0
{
568
0
  private_pkcs12_t *this;
569
0
  chunk_t blob = chunk_empty;
570
571
0
  while (TRUE)
572
0
  {
573
0
    switch (va_arg(args, builder_part_t))
574
0
    {
575
0
      case BUILD_BLOB_ASN1_DER:
576
0
        blob = va_arg(args, chunk_t);
577
0
        continue;
578
0
      case BUILD_END:
579
0
        break;
580
0
      default:
581
0
        return NULL;
582
0
    }
583
0
    break;
584
0
  }
585
0
  if (blob.len)
586
0
  {
587
0
    if (blob.len >= 2 &&
588
0
      blob.ptr[0] == ASN1_SEQUENCE && blob.ptr[1] == 0x80)
589
0
    { /* looks like infinite length BER encoding, but we can't handle it.
590
       */
591
0
      return NULL;
592
0
    }
593
0
    this = pkcs12_create();
594
0
    if (parse_PFX(this, blob))
595
0
    {
596
0
      return &this->public;
597
0
    }
598
0
    destroy(this);
599
0
  }
600
0
  return NULL;
601
0
}