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