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