5082162e75051d35993382aaa4df794b7a947602
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 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 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcauth.h"
23
24 /******************************************************************************
25
26                            Authentication Payload
27
28 ******************************************************************************/
29
30 /* Authentication Payload structure */
31 struct SilcAuthPayloadStruct {
32   SilcStack stack;
33   unsigned char *random_data;
34   unsigned char *auth_data;
35   SilcUInt16 auth_len;
36   SilcUInt16 len;
37   SilcUInt16 auth_method;
38   SilcUInt16 random_len;
39 };
40
41 /* Parses and returns Authentication Payload */
42
43 SilcAuthPayload silc_auth_payload_parse(SilcStack stack,
44                                         const unsigned char *data,
45                                         SilcUInt32 data_len)
46 {
47   SilcBufferStruct buffer;
48   SilcAuthPayload newp;
49   int ret;
50
51   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
52
53   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
54
55   if (stack)
56     stack = silc_stack_alloc(0, stack);
57
58   newp = silc_scalloc(stack, 1, sizeof(*newp));
59   if (!newp) {
60     silc_stack_free(stack);
61     return NULL;
62   }
63   newp->stack = stack;
64
65   /* Parse the payload */
66   ret = silc_buffer_sunformat(stack, &buffer,
67                               SILC_STR_UI_SHORT(&newp->len),
68                               SILC_STR_UI_SHORT(&newp->auth_method),
69                               SILC_STR_UI16_NSTRING_ALLOC(&newp->random_data,
70                                                           &newp->random_len),
71                               SILC_STR_UI16_NSTRING_ALLOC(&newp->auth_data,
72                                                           &newp->auth_len),
73                               SILC_STR_END);
74   if (ret == -1) {
75     silc_sfree(stack, newp);
76     silc_stack_free(stack);
77     return NULL;
78   }
79
80   if (newp->len != silc_buffer_len(&buffer) ||
81       newp->random_len + newp->auth_len > silc_buffer_len(&buffer) - 8) {
82     silc_auth_payload_free(newp);
83     return NULL;
84   }
85
86   /* Authentication data must be provided */
87   if (newp->auth_len < 1)  {
88     silc_auth_payload_free(newp);
89     return NULL;
90   }
91
92   /* If password authentication, random data must not be set */
93   if (newp->auth_method == SILC_AUTH_PASSWORD && newp->random_len) {
94     silc_auth_payload_free(newp);
95     return NULL;
96   }
97
98   /* If public key authentication, random data must be at least 128 bytes */
99   if (newp->auth_method == SILC_AUTH_PUBLIC_KEY && newp->random_len < 128) {
100     silc_auth_payload_free(newp);
101     return NULL;
102   }
103
104   return newp;
105 }
106
107 /* Encodes authentication payload into buffer and returns it */
108
109 SilcBuffer silc_auth_payload_encode(SilcStack stack,
110                                     SilcAuthMethod method,
111                                     const unsigned char *random_data,
112                                     SilcUInt16 random_len,
113                                     const unsigned char *auth_data,
114                                     SilcUInt16 auth_len)
115 {
116   SilcBuffer buffer;
117   SilcUInt32 len;
118   unsigned char *autf8 = NULL;
119   SilcUInt32 autf8_len;
120
121   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
122
123   /* Passphrase MUST be UTF-8 encoded, encode if it is not */
124   if (method == SILC_AUTH_PASSWORD && !silc_utf8_valid(auth_data, auth_len)) {
125     autf8_len = silc_utf8_encoded_len(auth_data, auth_len, 0);
126     if (!autf8_len)
127       return NULL;
128     autf8 = silc_scalloc(stack, autf8_len, sizeof(*autf8));
129     auth_len = silc_utf8_encode(auth_data, auth_len, 0, autf8, autf8_len);
130     auth_data = (const unsigned char *)autf8;
131   }
132
133   len = 2 + 2 + 2 + random_len + 2 + auth_len;
134   buffer = silc_buffer_salloc_size(stack, len);
135   if (!buffer) {
136     silc_sfree(stack, autf8);
137     return NULL;
138   }
139
140   silc_buffer_sformat(stack, buffer,
141                       SILC_STR_UI_SHORT(len),
142                       SILC_STR_UI_SHORT(method),
143                       SILC_STR_UI_SHORT(random_len),
144                       SILC_STR_UI_XNSTRING(random_data, random_len),
145                       SILC_STR_UI_SHORT(auth_len),
146                       SILC_STR_UI_XNSTRING(auth_data, auth_len),
147                       SILC_STR_END);
148
149   silc_sfree(stack, autf8);
150   return buffer;
151 }
152
153 /* Frees authentication payload. */
154
155 void silc_auth_payload_free(SilcAuthPayload payload)
156 {
157   if (payload) {
158     SilcStack stack = payload->stack;
159
160     if (payload->random_data) {
161       memset(payload->random_data, 0, payload->random_len);
162       silc_sfree(stack, payload->random_data);
163     }
164     if (payload->auth_data) {
165       memset(payload->auth_data, 0, payload->auth_len);
166       silc_sfree(stack, payload->auth_data);
167     }
168
169     silc_sfree(stack, payload);
170     silc_stack_free(stack);
171   }
172 }
173
174 /* Get authentication method */
175
176 SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
177 {
178   return payload->auth_method;
179 }
180
181 /* Get the public data from the auth payload. */
182
183 unsigned char *silc_auth_get_public_data(SilcAuthPayload payload,
184                                          SilcUInt32 *pubdata_len)
185 {
186   if (pubdata_len)
187     *pubdata_len = (SilcUInt32)payload->random_len;
188
189   return payload->random_data;
190 }
191
192 /* Get the authentication data. If this is passphrase it is UTF-8 encoded. */
193
194 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
195                                   SilcUInt32 *auth_len)
196 {
197   if (auth_len)
198     *auth_len = (SilcUInt32)payload->auth_len;
199
200   return payload->auth_data;
201 }
202
203 /******************************************************************************
204
205                            Authentication Routines
206
207 ******************************************************************************/
208
209 /* Encodes the authentication data for hashing and signing as the protocol
210    dictates. */
211
212 static unsigned char *
213 silc_auth_public_key_encode_data(SilcStack stack,
214                                  SilcPublicKey public_key,
215                                  const unsigned char *randomdata,
216                                  SilcUInt32 random_len, const void *id,
217                                  SilcIdType type, SilcUInt32 *ret_len)
218 {
219   SilcBuffer buf;
220   unsigned char *pk, id_data[32], *ret;
221   SilcUInt32 pk_len, id_len;
222
223   pk = silc_pkcs_public_key_encode(stack, public_key, &pk_len);
224   if (!pk)
225     return NULL;
226
227   if (!silc_id_id2str(id, type, id_data, sizeof(id_data), &id_len)) {
228     silc_free(pk);
229     return NULL;
230   }
231
232   buf = silc_buffer_salloc_size(stack, random_len + id_len + pk_len);
233   if (!buf) {
234     silc_free(pk);
235     return NULL;
236   }
237   silc_buffer_sformat(stack, buf,
238                       SILC_STR_UI_XNSTRING(randomdata, random_len),
239                       SILC_STR_UI_XNSTRING(id_data, id_len),
240                       SILC_STR_UI_XNSTRING(pk, pk_len),
241                       SILC_STR_END);
242
243   ret = silc_buffer_steal(buf, ret_len);
244
245   silc_buffer_sfree(stack, buf);
246   silc_sfree(stack, pk);
247
248   return ret;
249 }
250
251 typedef struct {
252   SilcStack stack;
253   unsigned char *pubdata;
254   SilcUInt32 pubdata_len;
255   SilcAuthGenerated generated;
256   void *context;
257 } *SilcAuthGenerateContext;
258
259 /* Signature callback */
260
261 static void
262 silc_auth_public_key_auth_generate_cb(SilcBool success,
263                                       const unsigned char *signature,
264                                       SilcUInt32 signature_len,
265                                       void *context)
266 {
267   SilcAuthGenerateContext a = context;
268   SilcStack stack = a->stack;
269   SilcBuffer buf;
270
271   if (!success) {
272     a->generated(NULL, context);
273     silc_sfree(stack, a->pubdata);
274     silc_sfree(stack, a);
275     silc_stack_free(stack);
276     return;
277   }
278
279   /* Encode Authentication Payload */
280   buf = silc_auth_payload_encode(stack, SILC_AUTH_PUBLIC_KEY, a->pubdata,
281                                  a->pubdata_len, signature, signature_len);
282
283   a->generated(buf, context);
284
285   silc_buffer_sfree(stack, buf);
286   silc_sfree(stack, a->pubdata);
287   silc_sfree(stack, a);
288   silc_stack_free(stack);
289 }
290
291 /* Generates Authentication Payload with authentication data. This is used
292    to do public key based authentication. This generates the random data
293    and the actual authentication data. Returns NULL on error. */
294
295 SilcAsyncOperation
296 silc_auth_public_key_auth_generate(SilcPublicKey public_key,
297                                    SilcPrivateKey private_key,
298                                    SilcRng rng, SilcHash hash,
299                                    const void *id, SilcIdType type,
300                                    SilcAuthGenerated generated,
301                                    void *context)
302 {
303   unsigned char randomdata[256];
304
305   /* Get random data */
306   if (rng)
307     silc_rng_get_rn_data(rng, sizeof(randomdata), randomdata,
308                          sizeof(randomdata));
309   else
310     silc_rng_global_get_rn_data(rng, sizeof(randomdata), randomdata,
311                                 sizeof(randomdata));
312
313   return silc_auth_public_key_auth_generate_wpub(public_key, private_key,
314                                                  randomdata, sizeof(randomdata),
315                                                  hash, id, type, generated,
316                                                  context);
317 }
318
319 /* Generates Authentication Payload with authentication data. This is used
320    to do public key based authentication. This generates the random data
321    and the actual authentication data. Returns NULL on error. */
322
323 SilcAsyncOperation
324 silc_auth_public_key_auth_generate_wpub(SilcPublicKey public_key,
325                                         SilcPrivateKey private_key,
326                                         const unsigned char *pubdata,
327                                         SilcUInt32 pubdata_len,
328                                         SilcHash hash,
329                                         const void *id, SilcIdType type,
330                                         SilcAuthGenerated generated,
331                                         void *context)
332 {
333   SilcAuthGenerateContext a;
334   SilcAsyncOperation op;
335   unsigned char *tmp;
336   SilcUInt32 tmp_len;
337   SilcStack stack;
338
339   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
340
341   /* We use the Crypto Toolkit's stack since we're doing crypto */
342   stack = silc_stack_alloc(2048, silc_crypto_stack());
343
344   a = silc_scalloc(stack, 1, sizeof(*a));
345   if (!a) {
346     generated(NULL, context);
347     return NULL;
348   }
349   a->stack = stack;
350
351   /* Encode the auth data */
352   tmp = silc_auth_public_key_encode_data(stack, public_key, pubdata,
353                                          pubdata_len, id, type, &tmp_len);
354   if (!tmp) {
355     silc_sfree(stack, a);
356     silc_stack_free(stack);
357     generated(NULL, context);
358     return NULL;
359   }
360
361   a->pubdata = silc_smemdup(stack, pubdata, pubdata_len);
362   if (!a->pubdata) {
363     memset(tmp, 0, tmp_len);
364     silc_sfree(stack, tmp);
365     silc_sfree(stack, a);
366     silc_stack_free(stack);
367     generated(NULL, context);
368     return NULL;
369   }
370
371   /* Compute the hash and the signature. */
372   op = silc_pkcs_sign(private_key, tmp, tmp_len, TRUE, hash,
373                       silc_auth_public_key_auth_generate_cb, a);
374
375   memset(tmp, 0, tmp_len);
376   silc_sfree(stack, tmp);
377
378   return op;
379 }
380
381 /* Verifies the authentication data. */
382
383 SilcAsyncOperation
384 silc_auth_public_key_auth_verify(SilcAuthPayload payload,
385                                  SilcPublicKey public_key,
386                                  SilcHash hash,
387                                  const void *id,
388                                  SilcIdType type,
389                                  SilcAuthResultCb result,
390                                  void *context)
391 {
392   SilcAsyncOperation op;
393   unsigned char *tmp;
394   SilcUInt32 tmp_len;
395
396   SILC_LOG_DEBUG(("Verifying authentication data"));
397
398   /* Encode auth data */
399   tmp = silc_auth_public_key_encode_data(payload->stack,
400                                          public_key, payload->random_data,
401                                          payload->random_len,
402                                          id, type, &tmp_len);
403   if (!tmp) {
404     SILC_LOG_DEBUG(("Authentication failed"));
405     result(FALSE, context);
406     return NULL;
407   }
408
409   /* Verify the authentication data */
410   op = silc_pkcs_verify(public_key, payload->auth_data,
411                         payload->auth_len, tmp, tmp_len, hash,
412                         result, context);
413
414   memset(tmp, 0, tmp_len);
415   silc_sfree(payload->stack, tmp);
416
417   return op;
418 }
419
420 /* Same as above but the payload is not parsed yet. This will parse it. */
421
422 SilcAsyncOperation
423 silc_auth_public_key_auth_verify_data(const unsigned char *payload,
424                                       SilcUInt32 payload_len,
425                                       SilcPublicKey public_key,
426                                       SilcHash hash,
427                                       const void *id,
428                                       SilcIdType type,
429                                       SilcAuthResultCb result,
430                                       void *context)
431 {
432   SilcAsyncOperation op;
433   SilcAuthPayload auth_payload;
434
435   auth_payload = silc_auth_payload_parse(silc_crypto_stack(), payload,
436                                          payload_len);
437   if (!auth_payload) {
438     SILC_LOG_DEBUG(("Authentication failed"));
439     result(FALSE, context);
440     return NULL;
441   }
442
443   op = silc_auth_public_key_auth_verify(auth_payload, public_key, hash,
444                                         id, type, result, context);
445
446   silc_auth_payload_free(auth_payload);
447
448   return op;
449 }
450
451 /* Verifies the authentication data directly from the Authentication
452    Payload. Supports all authentication methods. If the authentication
453    method is passphrase based then the `auth_data' and `auth_data_len'
454    are the passphrase and its length. If the method is public key
455    authentication then the `auth_data' is the SilcPublicKey and the
456    `auth_data_len' is ignored. */
457
458 SilcAsyncOperation
459 silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
460                  const void *auth_data, SilcUInt32 auth_data_len,
461                  SilcHash hash, const void *id, SilcIdType type,
462                  SilcAuthResultCb result, void *context)
463 {
464   SILC_LOG_DEBUG(("Verifying authentication"));
465
466   if (!payload || auth_method != payload->auth_method) {
467     result(FALSE, context);
468     return NULL;
469   }
470
471   switch (payload->auth_method) {
472   case SILC_AUTH_NONE:
473     /* No authentication */
474     SILC_LOG_DEBUG(("No authentication required"));
475     result(TRUE, context);
476     return NULL;
477
478   case SILC_AUTH_PASSWORD:
479     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
480        arguments are not needed. */
481
482     /* Sanity checks */
483     if ((payload->auth_len == 0) || !auth_data ||
484         payload->auth_len != auth_data_len)
485       break;
486
487     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
488       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
489       result(TRUE, context);
490       return NULL;
491     }
492     break;
493
494   case SILC_AUTH_PUBLIC_KEY:
495     /* Public key based authentication */
496     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
497                                             hash, id, type, result, context);
498     break;
499
500   default:
501     break;
502   }
503
504   SILC_LOG_DEBUG(("Authentication failed"));
505   result(FALSE, context);
506
507   return NULL;
508 }
509
510 /* Same as above but parses the authentication payload before verify. */
511
512 SilcAsyncOperation
513 silc_auth_verify_data(const unsigned char *payload,
514                       SilcUInt32 payload_len,
515                       SilcAuthMethod auth_method,
516                       const void *auth_data,
517                       SilcUInt32 auth_data_len, SilcHash hash,
518                       const void *id, SilcIdType type,
519                       SilcAuthResultCb result, void *context)
520 {
521   SilcAsyncOperation op;
522   SilcAuthPayload auth_payload;
523
524   auth_payload = silc_auth_payload_parse(silc_crypto_stack(), payload,
525                                          payload_len);
526   if (!auth_payload || (auth_payload->auth_len == 0)) {
527     result(FALSE, context);
528     return NULL;
529   }
530
531   op = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
532                          hash, id, type, result, context);
533
534   silc_auth_payload_free(auth_payload);
535
536   return op;
537 }
538
539 /******************************************************************************
540
541                             Key Agreement Payload
542
543 ******************************************************************************/
544
545 /* The Key Agreement protocol structure */
546 struct SilcKeyAgreementPayloadStruct {
547   SilcUInt16 hostname_len;
548   unsigned char *hostname;
549   SilcUInt16 protocol;
550   SilcUInt16 port;
551 };
552
553 /* Parses and returns an allocated Key Agreement payload. */
554
555 SilcKeyAgreementPayload
556 silc_key_agreement_payload_parse(const unsigned char *payload,
557                                  SilcUInt32 payload_len)
558 {
559   SilcBufferStruct buffer;
560   SilcKeyAgreementPayload newp;
561   int ret;
562
563   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
564
565   newp = silc_calloc(1, sizeof(*newp));
566   if (!newp)
567     return NULL;
568
569   /* Parse the payload */
570   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
571   ret = silc_buffer_unformat(&buffer,
572                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
573                                                          &newp->hostname_len),
574                              SILC_STR_UI_SHORT(&newp->protocol),
575                              SILC_STR_UI_SHORT(&newp->port),
576                              SILC_STR_END);
577   if (ret == -1 || newp->hostname_len > silc_buffer_len(&buffer) - 6) {
578     silc_free(newp);
579     return NULL;
580   }
581
582   return newp;
583 }
584
585 /* Encodes the Key Agreement protocol and returns the encoded buffer */
586
587 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
588                                              SilcUInt16 protocol,
589                                              SilcUInt16 port)
590 {
591   SilcBuffer buffer;
592   SilcUInt32 len = hostname ? strlen(hostname) : 0;
593
594   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
595
596   buffer = silc_buffer_alloc_size(2 + len + 4);
597   if (!buffer)
598     return NULL;
599   silc_buffer_format(buffer,
600                      SILC_STR_UI_SHORT(len),
601                      SILC_STR_UI_XNSTRING(hostname, len),
602                      SILC_STR_UI_SHORT(protocol),
603                      SILC_STR_UI_SHORT(port),
604                      SILC_STR_END);
605
606   return buffer;
607 }
608
609 /* Frees the Key Agreement protocol */
610
611 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
612 {
613   if (payload) {
614     silc_free(payload->hostname);
615     silc_free(payload);
616   }
617 }
618
619 /* Returns the hostname in the payload */
620
621 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
622 {
623   return payload->hostname;
624 }
625
626 /* Returns the protocol in the payload */
627
628 SilcUInt16 silc_key_agreement_get_protocol(SilcKeyAgreementPayload payload)
629 {
630   return payload->protocol;
631 }
632
633 /* Returns the port in the payload */
634
635 SilcUInt16 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
636 {
637   return payload->port;
638 }