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