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