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