72cce5aad9492891f625afb0202368eb6173c8f6
[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, rng, 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                                         SilcRng rng,
330                                         const void *id, SilcIdType type,
331                                         SilcAuthGenerated generated,
332                                         void *context)
333 {
334   SilcAuthGenerateContext a;
335   SilcAsyncOperation op;
336   unsigned char *tmp;
337   SilcUInt32 tmp_len;
338   SilcStack stack;
339
340   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
341
342   /* We use the Crypto Toolkit's stack since we're doing crypto */
343   stack = silc_stack_alloc(2048, silc_crypto_stack());
344
345   a = silc_scalloc(stack, 1, sizeof(*a));
346   if (!a) {
347     generated(NULL, context);
348     return NULL;
349   }
350   a->stack = stack;
351
352   /* Encode the auth data */
353   tmp = silc_auth_public_key_encode_data(stack, public_key, pubdata,
354                                          pubdata_len, id, type, &tmp_len);
355   if (!tmp) {
356     silc_sfree(stack, a);
357     silc_stack_free(stack);
358     generated(NULL, context);
359     return NULL;
360   }
361
362   a->pubdata = silc_smemdup(stack, pubdata, pubdata_len);
363   if (!a->pubdata) {
364     memset(tmp, 0, tmp_len);
365     silc_sfree(stack, tmp);
366     silc_sfree(stack, a);
367     silc_stack_free(stack);
368     generated(NULL, context);
369     return NULL;
370   }
371
372   /* Compute the hash and the signature. */
373   op = silc_pkcs_sign(private_key, tmp, tmp_len, TRUE, hash, rng,
374                       silc_auth_public_key_auth_generate_cb, a);
375
376   memset(tmp, 0, tmp_len);
377   silc_sfree(stack, tmp);
378
379   return op;
380 }
381
382 /* Verifies the authentication data. */
383
384 SilcAsyncOperation
385 silc_auth_public_key_auth_verify(SilcAuthPayload payload,
386                                  SilcPublicKey public_key,
387                                  SilcHash hash,
388                                  const void *id,
389                                  SilcIdType type,
390                                  SilcAuthResultCb result,
391                                  void *context)
392 {
393   SilcAsyncOperation op;
394   unsigned char *tmp;
395   SilcUInt32 tmp_len;
396
397   SILC_LOG_DEBUG(("Verifying authentication data"));
398
399   /* Encode auth data */
400   tmp = silc_auth_public_key_encode_data(payload->stack,
401                                          public_key, payload->random_data,
402                                          payload->random_len,
403                                          id, type, &tmp_len);
404   if (!tmp) {
405     SILC_LOG_DEBUG(("Authentication failed"));
406     result(FALSE, context);
407     return NULL;
408   }
409
410   /* Verify the authentication data */
411   op = silc_pkcs_verify(public_key, payload->auth_data,
412                         payload->auth_len, tmp, tmp_len, hash,
413                         result, context);
414
415   memset(tmp, 0, tmp_len);
416   silc_sfree(payload->stack, tmp);
417
418   return op;
419 }
420
421 /* Same as above but the payload is not parsed yet. This will parse it. */
422
423 SilcAsyncOperation
424 silc_auth_public_key_auth_verify_data(const unsigned char *payload,
425                                       SilcUInt32 payload_len,
426                                       SilcPublicKey public_key,
427                                       SilcHash hash,
428                                       const void *id,
429                                       SilcIdType type,
430                                       SilcAuthResultCb result,
431                                       void *context)
432 {
433   SilcAsyncOperation op;
434   SilcAuthPayload auth_payload;
435
436   auth_payload = silc_auth_payload_parse(silc_crypto_stack(), payload,
437                                          payload_len);
438   if (!auth_payload) {
439     SILC_LOG_DEBUG(("Authentication failed"));
440     result(FALSE, context);
441     return NULL;
442   }
443
444   op = silc_auth_public_key_auth_verify(auth_payload, public_key, hash,
445                                         id, type, result, context);
446
447   silc_auth_payload_free(auth_payload);
448
449   return op;
450 }
451
452 /* Verifies the authentication data directly from the Authentication
453    Payload. Supports all authentication methods. If the authentication
454    method is passphrase based then the `auth_data' and `auth_data_len'
455    are the passphrase and its length. If the method is public key
456    authentication then the `auth_data' is the SilcPublicKey and the
457    `auth_data_len' is ignored. */
458
459 SilcAsyncOperation
460 silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
461                  const void *auth_data, SilcUInt32 auth_data_len,
462                  SilcHash hash, const void *id, SilcIdType type,
463                  SilcAuthResultCb result, void *context)
464 {
465   SILC_LOG_DEBUG(("Verifying authentication"));
466
467   if (!payload || auth_method != payload->auth_method) {
468     result(FALSE, context);
469     return NULL;
470   }
471
472   switch (payload->auth_method) {
473   case SILC_AUTH_NONE:
474     /* No authentication */
475     SILC_LOG_DEBUG(("No authentication required"));
476     result(TRUE, context);
477     return NULL;
478
479   case SILC_AUTH_PASSWORD:
480     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
481        arguments are not needed. */
482
483     /* Sanity checks */
484     if ((payload->auth_len == 0) || !auth_data ||
485         payload->auth_len != auth_data_len)
486       break;
487
488     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
489       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
490       result(TRUE, context);
491       return NULL;
492     }
493     break;
494
495   case SILC_AUTH_PUBLIC_KEY:
496     /* Public key based authentication */
497     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
498                                             hash, id, type, result, context);
499     break;
500
501   default:
502     break;
503   }
504
505   SILC_LOG_DEBUG(("Authentication failed"));
506   result(FALSE, context);
507
508   return NULL;
509 }
510
511 /* Same as above but parses the authentication payload before verify. */
512
513 SilcAsyncOperation
514 silc_auth_verify_data(const unsigned char *payload,
515                       SilcUInt32 payload_len,
516                       SilcAuthMethod auth_method,
517                       const void *auth_data,
518                       SilcUInt32 auth_data_len, SilcHash hash,
519                       const void *id, SilcIdType type,
520                       SilcAuthResultCb result, void *context)
521 {
522   SilcAsyncOperation op;
523   SilcAuthPayload auth_payload;
524
525   auth_payload = silc_auth_payload_parse(silc_crypto_stack(), payload,
526                                          payload_len);
527   if (!auth_payload || (auth_payload->auth_len == 0)) {
528     result(FALSE, context);
529     return NULL;
530   }
531
532   op = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
533                          hash, id, type, result, context);
534
535   silc_auth_payload_free(auth_payload);
536
537   return op;
538 }
539
540 /******************************************************************************
541
542                             Key Agreement Payload
543
544 ******************************************************************************/
545
546 /* The Key Agreement protocol structure */
547 struct SilcKeyAgreementPayloadStruct {
548   SilcUInt16 hostname_len;
549   unsigned char *hostname;
550   SilcUInt16 protocol;
551   SilcUInt16 port;
552 };
553
554 /* Parses and returns an allocated Key Agreement payload. */
555
556 SilcKeyAgreementPayload
557 silc_key_agreement_payload_parse(const unsigned char *payload,
558                                  SilcUInt32 payload_len)
559 {
560   SilcBufferStruct buffer;
561   SilcKeyAgreementPayload newp;
562   int ret;
563
564   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
565
566   newp = silc_calloc(1, sizeof(*newp));
567   if (!newp)
568     return NULL;
569
570   /* Parse the payload */
571   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
572   ret = silc_buffer_unformat(&buffer,
573                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
574                                                          &newp->hostname_len),
575                              SILC_STR_UI_SHORT(&newp->protocol),
576                              SILC_STR_UI_SHORT(&newp->port),
577                              SILC_STR_END);
578   if (ret == -1 || newp->hostname_len > silc_buffer_len(&buffer) - 6) {
579     silc_free(newp);
580     return NULL;
581   }
582
583   return newp;
584 }
585
586 /* Encodes the Key Agreement protocol and returns the encoded buffer */
587
588 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
589                                              SilcUInt16 protocol,
590                                              SilcUInt16 port)
591 {
592   SilcBuffer buffer;
593   SilcUInt32 len = hostname ? strlen(hostname) : 0;
594
595   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
596
597   buffer = silc_buffer_alloc_size(2 + len + 4);
598   if (!buffer)
599     return NULL;
600   silc_buffer_format(buffer,
601                      SILC_STR_UI_SHORT(len),
602                      SILC_STR_UI_XNSTRING(hostname, len),
603                      SILC_STR_UI_SHORT(protocol),
604                      SILC_STR_UI_SHORT(port),
605                      SILC_STR_END);
606
607   return buffer;
608 }
609
610 /* Frees the Key Agreement protocol */
611
612 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
613 {
614   if (payload) {
615     silc_free(payload->hostname);
616     silc_free(payload);
617   }
618 }
619
620 /* Returns the hostname in the payload */
621
622 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
623 {
624   return payload->hostname;
625 }
626
627 /* Returns the protocol in the payload */
628
629 SilcUInt16 silc_key_agreement_get_protocol(SilcKeyAgreementPayload payload)
630 {
631   return payload->protocol;
632 }
633
634 /* Returns the port in the payload */
635
636 SilcUInt16 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
637 {
638   return payload->port;
639 }