1 /*
2 
3   silcauth.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2001 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
17 
18 */
19 /* $Id$ */
20 
21 #include "silc.h"
22 #include "silcauth.h"
23 
24 /******************************************************************************
25 
26                            Authentication Payload
27 
28 ******************************************************************************/
29 
30 /* Authentication Payload structure */
31 struct SilcAuthPayloadStruct {
32   SilcUInt16 len;
33   SilcUInt16 auth_method;
34   SilcUInt16 random_len;
35   unsigned char *random_data;
36   SilcUInt16 auth_len;
37   unsigned char *auth_data;
38 };
39 
40 /* Parses and returns Authentication Payload */
41 
silc_auth_payload_parse(const unsigned char * data,SilcUInt32 data_len)42 SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
43 					SilcUInt32 data_len)
44 {
45   SilcBufferStruct buffer;
46   SilcAuthPayload newp;
47   int ret;
48 
49   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
50 
51   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
52   newp = silc_calloc(1, sizeof(*newp));
53   if (!newp)
54     return NULL;
55 
56   /* Parse the payload */
57   ret = silc_buffer_unformat(&buffer,
58 			     SILC_STR_UI_SHORT(&newp->len),
59 			     SILC_STR_UI_SHORT(&newp->auth_method),
60 			     SILC_STR_UI16_NSTRING_ALLOC(&newp->random_data,
61 							 &newp->random_len),
62 			     SILC_STR_UI16_NSTRING_ALLOC(&newp->auth_data,
63 							 &newp->auth_len),
64 			     SILC_STR_END);
65   if (ret == -1) {
66     silc_free(newp);
67     return NULL;
68   }
69 
70   if (newp->len != silc_buffer_len(&buffer) ||
71       newp->random_len + newp->auth_len > silc_buffer_len(&buffer) - 8) {
72     silc_auth_payload_free(newp);
73     return NULL;
74   }
75 
76   /* Authentication data must be provided */
77   if (newp->auth_len < 1)  {
78     silc_auth_payload_free(newp);
79     return NULL;
80   }
81 
82   /* If password authentication, random data must not be set */
83   if (newp->auth_method == SILC_AUTH_PASSWORD && newp->random_len) {
84     silc_auth_payload_free(newp);
85     return NULL;
86   }
87 
88   /* If public key authentication, random data must be at least 128 bytes */
89   if (newp->auth_method == SILC_AUTH_PUBLIC_KEY && newp->random_len < 128) {
90     silc_auth_payload_free(newp);
91     return NULL;
92   }
93 
94   return newp;
95 }
96 
97 /* Encodes authentication payload into buffer and returns it */
98 
silc_auth_payload_encode(SilcAuthMethod method,const unsigned char * random_data,SilcUInt16 random_len,const unsigned char * auth_data,SilcUInt16 auth_len)99 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
100 				    const unsigned char *random_data,
101 				    SilcUInt16 random_len,
102 				    const unsigned char *auth_data,
103 				    SilcUInt16 auth_len)
104 {
105   SilcBuffer buffer;
106   SilcUInt32 len;
107   unsigned char *autf8 = NULL;
108   SilcUInt32 autf8_len;
109 
110   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
111 
112   /* Passphrase MUST be UTF-8 encoded, encode if it is not */
113   if (method == SILC_AUTH_PASSWORD && !silc_utf8_valid(auth_data, auth_len)) {
114     autf8_len = silc_utf8_encoded_len(auth_data, auth_len, 0);
115     if (!autf8_len)
116       return NULL;
117     autf8 = silc_calloc(autf8_len, sizeof(*autf8));
118     auth_len = silc_utf8_encode(auth_data, auth_len, 0, autf8, autf8_len);
119     auth_data = (const unsigned char *)autf8;
120   }
121 
122   len = 2 + 2 + 2 + random_len + 2 + auth_len;
123   buffer = silc_buffer_alloc_size(len);
124   if (!buffer) {
125     silc_free(autf8);
126     return NULL;
127   }
128 
129   silc_buffer_format(buffer,
130 		     SILC_STR_UI_SHORT(len),
131 		     SILC_STR_UI_SHORT(method),
132 		     SILC_STR_UI_SHORT(random_len),
133 		     SILC_STR_UI_XNSTRING(random_data, random_len),
134 		     SILC_STR_UI_SHORT(auth_len),
135 		     SILC_STR_UI_XNSTRING(auth_data, auth_len),
136 		     SILC_STR_END);
137 
138   silc_free(autf8);
139   return buffer;
140 }
141 
142 /* Frees authentication payload. */
143 
silc_auth_payload_free(SilcAuthPayload payload)144 void silc_auth_payload_free(SilcAuthPayload payload)
145 {
146   if (payload) {
147     if (payload->random_data) {
148       memset(payload->random_data, 0, payload->random_len);
149       silc_free(payload->random_data);
150     }
151     if (payload->auth_data) {
152       memset(payload->auth_data, 0, payload->auth_len);
153       silc_free(payload->auth_data);
154     }
155     silc_free(payload);
156   }
157 }
158 
159 /* Get authentication method */
160 
silc_auth_get_method(SilcAuthPayload payload)161 SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
162 {
163   return payload->auth_method;
164 }
165 
166 /* Get the public data from the auth payload. */
167 
silc_auth_get_public_data(SilcAuthPayload payload,SilcUInt32 * pubdata_len)168 unsigned char *silc_auth_get_public_data(SilcAuthPayload payload,
169 					 SilcUInt32 *pubdata_len)
170 {
171   if (pubdata_len)
172     *pubdata_len = (SilcUInt32)payload->random_len;
173 
174   return payload->random_data;
175 }
176 
177 /* Get the authentication data. If this is passphrase it is UTF-8 encoded. */
178 
silc_auth_get_data(SilcAuthPayload payload,SilcUInt32 * auth_len)179 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
180 				  SilcUInt32 *auth_len)
181 {
182   if (auth_len)
183     *auth_len = (SilcUInt32)payload->auth_len;
184 
185   return payload->auth_data;
186 }
187 
188 /******************************************************************************
189 
190                            Authentication Routines
191 
192 ******************************************************************************/
193 
194 /* Encodes the authentication data for hashing and signing as the protocol
195    dictates. */
196 
197 static unsigned char *
silc_auth_public_key_encode_data(SilcPublicKey public_key,const unsigned char * randomdata,SilcUInt32 random_len,const void * id,SilcIdType type,SilcUInt32 * ret_len)198 silc_auth_public_key_encode_data(SilcPublicKey public_key,
199 				 const unsigned char *randomdata,
200 				 SilcUInt32 random_len, const void *id,
201 				 SilcIdType type, SilcUInt32 *ret_len)
202 {
203   SilcBuffer buf;
204   unsigned char *pk, id_data[32], *ret;
205   SilcUInt32 pk_len, id_len;
206 
207   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
208   if (!pk)
209     return NULL;
210 
211   if (!silc_id_id2str(id, type, id_data, sizeof(id_data), &id_len)) {
212     silc_free(pk);
213     return NULL;
214   }
215 
216   buf = silc_buffer_alloc_size(random_len + id_len + pk_len);
217   if (!buf) {
218     silc_free(pk);
219     return NULL;
220   }
221   silc_buffer_format(buf,
222 		     SILC_STR_UI_XNSTRING(randomdata, random_len),
223 		     SILC_STR_UI_XNSTRING(id_data, id_len),
224 		     SILC_STR_UI_XNSTRING(pk, pk_len),
225 		     SILC_STR_END);
226 
227   ret = silc_buffer_steal(buf, ret_len);
228 
229   silc_buffer_free(buf);
230   silc_free(pk);
231 
232   return ret;
233 }
234 
235 /* Generates Authentication Payload with authentication data. This is used
236    to do public key based authentication. This generates the random data
237    and the actual authentication data. Returns NULL on error. */
238 
silc_auth_public_key_auth_generate(SilcPublicKey public_key,SilcPrivateKey private_key,SilcRng rng,SilcHash hash,const void * id,SilcIdType type)239 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
240 					      SilcPrivateKey private_key,
241 					      SilcRng rng, SilcHash hash,
242 					      const void *id, SilcIdType type)
243 {
244   unsigned char *randomdata;
245   SilcBuffer buf;
246 
247   /* Get 256 bytes of random data */
248   if (rng)
249     randomdata = silc_rng_get_rn_data(rng, 256);
250   else
251     randomdata = silc_rng_global_get_rn_data(256);
252   if (!randomdata)
253     return NULL;
254 
255   buf = silc_auth_public_key_auth_generate_wpub(public_key, private_key,
256 						randomdata, 256, hash,
257 						id, type);
258 
259   memset(randomdata, 0, 256);
260   silc_free(randomdata);
261 
262   return buf;
263 }
264 
265 /* Generates Authentication Payload with authentication data. This is used
266    to do public key based authentication. This generates the random data
267    and the actual authentication data. Returns NULL on error. */
268 
269 SilcBuffer
silc_auth_public_key_auth_generate_wpub(SilcPublicKey public_key,SilcPrivateKey private_key,const unsigned char * pubdata,SilcUInt32 pubdata_len,SilcHash hash,const void * id,SilcIdType type)270 silc_auth_public_key_auth_generate_wpub(SilcPublicKey public_key,
271 					SilcPrivateKey private_key,
272 					const unsigned char *pubdata,
273 					SilcUInt32 pubdata_len,
274 					SilcHash hash,
275 					const void *id, SilcIdType type)
276 {
277   unsigned char auth_data[65536 + 1];
278   SilcUInt32 auth_len;
279   unsigned char *tmp;
280   SilcUInt32 tmp_len;
281   SilcBuffer buf;
282 
283   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
284 
285   /* Encode the auth data */
286   tmp = silc_auth_public_key_encode_data(public_key, pubdata, pubdata_len, id,
287 					 type, &tmp_len);
288   if (!tmp)
289     return NULL;
290 
291   /* Compute the hash and the signature. */
292   if (!silc_pkcs_sign(private_key, tmp, tmp_len, auth_data,
293 		      sizeof(auth_data) - 1, &auth_len, TRUE, hash)) {
294     memset(tmp, 0, tmp_len);
295     silc_free(tmp);
296     return NULL;
297   }
298 
299   /* Encode Authentication Payload */
300   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, pubdata, pubdata_len,
301 				 auth_data, auth_len);
302 
303   memset(tmp, 0, tmp_len);
304   memset(auth_data, 0, sizeof(auth_data));
305   silc_free(tmp);
306 
307   return buf;
308 }
309 
310 /* Verifies the authentication data. Returns TRUE if authentication was
311    successful. */
312 
silc_auth_public_key_auth_verify(SilcAuthPayload payload,SilcPublicKey public_key,SilcHash hash,const void * id,SilcIdType type)313 SilcBool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
314 					  SilcPublicKey public_key,
315 					  SilcHash hash,
316 					  const void *id, SilcIdType type)
317 {
318   unsigned char *tmp;
319   SilcUInt32 tmp_len;
320 
321   SILC_LOG_DEBUG(("Verifying authentication data"));
322 
323   /* Encode auth data */
324   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data,
325 					 payload->random_len,
326 					 id, type, &tmp_len);
327   if (!tmp) {
328     SILC_LOG_DEBUG(("Authentication failed"));
329     return FALSE;
330   }
331 
332   /* Verify the authentication data */
333   if (!silc_pkcs_verify(public_key, payload->auth_data,
334 			payload->auth_len, tmp, tmp_len, hash)) {
335 
336     memset(tmp, 0, tmp_len);
337     silc_free(tmp);
338     SILC_LOG_DEBUG(("Authentication failed"));
339     return FALSE;
340   }
341 
342   memset(tmp, 0, tmp_len);
343   silc_free(tmp);
344 
345   SILC_LOG_DEBUG(("Authentication successful"));
346 
347   return TRUE;
348 }
349 
350 /* Same as above but the payload is not parsed yet. This will parse it. */
351 
silc_auth_public_key_auth_verify_data(const unsigned char * payload,SilcUInt32 payload_len,SilcPublicKey public_key,SilcHash hash,const void * id,SilcIdType type)352 SilcBool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
353 					       SilcUInt32 payload_len,
354 					       SilcPublicKey public_key,
355 					       SilcHash hash,
356 					       const void *id, SilcIdType type)
357 {
358   SilcAuthPayload auth_payload;
359   int ret;
360 
361   auth_payload = silc_auth_payload_parse(payload, payload_len);
362   if (!auth_payload) {
363     SILC_LOG_DEBUG(("Authentication failed"));
364     return FALSE;
365   }
366 
367   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash,
368 					 id, type);
369 
370   silc_auth_payload_free(auth_payload);
371 
372   return ret;
373 }
374 
375 /* Verifies the authentication data directly from the Authentication
376    Payload. Supports all authentication methods. If the authentication
377    method is passphrase based then the `auth_data' and `auth_data_len'
378    are the passphrase and its length. If the method is public key
379    authentication then the `auth_data' is the SilcPublicKey and the
380    `auth_data_len' is ignored. */
381 
silc_auth_verify(SilcAuthPayload payload,SilcAuthMethod auth_method,const void * auth_data,SilcUInt32 auth_data_len,SilcHash hash,const void * id,SilcIdType type)382 SilcBool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
383 			  const void *auth_data, SilcUInt32 auth_data_len,
384 			  SilcHash hash, const void *id, SilcIdType type)
385 {
386   SILC_LOG_DEBUG(("Verifying authentication"));
387 
388   if (!payload || auth_method != payload->auth_method)
389     return FALSE;
390 
391   switch (payload->auth_method) {
392   case SILC_AUTH_NONE:
393     /* No authentication */
394     SILC_LOG_DEBUG(("No authentication required"));
395     return TRUE;
396 
397   case SILC_AUTH_PASSWORD:
398     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
399        arguments are not needed. */
400 
401     /* Sanity checks */
402     if ((payload->auth_len == 0) || !auth_data ||
403 	payload->auth_len != auth_data_len)
404       break;
405 
406     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
407       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
408       return TRUE;
409     }
410     break;
411 
412   case SILC_AUTH_PUBLIC_KEY:
413     /* Public key based authentication */
414     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
415 					    hash, id, type);
416     break;
417 
418   default:
419     break;
420   }
421 
422   SILC_LOG_DEBUG(("Authentication failed"));
423 
424   return FALSE;
425 }
426 
427 /* Same as above but parses the authentication payload before verify. */
428 
silc_auth_verify_data(const unsigned char * payload,SilcUInt32 payload_len,SilcAuthMethod auth_method,const void * auth_data,SilcUInt32 auth_data_len,SilcHash hash,const void * id,SilcIdType type)429 SilcBool silc_auth_verify_data(const unsigned char *payload,
430 			       SilcUInt32 payload_len,
431 			       SilcAuthMethod auth_method,
432 			       const void *auth_data,
433 			       SilcUInt32 auth_data_len, SilcHash hash,
434 			       const void *id, SilcIdType type)
435 {
436   SilcAuthPayload auth_payload;
437   SilcBool ret;
438 
439   auth_payload = silc_auth_payload_parse(payload, payload_len);
440   if (!auth_payload || (auth_payload->auth_len == 0))
441     return FALSE;
442 
443   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
444 			 hash, id, type);
445 
446   silc_auth_payload_free(auth_payload);
447 
448   return ret;
449 }
450 
451 /******************************************************************************
452 
453                             Key Agreement Payload
454 
455 ******************************************************************************/
456 
457 /* The Key Agreement protocol structure */
458 struct SilcKeyAgreementPayloadStruct {
459   SilcUInt16 hostname_len;
460   unsigned char *hostname;
461   SilcUInt16 protocol;
462   SilcUInt16 port;
463 };
464 
465 /* Parses and returns an allocated Key Agreement payload. */
466 
467 SilcKeyAgreementPayload
silc_key_agreement_payload_parse(const unsigned char * payload,SilcUInt32 payload_len)468 silc_key_agreement_payload_parse(const unsigned char *payload,
469 				 SilcUInt32 payload_len)
470 {
471   SilcBufferStruct buffer;
472   SilcKeyAgreementPayload newp;
473   int ret;
474 
475   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
476 
477   newp = silc_calloc(1, sizeof(*newp));
478   if (!newp)
479     return NULL;
480 
481   /* Parse the payload */
482   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
483   ret = silc_buffer_unformat(&buffer,
484 			     SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
485 							 &newp->hostname_len),
486 			     SILC_STR_UI_SHORT(&newp->protocol),
487 			     SILC_STR_UI_SHORT(&newp->port),
488 			     SILC_STR_END);
489   if (ret == -1 || newp->hostname_len > silc_buffer_len(&buffer) - 6) {
490     silc_free(newp);
491     return NULL;
492   }
493 
494   return newp;
495 }
496 
497 /* Encodes the Key Agreement protocol and returns the encoded buffer */
498 
silc_key_agreement_payload_encode(const char * hostname,SilcUInt16 protocol,SilcUInt16 port)499 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
500 					     SilcUInt16 protocol,
501 					     SilcUInt16 port)
502 {
503   SilcBuffer buffer;
504   SilcUInt32 len = hostname ? strlen(hostname) : 0;
505 
506   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
507 
508   buffer = silc_buffer_alloc_size(2 + len + 4);
509   if (!buffer)
510     return NULL;
511   silc_buffer_format(buffer,
512 		     SILC_STR_UI_SHORT(len),
513 		     SILC_STR_UI_XNSTRING(hostname, len),
514 		     SILC_STR_UI_SHORT(protocol),
515 		     SILC_STR_UI_SHORT(port),
516 		     SILC_STR_END);
517 
518   return buffer;
519 }
520 
521 /* Frees the Key Agreement protocol */
522 
silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)523 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
524 {
525   if (payload) {
526     silc_free(payload->hostname);
527     silc_free(payload);
528   }
529 }
530 
531 /* Returns the hostname in the payload */
532 
silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)533 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
534 {
535   return payload->hostname;
536 }
537 
538 /* Returns the protocol in the payload */
539 
silc_key_agreement_get_protocol(SilcKeyAgreementPayload payload)540 SilcUInt16 silc_key_agreement_get_protocol(SilcKeyAgreementPayload payload)
541 {
542   return payload->protocol;
543 }
544 
545 /* Returns the port in the payload */
546 
silc_key_agreement_get_port(SilcKeyAgreementPayload payload)547 SilcUInt16 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
548 {
549   return payload->port;
550 }
551