/* * Copyright (C) 2011-2014 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2010 revosec AG * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include "encrypted_payload.h" #include "encrypted_fragment_payload.h" #include #include #include #include typedef struct private_encrypted_payload_t private_encrypted_payload_t; typedef struct private_encrypted_fragment_payload_t private_encrypted_fragment_payload_t; struct private_encrypted_payload_t { /** * Public encrypted_payload_t interface. */ encrypted_payload_t public; /** * There is no next payload for an encrypted payload, * since encrypted payload MUST be the last one. * next_payload means here the first payload of the * contained, encrypted payload. */ uint8_t next_payload; /** * Flags, including reserved bits */ uint8_t flags; /** * Length of this payload */ uint16_t payload_length; /** * Chunk containing the IV, plain, padding and ICV. */ chunk_t encrypted; /** * AEAD transform to use */ aead_t *aead; /** * Contained payloads */ linked_list_t *payloads; /** * Type of payload, PLV2_ENCRYPTED or PLV1_ENCRYPTED */ payload_type_t type; }; struct private_encrypted_fragment_payload_t { /** * Public interface. */ encrypted_fragment_payload_t public; /** * The first fragment contains the type of the first payload contained in * the original encrypted payload, for all other fragments it MUST be set * to zero. */ uint8_t next_payload; /** * Flags, including reserved bits */ uint8_t flags; /** * Length of this payload */ uint16_t payload_length; /** * Chunk containing the IV, plain, padding and ICV. */ chunk_t encrypted; /** * Fragment number */ uint16_t fragment_number; /** * Total fragments */ uint16_t total_fragments; /** * AEAD transform to use */ aead_t *aead; /** * Chunk containing the plain packet data. */ chunk_t plain; }; /** * Encoding rules to parse or generate a IKEv2-Encrypted Payload. * * The defined offsets are the positions in a object of type * private_encrypted_payload_t. */ static encoding_rule_t encodings_v2[] = { /* 1 Byte next payload type, stored in the field next_payload */ { U_INT_8, offsetof(private_encrypted_payload_t, next_payload) }, /* Critical and 7 reserved bits, all stored for reconstruction */ { U_INT_8, offsetof(private_encrypted_payload_t, flags) }, /* Length of the whole encrypted payload*/ { PAYLOAD_LENGTH, offsetof(private_encrypted_payload_t, payload_length) }, /* encrypted data, stored in a chunk. contains iv, data, padding */ { CHUNK_DATA, offsetof(private_encrypted_payload_t, encrypted) }, }; /* 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Next Payload !C! RESERVED ! Payload Length ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Initialization Vector ! ! (length is block size for encryption algorithm) ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Encrypted IKE Payloads ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! ! Padding (0-255 octets) ! +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ! ! Pad Length ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~ Integrity Checksum Data ~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /** * Encoding rules to parse or generate a complete encrypted IKEv1 message. * * The defined offsets are the positions in a object of type * private_encrypted_payload_t. */ static encoding_rule_t encodings_v1[] = { /* encrypted data, stored in a chunk */ { ENCRYPTED_DATA, offsetof(private_encrypted_payload_t, encrypted) }, }; /* 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Encrypted IKE Payloads ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! ! Padding (0-255 octets) ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /** * Encoding rules to parse or generate an IKEv2-Encrypted Fragment Payload. * * The defined offsets are the positions in a object of type * private_encrypted_payload_t. */ static encoding_rule_t encodings_fragment[] = { /* 1 Byte next payload type, stored in the field next_payload */ { U_INT_8, offsetof(private_encrypted_fragment_payload_t, next_payload) }, /* Critical and 7 reserved bits, all stored for reconstruction */ { U_INT_8, offsetof(private_encrypted_fragment_payload_t, flags) }, /* Length of the whole encryption payload*/ { PAYLOAD_LENGTH, offsetof(private_encrypted_fragment_payload_t, payload_length) }, /* Fragment number */ { U_INT_16, offsetof(private_encrypted_fragment_payload_t, fragment_number) }, /* Total number of fragments */ { U_INT_16, offsetof(private_encrypted_fragment_payload_t, total_fragments) }, /* encrypted data, stored in a chunk. contains iv, data, padding */ { CHUNK_DATA, offsetof(private_encrypted_fragment_payload_t, encrypted) }, }; /* 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Next Payload !C! RESERVED ! Payload Length ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Fragment Number | Total Fragments ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Initialization Vector ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! Encrypted IKE Payloads ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ! ! Padding (0-255 octets) ! +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ! ! Pad Length ! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~ Integrity Checksum Data ~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ METHOD(payload_t, verify, status_t, private_encrypted_payload_t *this) { return SUCCESS; } METHOD(payload_t, get_encoding_rules, int, private_encrypted_payload_t *this, encoding_rule_t **rules) { if (this->type == PLV2_ENCRYPTED) { *rules = encodings_v2; return countof(encodings_v2); } *rules = encodings_v1; return countof(encodings_v1); } METHOD(payload_t, get_header_length, int, private_encrypted_payload_t *this) { if (this->type == PLV2_ENCRYPTED) { return 4; } return 0; } METHOD(payload_t, get_type, payload_type_t, private_encrypted_payload_t *this) { return this->type; } METHOD(payload_t, get_next_type, payload_type_t, private_encrypted_payload_t *this) { return this->next_payload; } METHOD(payload_t, set_next_type, void, private_encrypted_payload_t *this, payload_type_t type) { /* the next payload is set during add, still allow this for IKEv1 */ this->next_payload = type; } /** * Get length of encryption/integrity overhead for the given plaintext length */ static size_t compute_overhead(aead_t *aead, size_t len) { size_t bs, overhead; /* padding */ bs = aead->get_block_size(aead); overhead = bs - (len % bs); /* add iv */ overhead += aead->get_iv_size(aead); /* add icv */ overhead += aead->get_icv_size(aead); return overhead; } /** * Compute the length of the whole payload */ static void compute_length(private_encrypted_payload_t *this) { enumerator_t *enumerator; payload_t *payload; size_t length = 0; if (this->encrypted.len) { length = this->encrypted.len; } else { enumerator = this->payloads->create_enumerator(this->payloads); while (enumerator->enumerate(enumerator, &payload)) { length += payload->get_length(payload); } enumerator->destroy(enumerator); if (this->aead) { length += compute_overhead(this->aead, length); } } length += get_header_length(this); this->payload_length = length; } METHOD2(payload_t, encrypted_payload_t, get_length, size_t, private_encrypted_payload_t *this) { compute_length(this); return this->payload_length; } METHOD(encrypted_payload_t, add_payload, void, private_encrypted_payload_t *this, payload_t *payload) { payload_t *last_payload; if (this->payloads->get_count(this->payloads) > 0) { this->payloads->get_last(this->payloads, (void **)&last_payload); last_payload->set_next_type(last_payload, payload->get_type(payload)); } else { this->next_payload = payload->get_type(payload); } payload->set_next_type(payload, PL_NONE); this->payloads->insert_last(this->payloads, payload); compute_length(this); } METHOD(encrypted_payload_t, remove_payload, payload_t *, private_encrypted_payload_t *this) { payload_t *payload; if (this->payloads->remove_first(this->payloads, (void**)&payload) == SUCCESS) { return payload; } return NULL; } /** * Generate payload before encryption */ static chunk_t generate(private_encrypted_payload_t *this, generator_t *generator) { payload_t *current, *next; enumerator_t *enumerator; uint32_t *lenpos; chunk_t chunk = chunk_empty; enumerator = this->payloads->create_enumerator(this->payloads); if (enumerator->enumerate(enumerator, ¤t)) { this->next_payload = current->get_type(current); while (enumerator->enumerate(enumerator, &next)) { current->set_next_type(current, next->get_type(next)); generator->generate_payload(generator, current); current = next; } current->set_next_type(current, PL_NONE); generator->generate_payload(generator, current); chunk = generator->get_chunk(generator, &lenpos); DBG2(DBG_ENC, "generated content in encrypted payload"); } enumerator->destroy(enumerator); return chunk; } METHOD(encrypted_payload_t, generate_payloads, void, private_encrypted_payload_t *this, generator_t *generator) { generate(this, generator); } /** * Append the encrypted payload header to the associated data */ static chunk_t append_header(private_encrypted_payload_t *this, chunk_t assoc) { struct { uint8_t next_payload; uint8_t flags; uint16_t length; } __attribute__((packed)) header = { .next_payload = this->next_payload, .flags = this->flags, .length = htons(get_length(this)), }; return chunk_cat("cc", assoc, chunk_from_thing(header)); } /** * Encrypts the data in plain and returns it in an allocated chunk. */ static status_t encrypt_content(char *label, aead_t *aead, uint64_t mid, chunk_t plain, chunk_t assoc, chunk_t *encrypted) { chunk_t iv, padding, icv, crypt; iv_gen_t *iv_gen; rng_t *rng; size_t bs; rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) { DBG1(DBG_ENC, "encrypting %s failed, no RNG found", label); return NOT_SUPPORTED; } iv_gen = aead->get_iv_gen(aead); if (!iv_gen) { DBG1(DBG_ENC, "encrypting %s failed, no IV generator", label); return NOT_SUPPORTED; } bs = aead->get_block_size(aead); /* we need at least one byte padding to store the padding length */ padding.len = bs - (plain.len % bs); iv.len = aead->get_iv_size(aead); icv.len = aead->get_icv_size(aead); /* prepare data to authenticate-encrypt: * | IV | plain | padding | ICV | * \____crypt______/ ^ * | / * v / * assoc -> + ------->/ */ *encrypted = chunk_alloc(iv.len + plain.len + padding.len + icv.len); iv.ptr = encrypted->ptr; memcpy(iv.ptr + iv.len, plain.ptr, plain.len); plain.ptr = iv.ptr + iv.len; padding.ptr = plain.ptr + plain.len; icv.ptr = padding.ptr + padding.len; crypt = chunk_create(plain.ptr, plain.len + padding.len); if (!iv_gen->get_iv(iv_gen, mid, iv.len, iv.ptr) || !rng->get_bytes(rng, padding.len - 1, padding.ptr)) { DBG1(DBG_ENC, "encrypting %s failed, no IV or padding", label); rng->destroy(rng); return FAILED; } padding.ptr[padding.len - 1] = padding.len - 1; rng->destroy(rng); DBG3(DBG_ENC, "%s encryption:", label); DBG3(DBG_ENC, "IV %B", &iv); DBG3(DBG_ENC, "plain %B", &plain); DBG3(DBG_ENC, "padding %B", &padding); DBG3(DBG_ENC, "assoc %B", &assoc); if (!aead->encrypt(aead, crypt, assoc, iv, NULL)) { return FAILED; } DBG3(DBG_ENC, "encrypted %B", &crypt); DBG3(DBG_ENC, "ICV %B", &icv); return SUCCESS; } METHOD(encrypted_payload_t, encrypt, status_t, private_encrypted_payload_t *this, uint64_t mid, chunk_t assoc) { generator_t *generator; chunk_t plain; status_t status; if (this->aead == NULL) { DBG1(DBG_ENC, "encrypting encrypted payload failed, transform missing"); return INVALID_STATE; } free(this->encrypted.ptr); generator = generator_create(); plain = generate(this, generator); assoc = append_header(this, assoc); /* lower 32-bits are for fragment number, if used */ mid <<= 32; status = encrypt_content("encrypted payload", this->aead, mid, plain, assoc, &this->encrypted); generator->destroy(generator); free(assoc.ptr); return status; } METHOD(encrypted_payload_t, encrypt_v1, status_t, private_encrypted_payload_t *this, uint64_t mid, chunk_t iv) { generator_t *generator; chunk_t plain, padding; size_t bs; if (this->aead == NULL) { DBG1(DBG_ENC, "encryption failed, transform missing"); return INVALID_STATE; } generator = generator_create(); plain = generate(this, generator); bs = this->aead->get_block_size(this->aead); padding.len = bs - (plain.len % bs); /* prepare data to encrypt: * | plain | padding | */ free(this->encrypted.ptr); this->encrypted = chunk_alloc(plain.len + padding.len); memcpy(this->encrypted.ptr, plain.ptr, plain.len); plain.ptr = this->encrypted.ptr; padding.ptr = plain.ptr + plain.len; memset(padding.ptr, 0, padding.len); generator->destroy(generator); DBG3(DBG_ENC, "encrypting payloads:"); DBG3(DBG_ENC, "IV %B", &iv); DBG3(DBG_ENC, "plain %B", &plain); DBG3(DBG_ENC, "padding %B", &padding); if (!this->aead->encrypt(this->aead, this->encrypted, chunk_empty, iv, NULL)) { return FAILED; } DBG3(DBG_ENC, "encrypted %B", &this->encrypted); return SUCCESS; } /** * Parse the payloads after decryption. */ static status_t parse(private_encrypted_payload_t *this, chunk_t plain) { parser_t *parser; payload_type_t type; parser = parser_create(plain); parser->set_major_version(parser, this->type == PLV1_ENCRYPTED ? 1 : 2); type = this->next_payload; while (type != PL_NONE) { payload_t *payload; if (plain.len < 4 || untoh16(plain.ptr + 2) > plain.len) { DBG1(DBG_ENC, "invalid %N payload length, decryption failed?", payload_type_names, type); parser->destroy(parser); return PARSE_ERROR; } if (parser->parse_payload(parser, type, &payload) != SUCCESS) { parser->destroy(parser); return PARSE_ERROR; } if (payload->verify(payload) != SUCCESS) { DBG1(DBG_ENC, "%N verification failed", payload_type_names, payload->get_type(payload)); payload->destroy(payload); parser->destroy(parser); return VERIFY_ERROR; } type = payload->get_next_type(payload); this->payloads->insert_last(this->payloads, payload); } parser->destroy(parser); DBG2(DBG_ENC, "parsed content of encrypted payload"); return SUCCESS; } /** * Decrypts the given data in-place and returns a chunk pointing to the * resulting plaintext. */ static status_t decrypt_content(char *label, aead_t *aead, chunk_t encrypted, chunk_t assoc, chunk_t *plain) { chunk_t iv, padding, icv, crypt; size_t bs; /* prepare data to authenticate-decrypt: * | IV | plain | padding | ICV | * \____crypt______/ ^ * | / * v / * assoc -> + ------->/ */ bs = aead->get_block_size(aead); iv.len = aead->get_iv_size(aead); iv.ptr = encrypted.ptr; icv.len = aead->get_icv_size(aead); icv.ptr = encrypted.ptr + encrypted.len - icv.len; crypt.ptr = iv.ptr + iv.len; crypt.len = encrypted.len - iv.len; if (iv.len + icv.len > encrypted.len || (crypt.len - icv.len) % bs) { DBG1(DBG_ENC, "decrypting %s payload failed, invalid length", label); return FAILED; } DBG3(DBG_ENC, "%s decryption:", label); DBG3(DBG_ENC, "IV %B", &iv); DBG3(DBG_ENC, "encrypted %B", &crypt); DBG3(DBG_ENC, "ICV %B", &icv); DBG3(DBG_ENC, "assoc %B", &assoc); if (!aead->decrypt(aead, crypt, assoc, iv, NULL)) { DBG1(DBG_ENC, "verifying %s integrity failed", label); return FAILED; } *plain = chunk_create(crypt.ptr, crypt.len - icv.len); padding.len = plain->ptr[plain->len - 1] + 1; if (padding.len > plain->len) { DBG1(DBG_ENC, "decrypting %s failed, padding invalid %B", label, &crypt); return PARSE_ERROR; } plain->len -= padding.len; padding.ptr = plain->ptr + plain->len; DBG3(DBG_ENC, "plain %B", plain); DBG3(DBG_ENC, "padding %B", &padding); return SUCCESS; } METHOD(encrypted_payload_t, decrypt, status_t, private_encrypted_payload_t *this, chunk_t assoc) { chunk_t plain; status_t status; if (this->aead == NULL) { DBG1(DBG_ENC, "decrypting encrypted payload failed, transform missing"); return INVALID_STATE; } assoc = append_header(this, assoc); status = decrypt_content("encrypted payload", this->aead, this->encrypted, assoc, &plain); free(assoc.ptr); if (status != SUCCESS) { return status; } return parse(this, plain); } METHOD(encrypted_payload_t, decrypt_plain, status_t, private_encrypted_payload_t *this, chunk_t assoc) { if (!this->encrypted.ptr) { return FAILED; } return parse(this, this->encrypted); } METHOD(encrypted_payload_t, decrypt_v1, status_t, private_encrypted_payload_t *this, chunk_t iv) { if (this->aead == NULL) { DBG1(DBG_ENC, "decryption failed, transform missing"); return INVALID_STATE; } /* data must be a multiple of block size */ if (iv.len != this->aead->get_block_size(this->aead) || this->encrypted.len < iv.len || this->encrypted.len % iv.len) { DBG1(DBG_ENC, "decryption failed, invalid length"); return FAILED; } DBG3(DBG_ENC, "decrypting payloads:"); DBG3(DBG_ENC, "encrypted %B", &this->encrypted); if (!this->aead->decrypt(this->aead, this->encrypted, chunk_empty, iv, NULL)) { return FAILED; } DBG3(DBG_ENC, "plain %B", &this->encrypted); return parse(this, this->encrypted); } METHOD(encrypted_payload_t, set_transform, void, private_encrypted_payload_t *this, aead_t* aead) { this->aead = aead; } METHOD2(payload_t, encrypted_payload_t, destroy, void, private_encrypted_payload_t *this) { this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); free(this->encrypted.ptr); free(this); } /* * Described in header */ encrypted_payload_t *encrypted_payload_create(payload_type_t type) { private_encrypted_payload_t *this; INIT(this, .public = { .payload_interface = { .verify = _verify, .get_encoding_rules = _get_encoding_rules, .get_header_length = _get_header_length, .get_length = _get_length, .get_next_type = _get_next_type, .set_next_type = _set_next_type, .get_type = _get_type, .destroy = _destroy, }, .get_length = _get_length, .add_payload = _add_payload, .remove_payload = _remove_payload, .generate_payloads = _generate_payloads, .set_transform = _set_transform, .encrypt = _encrypt, .decrypt = _decrypt, .destroy = _destroy, }, .next_payload = PL_NONE, .payloads = linked_list_create(), .type = type, ); this->payload_length = get_header_length(this); if (type == PLV1_ENCRYPTED) { this->public.encrypt = _encrypt_v1; this->public.decrypt = _decrypt_v1; } return &this->public; } /* * Described in header */ encrypted_payload_t *encrypted_payload_create_from_plain(payload_type_t next, chunk_t plain) { private_encrypted_payload_t *this; this = (private_encrypted_payload_t*)encrypted_payload_create(PLV2_ENCRYPTED); this->public.decrypt = _decrypt_plain; this->next_payload = next; this->encrypted = plain; compute_length(this); return &this->public; } METHOD(payload_t, frag_verify, status_t, private_encrypted_fragment_payload_t *this) { if (!this->fragment_number || !this->total_fragments || this->fragment_number > this->total_fragments) { DBG1(DBG_ENC, "invalid fragment number (%u) or total fragments (%u)", this->fragment_number, this->total_fragments); return FAILED; } if (this->fragment_number > 1 && this->next_payload != 0) { DBG1(DBG_ENC, "invalid next payload (%u) for fragment %u, ignored", this->next_payload, this->fragment_number); this->next_payload = 0; } return SUCCESS; } METHOD(payload_t, frag_get_encoding_rules, int, private_encrypted_fragment_payload_t *this, encoding_rule_t **rules) { *rules = encodings_fragment; return countof(encodings_fragment); } METHOD(payload_t, frag_get_header_length, int, private_encrypted_fragment_payload_t *this) { return 8; } METHOD(payload_t, frag_get_type, payload_type_t, private_encrypted_fragment_payload_t *this) { return PLV2_FRAGMENT; } METHOD(payload_t, frag_get_next_type, payload_type_t, private_encrypted_fragment_payload_t *this) { return this->next_payload; } METHOD(payload_t, frag_set_next_type, void, private_encrypted_fragment_payload_t *this, payload_type_t type) { if (this->fragment_number == 1 && this->next_payload == PL_NONE) { this->next_payload = type; } } METHOD2(payload_t, encrypted_payload_t, frag_get_length, size_t, private_encrypted_fragment_payload_t *this) { if (this->encrypted.len) { this->payload_length = this->encrypted.len; } else { this->payload_length = this->plain.len; if (this->aead) { this->payload_length += compute_overhead(this->aead, this->payload_length); } } this->payload_length += frag_get_header_length(this); return this->payload_length; } METHOD(encrypted_fragment_payload_t, get_fragment_number, uint16_t, private_encrypted_fragment_payload_t *this) { return this->fragment_number; } METHOD(encrypted_fragment_payload_t, get_total_fragments, uint16_t, private_encrypted_fragment_payload_t *this) { return this->total_fragments; } METHOD(encrypted_fragment_payload_t, frag_get_content, chunk_t, private_encrypted_fragment_payload_t *this) { return this->plain; } METHOD(encrypted_payload_t, frag_add_payload, void, private_encrypted_fragment_payload_t *this, payload_t* payload) { payload->destroy(payload); } METHOD(encrypted_payload_t, frag_set_transform, void, private_encrypted_fragment_payload_t *this, aead_t* aead) { this->aead = aead; } /** * Append the encrypted fragment payload header to the associated data */ static chunk_t append_header_frag(private_encrypted_fragment_payload_t *this, chunk_t assoc) { struct { uint8_t next_payload; uint8_t flags; uint16_t length; uint16_t fragment_number; uint16_t total_fragments; } __attribute__((packed)) header = { .next_payload = this->next_payload, .flags = this->flags, .length = htons(frag_get_length(this)), .fragment_number = htons(this->fragment_number), .total_fragments = htons(this->total_fragments), }; return chunk_cat("cc", assoc, chunk_from_thing(header)); } METHOD(encrypted_payload_t, frag_encrypt, status_t, private_encrypted_fragment_payload_t *this, uint64_t mid, chunk_t assoc) { status_t status; if (!this->aead) { DBG1(DBG_ENC, "encrypting encrypted fragment payload failed, " "transform missing"); return INVALID_STATE; } free(this->encrypted.ptr); assoc = append_header_frag(this, assoc); /* IKEv2 message IDs are not unique if fragmentation is used, hence include * the fragment number to make it unique */ mid = mid << 32 | this->fragment_number; status = encrypt_content("encrypted fragment payload", this->aead, mid, this->plain, assoc, &this->encrypted); free(assoc.ptr); return status; } METHOD(encrypted_payload_t, frag_decrypt, status_t, private_encrypted_fragment_payload_t *this, chunk_t assoc) { status_t status; if (!this->aead) { DBG1(DBG_ENC, "decrypting encrypted fragment payload failed, " "transform missing"); return INVALID_STATE; } free(this->plain.ptr); assoc = append_header_frag(this, assoc); status = decrypt_content("encrypted fragment payload", this->aead, this->encrypted, assoc, &this->plain); this->plain = chunk_clone(this->plain); free(assoc.ptr); return status; } METHOD2(payload_t, encrypted_payload_t, frag_destroy, void, private_encrypted_fragment_payload_t *this) { free(this->encrypted.ptr); free(this->plain.ptr); free(this); } /* * Described in header */ encrypted_fragment_payload_t *encrypted_fragment_payload_create() { private_encrypted_fragment_payload_t *this; INIT(this, .public = { .encrypted = { .payload_interface = { .verify = _frag_verify, .get_encoding_rules = _frag_get_encoding_rules, .get_header_length = _frag_get_header_length, .get_length = _frag_get_length, .get_next_type = _frag_get_next_type, .set_next_type = _frag_set_next_type, .get_type = _frag_get_type, .destroy = _frag_destroy, }, .get_length = _frag_get_length, .add_payload = _frag_add_payload, .remove_payload = (void*)return_null, .generate_payloads = nop, .set_transform = _frag_set_transform, .encrypt = _frag_encrypt, .decrypt = _frag_decrypt, .destroy = _frag_destroy, }, .get_fragment_number = _get_fragment_number, .get_total_fragments = _get_total_fragments, .get_content = _frag_get_content, }, .next_payload = PL_NONE, ); this->payload_length = frag_get_header_length(this); return &this->public; } /* * Described in header */ encrypted_fragment_payload_t *encrypted_fragment_payload_create_from_data( uint16_t num, uint16_t total, chunk_t plain) { private_encrypted_fragment_payload_t *this; this = (private_encrypted_fragment_payload_t*)encrypted_fragment_payload_create(); this->fragment_number = num; this->total_fragments = total; this->plain = chunk_clone(plain); return &this->public; }