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