ESP packet wrapper added, handles encryption/decryption/verification etc.
authorTobias Brunner <tobias@strongswan.org>
Thu, 5 Jul 2012 11:56:24 +0000 (13:56 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 8 Aug 2012 13:41:02 +0000 (15:41 +0200)
src/libipsec/Android.mk
src/libipsec/Makefile.am
src/libipsec/esp_packet.c [new file with mode: 0644]
src/libipsec/esp_packet.h [new file with mode: 0644]

index d452793..7292bff 100644 (file)
@@ -4,7 +4,8 @@ include $(CLEAR_VARS)
 # copy-n-paste from Makefile.am
 LOCAL_SRC_FILES := \
 ipsec.c ipsec.h \
-esp_context.c esp_context.h
+esp_context.c esp_context.h \
+esp_packet.c esp_packet.h
 
 # build libipsec ---------------------------------------------------------------
 
index df0c9ac..b9ae6d3 100644 (file)
@@ -2,7 +2,8 @@ ipseclib_LTLIBRARIES = libipsec.la
 
 libipsec_la_SOURCES = \
 ipsec.c ipsec.h \
-esp_context.c esp_context.h
+esp_context.c esp_context.h \
+esp_packet.c esp_packet.h
 
 libipsec_la_LIBADD =
 
diff --git a/src/libipsec/esp_packet.c b/src/libipsec/esp_packet.c
new file mode 100644 (file)
index 0000000..193bc62
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "esp_packet.h"
+
+#include <library.h>
+#include <debug.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+#include <bio/bio_reader.h>
+#include <bio/bio_writer.h>
+
+#include <netinet/in.h>
+
+typedef struct private_esp_packet_t private_esp_packet_t;
+
+/**
+ * Private additions to esp_packet_t.
+ */
+struct private_esp_packet_t {
+
+       /**
+        * Public members
+        */
+       esp_packet_t public;
+
+       /**
+        * Source address
+        */
+       host_t *src;
+
+       /**
+        * Destination address
+        */
+       host_t *dst;
+
+       /**
+        * Payload of this packet
+        */
+       chunk_t payload;
+
+       /**
+        * Next Header info (e.g. IPPROTO_IPIP)
+        */
+       u_int8_t next_header;
+
+       /**
+        * Raw packet data
+        */
+       chunk_t packet_data;
+};
+
+METHOD(esp_packet_t, parse_header, bool,
+       private_esp_packet_t *this, u_int32_t *spi)
+{
+       bio_reader_t *reader;
+       u_int32_t seq;
+
+       reader = bio_reader_create(this->packet_data);
+       if (!reader->read_uint32(reader, spi) ||
+               !reader->read_uint32(reader, &seq))
+       {
+               DBG1(DBG_ESP, "failed to parse ESP header: invalid length");
+               reader->destroy(reader);
+               return FALSE;
+       }
+       reader->destroy(reader);
+
+       DBG2(DBG_ESP, "parsed ESP header with SPI %.8x [seq %u]", *spi, seq);
+       *spi = htonl(*spi);
+       return TRUE;
+}
+
+/**
+ * Check padding as specified in RFC 4303
+ */
+static bool check_padding(chunk_t padding)
+{
+       size_t i;
+
+       for (i = 0; i < padding.len; ++i)
+       {
+               if (padding.ptr[i] != (u_int8_t)(i + 1))
+               {
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
+/**
+ * Remove the padding from the payload and set the next header info
+ */
+static bool remove_padding(private_esp_packet_t *this)
+{
+       u_int8_t next_header, pad_length;
+       chunk_t padding;
+       bio_reader_t *reader;
+
+       reader = bio_reader_create(this->payload);
+       if (!reader->read_uint8_end(reader, &next_header) ||
+               !reader->read_uint8_end(reader, &pad_length))
+       {
+               DBG1(DBG_ESP, "parsing ESP payload failed: invalid length");
+               reader->destroy(reader);
+               return FALSE;
+       }
+       if (!reader->read_data_end(reader, pad_length, &padding) ||
+               !check_padding(padding))
+       {
+               DBG1(DBG_ESP, "parsing ESP payload failed: invalid padding");
+               reader->destroy(reader);
+               return FALSE;
+       }
+       this->payload = reader->peek(reader);
+       this->next_header = next_header;
+       reader->destroy(reader);
+
+       DBG3(DBG_ESP, "ESP payload:\n  payload %B\n  padding %B\n  "
+                "padding length = %hhu, next header = %hhu", &this->payload,
+                &padding, pad_length, this->next_header);
+       return TRUE;
+}
+
+METHOD(esp_packet_t, decrypt, status_t,
+       private_esp_packet_t *this, esp_context_t *esp_context)
+{
+       bio_reader_t *reader;
+       u_int32_t spi, seq;
+       chunk_t spi_seq, iv, icv, ciphertext;
+       crypter_t *crypter;
+       signer_t *signer;
+
+       chunk_free(&this->payload);
+
+       crypter = esp_context->get_crypter(esp_context);
+       signer = esp_context->get_signer(esp_context);
+
+       reader = bio_reader_create(this->packet_data);
+       if (!reader->read_uint32(reader, &spi) ||
+               !reader->read_uint32(reader, &seq) ||
+               !reader->read_data(reader, crypter->get_iv_size(crypter), &iv) ||
+               !reader->read_data_end(reader, signer->get_block_size(signer), &icv) ||
+               reader->remaining(reader) % crypter->get_block_size(crypter))
+       {
+               DBG1(DBG_ESP, "ESP decryption failed: invalid length");
+               return PARSE_ERROR;
+       }
+       ciphertext = reader->peek(reader);
+       reader->destroy(reader);
+
+       if (!esp_context->verify_seqno(esp_context, seq))
+       {
+               DBG1(DBG_ESP, "ESP sequence number verification failed:\n  "
+                        "src %H, dst %H, SPI %.8x [seq %u]",
+                        this->src, this->dst, spi, seq);
+               return VERIFY_ERROR;
+       }
+       DBG3(DBG_ESP, "ESP decryption:\n  SPI %.8x [seq %u]\n  IV %B\n  "
+                "encrypted %B\n  ICV %B", spi, seq, &iv, &ciphertext, &icv);
+
+       spi_seq = chunk_create(this->packet_data.ptr, 8);
+       if (!signer->get_signature(signer, spi_seq, NULL) ||
+               !signer->get_signature(signer, iv, NULL) ||
+               !signer->verify_signature(signer, ciphertext, icv))
+       {
+               DBG1(DBG_ESP, "ICV verification failed!");
+               return FAILED;
+       }
+       esp_context->set_authenticated_seqno(esp_context, seq);
+
+       if (!crypter->decrypt(crypter, ciphertext, iv, &this->payload))
+       {
+               DBG1(DBG_ESP, "ESP decryption failed");
+               return FAILED;
+       }
+
+       if (!remove_padding(this))
+       {
+               chunk_free(&this->payload);
+               return PARSE_ERROR;
+       }
+       return SUCCESS;
+}
+
+/**
+ * Generate the padding as specified in RFC4303
+ */
+static void generate_padding(chunk_t padding)
+{
+       size_t i;
+
+       for (i = 0; i < padding.len; ++i)
+       {
+               padding.ptr[i] = (u_int8_t)(i + 1);
+       }
+}
+
+METHOD(esp_packet_t, encrypt, status_t,
+       private_esp_packet_t *this, esp_context_t *esp_context, u_int32_t spi)
+{
+       chunk_t iv, icv, padding, ciphertext, auth_data;
+       bio_writer_t *writer;
+       u_int32_t next_seqno;
+       size_t blocksize, plainlen;
+       crypter_t *crypter;
+       signer_t *signer;
+       rng_t *rng;
+
+       chunk_free(&this->packet_data);
+
+       if (!esp_context->next_seqno(esp_context, &next_seqno))
+       {
+               DBG1(DBG_ESP, "ESP encapsulation failed: sequence numbers cycled");
+               return FAILED;
+       }
+
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng)
+       {
+               DBG1(DBG_ESP, "ESP encryption failed: could not find RNG");
+               return NOT_FOUND;
+       }
+       crypter = esp_context->get_crypter(esp_context);
+       signer = esp_context->get_signer(esp_context);
+
+       blocksize = crypter->get_block_size(crypter);
+       iv.len = crypter->get_iv_size(crypter);
+       icv.len = signer->get_block_size(signer);
+
+       /* plaintext = payload, padding, pad_length, next_header */
+       plainlen = this->payload.len + 2;
+       padding.len = blocksize - (plainlen % blocksize);
+       plainlen += padding.len;
+
+       /* len = spi, seq, IV, plaintext, ICV */
+       writer = bio_writer_create(2 * sizeof(u_int32_t) + iv.len + plainlen +
+                                                          icv.len);
+       writer->write_uint32(writer, ntohl(spi));
+       writer->write_uint32(writer, next_seqno);
+
+       iv = writer->skip(writer, iv.len);
+       if (!rng->get_bytes(rng, iv.len, iv.ptr))
+       {
+               DBG1(DBG_ESP, "ESP encryption failed: could not generate IV");
+               writer->destroy(writer);
+               rng->destroy(rng);
+               return FAILED;
+       }
+       rng->destroy(rng);
+
+       /* plain-/ciphertext will start here */
+       ciphertext = writer->get_buf(writer);
+       ciphertext.ptr += ciphertext.len;
+       ciphertext.len = plainlen;
+
+       writer->write_data(writer, this->payload);
+
+       padding = writer->skip(writer, padding.len);
+       generate_padding(padding);
+
+       writer->write_uint8(writer, padding.len);
+       writer->write_uint8(writer, this->next_header);
+
+       DBG3(DBG_ESP, "ESP before encryption:\n  payload = %B\n  padding = %B\n  "
+                "padding length = %hhu, next header = %hhu", &this->payload, &padding,
+                (u_int8_t)padding.len, this->next_header);
+
+       /* encrypt the content inline */
+       if (!crypter->encrypt(crypter, ciphertext, iv, NULL))
+       {
+               DBG1(DBG_ESP, "ESP encryption failed");
+               writer->destroy(writer);
+               return FAILED;
+       }
+
+       /* calculate signature */
+       auth_data = writer->get_buf(writer);
+       icv = writer->skip(writer, icv.len);
+       if (!signer->get_signature(signer, auth_data, icv.ptr))
+       {
+               DBG1(DBG_ESP, "ESP encryption failed: signature generation failed");
+               writer->destroy(writer);
+               return FAILED;
+       }
+
+       DBG3(DBG_ESP, "ESP packet:\n  SPI %.8x [seq %u]\n  IV %B\n  "
+                "encrypted %B\n  ICV %B", ntohl(spi), next_seqno, &iv,
+                &ciphertext, &icv);
+
+       this->packet_data = writer->extract_buf(writer);
+       writer->destroy(writer);
+       return SUCCESS;
+}
+
+METHOD(esp_packet_t, get_next_header, u_int8_t,
+       private_esp_packet_t *this)
+{
+       return this->next_header;
+}
+
+METHOD(esp_packet_t, get_payload, chunk_t,
+       private_esp_packet_t *this)
+{
+       return this->payload;
+}
+
+METHOD(esp_packet_t, get_packet_data, chunk_t,
+       private_esp_packet_t *this)
+{
+       return this->packet_data;
+}
+
+METHOD(esp_packet_t, get_source, host_t*,
+       private_esp_packet_t *this)
+{
+       return this->src;
+}
+
+METHOD(esp_packet_t, get_destination, host_t*,
+       private_esp_packet_t *this)
+{
+       return this->dst;
+}
+
+METHOD(esp_packet_t, destroy, void,
+       private_esp_packet_t *this)
+{
+       chunk_free(&this->payload);
+       chunk_free(&this->packet_data);
+       this->src->destroy(this->src);
+       this->dst->destroy(this->dst);
+       free(this);
+}
+
+static private_esp_packet_t *esp_packet_create_empty(host_t *src, host_t *dst)
+{
+       private_esp_packet_t *this;
+
+       INIT(this,
+               .public = {
+                       .get_source = _get_source,
+                       .get_destination = _get_destination,
+                       .get_packet_data = _get_packet_data,
+                       .get_payload = _get_payload,
+                       .get_next_header = _get_next_header,
+                       .parse_header = _parse_header,
+                       .decrypt = _decrypt,
+                       .encrypt = _encrypt,
+                       .destroy = _destroy,
+               },
+               .src = src,
+               .dst = dst,
+               .next_header = IPPROTO_NONE,
+       );
+       return this;
+}
+
+/**
+ * Described in header.
+ */
+esp_packet_t *esp_packet_create_from_packet(host_t *src, host_t *dst,
+                                                                                       chunk_t packet_data)
+{
+       private_esp_packet_t *this;
+
+       this = esp_packet_create_empty(src, dst);
+       this->packet_data = packet_data;
+
+       return &this->public;
+}
+
+/**
+ * Described in header.
+ */
+esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst,
+                                                                               chunk_t payload, u_int8_t next_header)
+{
+       private_esp_packet_t *this;
+
+       this = esp_packet_create_empty(src, dst);
+       this->next_header = next_header;
+       this->payload = payload;
+
+       return &this->public;
+}
+
diff --git a/src/libipsec/esp_packet.h b/src/libipsec/esp_packet.h
new file mode 100644 (file)
index 0000000..473eeb4
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup esp_packet esp_packet
+ * @{ @ingroup libipsec
+ */
+
+#ifndef ESP_PACKET_H_
+#define ESP_PACKET_H_
+
+#include "esp_context.h"
+
+#include <library.h>
+#include <utils/host.h>
+
+typedef struct esp_packet_t esp_packet_t;
+
+/**
+ *  ESP packet
+ */
+struct esp_packet_t {
+
+       /**
+        * Get the source address of this packet
+        *
+        * @return                              source host
+        */
+       host_t *(*get_source)(esp_packet_t *this);
+
+       /**
+        * Get the destination address of this packet
+        *
+        * @return                              destination host
+        */
+       host_t *(*get_destination)(esp_packet_t *this);
+
+       /**
+        * Parse the packet header before decryption. Tries to read the SPI
+        * from the packet to find a corresponding SA.
+        *
+        * @param spi                   parsed SPI, in network byte order
+        * @return                              TRUE when successful, FALSE otherwise (e.g. when the
+        *                                              length of the packet is invalid)
+        */
+       bool (*parse_header)(esp_packet_t *this, u_int32_t *spi);
+
+       /**
+        * Authenticate and decrypt the packet. Also verifies the sequence number
+        * using the supplied ESP context and updates the anti-replay window.
+        *
+        * @param esp_context           ESP context of corresponding inbound IPsec SA
+        * @return                                      - SUCCESS if successfully authenticated,
+        *                                                        decrypted and parsed
+        *                                                      - PARSE_ERROR if the length of the packet or the
+        *                                                        padding is invalid
+        *                                                      - VERIFY_ERROR if the sequence number
+        *                                                        verification failed
+        *                                                      - FAILED if the ICV (MAC) check or the actual
+        *                                                        decryption failed
+        */
+       status_t (*decrypt)(esp_packet_t *this, esp_context_t *esp_context);
+
+       /**
+        * Encapsulate and encrypt the packet. The sequence number will be generated
+        * using the supplied ESP context.
+        *
+        * @param esp_context           ESP context of corresponding outbound IPsec SA
+        * @param spi                           SPI value to use, in network byte order
+        * @return                                      - SUCCESS if encrypted
+        *                                                      - FAILED if sequence number cycled or any of the
+        *                                                        cryptographic functions failed
+        *                                                      - NOT_FOUND if no suitable RNG could be found
+        */
+       status_t (*encrypt)(esp_packet_t *this, esp_context_t *esp_context,
+                                               u_int32_t spi);
+
+       /**
+        * Get the next header field of a packet.
+        *
+        * @note Packet has to be in the decrypted state.
+        *
+        * @return                                      next header field
+        */
+       u_int8_t (*get_next_header)(esp_packet_t *this);
+
+       /**
+        * Get the plaintext payload of this packet (e.g. inner IP packet).
+        *
+        * @return                                      plaintext payload (internal data),
+        *                                                      chunk_empty if not decrypted
+        */
+       chunk_t (*get_payload)(esp_packet_t *this);
+
+       /**
+        * Get the packet data to send / as received on the wire.
+        *
+        * @return                                      encrypted packet data (internal data),
+        *                                                      chunk_empty if not encrypted
+        */
+       chunk_t (*get_packet_data)(esp_packet_t *this);
+
+       /**
+        * Destroy an esp_packet_t
+        */
+       void (*destroy)(esp_packet_t *this);
+
+};
+
+/**
+ * Create an ESP packet out of data from the wire.
+ *
+ * @param src                  source address from which the packet was sent, owned
+ * @param dst                  destination address to which the packet was sent, owned
+ * @param data                 the packet data as received, gets owned
+ * @return                             esp_packet_t instance
+ */
+esp_packet_t *esp_packet_create_from_packet(host_t *src, host_t *dst,
+                                                                                       chunk_t data);
+
+/**
+ * Create an ESP packet from a plaintext payload (e.g. inner IP packet)
+ *
+ * @param src                  source address
+ * @param dst                  destination address
+ * @param payload              plaintext payload (e.g. inner IP packet), gets owned
+ * @param next_header  next header type of the payload (e.g IPPROTO_IPIP)
+ * @return                             esp_packet_t instance
+ */
+esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst,
+                                                                               chunk_t payload, u_int8_t next_header);
+
+#endif /** ESP_PACKET_H_ @}*/
+