Added OpenPGP library to lib/silcpgp
[crypto.git] / lib / silcpgp / silcpgp_pubkey.c
1 /*
2
3   silcpgp_pubkey.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 - 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silccrypto.h"
21 #include "rsa.h"
22 #include "dsa.h"
23
24 /************************ Static utility functions **************************/
25
26 /* Computes fingerprint of the OpenPGP public key and saves it to the key.
27    Saves also the key IDs to public key context. */
28
29 static SilcBool silc_pgp_compute_fingerprint(SilcBuffer keybuf,
30                                              SilcPGPPublicKey pubkey)
31 {
32   SILC_LOG_DEBUG(("Computing fingerprint"));
33
34   if (pubkey->version >= 4) {
35     /* Version 4 */
36     SilcHash sha1;
37     unsigned char tmp[3];
38
39     if (!silc_hash_alloc("sha1", &sha1))
40       return FALSE;
41
42     tmp[0] = 0x99;
43     SILC_PUT16_MSB(silc_buffer_len(keybuf), tmp + 1);
44
45     silc_hash_init(sha1);
46     silc_hash_update(sha1, tmp, 3);
47     silc_hash_update(sha1, silc_buffer_data(keybuf), silc_buffer_len(keybuf));
48     silc_hash_final(sha1, pubkey->fingerprint);
49     silc_hash_free(sha1);
50
51     /* Save key ID */
52     memcpy(pubkey->key_id, pubkey->fingerprint + 12, 8);
53   } else {
54     /* Versions 2 and 3 */
55     SilcHash md5;
56     unsigned char *n, *e;
57     SilcUInt16 n_len, e_len;
58
59     if (!silc_hash_alloc("md5", &md5))
60       return FALSE;
61
62     silc_buffer_format(keybuf,
63                        SILC_STR_OFFSET(8),
64                        SILC_STR_UI16_NSTRING(&n, &n_len),
65                        SILC_STR_UI16_NSTRING(&e, &e_len),
66                        SILC_STR_END);
67
68     n_len = (n_len + 7) / 8;
69     e_len = (e_len + 7) / 8;
70
71     silc_hash_init(md5);
72     silc_hash_update(md5, n, n_len);
73     silc_hash_update(md5, e, e_len);
74     silc_hash_final(md5, pubkey->fingerprint);
75     silc_hash_free(md5);
76
77     /* Save key ID */
78     memcpy(pubkey->key_id, n + (n_len - 8), 8);
79   }
80
81   return TRUE;
82 }
83
84 /*************************** Public Key Routines ****************************/
85
86 /* Decode OpenPGP Public Key packet */
87
88 int silc_pgp_packet_public_key_decode(unsigned char *key, SilcUInt32 key_len,
89                                       SilcPGPPublicKey pubkey)
90 {
91   SilcBufferStruct keybuf, fbuf;
92   const SilcPKCSAlgorithm *pkcs;
93   int ret;
94
95   SILC_LOG_DEBUG(("Parse OpenPGP public key packet"));
96
97   if (!key || !key_len)
98     return 0;
99   silc_buffer_set(&keybuf, key, key_len);
100
101   SILC_LOG_HEXDUMP(("OpenPGP public key"), key, key_len);
102
103   /* Decode the key */
104   if (silc_buffer_unformat(&keybuf,
105                            SILC_STR_ADVANCE,
106                            SILC_STR_UINT8(&pubkey->version),
107                            SILC_STR_UI_INT(&pubkey->created),
108                            SILC_STR_END) < 0) {
109     SILC_LOG_DEBUG(("Malformed public key"));
110     goto err;
111   }
112
113   if (pubkey->version < 2) {
114     SILC_LOG_DEBUG(("Invalid version %d", pubkey->version));
115     goto err;
116   }
117
118   SILC_LOG_DEBUG(("Public key version %d", pubkey->version));
119
120   if (pubkey->version <= 3) {
121     /* Versions 2 and 3 */
122     if (silc_buffer_unformat(&keybuf,
123                              SILC_STR_ADVANCE,
124                              SILC_STR_UINT16(&pubkey->valid),
125                              SILC_STR_UINT8(&pubkey->algorithm),
126                              SILC_STR_END) < 0) {
127       SILC_LOG_DEBUG(("Malformed public key"));
128       goto err;
129     }
130   } else {
131     /* Version 4 */
132     if (silc_buffer_unformat(&keybuf,
133                              SILC_STR_ADVANCE,
134                              SILC_STR_UINT8(&pubkey->algorithm),
135                              SILC_STR_END) < 0) {
136       SILC_LOG_DEBUG(("Malformed public key"));
137       goto err;
138     }
139   }
140
141   SILC_LOG_DEBUG(("Parse algorithm %d", pubkey->algorithm));
142
143   /* Decode the public key algorithm */
144   switch (pubkey->algorithm) {
145   case SILC_PGP_PKCS_RSA:
146   case SILC_PGP_PKCS_RSA_ENC_ONLY:
147   case SILC_PGP_PKCS_RSA_SIG_ONLY:
148     /* Get PKCS object */
149     pkcs = silc_pkcs_find_algorithm("rsa", "openpgp");
150     if (!pkcs) {
151       SILC_LOG_ERROR(("Unsupported PKCS algorithm (rsa/openpgp)"));
152       goto err;
153     }
154     break;
155
156   case SILC_PGP_PKCS_DSA:
157     /* Get PKCS object */
158     pkcs = silc_pkcs_find_algorithm("dsa", "openpgp");
159     if (!pkcs) {
160       SILC_LOG_ERROR(("Unsupported PKCS algorithm (dsa/openpgp)"));
161       goto err;
162     }
163     break;
164
165   case SILC_PGP_PKCS_ELGAMAL_ENC_ONLY:
166   case SILC_PGP_PKCS_ELGAMAL:
167     /* Get PKCS object */
168     pkcs = silc_pkcs_find_algorithm("elgamal", "openpgp");
169     if (!pkcs) {
170       SILC_LOG_ERROR(("Unsupported PKCS algorithm (elgamal/openpgp)"));
171       goto err;
172     }
173     break;
174
175   default:
176     SILC_LOG_DEBUG(("Unsupported OpenPGP public key algorithm %d",
177                     pubkey->algorithm));
178     goto err;
179   }
180   pubkey->pkcs = pkcs;
181
182   /* Import the algorithm public key */
183   ret = pkcs->import_public_key(pkcs, silc_buffer_data(&keybuf),
184                                 silc_buffer_len(&keybuf),
185                                 &pubkey->public_key);
186   if (!ret) {
187     SILC_LOG_DEBUG(("Malformed public key"));
188     goto err;
189   }
190
191   /* Compute and save fingerprint */
192   silc_buffer_set(&fbuf, key, silc_buffer_headlen(&keybuf) + ret);
193   if (!silc_pgp_compute_fingerprint(&fbuf, pubkey))
194     goto err;
195
196   return silc_buffer_headlen(&keybuf) + ret;
197
198  err:
199   return 0;
200 }
201
202 /* Decode public key from PGP packets */
203
204 SilcBool silc_pgp_public_key_decode(SilcList *list,
205                                     SilcPGPPublicKey *ret_public_key)
206 {
207   SilcPGPPublicKey pubkey, subkey;
208   unsigned char *data;
209   SilcUInt32 data_len;
210   SilcPGPPacket pub, packet;
211
212   SILC_LOG_DEBUG(("Parse OpenPGP public key"));
213
214   pubkey = silc_calloc(1, sizeof(*pubkey));
215   if (!pubkey)
216     goto err;
217
218   /* First packet must be public key packet */
219   pub = silc_list_get(*list);
220   if (!pub)
221     goto err;
222   if (silc_pgp_packet_get_tag(pub) != SILC_PGP_PACKET_PUBKEY &&
223       silc_pgp_packet_get_tag(pub) != SILC_PGP_PACKET_PUBKEY_SUB)
224     goto err;
225
226   /* Parse the public key */
227   data = silc_pgp_packet_get_data(pub, &data_len);
228   if (!silc_pgp_packet_public_key_decode(data, data_len, pubkey))
229     goto err;
230
231   /* Parse any and all packets until we hit end of the packets or next
232      public key in the list.  We simply copy the raw data, and actual
233      parsing is done later if and when the packets are needed. */
234   if (silc_pgp_packet_get_tag(pub) == SILC_PGP_PACKET_PUBKEY) {
235     silc_list_init(pubkey->packets, struct SilcPGPPacketStruct, next);
236
237     /* Copy the raw public key packet */
238     packet = silc_pgp_packet_copy(pub);
239     if (packet)
240       silc_list_add(pubkey->packets, packet);
241
242     while ((packet = silc_list_get(*list))) {
243       SILC_LOG_DEBUG(("Adding %d (%s) packet to public key",
244                       silc_pgp_packet_get_tag(packet),
245                       silc_pgp_packet_name(silc_pgp_packet_get_tag(packet))));
246
247       switch (silc_pgp_packet_get_tag(packet)) {
248
249       case SILC_PGP_PACKET_PUBKEY:
250         /* Next public key, stop decoding.  Set list pointer so that the list
251            points to the next public key. */
252         list->current = packet;
253         break;
254
255       case SILC_PGP_PACKET_PUBKEY_SUB:
256         /* Parse subkeys recursively */
257         list->current = packet;
258         if (!silc_pgp_public_key_decode(list, &subkey))
259           goto err;
260
261         if (!pubkey->subkeys) {
262           pubkey->subkeys = silc_dlist_init();
263           if (!pubkey->subkeys)
264             goto err;
265         }
266         silc_dlist_add(pubkey->subkeys, subkey);
267
268       default:
269         /* Copy packet to the public key */
270         packet = silc_pgp_packet_copy(packet);
271         if (packet)
272           silc_list_add(pubkey->packets, packet);
273         break;
274       }
275     }
276   }
277
278   if (ret_public_key)
279     *ret_public_key = pubkey;
280
281   return TRUE;
282
283  err:
284   silc_free(pubkey);
285   return FALSE;
286 }
287
288 /* Free public key */
289
290 void silc_pgp_public_key_free(SilcPGPPublicKey public_key)
291 {
292   SilcPGPPublicKey p;
293   SilcPGPPacket packet;
294
295   if (public_key->pkcs)
296     public_key->pkcs->public_key_free(public_key->pkcs,
297                                       public_key->public_key);
298
299   if (public_key->subkeys) {
300     silc_dlist_start(public_key->subkeys);
301     while ((p = silc_dlist_get(public_key->subkeys)))
302       silc_pgp_public_key_free(p);
303     silc_dlist_uninit(public_key->subkeys);
304   }
305
306   silc_list_start(public_key->packets);
307   while ((packet = silc_list_get(public_key->packets)))
308     silc_pgp_packet_free(packet);
309
310   silc_free(public_key);
311 }