Merged silc_1_0_branch to 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 - 2003 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 "silcincludes.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 != buffer.len ||
71       newp->random_len + newp->auth_len > buffer.len - 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, *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   id_data = silc_id_id2str(id, type);
212   if (!id_data) {
213     silc_free(pk);
214     return NULL;
215   }
216   id_len = silc_id_get_len(id, type);
217
218   buf = silc_buffer_alloc_size(random_len + id_len + pk_len);
219   if (!buf) {
220     silc_free(pk);
221     silc_free(id_data);
222     return NULL;
223   }
224   silc_buffer_format(buf,
225                      SILC_STR_UI_XNSTRING(randomdata, random_len),
226                      SILC_STR_UI_XNSTRING(id_data, id_len),
227                      SILC_STR_UI_XNSTRING(pk, pk_len),
228                      SILC_STR_END);
229
230   ret = silc_buffer_steal(buf, ret_len);
231
232   silc_buffer_free(buf);
233   silc_free(id_data);
234   silc_free(pk);
235
236   return ret;
237 }
238
239 /* Generates Authentication Payload with authentication data. This is used
240    to do public key based authentication. This generates the random data
241    and the actual authentication data. Returns NULL on error. */
242
243 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
244                                               SilcPrivateKey private_key,
245                                               SilcRng rng, SilcHash hash,
246                                               const void *id, SilcIdType type)
247 {
248   unsigned char *randomdata;
249   SilcBuffer buf;
250
251   /* Get 256 bytes of random data */
252   if (rng)
253     randomdata = silc_rng_get_rn_data(rng, 256);
254   else
255     randomdata = silc_rng_global_get_rn_data(256);
256   if (!randomdata)
257     return NULL;
258
259   buf = silc_auth_public_key_auth_generate_wpub(public_key, private_key,
260                                                 randomdata, 256, hash,
261                                                 id, type);
262
263   memset(randomdata, 0, 256);
264   silc_free(randomdata);
265
266   return buf;
267 }
268
269 /* Generates Authentication Payload with authentication data. This is used
270    to do public key based authentication. This generates the random data
271    and the actual authentication data. Returns NULL on error. */
272
273 SilcBuffer
274 silc_auth_public_key_auth_generate_wpub(SilcPublicKey public_key,
275                                         SilcPrivateKey private_key,
276                                         const unsigned char *pubdata,
277                                         SilcUInt32 pubdata_len,
278                                         SilcHash hash,
279                                         const void *id, SilcIdType type)
280 {
281   unsigned char auth_data[2048 + 1];
282   SilcUInt32 auth_len;
283   unsigned char *tmp;
284   SilcUInt32 tmp_len;
285   SilcBuffer buf;
286   SilcPKCS pkcs;
287
288   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
289
290   /* Encode the auth data */
291   tmp = silc_auth_public_key_encode_data(public_key, pubdata, pubdata_len, id,
292                                          type, &tmp_len);
293   if (!tmp)
294     return NULL;
295
296   /* Allocate PKCS object */
297   if (!silc_pkcs_alloc(private_key->name, &pkcs)) {
298     memset(tmp, 0, tmp_len);
299     silc_free(tmp);
300     return NULL;
301   }
302   silc_pkcs_public_key_set(pkcs, public_key);
303   silc_pkcs_private_key_set(pkcs, private_key);
304
305   /* Compute the hash and the signature. */
306   if (silc_pkcs_get_key_len(pkcs) / 8 > sizeof(auth_data) - 1 ||
307       !silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
308                                 &auth_len)) {
309     memset(tmp, 0, tmp_len);
310     silc_free(tmp);
311     silc_pkcs_free(pkcs);
312     return NULL;
313   }
314
315   /* Encode Authentication Payload */
316   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, pubdata, pubdata_len,
317                                  auth_data, auth_len);
318
319   memset(tmp, 0, tmp_len);
320   memset(auth_data, 0, sizeof(auth_data));
321   silc_free(tmp);
322   silc_pkcs_free(pkcs);
323
324   return buf;
325 }
326
327 /* Verifies the authentication data. Returns TRUE if authentication was
328    successful. */
329
330 bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
331                                       SilcPublicKey public_key, SilcHash hash,
332                                       const void *id, SilcIdType type)
333 {
334   unsigned char *tmp;
335   SilcUInt32 tmp_len;
336   SilcPKCS pkcs;
337
338   SILC_LOG_DEBUG(("Verifying authentication data"));
339
340   /* Encode auth data */
341   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data,
342                                          payload->random_len,
343                                          id, type, &tmp_len);
344   if (!tmp) {
345     SILC_LOG_DEBUG(("Authentication failed"));
346     return FALSE;
347   }
348
349   /* Allocate PKCS object */
350   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
351     memset(tmp, 0, tmp_len);
352     silc_free(tmp);
353     return FALSE;
354   }
355   silc_pkcs_public_key_set(pkcs, public_key);
356
357   /* Verify the authentication data */
358   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
359                                   payload->auth_len, tmp, tmp_len)) {
360
361     memset(tmp, 0, tmp_len);
362     silc_free(tmp);
363     silc_pkcs_free(pkcs);
364     SILC_LOG_DEBUG(("Authentication failed"));
365     return FALSE;
366   }
367
368   memset(tmp, 0, tmp_len);
369   silc_free(tmp);
370   silc_pkcs_free(pkcs);
371
372   SILC_LOG_DEBUG(("Authentication successful"));
373
374   return TRUE;
375 }
376
377 /* Same as above but the payload is not parsed yet. This will parse it. */
378
379 bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
380                                            SilcUInt32 payload_len,
381                                            SilcPublicKey public_key,
382                                            SilcHash hash,
383                                            const void *id, SilcIdType type)
384 {
385   SilcAuthPayload auth_payload;
386   int ret;
387
388   auth_payload = silc_auth_payload_parse(payload, payload_len);
389   if (!auth_payload) {
390     SILC_LOG_DEBUG(("Authentication failed"));
391     return FALSE;
392   }
393
394   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash,
395                                          id, type);
396
397   silc_auth_payload_free(auth_payload);
398
399   return ret;
400 }
401
402 /* Verifies the authentication data directly from the Authentication
403    Payload. Supports all authentication methods. If the authentication
404    method is passphrase based then the `auth_data' and `auth_data_len'
405    are the passphrase and its length. If the method is public key
406    authentication then the `auth_data' is the SilcPublicKey and the
407    `auth_data_len' is ignored. */
408
409 bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
410                       const void *auth_data, SilcUInt32 auth_data_len,
411                       SilcHash hash, const void *id, SilcIdType type)
412 {
413   SILC_LOG_DEBUG(("Verifying authentication"));
414
415   if (!payload || auth_method != payload->auth_method)
416     return FALSE;
417
418   switch (payload->auth_method) {
419   case SILC_AUTH_NONE:
420     /* No authentication */
421     SILC_LOG_DEBUG(("No authentication required"));
422     return TRUE;
423
424   case SILC_AUTH_PASSWORD:
425     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
426        arguments are not needed. */
427
428     /* Sanity checks */
429     if ((payload->auth_len == 0) || !auth_data ||
430         payload->auth_len != auth_data_len)
431       break;
432
433     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
434       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
435       return TRUE;
436     }
437     break;
438
439   case SILC_AUTH_PUBLIC_KEY:
440     /* Public key based authentication */
441     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
442                                             hash, id, type);
443     break;
444
445   default:
446     break;
447   }
448
449   SILC_LOG_DEBUG(("Authentication failed"));
450
451   return FALSE;
452 }
453
454 /* Same as above but parses the authentication payload before verify. */
455
456 bool silc_auth_verify_data(const unsigned char *payload,
457                            SilcUInt32 payload_len,
458                            SilcAuthMethod auth_method, const void *auth_data,
459                            SilcUInt32 auth_data_len, SilcHash hash,
460                            const void *id, SilcIdType type)
461 {
462   SilcAuthPayload auth_payload;
463   bool ret;
464
465   auth_payload = silc_auth_payload_parse(payload, payload_len);
466   if (!auth_payload || (auth_payload->auth_len == 0))
467     return FALSE;
468
469   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
470                          hash, id, type);
471
472   silc_auth_payload_free(auth_payload);
473
474   return ret;
475 }
476
477 /******************************************************************************
478
479                             Key Agreement Payload
480
481 ******************************************************************************/
482
483 /* The Key Agreement protocol structure */
484 struct SilcKeyAgreementPayloadStruct {
485   SilcUInt16 hostname_len;
486   unsigned char *hostname;
487   SilcUInt32 port;
488 };
489
490 /* Parses and returns an allocated Key Agreement payload. */
491
492 SilcKeyAgreementPayload
493 silc_key_agreement_payload_parse(const unsigned char *payload,
494                                  SilcUInt32 payload_len)
495 {
496   SilcBufferStruct buffer;
497   SilcKeyAgreementPayload newp;
498   int ret;
499
500   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
501
502   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
503   newp = silc_calloc(1, sizeof(*newp));
504   if (!newp)
505     return NULL;
506
507   /* Parse the payload */
508   ret = silc_buffer_unformat(&buffer,
509                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
510                                                          &newp->hostname_len),
511                              SILC_STR_UI_INT(&newp->port),
512                              SILC_STR_END);
513   if (ret == -1 || newp->hostname_len > buffer.len - 6) {
514     silc_free(newp);
515     return NULL;
516   }
517
518   return newp;
519 }
520
521 /* Encodes the Key Agreement protocol and returns the encoded buffer */
522
523 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
524                                              SilcUInt32 port)
525 {
526   SilcBuffer buffer;
527   SilcUInt32 len = hostname ? strlen(hostname) : 0;
528
529   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
530
531   buffer = silc_buffer_alloc_size(2 + len + 4);
532   if (!buffer)
533     return NULL;
534   silc_buffer_format(buffer,
535                      SILC_STR_UI_SHORT(len),
536                      SILC_STR_UI_XNSTRING(hostname, len),
537                      SILC_STR_UI_INT(port),
538                      SILC_STR_END);
539
540   return buffer;
541 }
542
543 /* Frees the Key Agreement protocol */
544
545 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
546 {
547   if (payload) {
548     silc_free(payload->hostname);
549     silc_free(payload);
550   }
551 }
552
553 /* Returns the hostname in the payload */
554
555 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
556 {
557   return payload->hostname;
558 }
559
560 /* Returns the port in the payload */
561
562 SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
563 {
564   return payload->port;
565 }