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