updates.
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcauth.h"
24
25 /******************************************************************************
26
27                            Authentication Payload
28
29 ******************************************************************************/
30
31 /* Authentication Payload structure */
32 struct SilcAuthPayloadStruct {
33   unsigned short len;
34   unsigned short auth_method;
35   unsigned short random_len;
36   unsigned char *random_data;
37   unsigned short auth_len;
38   unsigned char *auth_data;
39 };
40
41 /* Parses and returns Authentication Payload */
42
43 SilcAuthPayload silc_auth_payload_parse(SilcBuffer buffer)
44 {
45   SilcAuthPayload new;
46   int ret;
47
48   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
49
50   new = silc_calloc(1, sizeof(*new));
51
52   /* Parse the payload */
53   ret = silc_buffer_unformat(buffer, 
54                              SILC_STR_UI_SHORT(&new->len),
55                              SILC_STR_UI_SHORT(&new->auth_method),
56                              SILC_STR_UI16_NSTRING_ALLOC(&new->random_data,
57                                                          &new->random_len),
58                              SILC_STR_UI16_NSTRING_ALLOC(&new->auth_data,
59                                                          &new->auth_len),
60                              SILC_STR_END);
61   if (ret == -1) {
62     silc_free(new);
63     return NULL;
64   }
65
66   if (new->len != buffer->len) {
67     silc_auth_payload_free(new);
68     return NULL;
69   }
70
71   /* If password authentication, random data must not be set */
72   if (new->auth_method == SILC_AUTH_PASSWORD && new->random_len) {
73     silc_auth_payload_free(new);
74     return NULL;
75   }
76
77   return new;
78 }
79
80 /* Encodes authentication payload into buffer and returns it */
81
82 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
83                                     unsigned char *random_data,
84                                     unsigned short random_len,
85                                     unsigned char *auth_data,
86                                     unsigned short auth_len)
87 {
88   SilcBuffer buffer;
89   unsigned int len;
90
91   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
92
93   len = 4 + 4 + random_len + auth_len;
94   buffer = silc_buffer_alloc(len);
95   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
96   silc_buffer_format(buffer,
97                      SILC_STR_UI_SHORT(len),
98                      SILC_STR_UI_SHORT(method),
99                      SILC_STR_UI_SHORT(random_len),
100                      SILC_STR_UI_XNSTRING(random_data, random_len),
101                      SILC_STR_UI_SHORT(auth_len),
102                      SILC_STR_UI_XNSTRING(auth_data, auth_len),
103                      SILC_STR_END);
104
105   return buffer;
106 }
107
108 /* Frees authentication payload. */
109
110 void silc_auth_payload_free(SilcAuthPayload payload)
111 {
112   if (payload) {
113     if (payload->random_data) {
114       memset(payload->random_data, 0, payload->random_len);
115       silc_free(payload->random_data);
116     }
117     if (payload->auth_data) {
118       memset(payload->auth_data, 0, payload->auth_len);
119       silc_free(payload->auth_data);
120     }
121     silc_free(payload);
122   }
123 }
124
125 /******************************************************************************
126
127                            Authentication Routines
128
129 ******************************************************************************/
130
131 /* Encodes the authentication data for hashing and signing as the protocol
132    dictates. */
133
134 static unsigned char *
135 silc_auth_public_key_encode_data(SilcPublicKey public_key, 
136                                  unsigned char *random,
137                                  unsigned int random_len, void *id,
138                                  SilcIdType type, unsigned int *ret_len)
139 {
140   SilcBuffer buf;
141   unsigned char *pk, *id_data, *ret;
142   unsigned int pk_len, id_len;
143
144   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
145   if (!pk)
146     return NULL;
147
148   id_data = silc_id_id2str(id, type);
149   if (!id_data) {
150     silc_free(pk);
151     return NULL;
152   }
153   id_len = silc_id_get_len(type);
154
155   buf = silc_buffer_alloc(random_len + pk_len);
156   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
157   silc_buffer_format(buf,
158                      SILC_STR_UI_XNSTRING(random, random_len),
159                      SILC_STR_UI_XNSTRING(id_data, id_len),
160                      SILC_STR_UI_XNSTRING(pk, pk_len),
161                      SILC_STR_END);
162   
163   ret = silc_calloc(buf->len, sizeof(*ret));
164   memcpy(ret, buf->data, buf->len);
165
166   if (ret_len)
167     *ret_len = buf->len;
168
169   silc_buffer_free(buf);
170   silc_free(id_data);
171   silc_free(pk);
172
173   return ret;
174 }
175
176 /* Generates Authentication Payload with authentication data. This is used
177    to do public key based authentication. This generates the random data
178    and the actual authentication data. Returns NULL on error. */
179
180 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
181                                               SilcHash hash,
182                                               void *id, SilcIdType type)
183 {
184   unsigned char *random;
185   unsigned char auth_data[32];
186   unsigned int auth_len;
187   unsigned char *tmp;
188   unsigned int tmp_len;
189   SilcBuffer buf;
190   SilcPKCS pkcs;
191
192   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
193
194   /* Get 256 bytes of random data */
195   random = silc_rng_global_get_rn_data(256);
196   if (!random)
197     return NULL;
198   
199   /* Encode the auth data */
200   tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type, 
201                                          &tmp_len);
202   if (!tmp)
203     return NULL;
204
205   /* Allocate PKCS object */
206   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
207     memset(tmp, 0, tmp_len);
208     silc_free(tmp);
209     return NULL;
210   }
211
212   /* Compute the hash and the signature. */
213   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
214                                 &auth_len)) {
215     memset(random, 0, 256);
216     memset(tmp, 0, tmp_len);
217     silc_free(tmp);
218     silc_free(random);
219     silc_pkcs_free(pkcs);
220     return NULL;
221   }
222
223   /* Encode Authentication Payload */
224   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
225                                  auth_data, auth_len);
226
227   memset(tmp, 0, tmp_len);
228   memset(auth_data, 0, sizeof(auth_data));
229   memset(random, 0, 256);
230   silc_free(tmp);
231   silc_free(random);
232   silc_pkcs_free(pkcs);
233
234   return buf;
235 }
236
237 /* Verifies the authentication data. Returns TRUE if authentication was
238    successfull. */
239
240 int silc_auth_public_key_auth_verify(SilcAuthPayload payload,
241                                      SilcPublicKey public_key, SilcHash hash,
242                                      void *id, SilcIdType type)
243 {
244   unsigned char *tmp;
245   unsigned int tmp_len;
246   SilcPKCS pkcs;
247
248   SILC_LOG_DEBUG(("Verifying authentication data"));
249
250   /* Encode auth data */
251   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data, 
252                                          payload->random_len, 
253                                          id, type, &tmp_len);
254   if (!tmp) {
255     SILC_LOG_DEBUG(("Authentication failed"));
256     return FALSE;
257   }
258
259   /* Allocate PKCS object */
260   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
261     memset(tmp, 0, tmp_len);
262     silc_free(tmp);
263     return FALSE;
264   }
265
266   /* Verify the authentication data */
267   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
268                                   payload->auth_len, tmp, tmp_len)) {
269
270     memset(tmp, 0, tmp_len);
271     silc_free(tmp);
272     silc_pkcs_free(pkcs);
273     SILC_LOG_DEBUG(("Authentication failed"));
274     return FALSE;
275   }
276
277   memset(tmp, 0, tmp_len);
278   silc_free(tmp);
279   silc_pkcs_free(pkcs);
280
281   SILC_LOG_DEBUG(("Authentication successfull"));
282
283   return TRUE;
284 }
285
286 /* Same as above but the payload is not parsed yet. This will parse it. */
287
288 int silc_auth_public_key_auth_verify_data(SilcBuffer payload,
289                                           SilcPublicKey public_key, 
290                                           SilcHash hash,
291                                           void *id, SilcIdType type)
292 {
293   SilcAuthPayload auth_payload;
294   int ret;
295
296   auth_payload = silc_auth_payload_parse(payload);
297   if (!auth_payload) {
298     SILC_LOG_DEBUG(("Authentication failed"));
299     return FALSE;
300   }
301
302   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash, 
303                                          id, type);
304
305   silc_auth_payload_free(auth_payload);
306
307   return ret;
308 }
309
310 /* Verifies the authentication data directly from the Authentication 
311    Payload. Supports all authentication methods. If the authentication
312    method is passphrase based then the `auth_data' and `auth_data_len'
313    are the passphrase and its length. If the method is public key
314    authentication then the `auth_data' is the SilcPublicKey and the
315    `auth_data_len' is ignored. */
316
317 int silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
318                      void *auth_data, unsigned int auth_data_len, 
319                      SilcHash hash, void *id, SilcIdType type)
320 {
321   SILC_LOG_DEBUG(("Verifying authentication"));
322
323   if (auth_method != payload->auth_method)
324     return FALSE;
325
326   switch (payload->auth_method) {
327   case SILC_AUTH_NONE:
328     /* No authentication */
329     SILC_LOG_DEBUG(("No authentication required"));
330     return TRUE;
331
332   case SILC_AUTH_PASSWORD:
333     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
334        arguments are not needed. */
335     if (!memcmp(payload->auth_data, auth_data, payload->auth_len)) {
336       SILC_LOG_DEBUG(("Authentication successfull"));
337       return TRUE;
338     }
339     break;
340
341   case SILC_AUTH_PUBLIC_KEY:
342     /* Public key based authentication */
343     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
344                                             hash, id, type);
345     break;
346
347   default:
348     break;
349   }
350
351   SILC_LOG_DEBUG(("Authentication failed"));
352
353   return FALSE;
354 }
355
356 /* Same as above but parses the authentication payload before verify. */
357
358 int silc_auth_verify_data(unsigned char *payload, unsigned int payload_len,
359                           SilcAuthMethod auth_method, void *auth_data,
360                           unsigned int auth_data_len, SilcHash hash, 
361                           void *id, SilcIdType type)
362 {
363   SilcAuthPayload auth_payload;
364   SilcBuffer buffer;
365   int ret;
366
367   buffer = silc_buffer_alloc(payload_len);
368   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
369   silc_buffer_put(buffer, payload, payload_len);
370   auth_payload = silc_auth_payload_parse(buffer);
371   if (!auth_payload) {
372     silc_buffer_free(buffer);
373     return FALSE;
374   }
375
376   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len, 
377                          hash, id, type);
378
379   silc_auth_payload_free(auth_payload);
380   silc_buffer_free(buffer);
381
382   return ret;
383 }
384
385 /******************************************************************************
386
387                             Key Agreement Payload
388
389 ******************************************************************************/
390
391 /* The Key Agreement protocol structure */
392 struct SilcKeyAgreementPayloadStruct {
393   unsigned short hostname_len;
394   unsigned char *hostname;
395   unsigned int port;
396 };
397
398 /* Parses and returns an allocated Key Agreement payload. */
399
400 SilcKeyAgreementPayload silc_key_agreement_payload_parse(SilcBuffer buffer)
401 {
402   SilcKeyAgreementPayload new;
403   int ret;
404
405   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
406
407   new = silc_calloc(1, sizeof(*new));
408
409   /* Parse the payload */
410   ret = silc_buffer_unformat(buffer, 
411                              SILC_STR_UI16_NSTRING_ALLOC(&new->hostname,
412                                                          &new->hostname_len),
413                              SILC_STR_UI_INT(&new->port),
414                              SILC_STR_END);
415   if (ret == -1) {
416     silc_free(new);
417     return NULL;
418   }
419
420   return new;
421 }
422
423 /* Encodes the Key Agreement protocol and returns the encoded buffer */
424
425 SilcBuffer silc_key_agreement_payload_encode(char *hostname,
426                                              unsigned int port)
427 {
428   SilcBuffer buffer;
429   unsigned int len = hostname ? strlen(hostname) : 0;
430
431   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
432
433   buffer = silc_buffer_alloc(2 + len + 4);
434   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
435   silc_buffer_format(buffer,
436                      SILC_STR_UI_SHORT(len),
437                      SILC_STR_UI_XNSTRING(hostname, len),
438                      SILC_STR_UI_INT(port),
439                      SILC_STR_END);
440
441   return buffer;
442 }
443
444 /* Frees the Key Agreement protocol */
445
446 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
447 {
448   if (payload) {
449     silc_free(payload->hostname);
450     silc_free(payload);
451   }
452 }
453
454 /* Returns the hostname in the payload */
455
456 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
457 {
458   return payload->hostname;
459 }
460
461 /* Returns the port in the payload */
462
463 unsigned int silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
464 {
465   return payload->port;
466 }