]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/plugins/pgp/pgp_cert.c
Add a return value to hasher_t.allocate_hash()
[thirdparty/strongswan.git] / src / libstrongswan / plugins / pgp / pgp_cert.c
CommitLineData
4cb0e1bb
MW
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
23typedef struct private_pgp_cert_t private_pgp_cert_t;
24
25/**
26 * Private data of an pgp_cert_t object.
27 */
28struct 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
ab5762e3
AS
60 /**
61 * v3 or v4 fingerprint of the PGP public key
62 */
63 chunk_t fingerprint;
64
4cb0e1bb
MW
65 /**
66 * full PGP encoding
67 */
68 chunk_t encoding;
69
70 /**
71 * reference counter
72 */
73 refcount_t ref;
74};
75
76
4a679468
AS
77METHOD(certificate_t, get_type, certificate_type_t,
78 private_pgp_cert_t *this)
4cb0e1bb
MW
79{
80 return CERT_GPG;
81}
82
4a679468
AS
83METHOD(certificate_t, get_subject,identification_t*,
84 private_pgp_cert_t *this)
4cb0e1bb
MW
85{
86 return this->user_id;
87}
88
4a679468
AS
89METHOD(certificate_t, get_issuer, identification_t*,
90 private_pgp_cert_t *this)
4cb0e1bb
MW
91{
92 return this->user_id;
93}
94
4a679468
AS
95METHOD(certificate_t, has_subject, id_match_t,
96 private_pgp_cert_t *this, identification_t *subject)
4cb0e1bb 97{
262af161
AS
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;
4cb0e1bb
MW
108}
109
4a679468
AS
110METHOD(certificate_t, has_issuer, id_match_t,
111 private_pgp_cert_t *this, identification_t *issuer)
4cb0e1bb
MW
112{
113 return ID_MATCH_NONE;
114}
115
4a679468 116METHOD(certificate_t, issued_by,bool,
a37f2d20 117 private_pgp_cert_t *this, certificate_t *issuer, signature_scheme_t *scheme)
4cb0e1bb
MW
118{
119 /* TODO: check signature blobs for a valid signature */
120 return FALSE;
121}
122
4a679468
AS
123METHOD(certificate_t, get_public_key, public_key_t*,
124 private_pgp_cert_t *this)
4cb0e1bb
MW
125{
126 this->key->get_ref(this->key);
127 return this->key;
128}
129
4a679468
AS
130METHOD(certificate_t, get_ref, certificate_t*,
131 private_pgp_cert_t *this)
4cb0e1bb
MW
132{
133 ref_get(&this->ref);
4a679468 134 return &this->public.interface.interface;
4cb0e1bb
MW
135}
136
4a679468
AS
137METHOD(certificate_t, get_validity, bool,
138 private_pgp_cert_t *this, time_t *when, time_t *not_before,
139 time_t *not_after)
4cb0e1bb
MW
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 {
9f0327e6
AS
161 /* Jan 19 03:14:07 UTC 2038 */
162 until = TIME_32_BIT_SIGNED_MAX;
4cb0e1bb
MW
163 }
164 if (not_after)
165 {
166 *not_after = until;
167 }
168 return (t >= this->valid && t <= until);
169}
170
4a679468
AS
171METHOD(certificate_t, get_encoding, bool,
172 private_pgp_cert_t *this, cred_encoding_type_t type, chunk_t *encoding)
4cb0e1bb 173{
0406eeaa
MW
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);
4cb0e1bb
MW
181}
182
4a679468
AS
183METHOD(certificate_t, equals, bool,
184 private_pgp_cert_t *this, certificate_t *other)
4cb0e1bb
MW
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 }
0406eeaa
MW
201 if (!other->get_encoding(other, CERT_PGP_PKT, &encoding))
202 {
203 return FALSE;
204 }
4cb0e1bb
MW
205 equal = chunk_equals(this->encoding, encoding);
206 free(encoding.ptr);
207 return equal;
208}
209
4a679468
AS
210METHOD(certificate_t, destroy, void,
211 private_pgp_cert_t *this)
4cb0e1bb
MW
212{
213 if (ref_put(&this->ref))
214 {
215 DESTROY_IF(this->key);
216 DESTROY_IF(this->user_id);
ab5762e3 217 free(this->fingerprint.ptr);
4cb0e1bb
MW
218 free(this->encoding.ptr);
219 free(this);
220 }
221}
222
4a679468
AS
223METHOD(pgp_certificate_t, get_fingerprint, chunk_t,
224 private_pgp_cert_t *this)
ab5762e3
AS
225{
226 return this->fingerprint;
227}
228
4cb0e1bb
MW
229/**
230 * See header
231 */
232private_pgp_cert_t *create_empty()
233{
4a679468
AS
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 );
4cb0e1bb
MW
258
259 return this;
260}
261
262/**
263 * Parse the public key packet of a PGP certificate
264 */
265static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet)
266{
ab5762e3
AS
267 chunk_t pubkey_packet = packet;
268
4cb0e1bb
MW
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:
cc4b48e8 289 DBG1(DBG_ASN, "PGP packet version V%d not supported",
8b0e0910 290 this->version);
4cb0e1bb
MW
291 return FALSE;
292 }
bb7ae382
AS
293 if (this->valid)
294 {
cc4b48e8 295 DBG2(DBG_ASN, "L2 - created %T, valid %d days", &this->created, FALSE,
8b0e0910 296 this->valid);
bb7ae382
AS
297 }
298 else
299 {
cc4b48e8 300 DBG2(DBG_ASN, "L2 - created %T, never expires", &this->created, FALSE);
bb7ae382 301 }
4cb0e1bb
MW
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);
ab5762e3
AS
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 {
cc4b48e8 321 DBG1(DBG_ASN, "no SHA-1 hasher available");
ab5762e3
AS
322 return FALSE;
323 }
87dd205b
MW
324 if (!hasher->allocate_hash(hasher, pubkey_packet_header, NULL) ||
325 !hasher->allocate_hash(hasher, pubkey_packet, &this->fingerprint))
326 {
327 hasher->destroy(hasher);
328 return FALSE;
329 }
ab5762e3 330 hasher->destroy(hasher);
cc4b48e8 331 DBG2(DBG_ASN, "L2 - v4 fingerprint %#B", &this->fingerprint);
ab5762e3
AS
332 }
333 else
334 {
335 /* V3 fingerprint is computed by public_key_t class */
da9724e6 336 if (!this->key->get_fingerprint(this->key, KEYID_PGPV3,
ab5762e3
AS
337 &this->fingerprint))
338 {
339 return FALSE;
340 }
341 this->fingerprint = chunk_clone(this->fingerprint);
cc4b48e8 342 DBG2(DBG_ASN, "L2 - v3 fingerprint %#B", &this->fingerprint);
ab5762e3
AS
343 }
344 return TRUE;
4cb0e1bb
MW
345}
346
347/**
348 * Parse the signature packet of a PGP certificate
349 */
350static bool parse_signature(private_pgp_cert_t *this, chunk_t packet)
351{
352 u_int32_t version, len, type, created;
353
354 if (!pgp_read_scalar(&packet, 1, &version))
355 {
356 return FALSE;
357 }
bb7ae382
AS
358
359 /* we parse only v3 or v4 signature packets */
360 if (version != 3 && version != 4)
4cb0e1bb 361 {
cc4b48e8 362 DBG2(DBG_ASN, "L2 - v%d signature ignored", version);
4cb0e1bb
MW
363 return TRUE;
364 }
bb7ae382 365 if (version == 4)
4cb0e1bb 366 {
bb7ae382
AS
367 if (!pgp_read_scalar(&packet, 1, &type))
368 {
369 return FALSE;
370 }
cc4b48e8 371 DBG2(DBG_ASN, "L2 - v%d signature of type 0x%02x", version, type);
4cb0e1bb 372 }
bb7ae382 373 else
4cb0e1bb 374 {
bb7ae382
AS
375 if (!pgp_read_scalar(&packet, 1, &len) || len != 5)
376 {
377 return FALSE;
378 }
379 if (!pgp_read_scalar(&packet, 1, &type) ||
380 !pgp_read_scalar(&packet, 4, &created))
381 {
382 return FALSE;
383 }
cc4b48e8 384 DBG2(DBG_ASN, "L2 - v3 signature of type 0x%02x, created %T", type,
8b0e0910 385 &created, FALSE);
4cb0e1bb
MW
386 }
387 /* TODO: parse and save signature to a list */
388 return TRUE;
389}
390
391/**
392 * Parse the userid packet of a PGP certificate
393 */
394static bool parse_user_id(private_pgp_cert_t *this, chunk_t packet)
395{
396 DESTROY_IF(this->user_id);
9f45b19f 397 this->user_id = identification_create_from_encoding(ID_KEY_ID, packet);
cc4b48e8 398 DBG2(DBG_ASN, "L2 - '%Y'", this->user_id);
4cb0e1bb
MW
399 return TRUE;
400}
401
402/**
403 * See header.
404 */
79c6f162 405pgp_cert_t *pgp_cert_load(certificate_type_t type, va_list args)
4cb0e1bb
MW
406{
407 chunk_t packet, blob = chunk_empty;
408 pgp_packet_tag_t tag;
409 private_pgp_cert_t *this;
410
411 while (TRUE)
412 {
413 switch (va_arg(args, builder_part_t))
414 {
415 case BUILD_BLOB_PGP:
416 blob = va_arg(args, chunk_t);
417 continue;
418 case BUILD_END:
419 break;
420 default:
421 return NULL;
422 }
423 break;
424 }
425
426 this = create_empty();
427 this->encoding = chunk_clone(blob);
428 while (blob.len)
429 {
430 if (!pgp_read_packet(&blob, &packet, &tag))
431 {
432 destroy(this);
433 return NULL;
434 }
435 switch (tag)
436 {
437 case PGP_PKT_PUBLIC_KEY:
438 if (!parse_public_key(this, packet))
439 {
440 destroy(this);
441 return NULL;
442 }
443 break;
444 case PGP_PKT_SIGNATURE:
445 if (!parse_signature(this, packet))
446 {
447 destroy(this);
79d5c4f0 448 return NULL;
4cb0e1bb
MW
449 }
450 break;
451 case PGP_PKT_USER_ID:
452 if (!parse_user_id(this, packet))
453 {
454 destroy(this);
79d5c4f0 455 return NULL;
4cb0e1bb
MW
456 }
457 break;
458 default:
8b0e0910 459 DBG1(DBG_LIB, "ignoring %N packet in PGP certificate",
4cb0e1bb
MW
460 pgp_packet_tag_names, tag);
461 break;
462 }
463 }
464 if (this->key)
465 {
466 return &this->public;
467 }
468 destroy(this);
469 return NULL;
470}
471