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