Added SILC Server library.
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2005 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   SilcPKCS pkcs;
283
284   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
285
286   /* Encode the auth data */
287   tmp = silc_auth_public_key_encode_data(public_key, pubdata, pubdata_len, id,
288                                          type, &tmp_len);
289   if (!tmp)
290     return NULL;
291
292   /* Allocate PKCS object */
293   if (!silc_pkcs_alloc(private_key->name, SILC_PKCS_SILC, &pkcs)) {
294     memset(tmp, 0, tmp_len);
295     silc_free(tmp);
296     return NULL;
297   }
298   silc_pkcs_public_key_set(pkcs, public_key);
299   silc_pkcs_private_key_set(pkcs, private_key);
300
301   /* Compute the hash and the signature. */
302   if (silc_pkcs_get_key_len(pkcs) / 8 > sizeof(auth_data) - 1 ||
303       !silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
304                                 &auth_len)) {
305     memset(tmp, 0, tmp_len);
306     silc_free(tmp);
307     silc_pkcs_free(pkcs);
308     return NULL;
309   }
310
311   /* Encode Authentication Payload */
312   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, pubdata, pubdata_len,
313                                  auth_data, auth_len);
314
315   memset(tmp, 0, tmp_len);
316   memset(auth_data, 0, sizeof(auth_data));
317   silc_free(tmp);
318   silc_pkcs_free(pkcs);
319
320   return buf;
321 }
322
323 /* Verifies the authentication data. Returns TRUE if authentication was
324    successful. */
325
326 SilcBool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
327                                           SilcPublicKey public_key,
328                                           SilcHash hash,
329                                           const void *id, SilcIdType type)
330 {
331   unsigned char *tmp;
332   SilcUInt32 tmp_len;
333   SilcPKCS pkcs;
334
335   SILC_LOG_DEBUG(("Verifying authentication data"));
336
337   /* Encode auth data */
338   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data,
339                                          payload->random_len,
340                                          id, type, &tmp_len);
341   if (!tmp) {
342     SILC_LOG_DEBUG(("Authentication failed"));
343     return FALSE;
344   }
345
346   /* Allocate PKCS object */
347   if (!silc_pkcs_alloc(public_key->name, SILC_PKCS_SILC, &pkcs)) {
348     memset(tmp, 0, tmp_len);
349     silc_free(tmp);
350     return FALSE;
351   }
352   silc_pkcs_public_key_set(pkcs, public_key);
353
354   /* Verify the authentication data */
355   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
356                                   payload->auth_len, tmp, tmp_len)) {
357
358     memset(tmp, 0, tmp_len);
359     silc_free(tmp);
360     silc_pkcs_free(pkcs);
361     SILC_LOG_DEBUG(("Authentication failed"));
362     return FALSE;
363   }
364
365   memset(tmp, 0, tmp_len);
366   silc_free(tmp);
367   silc_pkcs_free(pkcs);
368
369   SILC_LOG_DEBUG(("Authentication successful"));
370
371   return TRUE;
372 }
373
374 /* Same as above but the payload is not parsed yet. This will parse it. */
375
376 SilcBool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
377                                                SilcUInt32 payload_len,
378                                                SilcPublicKey public_key,
379                                                SilcHash hash,
380                                                const void *id, SilcIdType type)
381 {
382   SilcAuthPayload auth_payload;
383   int ret;
384
385   auth_payload = silc_auth_payload_parse(payload, payload_len);
386   if (!auth_payload) {
387     SILC_LOG_DEBUG(("Authentication failed"));
388     return FALSE;
389   }
390
391   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash,
392                                          id, type);
393
394   silc_auth_payload_free(auth_payload);
395
396   return ret;
397 }
398
399 /* Verifies the authentication data directly from the Authentication
400    Payload. Supports all authentication methods. If the authentication
401    method is passphrase based then the `auth_data' and `auth_data_len'
402    are the passphrase and its length. If the method is public key
403    authentication then the `auth_data' is the SilcPublicKey and the
404    `auth_data_len' is ignored. */
405
406 SilcBool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
407                           const void *auth_data, SilcUInt32 auth_data_len,
408                           SilcHash hash, const void *id, SilcIdType type)
409 {
410   SILC_LOG_DEBUG(("Verifying authentication"));
411
412   if (!payload || auth_method != payload->auth_method)
413     return FALSE;
414
415   switch (payload->auth_method) {
416   case SILC_AUTH_NONE:
417     /* No authentication */
418     SILC_LOG_DEBUG(("No authentication required"));
419     return TRUE;
420
421   case SILC_AUTH_PASSWORD:
422     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
423        arguments are not needed. */
424
425     /* Sanity checks */
426     if ((payload->auth_len == 0) || !auth_data ||
427         payload->auth_len != auth_data_len)
428       break;
429
430     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
431       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
432       return TRUE;
433     }
434     break;
435
436   case SILC_AUTH_PUBLIC_KEY:
437     /* Public key based authentication */
438     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
439                                             hash, id, type);
440     break;
441
442   default:
443     break;
444   }
445
446   SILC_LOG_DEBUG(("Authentication failed"));
447
448   return FALSE;
449 }
450
451 /* Same as above but parses the authentication payload before verify. */
452
453 SilcBool silc_auth_verify_data(const unsigned char *payload,
454                                SilcUInt32 payload_len,
455                                SilcAuthMethod auth_method,
456                                const void *auth_data,
457                                SilcUInt32 auth_data_len, SilcHash hash,
458                                const void *id, SilcIdType type)
459 {
460   SilcAuthPayload auth_payload;
461   SilcBool ret;
462
463   auth_payload = silc_auth_payload_parse(payload, payload_len);
464   if (!auth_payload || (auth_payload->auth_len == 0))
465     return FALSE;
466
467   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
468                          hash, id, type);
469
470   silc_auth_payload_free(auth_payload);
471
472   return ret;
473 }
474
475 /******************************************************************************
476
477                             Key Agreement Payload
478
479 ******************************************************************************/
480
481 /* The Key Agreement protocol structure */
482 struct SilcKeyAgreementPayloadStruct {
483   SilcUInt16 hostname_len;
484   unsigned char *hostname;
485   SilcUInt32 port;
486 };
487
488 /* Parses and returns an allocated Key Agreement payload. */
489
490 SilcKeyAgreementPayload
491 silc_key_agreement_payload_parse(const unsigned char *payload,
492                                  SilcUInt32 payload_len)
493 {
494   SilcBufferStruct buffer;
495   SilcKeyAgreementPayload newp;
496   int ret;
497
498   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
499
500   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
501   newp = silc_calloc(1, sizeof(*newp));
502   if (!newp)
503     return NULL;
504
505   /* Parse the payload */
506   ret = silc_buffer_unformat(&buffer,
507                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
508                                                          &newp->hostname_len),
509                              SILC_STR_UI_INT(&newp->port),
510                              SILC_STR_END);
511   if (ret == -1 || newp->hostname_len > silc_buffer_len(&buffer) - 6) {
512     silc_free(newp);
513     return NULL;
514   }
515
516   return newp;
517 }
518
519 /* Encodes the Key Agreement protocol and returns the encoded buffer */
520
521 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
522                                              SilcUInt32 port)
523 {
524   SilcBuffer buffer;
525   SilcUInt32 len = hostname ? strlen(hostname) : 0;
526
527   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
528
529   buffer = silc_buffer_alloc_size(2 + len + 4);
530   if (!buffer)
531     return NULL;
532   silc_buffer_format(buffer,
533                      SILC_STR_UI_SHORT(len),
534                      SILC_STR_UI_XNSTRING(hostname, len),
535                      SILC_STR_UI_INT(port),
536                      SILC_STR_END);
537
538   return buffer;
539 }
540
541 /* Frees the Key Agreement protocol */
542
543 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
544 {
545   if (payload) {
546     silc_free(payload->hostname);
547     silc_free(payload);
548   }
549 }
550
551 /* Returns the hostname in the payload */
552
553 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
554 {
555   return payload->hostname;
556 }
557
558 /* Returns the port in the payload */
559
560 SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
561 {
562   return payload->port;
563 }