Coverage Report

Created: 2026-06-30 06:14

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
144
0
  if (when)
145
0
  {
146
0
    t = *when;
147
0
  }
148
0
  else
149
0
  {
150
0
    t = time(NULL);
151
0
  }
152
0
  if (not_before)
153
0
  {
154
0
    *not_before = this->created;
155
0
  }
156
0
  if (this->valid)
157
0
  {
158
0
    until = this->valid + this->created * 24 * 60 * 60;
159
0
  }
160
0
  else
161
0
  {
162
    /* Jan 19 03:14:07 UTC 2038 */
163
0
    until = TIME_32_BIT_SIGNED_MAX;
164
0
  }
165
0
  if (not_after)
166
0
  {
167
0
    *not_after = until;
168
0
  }
169
0
  return (t >= this->valid && t <= until);
170
0
}
171
172
METHOD(certificate_t, get_encoding, bool,
173
  private_pgp_cert_t *this, cred_encoding_type_t type, chunk_t *encoding)
174
0
{
175
0
  if (type == CERT_PGP_PKT)
176
0
  {
177
0
    *encoding = chunk_clone(this->encoding);
178
0
    return TRUE;
179
0
  }
180
0
  return lib->encoding->encode(lib->encoding, type, NULL, encoding,
181
0
            CRED_PART_PGP_CERT, this->encoding, CRED_PART_END);
182
0
}
183
184
METHOD(certificate_t, equals, bool,
185
  private_pgp_cert_t *this, certificate_t *other)
186
0
{
187
0
  chunk_t encoding;
188
0
  bool equal;
189
190
0
  if (this == (private_pgp_cert_t*)other)
191
0
  {
192
0
    return TRUE;
193
0
  }
194
0
  if (other->get_type(other) != CERT_X509)
195
0
  {
196
0
    return FALSE;
197
0
  }
198
0
  if (other->equals == (void*)equals)
199
0
  { /* skip allocation if we have the same implementation */
200
0
    return chunk_equals(this->encoding, ((private_pgp_cert_t*)other)->encoding);
201
0
  }
202
0
  if (!other->get_encoding(other, CERT_PGP_PKT, &encoding))
203
0
  {
204
0
    return FALSE;
205
0
  }
206
0
  equal = chunk_equals(this->encoding, encoding);
207
0
  free(encoding.ptr);
208
0
  return equal;
209
0
}
210
211
METHOD(certificate_t, destroy, void,
212
  private_pgp_cert_t *this)
213
0
{
214
0
  if (ref_put(&this->ref))
215
0
  {
216
0
    DESTROY_IF(this->key);
217
0
    DESTROY_IF(this->user_id);
218
0
    free(this->fingerprint.ptr);
219
0
    free(this->encoding.ptr);
220
0
    free(this);
221
0
  }
222
0
}
223
224
METHOD(pgp_certificate_t, get_fingerprint, chunk_t,
225
  private_pgp_cert_t *this)
226
0
{
227
0
  return this->fingerprint;
228
0
}
229
230
/**
231
 * See header
232
 */
233
private_pgp_cert_t *create_empty()
234
0
{
235
0
  private_pgp_cert_t *this;
236
237
0
  INIT(this,
238
0
    .public = {
239
0
      .interface = {
240
0
        .interface = {
241
0
          .get_type = _get_type,
242
0
          .get_subject = _get_subject,
243
0
          .get_issuer = _get_issuer,
244
0
          .has_subject = _has_subject,
245
0
          .has_issuer = _has_issuer,
246
0
          .issued_by = _issued_by,
247
0
          .get_public_key = _get_public_key,
248
0
          .get_validity = _get_validity,
249
0
          .get_encoding = _get_encoding,
250
0
          .equals = _equals,
251
0
          .get_ref = _get_ref,
252
0
          .destroy = _destroy,
253
0
        },
254
0
        .get_fingerprint = _get_fingerprint,
255
0
      },
256
0
    },
257
0
    .ref = 1,
258
0
  );
259
260
0
  return this;
261
0
}
262
263
/**
264
 * Parse the public key packet of a PGP certificate
265
 */
266
static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet)
267
0
{
268
0
  chunk_t pubkey_packet = packet;
269
270
0
  if (!pgp_read_scalar(&packet, 1, &this->version))
271
0
  {
272
0
    return FALSE;
273
0
  }
274
0
  switch (this->version)
275
0
  {
276
0
    case 3:
277
0
      if (!pgp_read_scalar(&packet, 4, &this->created) ||
278
0
        !pgp_read_scalar(&packet, 2, &this->valid))
279
0
      {
280
0
        return FALSE;
281
0
      }
282
0
      break;
283
0
    case 4:
284
0
      if (!pgp_read_scalar(&packet, 4, &this->created))
285
0
      {
286
0
        return FALSE;
287
0
      }
288
0
      break;
289
0
    default:
290
0
      DBG1(DBG_ASN, "PGP packet version V%d not supported",
291
0
         this->version);
292
0
      return FALSE;
293
0
  }
294
0
  if (this->valid)
295
0
  {
296
0
    DBG2(DBG_ASN, "L2 - created %T, valid %d days", &this->created, FALSE,
297
0
       this->valid);
298
0
  }
299
0
  else
300
0
  {
301
0
    DBG2(DBG_ASN, "L2 - created %T, never expires", &this->created, FALSE);
302
0
  }
303
0
  DESTROY_IF(this->key);
304
0
  this->key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
305
0
                  BUILD_BLOB_PGP, packet, BUILD_END);
306
0
  if (this->key == NULL)
307
0
  {
308
0
    return FALSE;
309
0
  }
310
311
  /* compute V4 or V3 fingerprint according to section 12.2 of RFC 4880 */
312
0
  if (this->version == 4)
313
0
  {
314
0
    chunk_t pubkey_packet_header = chunk_from_chars(
315
0
          0x99, pubkey_packet.len / 256, pubkey_packet.len % 256
316
0
        );
317
0
    hasher_t *hasher;
318
319
0
    hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
320
0
    if (hasher == NULL)
321
0
    {
322
0
      DBG1(DBG_ASN, "no SHA-1 hasher available");
323
0
      return FALSE;
324
0
    }
325
0
    if (!hasher->allocate_hash(hasher, pubkey_packet_header, NULL) ||
326
0
      !hasher->allocate_hash(hasher, pubkey_packet, &this->fingerprint))
327
0
    {
328
0
      hasher->destroy(hasher);
329
0
      return FALSE;
330
0
    }
331
0
    hasher->destroy(hasher);
332
0
    DBG2(DBG_ASN, "L2 - v4 fingerprint %#B", &this->fingerprint);
333
0
  }
334
0
  else
335
0
  {
336
    /* V3 fingerprint is computed by public_key_t class */
337
0
    if (!this->key->get_fingerprint(this->key, KEYID_PGPV3,
338
0
                    &this->fingerprint))
339
0
    {
340
0
      return FALSE;
341
0
    }
342
0
    this->fingerprint = chunk_clone(this->fingerprint);
343
0
    DBG2(DBG_ASN, "L2 - v3 fingerprint %#B", &this->fingerprint);
344
0
  }
345
0
  return TRUE;
346
0
}
347
348
/**
349
 * Parse the signature packet of a PGP certificate
350
 */
351
static bool parse_signature(private_pgp_cert_t *this, chunk_t packet)
352
0
{
353
0
  uint32_t version, len, type, created;
354
355
0
  if (!pgp_read_scalar(&packet, 1, &version))
356
0
  {
357
0
    return FALSE;
358
0
  }
359
360
  /* we parse only v3 or v4 signature packets */
361
0
  if (version != 3 && version != 4)
362
0
  {
363
0
    DBG2(DBG_ASN, "L2 - v%d signature ignored", version);
364
0
    return TRUE;
365
0
  }
366
0
  if (version == 4)
367
0
  {
368
0
    if (!pgp_read_scalar(&packet, 1, &type))
369
0
    {
370
0
      return FALSE;
371
0
    }
372
0
    DBG2(DBG_ASN, "L2 - v%d signature of type 0x%02x", version, type);
373
0
  }
374
0
  else
375
0
  {
376
0
    if (!pgp_read_scalar(&packet, 1, &len) || len != 5)
377
0
    {
378
0
      return FALSE;
379
0
    }
380
0
    if (!pgp_read_scalar(&packet, 1, &type) ||
381
0
      !pgp_read_scalar(&packet, 4, &created))
382
0
    {
383
0
      return FALSE;
384
0
    }
385
0
    DBG2(DBG_ASN, "L2 - v3 signature of type 0x%02x, created %T", type,
386
0
       &created, FALSE);
387
0
  }
388
  /* TODO: parse and save signature to a list */
389
0
  return TRUE;
390
0
}
391
392
/**
393
 * Parse the userid packet of a PGP certificate
394
 */
395
static bool parse_user_id(private_pgp_cert_t *this, chunk_t packet)
396
0
{
397
0
  DESTROY_IF(this->user_id);
398
0
  this->user_id = identification_create_from_encoding(ID_KEY_ID, packet);
399
0
  DBG2(DBG_ASN, "L2 - '%Y'", this->user_id);
400
0
  return TRUE;
401
0
}
402
403
/**
404
 * See header.
405
 */
406
pgp_cert_t *pgp_cert_load(certificate_type_t type, va_list args)
407
0
{
408
0
  chunk_t packet, blob = chunk_empty;
409
0
  pgp_packet_tag_t tag;
410
0
  private_pgp_cert_t *this;
411
412
0
  while (TRUE)
413
0
  {
414
0
    switch (va_arg(args, builder_part_t))
415
0
    {
416
0
      case BUILD_BLOB_PGP:
417
0
        blob = va_arg(args, chunk_t);
418
0
        continue;
419
0
      case BUILD_END:
420
0
        break;
421
0
      default:
422
0
        return NULL;
423
0
    }
424
0
    break;
425
0
  }
426
427
0
  this = create_empty();
428
0
  this->encoding = chunk_clone(blob);
429
0
  while (blob.len)
430
0
  {
431
0
    if (!pgp_read_packet(&blob, &packet, &tag))
432
0
    {
433
0
      destroy(this);
434
0
      return NULL;
435
0
    }
436
0
    switch (tag)
437
0
    {
438
0
      case PGP_PKT_PUBLIC_KEY:
439
0
        if (!parse_public_key(this, packet))
440
0
        {
441
0
          destroy(this);
442
0
          return NULL;
443
0
        }
444
0
        break;
445
0
      case PGP_PKT_SIGNATURE:
446
0
        if (!parse_signature(this, packet))
447
0
        {
448
0
          destroy(this);
449
0
          return NULL;
450
0
        }
451
0
        break;
452
0
      case PGP_PKT_USER_ID:
453
0
        if (!parse_user_id(this, packet))
454
0
        {
455
0
          destroy(this);
456
0
          return NULL;
457
0
        }
458
0
        break;
459
0
      default:
460
0
        DBG1(DBG_LIB, "ignoring %N packet in PGP certificate",
461
0
           pgp_packet_tag_names, tag);
462
0
        break;
463
0
    }
464
0
  }
465
0
  if (this->key)
466
0
  {
467
0
    return &this->public;
468
0
  }
469
0
  destroy(this);
470
  return NULL;
471
0
}
472