updates.
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 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   /* If password authentication, random data must not be set */
77   if (newp->auth_method == SILC_AUTH_PASSWORD && newp->random_len) {
78     silc_auth_payload_free(newp);
79     return NULL;
80   }
81
82   return newp;
83 }
84
85 /* Encodes authentication payload into buffer and returns it */
86
87 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
88                                     const unsigned char *random_data,
89                                     SilcUInt16 random_len,
90                                     const unsigned char *auth_data,
91                                     SilcUInt16 auth_len)
92 {
93   SilcBuffer buffer;
94   SilcUInt32 len;
95
96   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
97
98   len = 2 + 2 + 2 + random_len + 2 + auth_len;
99   buffer = silc_buffer_alloc_size(len);
100   if (!buffer)
101     return NULL;
102   silc_buffer_format(buffer,
103                      SILC_STR_UI_SHORT(len),
104                      SILC_STR_UI_SHORT(method),
105                      SILC_STR_UI_SHORT(random_len),
106                      SILC_STR_UI_XNSTRING(random_data, random_len),
107                      SILC_STR_UI_SHORT(auth_len),
108                      SILC_STR_UI_XNSTRING(auth_data, auth_len),
109                      SILC_STR_END);
110
111   return buffer;
112 }
113
114 /* Frees authentication payload. */
115
116 void silc_auth_payload_free(SilcAuthPayload payload)
117 {
118   if (payload) {
119     if (payload->random_data) {
120       memset(payload->random_data, 0, payload->random_len);
121       silc_free(payload->random_data);
122     }
123     if (payload->auth_data) {
124       memset(payload->auth_data, 0, payload->auth_len);
125       silc_free(payload->auth_data);
126     }
127     silc_free(payload);
128   }
129 }
130
131 /* Get authentication method */
132
133 SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
134 {
135   return payload->auth_method;
136 }
137
138 /* Get the authentication data */
139
140 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
141                                   SilcUInt32 *auth_len)
142 {
143   if (auth_len)
144     *auth_len = payload->auth_len;
145
146   return payload->auth_data;
147 }
148
149 /******************************************************************************
150
151                            Authentication Routines
152
153 ******************************************************************************/
154
155 /* Encodes the authentication data for hashing and signing as the protocol
156    dictates. */
157
158 static unsigned char *
159 silc_auth_public_key_encode_data(SilcPublicKey public_key, 
160                                  const unsigned char *random,
161                                  SilcUInt32 random_len, const void *id,
162                                  SilcIdType type, SilcUInt32 *ret_len)
163 {
164   SilcBuffer buf;
165   unsigned char *pk, *id_data, *ret;
166   SilcUInt32 pk_len, id_len;
167
168   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
169   if (!pk)
170     return NULL;
171
172   id_data = silc_id_id2str(id, type);
173   if (!id_data) {
174     silc_free(pk);
175     return NULL;
176   }
177   id_len = silc_id_get_len(id, type);
178
179   buf = silc_buffer_alloc_size(random_len + id_len + pk_len);
180   if (!buf) {
181     silc_free(pk);
182     silc_free(id_data);
183     return NULL;
184   }
185   silc_buffer_format(buf,
186                      SILC_STR_UI_XNSTRING(random, random_len),
187                      SILC_STR_UI_XNSTRING(id_data, id_len),
188                      SILC_STR_UI_XNSTRING(pk, pk_len),
189                      SILC_STR_END);
190   
191   ret = silc_memdup(buf->data, buf->len);
192   if (!ret)
193     return NULL;
194
195   if (ret_len)
196     *ret_len = buf->len;
197
198   silc_buffer_free(buf);
199   silc_free(id_data);
200   silc_free(pk);
201
202   return ret;
203 }
204
205 /* Generates Authentication Payload with authentication data. This is used
206    to do public key based authentication. This generates the random data
207    and the actual authentication data. Returns NULL on error. */
208
209 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
210                                               SilcPrivateKey private_key,
211                                               SilcRng rng, SilcHash hash,
212                                               const void *id, SilcIdType type)
213 {
214   unsigned char *random;
215   unsigned char auth_data[1024];
216   SilcUInt32 auth_len;
217   unsigned char *tmp;
218   SilcUInt32 tmp_len;
219   SilcBuffer buf;
220   SilcPKCS pkcs;
221
222   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
223
224   /* Get 256 bytes of random data */
225   if (rng)
226     random = silc_rng_get_rn_data(rng, 256);
227   else
228     random = silc_rng_global_get_rn_data(256);
229   if (!random)
230     return NULL;
231   
232   /* Encode the auth data */
233   tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type, 
234                                          &tmp_len);
235   if (!tmp)
236     return NULL;
237
238   /* Allocate PKCS object */
239   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
240     memset(tmp, 0, tmp_len);
241     silc_free(tmp);
242     return NULL;
243   }
244   silc_pkcs_public_key_set(pkcs, public_key);
245   silc_pkcs_private_key_set(pkcs, private_key);
246
247   /* Compute the hash and the signature. */
248   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
249                                 &auth_len)) {
250     memset(random, 0, 256);
251     memset(tmp, 0, tmp_len);
252     silc_free(tmp);
253     silc_free(random);
254     silc_pkcs_free(pkcs);
255     return NULL;
256   }
257
258   /* Encode Authentication Payload */
259   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
260                                  auth_data, auth_len);
261
262   memset(tmp, 0, tmp_len);
263   memset(auth_data, 0, sizeof(auth_data));
264   memset(random, 0, 256);
265   silc_free(tmp);
266   silc_free(random);
267   silc_pkcs_free(pkcs);
268
269   return buf;
270 }
271
272 /* Verifies the authentication data. Returns TRUE if authentication was
273    successful. */
274
275 bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
276                                       SilcPublicKey public_key, SilcHash hash,
277                                       const void *id, SilcIdType type)
278 {
279   unsigned char *tmp;
280   SilcUInt32 tmp_len;
281   SilcPKCS pkcs;
282
283   SILC_LOG_DEBUG(("Verifying authentication data"));
284
285   /* Encode auth data */
286   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data, 
287                                          payload->random_len, 
288                                          id, type, &tmp_len);
289   if (!tmp) {
290     SILC_LOG_DEBUG(("Authentication failed"));
291     return FALSE;
292   }
293
294   /* Allocate PKCS object */
295   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
296     memset(tmp, 0, tmp_len);
297     silc_free(tmp);
298     return FALSE;
299   }
300   silc_pkcs_public_key_set(pkcs, public_key);
301
302   /* Verify the authentication data */
303   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
304                                   payload->auth_len, tmp, tmp_len)) {
305
306     memset(tmp, 0, tmp_len);
307     silc_free(tmp);
308     silc_pkcs_free(pkcs);
309     SILC_LOG_DEBUG(("Authentication failed"));
310     return FALSE;
311   }
312
313   memset(tmp, 0, tmp_len);
314   silc_free(tmp);
315   silc_pkcs_free(pkcs);
316
317   SILC_LOG_DEBUG(("Authentication successful"));
318
319   return TRUE;
320 }
321
322 /* Same as above but the payload is not parsed yet. This will parse it. */
323
324 bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
325                                            SilcUInt32 payload_len,
326                                            SilcPublicKey public_key, 
327                                            SilcHash hash,
328                                            const void *id, SilcIdType type)
329 {
330   SilcAuthPayload auth_payload;
331   int ret;
332
333   auth_payload = silc_auth_payload_parse(payload, payload_len);
334   if (!auth_payload) {
335     SILC_LOG_DEBUG(("Authentication failed"));
336     return FALSE;
337   }
338
339   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash, 
340                                          id, type);
341
342   silc_auth_payload_free(auth_payload);
343
344   return ret;
345 }
346
347 /* Verifies the authentication data directly from the Authentication 
348    Payload. Supports all authentication methods. If the authentication
349    method is passphrase based then the `auth_data' and `auth_data_len'
350    are the passphrase and its length. If the method is public key
351    authentication then the `auth_data' is the SilcPublicKey and the
352    `auth_data_len' is ignored. */
353
354 bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
355                       const void *auth_data, SilcUInt32 auth_data_len, 
356                       SilcHash hash, const void *id, SilcIdType type)
357 {
358   SILC_LOG_DEBUG(("Verifying authentication"));
359
360   if (auth_method != payload->auth_method)
361     return FALSE;
362
363   switch (payload->auth_method) {
364   case SILC_AUTH_NONE:
365     /* No authentication */
366     SILC_LOG_DEBUG(("No authentication required"));
367     return TRUE;
368
369   case SILC_AUTH_PASSWORD:
370     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
371        arguments are not needed. */
372     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
373       SILC_LOG_DEBUG(("Authentication successful"));
374       return TRUE;
375     }
376     break;
377
378   case SILC_AUTH_PUBLIC_KEY:
379     /* Public key based authentication */
380     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
381                                             hash, id, type);
382     break;
383
384   default:
385     break;
386   }
387
388   SILC_LOG_DEBUG(("Authentication failed"));
389
390   return FALSE;
391 }
392
393 /* Same as above but parses the authentication payload before verify. */
394
395 bool silc_auth_verify_data(const unsigned char *payload, SilcUInt32 payload_len,
396                            SilcAuthMethod auth_method, const void *auth_data,
397                            SilcUInt32 auth_data_len, SilcHash hash, 
398                            const void *id, SilcIdType type)
399 {
400   SilcAuthPayload auth_payload;
401   int ret;
402
403   auth_payload = silc_auth_payload_parse(payload, payload_len);
404   if (!auth_payload)
405     return FALSE;
406
407   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len, 
408                          hash, id, type);
409
410   silc_auth_payload_free(auth_payload);
411
412   return ret;
413 }
414
415 /******************************************************************************
416
417                             Key Agreement Payload
418
419 ******************************************************************************/
420
421 /* The Key Agreement protocol structure */
422 struct SilcKeyAgreementPayloadStruct {
423   SilcUInt16 hostname_len;
424   unsigned char *hostname;
425   SilcUInt32 port;
426 };
427
428 /* Parses and returns an allocated Key Agreement payload. */
429
430 SilcKeyAgreementPayload 
431 silc_key_agreement_payload_parse(const unsigned char *payload,
432                                  SilcUInt32 payload_len)
433 {
434   SilcBufferStruct buffer;
435   SilcKeyAgreementPayload newp;
436   int ret;
437
438   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
439
440   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
441   newp = silc_calloc(1, sizeof(*newp));
442   if (!newp)
443     return NULL;
444
445   /* Parse the payload */
446   ret = silc_buffer_unformat(&buffer, 
447                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
448                                                          &newp->hostname_len),
449                              SILC_STR_UI_INT(&newp->port),
450                              SILC_STR_END);
451   if (ret == -1) {
452     silc_free(newp);
453     return NULL;
454   }
455
456   return newp;
457 }
458
459 /* Encodes the Key Agreement protocol and returns the encoded buffer */
460
461 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
462                                              SilcUInt32 port)
463 {
464   SilcBuffer buffer;
465   SilcUInt32 len = hostname ? strlen(hostname) : 0;
466
467   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
468
469   buffer = silc_buffer_alloc_size(2 + len + 4);
470   if (!buffer)
471     return NULL;
472   silc_buffer_format(buffer,
473                      SILC_STR_UI_SHORT(len),
474                      SILC_STR_UI_XNSTRING(hostname, len),
475                      SILC_STR_UI_INT(port),
476                      SILC_STR_END);
477
478   return buffer;
479 }
480
481 /* Frees the Key Agreement protocol */
482
483 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
484 {
485   if (payload) {
486     silc_free(payload->hostname);
487     silc_free(payload);
488   }
489 }
490
491 /* Returns the hostname in the payload */
492
493 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
494 {
495   return payload->hostname;
496 }
497
498 /* Returns the port in the payload */
499
500 SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
501 {
502   return payload->port;
503 }