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