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