updates.
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2002 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; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcauth.h"
24
25 /******************************************************************************
26
27                            Authentication Payload
28
29 ******************************************************************************/
30
31 /* Authentication Payload structure */
32 struct SilcAuthPayloadStruct {
33   SilcUInt16 len;
34   SilcUInt16 auth_method;
35   SilcUInt16 random_len;
36   unsigned char *random_data;
37   SilcUInt16 auth_len;
38   unsigned char *auth_data;
39 };
40
41 /* Parses and returns Authentication Payload */
42
43 SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
44                                         SilcUInt32 data_len)
45 {
46   SilcBufferStruct buffer;
47   SilcAuthPayload newp;
48   int ret;
49
50   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
51
52   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
53   newp = silc_calloc(1, sizeof(*newp));
54   if (!newp)
55     return NULL;
56
57   /* Parse the payload */
58   ret = silc_buffer_unformat(&buffer,
59                              SILC_STR_UI_SHORT(&newp->len),
60                              SILC_STR_UI_SHORT(&newp->auth_method),
61                              SILC_STR_UI16_NSTRING_ALLOC(&newp->random_data,
62                                                          &newp->random_len),
63                              SILC_STR_UI16_NSTRING_ALLOC(&newp->auth_data,
64                                                          &newp->auth_len),
65                              SILC_STR_END);
66   if (ret == -1) {
67     silc_free(newp);
68     return NULL;
69   }
70
71   if (newp->len != buffer.len) {
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 authentication data. If this is passphrase it is UTF-8 encoded. */
167
168 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
169                                   SilcUInt32 *auth_len)
170 {
171   if (auth_len)
172     *auth_len = payload->auth_len;
173
174   return payload->auth_data;
175 }
176
177 /******************************************************************************
178
179                            Authentication Routines
180
181 ******************************************************************************/
182
183 /* Encodes the authentication data for hashing and signing as the protocol
184    dictates. */
185
186 static unsigned char *
187 silc_auth_public_key_encode_data(SilcPublicKey public_key,
188                                  const unsigned char *random,
189                                  SilcUInt32 random_len, const void *id,
190                                  SilcIdType type, SilcUInt32 *ret_len)
191 {
192   SilcBuffer buf;
193   unsigned char *pk, *id_data, *ret;
194   SilcUInt32 pk_len, id_len;
195
196   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
197   if (!pk)
198     return NULL;
199
200   id_data = silc_id_id2str(id, type);
201   if (!id_data) {
202     silc_free(pk);
203     return NULL;
204   }
205   id_len = silc_id_get_len(id, type);
206
207   buf = silc_buffer_alloc_size(random_len + id_len + pk_len);
208   if (!buf) {
209     silc_free(pk);
210     silc_free(id_data);
211     return NULL;
212   }
213   silc_buffer_format(buf,
214                      SILC_STR_UI_XNSTRING(random, random_len),
215                      SILC_STR_UI_XNSTRING(id_data, id_len),
216                      SILC_STR_UI_XNSTRING(pk, pk_len),
217                      SILC_STR_END);
218
219   ret = silc_memdup(buf->data, buf->len);
220   if (!ret)
221     return NULL;
222
223   if (ret_len)
224     *ret_len = buf->len;
225
226   silc_buffer_free(buf);
227   silc_free(id_data);
228   silc_free(pk);
229
230   return ret;
231 }
232
233 /* Generates Authentication Payload with authentication data. This is used
234    to do public key based authentication. This generates the random data
235    and the actual authentication data. Returns NULL on error. */
236
237 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
238                                               SilcPrivateKey private_key,
239                                               SilcRng rng, SilcHash hash,
240                                               const void *id, SilcIdType type)
241 {
242   unsigned char *random;
243   unsigned char auth_data[1024];
244   SilcUInt32 auth_len;
245   unsigned char *tmp;
246   SilcUInt32 tmp_len;
247   SilcBuffer buf;
248   SilcPKCS pkcs;
249
250   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
251
252   /* Get 256 bytes of random data */
253   if (rng)
254     random = silc_rng_get_rn_data(rng, 256);
255   else
256     random = silc_rng_global_get_rn_data(256);
257   if (!random)
258     return NULL;
259
260   /* Encode the auth data */
261   tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type,
262                                          &tmp_len);
263   if (!tmp)
264     return NULL;
265
266   /* Allocate PKCS object */
267   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
268     memset(tmp, 0, tmp_len);
269     silc_free(tmp);
270     return NULL;
271   }
272   silc_pkcs_public_key_set(pkcs, public_key);
273   silc_pkcs_private_key_set(pkcs, private_key);
274
275   /* Compute the hash and the signature. */
276   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
277                                 &auth_len)) {
278     memset(random, 0, 256);
279     memset(tmp, 0, tmp_len);
280     silc_free(tmp);
281     silc_free(random);
282     silc_pkcs_free(pkcs);
283     return NULL;
284   }
285
286   /* Encode Authentication Payload */
287   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
288                                  auth_data, auth_len);
289
290   memset(tmp, 0, tmp_len);
291   memset(auth_data, 0, sizeof(auth_data));
292   memset(random, 0, 256);
293   silc_free(tmp);
294   silc_free(random);
295   silc_pkcs_free(pkcs);
296
297   return buf;
298 }
299
300 /* Verifies the authentication data. Returns TRUE if authentication was
301    successful. */
302
303 bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
304                                       SilcPublicKey public_key, SilcHash hash,
305                                       const void *id, SilcIdType type)
306 {
307   unsigned char *tmp;
308   SilcUInt32 tmp_len;
309   SilcPKCS pkcs;
310
311   SILC_LOG_DEBUG(("Verifying authentication data"));
312
313   /* Encode auth data */
314   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data,
315                                          payload->random_len,
316                                          id, type, &tmp_len);
317   if (!tmp) {
318     SILC_LOG_DEBUG(("Authentication failed"));
319     return FALSE;
320   }
321
322   /* Allocate PKCS object */
323   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
324     memset(tmp, 0, tmp_len);
325     silc_free(tmp);
326     return FALSE;
327   }
328   silc_pkcs_public_key_set(pkcs, public_key);
329
330   /* Verify the authentication data */
331   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
332                                   payload->auth_len, tmp, tmp_len)) {
333
334     memset(tmp, 0, tmp_len);
335     silc_free(tmp);
336     silc_pkcs_free(pkcs);
337     SILC_LOG_DEBUG(("Authentication failed"));
338     return FALSE;
339   }
340
341   memset(tmp, 0, tmp_len);
342   silc_free(tmp);
343   silc_pkcs_free(pkcs);
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 bool 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 bool 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 bool silc_auth_verify_data(const unsigned char *payload,
430                            SilcUInt32 payload_len,
431                            SilcAuthMethod auth_method, const void *auth_data,
432                            SilcUInt32 auth_data_len, SilcHash hash,
433                            const void *id, SilcIdType type)
434 {
435   SilcAuthPayload auth_payload;
436   bool ret;
437
438   auth_payload = silc_auth_payload_parse(payload, payload_len);
439   if (!auth_payload || (auth_payload->auth_len == 0))
440     return FALSE;
441
442   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
443                          hash, id, type);
444
445   silc_auth_payload_free(auth_payload);
446
447   return ret;
448 }
449
450 /******************************************************************************
451
452                             Key Agreement Payload
453
454 ******************************************************************************/
455
456 /* The Key Agreement protocol structure */
457 struct SilcKeyAgreementPayloadStruct {
458   SilcUInt16 hostname_len;
459   unsigned char *hostname;
460   SilcUInt32 port;
461 };
462
463 /* Parses and returns an allocated Key Agreement payload. */
464
465 SilcKeyAgreementPayload
466 silc_key_agreement_payload_parse(const unsigned char *payload,
467                                  SilcUInt32 payload_len)
468 {
469   SilcBufferStruct buffer;
470   SilcKeyAgreementPayload newp;
471   int ret;
472
473   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
474
475   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
476   newp = silc_calloc(1, sizeof(*newp));
477   if (!newp)
478     return NULL;
479
480   /* Parse the payload */
481   ret = silc_buffer_unformat(&buffer,
482                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
483                                                          &newp->hostname_len),
484                              SILC_STR_UI_INT(&newp->port),
485                              SILC_STR_END);
486   if (ret == -1) {
487     silc_free(newp);
488     return NULL;
489   }
490
491   return newp;
492 }
493
494 /* Encodes the Key Agreement protocol and returns the encoded buffer */
495
496 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
497                                              SilcUInt32 port)
498 {
499   SilcBuffer buffer;
500   SilcUInt32 len = hostname ? strlen(hostname) : 0;
501
502   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
503
504   buffer = silc_buffer_alloc_size(2 + len + 4);
505   if (!buffer)
506     return NULL;
507   silc_buffer_format(buffer,
508                      SILC_STR_UI_SHORT(len),
509                      SILC_STR_UI_XNSTRING(hostname, len),
510                      SILC_STR_UI_INT(port),
511                      SILC_STR_END);
512
513   return buffer;
514 }
515
516 /* Frees the Key Agreement protocol */
517
518 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
519 {
520   if (payload) {
521     silc_free(payload->hostname);
522     silc_free(payload);
523   }
524 }
525
526 /* Returns the hostname in the payload */
527
528 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
529 {
530   return payload->hostname;
531 }
532
533 /* Returns the port in the payload */
534
535 SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
536 {
537   return payload->port;
538 }