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