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