diff options
Diffstat (limited to 'src/libcharon/encoding/payloads/encryption_payload.c')
-rw-r--r-- | src/libcharon/encoding/payloads/encryption_payload.c | 152 |
1 files changed, 140 insertions, 12 deletions
diff --git a/src/libcharon/encoding/payloads/encryption_payload.c b/src/libcharon/encoding/payloads/encryption_payload.c index e7b8063b7..096079ad7 100644 --- a/src/libcharon/encoding/payloads/encryption_payload.c +++ b/src/libcharon/encoding/payloads/encryption_payload.c @@ -1,6 +1,7 @@ /* * 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 * @@ -71,6 +72,11 @@ struct private_encryption_payload_t { * Contained payloads */ linked_list_t *payloads; + + /** + * Type of payload, ENCRYPTED or ENCRYPTED_V1 + */ + payload_type_t type; }; /** @@ -79,7 +85,7 @@ struct private_encryption_payload_t { * The defined offsets are the positions in a object of type * private_encryption_payload_t. */ -encoding_rule_t encryption_payload_encodings[] = { +static encoding_rule_t encodings_v2[] = { /* 1 Byte next payload type, stored in the field next_payload */ { U_INT_8, offsetof(private_encryption_payload_t, next_payload) }, /* Critical and 7 reserved bits, all stored for reconstruction */ @@ -87,7 +93,7 @@ encoding_rule_t encryption_payload_encodings[] = { /* Length of the whole encryption payload*/ { PAYLOAD_LENGTH, offsetof(private_encryption_payload_t, payload_length) }, /* encrypted data, stored in a chunk. contains iv, data, padding */ - { ENCRYPTED_DATA, offsetof(private_encryption_payload_t, encrypted) }, + { CHUNK_DATA, offsetof(private_encryption_payload_t, encrypted) }, }; /* @@ -109,24 +115,59 @@ encoding_rule_t encryption_payload_encodings[] = { +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ +/** + * Encoding rules to parse or generate a complete encrypted IKEv1 message. + * + * The defined offsets are the positions in a object of type + * private_encryption_payload_t. + */ +static encoding_rule_t encodings_v1[] = { + /* encrypted data, stored in a chunk */ + { ENCRYPTED_DATA, offsetof(private_encryption_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_encryption_payload_t *this) { return SUCCESS; } -METHOD(payload_t, get_encoding_rules, void, - private_encryption_payload_t *this, encoding_rule_t **rules, - size_t *count) +METHOD(payload_t, get_encoding_rules, int, + private_encryption_payload_t *this, encoding_rule_t **rules) +{ + if (this->type == ENCRYPTED) + { + *rules = encodings_v2; + return countof(encodings_v2); + } + *rules = encodings_v1; + return countof(encodings_v1); +} + +METHOD(payload_t, get_header_length, int, + private_encryption_payload_t *this) { - *rules = encryption_payload_encodings; - *count = countof(encryption_payload_encodings); + if (this->type == ENCRYPTED) + { + return 4; + } + return 0; } METHOD(payload_t, get_type, payload_type_t, private_encryption_payload_t *this) { - return ENCRYPTED; + return this->type; } METHOD(payload_t, get_next_type, payload_type_t, @@ -138,7 +179,8 @@ METHOD(payload_t, get_next_type, payload_type_t, METHOD(payload_t, set_next_type, void, private_encryption_payload_t *this, payload_type_t type) { - /* the next payload is set during add */ + /* the next payload is set during add, still allow this for IKEv1 */ + this->next_payload = type; } /** @@ -174,7 +216,7 @@ static void compute_length(private_encryption_payload_t *this) length += this->aead->get_icv_size(this->aead); } } - length += ENCRYPTION_PAYLOAD_HEADER_LENGTH; + length += get_header_length(this); this->payload_length = length; } @@ -335,6 +377,47 @@ METHOD(encryption_payload_t, encrypt, bool, return TRUE; } +METHOD(encryption_payload_t, encrypt_v1, bool, + private_encryption_payload_t *this, chunk_t iv) +{ + generator_t *generator; + chunk_t plain, padding; + size_t bs; + + if (this->aead == NULL) + { + DBG1(DBG_ENC, "encryption failed, transform missing"); + chunk_free(&iv); + return FALSE; + } + + 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, "plain %B", &plain); + DBG3(DBG_ENC, "padding %B", &padding); + + this->aead->encrypt(this->aead, this->encrypted, chunk_empty, iv, NULL); + chunk_free(&iv); + + DBG3(DBG_ENC, "encrypted %B", &this->encrypted); + + return TRUE; +} + /** * Parse the payloads after decryption. */ @@ -349,6 +432,13 @@ static status_t parse(private_encryption_payload_t *this, chunk_t plain) { 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); @@ -438,6 +528,36 @@ METHOD(encryption_payload_t, decrypt, status_t, return parse(this, plain); } +METHOD(encryption_payload_t, decrypt_v1, status_t, + private_encryption_payload_t *this, chunk_t iv) +{ + if (this->aead == NULL) + { + DBG1(DBG_ENC, "decryption failed, transform missing"); + chunk_free(&iv); + 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"); + chunk_free(&iv); + return FAILED; + } + + DBG3(DBG_ENC, "decrypting payloads:"); + DBG3(DBG_ENC, "encrypted %B", &this->encrypted); + + this->aead->decrypt(this->aead, this->encrypted, chunk_empty, iv, NULL); + chunk_free(&iv); + + DBG3(DBG_ENC, "plain %B", &this->encrypted); + + return parse(this, this->encrypted); +} + METHOD(encryption_payload_t, set_transform, void, private_encryption_payload_t *this, aead_t* aead) { @@ -455,7 +575,7 @@ METHOD2(payload_t, encryption_payload_t, destroy, void, /* * Described in header */ -encryption_payload_t *encryption_payload_create() +encryption_payload_t *encryption_payload_create(payload_type_t type) { private_encryption_payload_t *this; @@ -464,6 +584,7 @@ encryption_payload_t *encryption_payload_create() .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, @@ -479,9 +600,16 @@ encryption_payload_t *encryption_payload_create() .destroy = _destroy, }, .next_payload = NO_PAYLOAD, - .payload_length = ENCRYPTION_PAYLOAD_HEADER_LENGTH, + .payload_length = get_header_length(this), .payloads = linked_list_create(), + .type = type, ); + if (type == ENCRYPTED_V1) + { + this->public.encrypt = _encrypt_v1; + this->public.decrypt = _decrypt_v1; + } + return &this->public; } |