aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/encoding/payloads/encrypted_payload.c
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2014-06-06 16:35:49 +0200
committerTobias Brunner <tobias@strongswan.org>2014-10-10 09:30:25 +0200
commit147fe503af6752972bb0934c57f0f387ac4759f1 (patch)
tree40cb6c8b07b1f046668e6963bf9533b1a99cea82 /src/libcharon/encoding/payloads/encrypted_payload.c
parent7fb363c8f73bb2d701826e947fb7d800e9690190 (diff)
downloadstrongswan-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.c631
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, &current))
+ {
+ 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;
+}