e66f9653834e69de3e69b0099bf82d4ef81a268d
[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(const 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, (unsigned char *)data, data_len);
53   new = silc_calloc(1, sizeof(*new));
54
55   /* Parse the payload */
56   ret = silc_buffer_unformat(&buffer, 
57                              SILC_STR_UI_SHORT(&new->len),
58                              SILC_STR_UI_SHORT(&new->auth_method),
59                              SILC_STR_UI16_NSTRING_ALLOC(&new->random_data,
60                                                          &new->random_len),
61                              SILC_STR_UI16_NSTRING_ALLOC(&new->auth_data,
62                                                          &new->auth_len),
63                              SILC_STR_END);
64   if (ret == -1) {
65     silc_free(new);
66     return NULL;
67   }
68
69   if (new->len != buffer.len) {
70     silc_auth_payload_free(new);
71     return NULL;
72   }
73
74   /* If password authentication, random data must not be set */
75   if (new->auth_method == SILC_AUTH_PASSWORD && new->random_len) {
76     silc_auth_payload_free(new);
77     return NULL;
78   }
79
80   return new;
81 }
82
83 /* Encodes authentication payload into buffer and returns it */
84
85 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
86                                     const unsigned char *random_data,
87                                     uint16 random_len,
88                                     const unsigned char *auth_data,
89                                     uint16 auth_len)
90 {
91   SilcBuffer buffer;
92   uint32 len;
93
94   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
95
96   len = 2 + 2 + 2 + random_len + 2 + auth_len;
97   buffer = silc_buffer_alloc(len);
98   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
99   silc_buffer_format(buffer,
100                      SILC_STR_UI_SHORT(len),
101                      SILC_STR_UI_SHORT(method),
102                      SILC_STR_UI_SHORT(random_len),
103                      SILC_STR_UI_XNSTRING(random_data, random_len),
104                      SILC_STR_UI_SHORT(auth_len),
105                      SILC_STR_UI_XNSTRING(auth_data, auth_len),
106                      SILC_STR_END);
107
108   return buffer;
109 }
110
111 /* Frees authentication payload. */
112
113 void silc_auth_payload_free(SilcAuthPayload payload)
114 {
115   if (payload) {
116     if (payload->random_data) {
117       memset(payload->random_data, 0, payload->random_len);
118       silc_free(payload->random_data);
119     }
120     if (payload->auth_data) {
121       memset(payload->auth_data, 0, payload->auth_len);
122       silc_free(payload->auth_data);
123     }
124     silc_free(payload);
125   }
126 }
127
128 /* Get authentication method */
129
130 SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
131 {
132   return payload->auth_method;
133 }
134
135 /* Get the authentication data */
136
137 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
138                                   uint32 *auth_len)
139 {
140   if (auth_len)
141     *auth_len = payload->auth_len;
142
143   return payload->auth_data;
144 }
145
146 /******************************************************************************
147
148                            Authentication Routines
149
150 ******************************************************************************/
151
152 /* Encodes the authentication data for hashing and signing as the protocol
153    dictates. */
154
155 static unsigned char *
156 silc_auth_public_key_encode_data(SilcPublicKey public_key, 
157                                  const unsigned char *random,
158                                  uint32 random_len, const void *id,
159                                  SilcIdType type, uint32 *ret_len)
160 {
161   SilcBuffer buf;
162   unsigned char *pk, *id_data, *ret;
163   uint32 pk_len, id_len;
164
165   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
166   if (!pk)
167     return NULL;
168
169   id_data = silc_id_id2str(id, type);
170   if (!id_data) {
171     silc_free(pk);
172     return NULL;
173   }
174   id_len = silc_id_get_len(id, type);
175
176   buf = silc_buffer_alloc(random_len + id_len + pk_len);
177   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
178   silc_buffer_format(buf,
179                      SILC_STR_UI_XNSTRING(random, random_len),
180                      SILC_STR_UI_XNSTRING(id_data, id_len),
181                      SILC_STR_UI_XNSTRING(pk, pk_len),
182                      SILC_STR_END);
183   
184   ret = silc_calloc(buf->len + 1, sizeof(*ret));
185   memcpy(ret, buf->data, buf->len);
186
187   if (ret_len)
188     *ret_len = buf->len;
189
190   silc_buffer_free(buf);
191   silc_free(id_data);
192   silc_free(pk);
193
194   return ret;
195 }
196
197 /* Generates Authentication Payload with authentication data. This is used
198    to do public key based authentication. This generates the random data
199    and the actual authentication data. Returns NULL on error. */
200
201 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
202                                               SilcPrivateKey private_key,
203                                               SilcHash hash,
204                                               const void *id, SilcIdType type)
205 {
206   unsigned char *random;
207   unsigned char auth_data[1024];
208   uint32 auth_len;
209   unsigned char *tmp;
210   uint32 tmp_len;
211   SilcBuffer buf;
212   SilcPKCS pkcs;
213
214   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
215
216   /* Get 256 bytes of random data */
217   random = silc_rng_global_get_rn_data(256);
218   if (!random)
219     return NULL;
220   
221   /* Encode the auth data */
222   tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type, 
223                                          &tmp_len);
224   if (!tmp)
225     return NULL;
226
227   /* Allocate PKCS object */
228   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
229     memset(tmp, 0, tmp_len);
230     silc_free(tmp);
231     return NULL;
232   }
233   silc_pkcs_public_key_set(pkcs, public_key);
234   silc_pkcs_private_key_set(pkcs, private_key);
235
236   /* Compute the hash and the signature. */
237   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
238                                 &auth_len)) {
239     memset(random, 0, 256);
240     memset(tmp, 0, tmp_len);
241     silc_free(tmp);
242     silc_free(random);
243     silc_pkcs_free(pkcs);
244     return NULL;
245   }
246
247   /* Encode Authentication Payload */
248   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
249                                  auth_data, auth_len);
250
251   memset(tmp, 0, tmp_len);
252   memset(auth_data, 0, sizeof(auth_data));
253   memset(random, 0, 256);
254   silc_free(tmp);
255   silc_free(random);
256   silc_pkcs_free(pkcs);
257
258   return buf;
259 }
260
261 /* Verifies the authentication data. Returns TRUE if authentication was
262    successful. */
263
264 bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
265                                       SilcPublicKey public_key, SilcHash hash,
266                                       const void *id, SilcIdType type)
267 {
268   unsigned char *tmp;
269   uint32 tmp_len;
270   SilcPKCS pkcs;
271
272   SILC_LOG_DEBUG(("Verifying authentication data"));
273
274   /* Encode auth data */
275   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data, 
276                                          payload->random_len, 
277                                          id, type, &tmp_len);
278   if (!tmp) {
279     SILC_LOG_DEBUG(("Authentication failed"));
280     return FALSE;
281   }
282
283   /* Allocate PKCS object */
284   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
285     memset(tmp, 0, tmp_len);
286     silc_free(tmp);
287     return FALSE;
288   }
289   silc_pkcs_public_key_set(pkcs, public_key);
290
291   /* Verify the authentication data */
292   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
293                                   payload->auth_len, tmp, tmp_len)) {
294
295     memset(tmp, 0, tmp_len);
296     silc_free(tmp);
297     silc_pkcs_free(pkcs);
298     SILC_LOG_DEBUG(("Authentication failed"));
299     return FALSE;
300   }
301
302   memset(tmp, 0, tmp_len);
303   silc_free(tmp);
304   silc_pkcs_free(pkcs);
305
306   SILC_LOG_DEBUG(("Authentication successful"));
307
308   return TRUE;
309 }
310
311 /* Same as above but the payload is not parsed yet. This will parse it. */
312
313 bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
314                                            uint32 payload_len,
315                                            SilcPublicKey public_key, 
316                                            SilcHash hash,
317                                            const void *id, SilcIdType type)
318 {
319   SilcAuthPayload auth_payload;
320   int ret;
321
322   auth_payload = silc_auth_payload_parse(payload, 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 bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
344                       const void *auth_data, uint32 auth_data_len, 
345                       SilcHash hash, const 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, auth_data_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 bool silc_auth_verify_data(const unsigned char *payload, uint32 payload_len,
385                            SilcAuthMethod auth_method, const void *auth_data,
386                            uint32 auth_data_len, SilcHash hash, 
387                            const 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 
420 silc_key_agreement_payload_parse(const unsigned char *payload,
421                                  uint32 payload_len)
422 {
423   SilcBufferStruct buffer;
424   SilcKeyAgreementPayload new;
425   int ret;
426
427   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
428
429   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
430   new = silc_calloc(1, sizeof(*new));
431
432   /* Parse the payload */
433   ret = silc_buffer_unformat(&buffer, 
434                              SILC_STR_UI16_NSTRING_ALLOC(&new->hostname,
435                                                          &new->hostname_len),
436                              SILC_STR_UI_INT(&new->port),
437                              SILC_STR_END);
438   if (ret == -1) {
439     silc_free(new);
440     return NULL;
441   }
442
443   return new;
444 }
445
446 /* Encodes the Key Agreement protocol and returns the encoded buffer */
447
448 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
449                                              uint32 port)
450 {
451   SilcBuffer buffer;
452   uint32 len = hostname ? strlen(hostname) : 0;
453
454   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
455
456   buffer = silc_buffer_alloc(2 + len + 4);
457   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
458   silc_buffer_format(buffer,
459                      SILC_STR_UI_SHORT(len),
460                      SILC_STR_UI_XNSTRING(hostname, len),
461                      SILC_STR_UI_INT(port),
462                      SILC_STR_END);
463
464   return buffer;
465 }
466
467 /* Frees the Key Agreement protocol */
468
469 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
470 {
471   if (payload) {
472     silc_free(payload->hostname);
473     silc_free(payload);
474   }
475 }
476
477 /* Returns the hostname in the payload */
478
479 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
480 {
481   return payload->hostname;
482 }
483
484 /* Returns the port in the payload */
485
486 uint32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
487 {
488   return payload->port;
489 }