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