Use the EAP-SIM/AKA crypto helper in EAP-SIM
[strongswan.git] / src / libsimaka / simaka_message.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "simaka_message.h"
17
18 typedef struct private_simaka_message_t private_simaka_message_t;
19 typedef struct hdr_t hdr_t;
20 typedef struct attr_hdr_t attr_hdr_t;
21 typedef struct attr_t attr_t;
22
23 /**
24 * packed EAP-SIM/AKA header struct
25 */
26 struct hdr_t {
27 /** EAP code (REQUEST/RESPONSE) */
28 u_int8_t code;
29 /** unique message identifier */
30 u_int8_t identifier;
31 /** length of whole message */
32 u_int16_t length;
33 /** EAP type => EAP_SIM/EAP_AKA */
34 u_int8_t type;
35 /** SIM subtype */
36 u_int8_t subtype;
37 /** reserved bytes */
38 u_int16_t reserved;
39 } __attribute__((__packed__));
40
41 /**
42 * packed EAP-SIM/AKA attribute header struct
43 */
44 struct attr_hdr_t {
45 /** attribute type */
46 u_int8_t type;
47 /** attibute length */
48 u_int8_t length;
49 } __attribute__((__packed__));
50
51 /**
52 * SIM/AKA attribute, parsed
53 */
54 struct attr_t {
55 /** type of attribute */
56 simaka_attribute_t type;
57 /** length of data */
58 size_t len;
59 /** start of data, variable length */
60 char data[];
61 };
62
63 ENUM_BEGIN(simaka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
64 "AKA_CHALLENGE",
65 "AKA_AUTHENTICATION_REJECT",
66 "AKA_3",
67 "AKA_SYNCHRONIZATION_FAILURE",
68 "AKA_IDENTITY");
69 ENUM_NEXT(simaka_subtype_names, SIM_START, AKA_CLIENT_ERROR, AKA_IDENTITY,
70 "SIM_START",
71 "SIM_CHALLENGE",
72 "SIM/AKA_NOTIFICATION",
73 "SIM/AKA_REAUTHENTICATION",
74 "SIM/AKA_CLIENT_ERROR");
75 ENUM_END(simaka_subtype_names, AKA_CLIENT_ERROR);
76
77
78 ENUM_BEGIN(simaka_attribute_names, AT_RAND, AT_CLIENT_ERROR_CODE,
79 "AT_RAND",
80 "AT_AUTN",
81 "AT_RES",
82 "AT_AUTS",
83 "AT_5",
84 "AT_PADDING",
85 "AT_NONCE_MT",
86 "AT_8",
87 "AT_9",
88 "AT_PERMANENT_ID_REQ",
89 "AT_MAC",
90 "AT_NOTIFICATION",
91 "AT_ANY_ID_REQ",
92 "AT_IDENTITY",
93 "AT_VERSION_LIST",
94 "AT_SELECTED_VERSION",
95 "AT_FULLAUTH_ID_REQ",
96 "AT_18",
97 "AT_COUNTER",
98 "AT_COUNTER_TOO_SMALL",
99 "AT_NONCE_S",
100 "AT_CLIENT_ERROR_CODE");
101 ENUM_NEXT(simaka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
102 "AT_IV",
103 "AT_ENCR_DATA",
104 "AT_131",
105 "AT_NEXT_PSEUDONYM",
106 "AT_NEXT_REAUTH_ID",
107 "AT_CHECKCODE",
108 "AT_RESULT_IND");
109 ENUM_END(simaka_attribute_names, AT_RESULT_IND);
110
111 /**
112 * Private data of an simaka_message_t object.
113 */
114 struct private_simaka_message_t {
115
116 /**
117 * Public simaka_message_t interface.
118 */
119 simaka_message_t public;
120
121 /**
122 * EAP message, starting with EAP header
123 */
124 hdr_t *hdr;
125
126 /**
127 * List of parsed attributes, attr_t
128 */
129 linked_list_t *attributes;
130
131 /**
132 * Currently parsing AT_ENCR_DATA wrapped attributes?
133 */
134 bool encrypted;
135
136 /**
137 * Phase a NOTIFICATION is sent within */
138 bool p_bit;
139
140 /**
141 * MAC value, pointing into message
142 */
143 chunk_t mac;
144 };
145
146 /**
147 * Implementation of simaka_message_t.is_request
148 */
149 static bool is_request(private_simaka_message_t *this)
150 {
151 return this->hdr->code == EAP_REQUEST;
152 }
153
154 /**
155 * Implementation of simaka_message_t.get_identifier
156 */
157 static u_int8_t get_identifier(private_simaka_message_t *this)
158 {
159 return this->hdr->identifier;
160 }
161
162 /**
163 * Implementation of simaka_message_t.get_subtype
164 */
165 static simaka_subtype_t get_subtype(private_simaka_message_t *this)
166 {
167 return this->hdr->subtype;
168 }
169
170 /**
171 * Implementation of simaka_message_t.get_type
172 */
173 static eap_type_t get_type(private_simaka_message_t *this)
174 {
175 return this->hdr->type;
176 }
177
178 /**
179 * convert attr_t to type and data enumeration
180 */
181 static bool attr_enum_filter(void *null, attr_t **in, simaka_attribute_t *type,
182 void *dummy, chunk_t *data)
183 {
184 attr_t *attr = *in;
185
186 *type = attr->type;
187 *data = chunk_create(attr->data, attr->len);
188 return TRUE;
189 }
190
191 /**
192 * Implementation of simaka_message_t.create_attribute_enumerator
193 */
194 static enumerator_t* create_attribute_enumerator(private_simaka_message_t *this)
195 {
196 return enumerator_create_filter(
197 this->attributes->create_enumerator(this->attributes),
198 (void*)attr_enum_filter, NULL, NULL);
199 }
200
201 /**
202 * Implementation of simaka_message_t.add_attribute
203 */
204 static void add_attribute(private_simaka_message_t *this,
205 simaka_attribute_t type, chunk_t data)
206 {
207 attr_t *attr;
208
209 attr = malloc(sizeof(attr_t) + data.len);
210 attr->len = data.len;
211 attr->type = type;
212 memcpy(attr->data, data.ptr, data.len);
213
214 this->attributes->insert_last(this->attributes, attr);
215 }
216
217 /**
218 * Implementation of simaka_message_t.parse
219 */
220 static bool parse(private_simaka_message_t *this, simaka_crypto_t *crypto,
221 chunk_t sigdata)
222 {
223 chunk_t in, iv = chunk_empty, encr = chunk_empty;
224
225 in = chunk_create((char*)(this->hdr + 1),
226 ntohs(this->hdr->length) - sizeof(hdr_t));
227
228 while (in.len)
229 {
230 attr_hdr_t *hdr;
231 chunk_t data;
232
233 if (in.len < sizeof(attr_hdr_t))
234 {
235 DBG1(DBG_IKE, "found short %N attribute header",
236 eap_type_names, this->hdr->type);
237 return FALSE;
238 }
239 hdr = (attr_hdr_t*)in.ptr;
240
241 switch (hdr->type)
242 {
243 /* attributes without data */
244 case AT_COUNTER_TOO_SMALL:
245 if (!this->encrypted)
246 {
247 return FALSE;
248 }
249 /* FALL */
250 case AT_ANY_ID_REQ:
251 case AT_PERMANENT_ID_REQ:
252 case AT_FULLAUTH_ID_REQ:
253 {
254 if (hdr->length != 1 || in.len < 4)
255 {
256 return FALSE;
257 }
258 data = chunk_empty;
259 in = chunk_skip(in, 4);
260 break;
261 }
262 /* attributes with two bytes data */
263 case AT_COUNTER:
264 if (!this->encrypted)
265 {
266 return FALSE;
267 }
268 /* FALL */
269 case AT_CLIENT_ERROR_CODE:
270 case AT_SELECTED_VERSION:
271 case AT_NOTIFICATION:
272 {
273 if (hdr->length != 1 || in.len < 4)
274 {
275 return FALSE;
276 }
277 data = chunk_create(in.ptr + 2, 2);
278 in = chunk_skip(in, 4);
279 break;
280 }
281 /* attributes with an additional actual-length */
282 case AT_NEXT_PSEUDONYM:
283 case AT_NEXT_REAUTH_ID:
284 if (!this->encrypted)
285 {
286 return FALSE;
287 }
288 /* FALL */
289 case AT_IDENTITY:
290 case AT_VERSION_LIST:
291 {
292 u_int16_t len;
293
294 if (hdr->length < 1 || in.len < 4)
295 {
296 return FALSE;
297 }
298 memcpy(&len, in.ptr + 2, 2);
299 len = ntohs(len);
300 if (len > hdr->length * 4 || len > in.len)
301 {
302 return FALSE;
303 }
304 data = chunk_create(in.ptr + 4, len);
305 in = chunk_skip(in, hdr->length * 4);
306 break;
307 }
308 /* attributes with two reserved bytes, 16 bytes length */
309 case AT_NONCE_S:
310 if (!this->encrypted)
311 {
312 return FALSE;
313 }
314 /* FALL */
315 case AT_AUTN:
316 case AT_RES:
317 case AT_NONCE_MT:
318 case AT_IV:
319 case AT_MAC:
320 {
321 if (hdr->length != 5 || in.len < 20)
322 {
323 return FALSE;
324 }
325 data = chunk_create(in.ptr + 4, 16);
326 in = chunk_skip(in, 20);
327 break;
328 }
329 /* attributes with two reserved bytes, variable length */
330 case AT_ENCR_DATA:
331 case AT_RAND:
332 {
333 if (hdr->length * 4 > in.len || in.len < 4)
334 {
335 return FALSE;
336 }
337 data = chunk_create(in.ptr + 4, hdr->length * 4 - 4);
338 in = chunk_skip(in, hdr->length * 4);
339 break;
340 }
341 /* attributes with no reserved bytes, 14 bytes length */
342 case AT_AUTS:
343 {
344 if (hdr->length != 4 || in.len < 16)
345 {
346 return FALSE;
347 }
348 data = chunk_create(in.ptr + 2, 14);
349 in = chunk_skip(in, 16);
350 break;
351 }
352 /* other attributes (with 4n + 2 length) */
353 case AT_PADDING:
354 default:
355 {
356 if (hdr->length * 4 > in.len || in.len < 4)
357 {
358 return FALSE;
359 }
360 data = chunk_create(in.ptr + 2, hdr->length * 4 - 2);
361 in = chunk_skip(in, hdr->length * 4);
362 break;
363 }
364 }
365
366 /* handle special attributes */
367 switch (hdr->type)
368 {
369 case AT_MAC:
370 this->mac = data;
371 break;
372 case AT_IV:
373 iv = data;
374 break;
375 case AT_ENCR_DATA:
376 encr = data;
377 break;
378 case AT_PADDING:
379 break;
380 case AT_NOTIFICATION:
381 if (this->p_bit)
382 { /* remember P bit for MAC verification */
383 this->p_bit = !!(data.ptr[0] & 0x40);
384 }
385 else if (!this->encrypted)
386 { /* found a P bit notify in an unencrypted message */
387 return FALSE;
388 }
389 /* FALL */
390 default:
391 add_attribute(this, hdr->type, data);
392 break;
393 }
394 }
395
396 /* decrypt, invoke parser recursively */
397 if (iv.len && encr.len)
398 {
399 bool success;
400 crypter_t *crypter;
401
402 if (this->encrypted)
403 {
404 DBG1(DBG_IKE, "%N message is recursively encrypted",
405 eap_type_names, this->hdr->type);
406 return FALSE;
407 }
408 crypter = crypto->get_crypter(crypto);
409 if (!crypter)
410 {
411 DBG1(DBG_IKE, "%N message contains unexpected encrypted data",
412 eap_type_names, this->hdr->type);
413 return FALSE;
414 }
415 if (encr.len % crypter->get_block_size(crypter))
416 {
417 DBG1(DBG_IKE, "%N ENCR_DATA not a multiple of block size",
418 eap_type_names, this->hdr->type);
419 return FALSE;
420 }
421
422 /* decrypt inline */
423 crypter->decrypt(crypter, encr, iv, NULL);
424
425 this->encrypted = TRUE;
426 success = parse(this, crypto, chunk_empty);
427 this->encrypted = FALSE;
428 return success;
429 }
430 return TRUE;
431 }
432
433 /**
434 * Implementation of simaka_message_t.verify
435 */
436 static bool verify(private_simaka_message_t *this,
437 simaka_crypto_t *crypto, chunk_t sigdata)
438 {
439 chunk_t data, backup;
440 signer_t *signer;
441
442 signer = crypto->get_signer(crypto);
443
444 switch (this->hdr->subtype)
445 {
446 case SIM_START:
447 case SIM_CLIENT_ERROR:
448 /* AKA_CLIENT_ERROR: */
449 case AKA_AUTHENTICATION_REJECT:
450 case AKA_SYNCHRONIZATION_FAILURE:
451 case AKA_IDENTITY:
452 {
453 if (this->mac.ptr)
454 { /* invalid if it contains a MAC */
455 return FALSE;
456 }
457 return TRUE;
458 }
459 case SIM_CHALLENGE:
460 /* AKA_CHALLENGE: */
461 case SIM_REAUTHENTICATION:
462 /* AKA_REAUTHENTICATION: */
463 {
464 if (!this->mac.ptr || !signer)
465 { /* require MAC, but not found */
466 return FALSE;
467 }
468 break;
469 }
470 case SIM_NOTIFICATION:
471 /* AKA_NOTIFICATION: */
472 {
473 if (this->p_bit)
474 { /* MAC not verified if in Phase 1 */
475 return TRUE;
476 }
477 if (!this->mac.ptr || !signer)
478 { /* require MAC, but not found */
479 return FALSE;
480 }
481 break;
482 }
483 default:
484 /* unknown message? */
485 return FALSE;
486 }
487
488 /* zero MAC for verification */
489 backup = chunk_clonea(this->mac);
490 memset(this->mac.ptr, 0, this->mac.len);
491
492 data = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
493 if (sigdata.len)
494 {
495 data = chunk_cata("cc", data, sigdata);
496 }
497 if (!signer->verify_signature(signer, data, backup))
498 {
499 DBG1(DBG_IKE, "%N MAC verification failed",
500 eap_type_names, this->hdr->type);
501 return FALSE;
502 }
503 return TRUE;
504 }
505
506 /**
507 * Implementation of simaka_message_t.generate
508 */
509 static eap_payload_t* generate(private_simaka_message_t *this,
510 simaka_crypto_t *crypto, chunk_t sigdata)
511 {
512 /* buffers large enough for messages we generate */
513 char out_buf[1024], encr_buf[512];
514 enumerator_t *enumerator;
515 chunk_t out, encr, data, *target, mac = chunk_empty;
516 simaka_attribute_t type;
517 attr_hdr_t *hdr;
518 u_int16_t len;
519 signer_t *signer;
520
521 out = chunk_create(out_buf, sizeof(out_buf));
522 encr = chunk_create(encr_buf, sizeof(encr_buf));
523
524 /* copy header */
525 memcpy(out.ptr, this->hdr, sizeof(hdr_t));
526 out = chunk_skip(out, sizeof(hdr_t));
527
528 /* encode attributes */
529 enumerator = create_attribute_enumerator(this);
530 while (enumerator->enumerate(enumerator, &type, &data))
531 {
532 /* encrypt this attribute? */
533 switch (type)
534 {
535 case AT_NONCE_S:
536 case AT_NEXT_PSEUDONYM:
537 case AT_NEXT_REAUTH_ID:
538 case AT_COUNTER:
539 case AT_COUNTER_TOO_SMALL:
540 target = &encr;
541 break;
542 case AT_NOTIFICATION:
543 /* P bit not set, encrypt */
544 if (!(data.ptr[0] & 0x40))
545 {
546 target = &encr;
547 break;
548 }
549 /* FALL */
550 default:
551 target = &out;
552 break;
553 }
554
555 hdr = (attr_hdr_t*)target->ptr;
556 hdr->type = type;
557
558 /* encode type specific */
559 switch (type)
560 {
561 /* attributes without data */
562 case AT_COUNTER_TOO_SMALL:
563 case AT_ANY_ID_REQ:
564 case AT_PERMANENT_ID_REQ:
565 case AT_FULLAUTH_ID_REQ:
566 {
567 hdr->length = 1;
568 memset(target->ptr + 2, 0, 2);
569 *target = chunk_skip(*target, 4);
570 break;
571 }
572 /* attributes with two bytes data */
573 case AT_COUNTER:
574 case AT_CLIENT_ERROR_CODE:
575 case AT_SELECTED_VERSION:
576 case AT_NOTIFICATION:
577 {
578 hdr->length = 1;
579 memcpy(target->ptr + 2, data.ptr, 2);
580 *target = chunk_skip(*target, 4);
581 break;
582 }
583 /* attributes with an additional actual-length */
584 case AT_NEXT_PSEUDONYM:
585 case AT_NEXT_REAUTH_ID:
586 case AT_IDENTITY:
587 case AT_VERSION_LIST:
588 {
589 u_int16_t len, padding;
590
591 len = htons(data.len);
592 memcpy(target->ptr + 2, &len, sizeof(len));
593 memcpy(target->ptr + 4, data.ptr, data.len);
594 hdr->length = data.len / 4 + 1;
595 padding = (4 - (data.len % 4)) % 4;
596 if (padding)
597 {
598 hdr->length++;
599 memset(target->ptr + 4 + data.len, 0, padding);
600 }
601 *target = chunk_skip(*target, hdr->length * 4);
602 break;
603 }
604 /* attributes with two reserved bytes, 16 bytes length */
605 case AT_NONCE_S:
606 case AT_NONCE_MT:
607 case AT_AUTN:
608 case AT_RES:
609 {
610 hdr->length = 5;
611 memset(target->ptr + 2, 0, 2);
612 memcpy(target->ptr + 4, data.ptr, data.len);
613 *target = chunk_skip(*target, 20);
614 break;
615 }
616 /* attributes with two reserved bytes, variable length */
617 case AT_RAND:
618 {
619 hdr->length = 1 + data.len / 4;
620 memset(target->ptr + 2, 0, 2);
621 memcpy(target->ptr + 4, data.ptr, data.len);
622 *target = chunk_skip(*target, data.len + 4);
623 break;
624 }
625 /* attributes with no reserved bytes, 14 bytes length */
626 case AT_AUTS:
627 {
628 hdr->length = 4;
629 memcpy(target->ptr + 2, data.ptr, data.len);
630 *target = chunk_skip(*target, 16);
631 break;
632 }
633 default:
634 {
635 DBG1(DBG_IKE, "no rule to encode %N, skipped",
636 simaka_attribute_names, type);
637 break;
638 }
639 }
640 }
641 enumerator->destroy(enumerator);
642
643 /* encrypt attributes, if any */
644 if (encr.len < sizeof(encr_buf))
645 {
646 chunk_t iv;
647 size_t bs;
648 crypter_t *crypter;
649 rng_t *rng;
650
651 crypter = crypto->get_crypter(crypto);
652 encr = chunk_create(encr_buf, sizeof(encr_buf) - encr.len);
653 bs = crypter->get_block_size(crypter);
654
655 /* add IV attribute */
656 hdr = (attr_hdr_t*)out.ptr;
657 hdr->type = AT_IV;
658 hdr->length = bs / 4 + 1;
659 memset(out.ptr + 2, 0, 2);
660 out = chunk_skip(out, 4);
661
662 rng = crypto->get_rng(crypto);
663 rng->get_bytes(rng, bs, out.ptr);
664
665 iv = chunk_clonea(chunk_create(out.ptr, bs));
666 out = chunk_skip(out, bs);
667
668 /* inline encryption */
669 crypter->encrypt(crypter, encr, iv, NULL);
670
671 /* add ENCR_DATA attribute */
672 hdr = (attr_hdr_t*)out.ptr;
673 hdr->type = AT_ENCR_DATA;
674 hdr->length = encr.len / 4 + 1;
675 memset(out.ptr + 2, 0, 2);
676 memcpy(out.ptr + 4, encr.ptr, encr.len);
677 out = chunk_skip(out, encr.len + 4);
678 }
679
680 /* include MAC ? */
681 signer = crypto->get_signer(crypto);
682 switch (this->hdr->subtype)
683 {
684 case SIM_CHALLENGE:
685 /* AKA_CHALLENGE: */
686 case SIM_REAUTHENTICATION:
687 /* AKA_REAUTHENTICATION: */
688 /* TODO: Notifications without P bit */
689 {
690 size_t bs;
691
692 bs = signer->get_block_size(signer);
693 hdr = (attr_hdr_t*)out.ptr;
694 hdr->type = AT_MAC;
695 hdr->length = bs / 4 + 1;
696 memset(out.ptr + 2, 0, 2 + bs);
697 mac = chunk_create(out.ptr + 4, bs);
698 out = chunk_skip(out, bs + 4);
699 break;
700 }
701 default:
702 break;
703 }
704
705 /* calculate message length */
706 out = chunk_create(out_buf, sizeof(out_buf) - out.len);
707 len = htons(out.len);
708 memcpy(out.ptr + 2, &len, sizeof(len));
709
710 /* generate MAC */
711 if (mac.len)
712 {
713 data = chunk_cata("cc", out, sigdata);
714 signer->get_signature(signer, data, mac.ptr);
715 }
716 return eap_payload_create_data(out);
717 }
718
719 /**
720 * Implementation of simaka_message_t.destroy.
721 */
722 static void destroy(private_simaka_message_t *this)
723 {
724 this->attributes->destroy_function(this->attributes, free);
725 free(this->hdr);
726 free(this);
727 }
728
729 /**
730 * Generic constructor.
731 */
732 static simaka_message_t *simaka_message_create_data(chunk_t data)
733 {
734 private_simaka_message_t *this;
735 hdr_t *hdr = (hdr_t*)data.ptr;
736
737 if (data.len < sizeof(hdr_t) || hdr->length != htons(data.len))
738 {
739 DBG1(DBG_IKE, "EAP-SIM/AKA header has invalid length");
740 return NULL;
741 }
742 if (hdr->code != EAP_REQUEST && hdr->code != EAP_RESPONSE)
743 {
744 DBG1(DBG_IKE, "invalid EAP code in EAP-SIM/AKA message",
745 eap_type_names, hdr->type);
746 return NULL;
747 }
748 if (hdr->type != EAP_SIM && hdr->type != EAP_AKA)
749 {
750 DBG1(DBG_IKE, "invalid EAP type in EAP-SIM/AKA message",
751 eap_type_names, hdr->type);
752 return NULL;
753 }
754
755 this = malloc_thing(private_simaka_message_t);
756
757 this->public.is_request = (bool(*)(simaka_message_t*))is_request;
758 this->public.get_identifier = (u_int8_t(*)(simaka_message_t*))get_identifier;
759 this->public.get_type = (eap_type_t(*)(simaka_message_t*))get_type;
760 this->public.get_subtype = (simaka_subtype_t(*)(simaka_message_t*))get_subtype;
761 this->public.create_attribute_enumerator = (enumerator_t*(*)(simaka_message_t*))create_attribute_enumerator;
762 this->public.add_attribute = (void(*)(simaka_message_t*, simaka_attribute_t type, chunk_t data))add_attribute;
763 this->public.parse = (bool(*)(simaka_message_t*, simaka_crypto_t* crypto))parse;
764 this->public.verify = (bool(*)(simaka_message_t*, simaka_crypto_t* crypto, chunk_t sigdata))verify;
765 this->public.generate = (eap_payload_t*(*)(simaka_message_t*, simaka_crypto_t* crypto, chunk_t sigdata))generate;
766 this->public.destroy = (void(*)(simaka_message_t*))destroy;
767
768 this->attributes = linked_list_create();
769 this->encrypted = FALSE;
770 this->p_bit = TRUE;
771 this->mac = chunk_empty;
772 this->hdr = malloc(data.len);
773 memcpy(this->hdr, hdr, data.len);
774
775 return &this->public;
776 }
777
778 /**
779 * See header.
780 */
781 simaka_message_t *simaka_message_create_from_payload(eap_payload_t *payload)
782 {
783 return simaka_message_create_data(payload->get_data(payload));
784 }
785
786 /**
787 * See header.
788 */
789 simaka_message_t *simaka_message_create(bool request, u_int8_t identifier,
790 eap_type_t type, simaka_subtype_t subtype)
791 {
792 hdr_t hdr = {
793 .code = request ? EAP_REQUEST : EAP_RESPONSE,
794 .identifier = identifier,
795 .length = htons(sizeof(hdr_t)),
796 .type = type,
797 .subtype = subtype,
798 };
799 return simaka_message_create_data(chunk_create((char*)&hdr, sizeof(hdr)));
800 }
801