diff options
author | Tobias Brunner <tobias@strongswan.org> | 2014-06-06 16:35:49 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2014-10-10 09:30:25 +0200 |
commit | 147fe503af6752972bb0934c57f0f387ac4759f1 (patch) | |
tree | 40cb6c8b07b1f046668e6963bf9533b1a99cea82 /src/libcharon/encoding/payloads/encrypted_payload.c | |
parent | 7fb363c8f73bb2d701826e947fb7d800e9690190 (diff) | |
download | strongswan-147fe503af6752972bb0934c57f0f387ac4759f1.tar.bz2 strongswan-147fe503af6752972bb0934c57f0f387ac4759f1.tar.xz |
ike: Rename encryption_payload to encrypted_payload
Diffstat (limited to 'src/libcharon/encoding/payloads/encrypted_payload.c')
-rw-r--r-- | src/libcharon/encoding/payloads/encrypted_payload.c | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/src/libcharon/encoding/payloads/encrypted_payload.c b/src/libcharon/encoding/payloads/encrypted_payload.c new file mode 100644 index 000000000..d71f514c2 --- /dev/null +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2005-2010 Martin Willi + * Copyright (C) 2010 revosec AG + * Copyright (C) 2011 Tobias Brunner + * 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <stddef.h> +#include <string.h> + +#include "encrypted_payload.h" + +#include <daemon.h> +#include <encoding/payloads/encodings.h> +#include <collections/linked_list.h> +#include <encoding/generator.h> +#include <encoding/parser.h> + +typedef struct private_encrypted_payload_t private_encrypted_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. + */ + u_int8_t next_payload; + + /** + * Flags, including reserved bits + */ + u_int8_t flags; + + /** + * Length of this payload + */ + u_int16_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; +}; + +/** + * 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) ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +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; +} + +/** + * Compute the length of the whole payload + */ +static void compute_length(private_encrypted_payload_t *this) +{ + enumerator_t *enumerator; + payload_t *payload; + size_t bs, 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) + { + /* append padding */ + bs = this->aead->get_block_size(this->aead); + length += bs - (length % bs); + /* add iv */ + length += this->aead->get_iv_size(this->aead); + /* add icv */ + length += this->aead->get_icv_size(this->aead); + } + } + 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; + u_int32_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; +} + +/** + * Append the encrypted payload header to the associated data + */ +static chunk_t append_header(private_encrypted_payload_t *this, chunk_t assoc) +{ + struct { + u_int8_t next_payload; + u_int8_t flags; + u_int16_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)); +} + +METHOD(encrypted_payload_t, encrypt, status_t, + private_encrypted_payload_t *this, u_int64_t mid, chunk_t assoc) +{ + chunk_t iv, plain, padding, icv, crypt; + generator_t *generator; + iv_gen_t *iv_gen; + rng_t *rng; + size_t bs; + + if (this->aead == NULL) + { + DBG1(DBG_ENC, "encrypting encrypted payload failed, transform missing"); + return INVALID_STATE; + } + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_ENC, "encrypting encrypted payload failed, no RNG found"); + return NOT_SUPPORTED; + } + + iv_gen = this->aead->get_iv_gen(this->aead); + if (!iv_gen) + { + DBG1(DBG_ENC, "encrypting encrypted payload failed, no IV generator"); + return NOT_SUPPORTED; + } + + assoc = append_header(this, assoc); + + generator = generator_create(); + plain = generate(this, generator); + bs = this->aead->get_block_size(this->aead); + /* we need at least one byte padding to store the padding length */ + padding.len = bs - (plain.len % bs); + iv.len = this->aead->get_iv_size(this->aead); + icv.len = this->aead->get_icv_size(this->aead); + + /* prepare data to authenticate-encrypt: + * | IV | plain | padding | ICV | + * \____crypt______/ ^ + * | / + * v / + * assoc -> + ------->/ + */ + free(this->encrypted.ptr); + this->encrypted = chunk_alloc(iv.len + plain.len + padding.len + icv.len); + iv.ptr = this->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); + generator->destroy(generator); + + 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 encrypted payload failed, no IV or padding"); + rng->destroy(rng); + free(assoc.ptr); + return FAILED; + } + padding.ptr[padding.len - 1] = padding.len - 1; + rng->destroy(rng); + + DBG3(DBG_ENC, "encrypted payload encryption:"); + 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 (!this->aead->encrypt(this->aead, crypt, assoc, iv, NULL)) + { + free(assoc.ptr); + return FAILED; + } + + DBG3(DBG_ENC, "encrypted %B", &crypt); + DBG3(DBG_ENC, "ICV %B", &icv); + + free(assoc.ptr); + + return SUCCESS; +} + +METHOD(encrypted_payload_t, encrypt_v1, status_t, + private_encrypted_payload_t *this, u_int64_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); + 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; +} + +METHOD(encrypted_payload_t, decrypt, status_t, + private_encrypted_payload_t *this, chunk_t assoc) +{ + chunk_t iv, plain, padding, icv, crypt; + size_t bs; + + if (this->aead == NULL) + { + DBG1(DBG_ENC, "decrypting encrypted payload failed, transform missing"); + return INVALID_STATE; + } + + /* prepare data to authenticate-decrypt: + * | IV | plain | padding | ICV | + * \____crypt______/ ^ + * | / + * v / + * assoc -> + ------->/ + */ + + bs = this->aead->get_block_size(this->aead); + iv.len = this->aead->get_iv_size(this->aead); + iv.ptr = this->encrypted.ptr; + icv.len = this->aead->get_icv_size(this->aead); + icv.ptr = this->encrypted.ptr + this->encrypted.len - icv.len; + crypt.ptr = iv.ptr + iv.len; + crypt.len = this->encrypted.len - iv.len; + + if (iv.len + icv.len > this->encrypted.len || + (crypt.len - icv.len) % bs) + { + DBG1(DBG_ENC, "decrypting encrypted payload failed, invalid length"); + return FAILED; + } + + assoc = append_header(this, assoc); + + DBG3(DBG_ENC, "encrypted payload decryption:"); + 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 (!this->aead->decrypt(this->aead, crypt, assoc, iv, NULL)) + { + DBG1(DBG_ENC, "verifying encrypted payload integrity failed"); + free(assoc.ptr); + return FAILED; + } + free(assoc.ptr); + + 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 encrypted payload failed, " + "padding invalid %B", &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 parse(this, plain); +} + +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, + .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; +} |