Files
strongswan-ext/src/libcharon/encoding/payloads/encrypted_payload.c
T
Tobias Brunner 11241a8379 encrypted-payload: Do basic decryption check only once
The check only references the original chunk, so for each parsed payload
it checks the same thing.  The length of each individual payload is
checked by the parser anyway.  So I think this was primarily added for
the IKEv1 "wrong PSK" use case.  Let's keep it for now.

Fixes: dd5c3787dc ("Give a hint that decryption failed if payload length invalid")
2026-07-24 08:47:39 +02:00

1059 lines
28 KiB
C

/*
* Copyright (C) 2011-2018 Tobias Brunner
* Copyright (C) 2005-2010 Martin Willi
* Copyright (C) 2005 Jan Hutter
*
* Copyright (C) secunet Security Networks AG
*
* 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 "encrypted_fragment_payload.h"
#include <daemon.h>
#include <encoding/payloads/encodings.h>
#include <collections/linked_list.h>
#include <encoding/parser.h>
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;
}
METHOD2(payload_t, encrypted_payload_t, get_length_plain, size_t,
private_encrypted_payload_t *this)
{
/* contains only the decrypted payload data, no IV, padding or ICV */
this->payload_length = this->encrypted.len;
if (this->aead)
{
this->payload_length += compute_overhead(this->aead,
this->payload_length);
}
this->payload_length += get_header_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, &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;
}
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 = this->next_payload;
if (type != PL_NONE &&
(plain.len < 4 || untoh16(plain.ptr + 2) > plain.len))
{
DBG1(DBG_ENC, "invalid %N payload length, decryption failed?",
payload_type_names, type);
return PARSE_ERROR;
}
parser = parser_create(plain);
parser->set_major_version(parser, this->type == PLV1_ENCRYPTED ? 1 : 2);
while (type != PL_NONE)
{
payload_t *payload;
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;
}
METHOD(encrypted_payload_t, get_transform, aead_t*,
private_encrypted_payload_t *this)
{
return this->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,
.get_transform = _get_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.payload_interface.get_length = _get_length_plain;
this->public.get_length = _get_length_plain;
this->public.decrypt = _decrypt_plain;
this->next_payload = next;
this->encrypted = plain;
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;
}
METHOD(encrypted_payload_t, frag_get_transform, aead_t*,
private_encrypted_fragment_payload_t *this)
{
return this->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 = (void*)nop,
.set_transform = _frag_set_transform,
.get_transform = _frag_get_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;
}