Coverage Report

Created: 2026-10-10 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/plugins/pgp/pgp_cert.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2009 Martin Willi
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 "pgp_cert.h"
18
#include "pgp_utils.h"
19
20
#include <time.h>
21
22
#include <utils/debug.h>
23
24
typedef struct private_pgp_cert_t private_pgp_cert_t;
25
26
/**
27
 * Private data of an pgp_cert_t object.
28
 */
29
struct private_pgp_cert_t {
30
31
  /**
32
   * Implements pgp_cert_t interface.
33
   */
34
  pgp_cert_t public;
35
36
  /**
37
   * Public key of the certificate
38
   */
39
  public_key_t *key;
40
41
  /**
42
   * version of the public key
43
   */
44
  uint32_t version;
45
46
  /**
47
   * creation time
48
   */
49
  uint32_t created;
50
51
  /**
52
   * days the certificate is valid
53
   */
54
  uint32_t valid;
55
56
  /**
57
   * userid of the certificate
58
   */
59
  identification_t *user_id;
60
61
  /**
62
   * v3 or v4 fingerprint of the PGP public key
63
   */
64
  chunk_t fingerprint;
65
66
  /**
67
   * full PGP encoding
68
   */
69
  chunk_t encoding;
70
71
  /**
72
   * reference counter
73
   */
74
  refcount_t ref;
75
};
76
77
78
METHOD(certificate_t, get_type, certificate_type_t,
79
  private_pgp_cert_t *this)
80
0
{
81
0
  return CERT_GPG;
82
0
}
83
84
METHOD(certificate_t, get_subject,identification_t*,
85
  private_pgp_cert_t *this)
86
0
{
87
0
  return this->user_id;
88
0
}
89
90
METHOD(certificate_t, get_issuer, identification_t*,
91
  private_pgp_cert_t *this)
92
0
{
93
0
  return this->user_id;
94
0
}
95
96
METHOD(certificate_t, has_subject, id_match_t,
97
  private_pgp_cert_t *this, identification_t *subject)
98
0
{
99
0
  id_match_t match_user_id;
100
101
0
  match_user_id = this->user_id->matches(this->user_id, subject);
102
0
  if (match_user_id == ID_MATCH_NONE &&
103
0
    subject->get_type(subject) == ID_KEY_ID &&
104
0
        chunk_equals(this->fingerprint, subject->get_encoding(subject)))
105
0
  {
106
0
    return ID_MATCH_PERFECT;
107
0
  }
108
0
  return match_user_id;
109
0
}
110
111
METHOD(certificate_t, has_issuer, id_match_t,
112
  private_pgp_cert_t *this, identification_t *issuer)
113
0
{
114
0
  return ID_MATCH_NONE;
115
0
}
116
117
METHOD(certificate_t, issued_by,bool,
118
  private_pgp_cert_t *this, certificate_t *issuer, signature_params_t **scheme)
119
0
{
120
  /* TODO: check signature blobs for a valid signature */
121
0
  return FALSE;
122
0
}
123
124
METHOD(certificate_t, get_public_key, public_key_t*,
125
  private_pgp_cert_t *this)
126
0
{
127
0
  this->key->get_ref(this->key);
128
0
  return this->key;
129
0
}
130
131
METHOD(certificate_t, get_ref, certificate_t*,
132
  private_pgp_cert_t *this)
133
0
{
134
0
  ref_get(&this->ref);
135
0
  return &this->public.interface.interface;
136
0
}
137
138
METHOD(certificate_t, get_validity, bool,
139
  private_pgp_cert_t *this, time_t *when, time_t *not_before,
140
  time_t *not_after)
141
0
{
142
0
  time_t t, until;
143
0
  uint64_t expiry = 0;
144
145
0
  if (when)
146
0
  {
147
0
    t = *when;
148
0
  }
149
0
  else
150
0
  {
151
0
    t = time(NULL);
152
0
  }
153
0
  if (not_before)
154
0
  {
155
0
    *not_before = this->created;
156
0
  }
157
0
  if (this->valid)
158
0
  {
159
0
    expiry = this->created + this->valid * 24 * 60 * 60;
160
0
  }
161
0
  if (!expiry || (sizeof(time_t) == 4 && expiry > TIME_32_BIT_SIGNED_MAX))
162
0
  {
163
    /* Jan 19 03:14:07 UTC 2038 */
164
0
    until = TIME_32_BIT_SIGNED_MAX;
165
0
  }
166
0
  else
167
0
  {
168
0
    until = expiry;
169
0
  }
170
0
  if (not_after)
171
0
  {
172
0
    *not_after = until;
173
0
  }
174
0
  return (t >= this->created && t <= until);
175
0
}
176
177
METHOD(certificate_t, get_encoding, bool,
178
  private_pgp_cert_t *this, cred_encoding_type_t type, chunk_t *encoding)
179
0
{
180
0
  if (type == CERT_PGP_PKT)
181
0
  {
182
0
    *encoding = chunk_clone(this->encoding);
183
0
    return TRUE;
184
0
  }
185
0
  return lib->encoding->encode(lib->encoding, type, NULL, encoding,
186
0
            CRED_PART_PGP_CERT, this->encoding, CRED_PART_END);
187
0
}
188
189
METHOD(certificate_t, equals, bool,
190
  private_pgp_cert_t *this, certificate_t *other)
191
0
{
192
0
  chunk_t encoding;
193
0
  bool equal;
194
195
0
  if (this == (private_pgp_cert_t*)other)
196
0
  {
197
0
    return TRUE;
198
0
  }
199
0
  if (other->get_type(other) != CERT_X509)
200
0
  {
201
0
    return FALSE;
202
0
  }
203
0
  if (other->equals == (void*)equals)
204
0
  { /* skip allocation if we have the same implementation */
205
0
    return chunk_equals(this->encoding, ((private_pgp_cert_t*)other)->encoding);
206
0
  }
207
0
  if (!other->get_encoding(other, CERT_PGP_PKT, &encoding))
208
0
  {
209
0
    return FALSE;
210
0
  }
211
0
  equal = chunk_equals(this->encoding, encoding);
212
0
  free(encoding.ptr);
213
0
  return equal;
214
0
}
215
216
METHOD(certificate_t, destroy, void,
217
  private_pgp_cert_t *this)
218
0
{
219
0
  if (ref_put(&this->ref))
220
0
  {
221
0
    DESTROY_IF(this->key);
222
0
    DESTROY_IF(this->user_id);
223
0
    free(this->fingerprint.ptr);
224
0
    free(this->encoding.ptr);
225
0
    free(this);
226
0
  }
227
0
}
228
229
METHOD(pgp_certificate_t, get_fingerprint, chunk_t,
230
  private_pgp_cert_t *this)
231
0
{
232
0
  return this->fingerprint;
233
0
}
234
235
/**
236
 * See header
237
 */
238
private_pgp_cert_t *create_empty()
239
0
{
240
0
  private_pgp_cert_t *this;
241
242
0
  INIT(this,
243
0
    .public = {
244
0
      .interface = {
245
0
        .interface = {
246
0
          .get_type = _get_type,
247
0
          .get_subject = _get_subject,
248
0
          .get_issuer = _get_issuer,
249
0
          .has_subject = _has_subject,
250
0
          .has_issuer = _has_issuer,
251
0
          .issued_by = _issued_by,
252
0
          .get_public_key = _get_public_key,
253
0
          .get_validity = _get_validity,
254
0
          .get_encoding = _get_encoding,
255
0
          .equals = _equals,
256
0
          .get_ref = _get_ref,
257
0
          .destroy = _destroy,
258
0
        },
259
0
        .get_fingerprint = _get_fingerprint,
260
0
      },
261
0
    },
262
0
    .ref = 1,
263
0
  );
264
265
0
  return this;
266
0
}
267
268
/**
269
 * Parse the public key packet of a PGP certificate
270
 */
271
static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet)
272
0
{
273
0
  chunk_t pubkey_packet = packet;
274
275
0
  if (!pgp_read_scalar(&packet, 1, &this->version))
276
0
  {
277
0
    return FALSE;
278
0
  }
279
0
  switch (this->version)
280
0
  {
281
0
    case 3:
282
0
      if (!pgp_read_scalar(&packet, 4, &this->created) ||
283
0
        !pgp_read_scalar(&packet, 2, &this->valid))
284
0
      {
285
0
        return FALSE;
286
0
      }
287
0
      break;
288
0
    case 4:
289
0
      if (!pgp_read_scalar(&packet, 4, &this->created))
290
0
      {
291
0
        return FALSE;
292
0
      }
293
0
      break;
294
0
    default:
295
0
      DBG1(DBG_ASN, "PGP packet version V%d not supported",
296
0
         this->version);
297
0
      return FALSE;
298
0
  }
299
0
  if (this->valid)
300
0
  {
301
0
    DBG2(DBG_ASN, "L2 - created %T, valid %d days", &this->created, FALSE,
302
0
       this->valid);
303
0
  }
304
0
  else
305
0
  {
306
0
    DBG2(DBG_ASN, "L2 - created %T, never expires", &this->created, FALSE);
307
0
  }
308
0
  DESTROY_IF(this->key);
309
0
  this->key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
310
0
                  BUILD_BLOB_PGP, packet, BUILD_END);
311
0
  if (this->key == NULL)
312
0
  {
313
0
    return FALSE;
314
0
  }
315
316
  /* compute V4 or V3 fingerprint according to section 12.2 of RFC 4880 */
317
0
  if (this->version == 4)
318
0
  {
319
0
    chunk_t pubkey_packet_header = chunk_from_chars(
320
0
          0x99, pubkey_packet.len / 256, pubkey_packet.len % 256
321
0
        );
322
0
    hasher_t *hasher;
323
324
0
    hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
325
0
    if (hasher == NULL)
326
0
    {
327
0
      DBG1(DBG_ASN, "no SHA-1 hasher available");
328
0
      return FALSE;
329
0
    }
330
0
    if (!hasher->allocate_hash(hasher, pubkey_packet_header, NULL) ||
331
0
      !hasher->allocate_hash(hasher, pubkey_packet, &this->fingerprint))
332
0
    {
333
0
      hasher->destroy(hasher);
334
0
      return FALSE;
335
0
    }
336
0
    hasher->destroy(hasher);
337
0
    DBG2(DBG_ASN, "L2 - v4 fingerprint %#B", &this->fingerprint);
338
0
  }
339
0
  else
340
0
  {
341
    /* V3 fingerprint is computed by public_key_t class */
342
0
    if (!this->key->get_fingerprint(this->key, KEYID_PGPV3,
343
0
                    &this->fingerprint))
344
0
    {
345
0
      return FALSE;
346
0
    }
347
0
    this->fingerprint = chunk_clone(this->fingerprint);
348
0
    DBG2(DBG_ASN, "L2 - v3 fingerprint %#B", &this->fingerprint);
349
0
  }
350
0
  return TRUE;
351
0
}
352
353
/**
354
 * Parse the signature packet of a PGP certificate
355
 */
356
static bool parse_signature(private_pgp_cert_t *this, chunk_t packet)
357
0
{
358
0
  uint32_t version, len, type, created;
359
360
0
  if (!pgp_read_scalar(&packet, 1, &version))
361
0
  {
362
0
    return FALSE;
363
0
  }
364
365
  /* we parse only v3 or v4 signature packets */
366
0
  if (version != 3 && version != 4)
367
0
  {
368
0
    DBG2(DBG_ASN, "L2 - v%d signature ignored", version);
369
0
    return TRUE;
370
0
  }
371
0
  if (version == 4)
372
0
  {
373
0
    if (!pgp_read_scalar(&packet, 1, &type))
374
0
    {
375
0
      return FALSE;
376
0
    }
377
0
    DBG2(DBG_ASN, "L2 - v%d signature of type 0x%02x", version, type);
378
0
  }
379
0
  else
380
0
  {
381
0
    if (!pgp_read_scalar(&packet, 1, &len) || len != 5)
382
0
    {
383
0
      return FALSE;
384
0
    }
385
0
    if (!pgp_read_scalar(&packet, 1, &type) ||
386
0
      !pgp_read_scalar(&packet, 4, &created))
387
0
    {
388
0
      return FALSE;
389
0
    }
390
0
    DBG2(DBG_ASN, "L2 - v3 signature of type 0x%02x, created %T", type,
391
0
       &created, FALSE);
392
0
  }
393
  /* TODO: parse and save signature to a list */
394
0
  return TRUE;
395
0
}
396
397
/**
398
 * Parse the userid packet of a PGP certificate
399
 */
400
static bool parse_user_id(private_pgp_cert_t *this, chunk_t packet)
401
0
{
402
0
  DESTROY_IF(this->user_id);
403
0
  this->user_id = identification_create_from_encoding(ID_KEY_ID, packet);
404
0
  DBG2(DBG_ASN, "L2 - '%Y'", this->user_id);
405
0
  return TRUE;
406
0
}
407
408
/**
409
 * See header.
410
 */
411
pgp_cert_t *pgp_cert_load(certificate_type_t type, va_list args)
412
0
{
413
0
  chunk_t packet, blob = chunk_empty;
414
0
  pgp_packet_tag_t tag;
415
0
  private_pgp_cert_t *this;
416
417
0
  while (TRUE)
418
0
  {
419
0
    switch (va_arg(args, builder_part_t))
420
0
    {
421
0
      case BUILD_BLOB_PGP:
422
0
        blob = va_arg(args, chunk_t);
423
0
        continue;
424
0
      case BUILD_END:
425
0
        break;
426
0
      default:
427
0
        return NULL;
428
0
    }
429
0
    break;
430
0
  }
431
432
0
  this = create_empty();
433
0
  this->encoding = chunk_clone(blob);
434
0
  while (blob.len)
435
0
  {
436
0
    if (!pgp_read_packet(&blob, &packet, &tag))
437
0
    {
438
0
      destroy(this);
439
0
      return NULL;
440
0
    }
441
0
    switch (tag)
442
0
    {
443
0
      case PGP_PKT_PUBLIC_KEY:
444
0
        if (!parse_public_key(this, packet))
445
0
        {
446
0
          destroy(this);
447
0
          return NULL;
448
0
        }
449
0
        break;
450
0
      case PGP_PKT_SIGNATURE:
451
0
        if (!parse_signature(this, packet))
452
0
        {
453
0
          destroy(this);
454
0
          return NULL;
455
0
        }
456
0
        break;
457
0
      case PGP_PKT_USER_ID:
458
0
        if (!parse_user_id(this, packet))
459
0
        {
460
0
          destroy(this);
461
0
          return NULL;
462
0
        }
463
0
        break;
464
0
      default:
465
0
        DBG1(DBG_LIB, "ignoring %N packet in PGP certificate",
466
0
           pgp_packet_tag_names, tag);
467
0
        break;
468
0
    }
469
0
  }
470
0
  if (this->key)
471
0
  {
472
0
    return &this->public;
473
0
  }
474
0
  destroy(this);
475
  return NULL;
476
0
}
477