Adding class to manage ESP context (crypto, sequence numbers)
authorTobias Brunner <tobias@strongswan.org>
Thu, 5 Jul 2012 11:44:57 +0000 (13:44 +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_context.c [new file with mode: 0644]
src/libipsec/esp_context.h [new file with mode: 0644]

index 99ff691..d452793 100644 (file)
@@ -3,7 +3,8 @@ include $(CLEAR_VARS)
 
 # copy-n-paste from Makefile.am
 LOCAL_SRC_FILES := \
-ipsec.c ipsec.h
+ipsec.c ipsec.h \
+esp_context.c esp_context.h
 
 # build libipsec ---------------------------------------------------------------
 
index 0b8faf7..df0c9ac 100644 (file)
@@ -1,7 +1,8 @@
 ipseclib_LTLIBRARIES = libipsec.la
 
 libipsec_la_SOURCES = \
-ipsec.c ipsec.h
+ipsec.c ipsec.h \
+esp_context.c esp_context.h
 
 libipsec_la_LIBADD =
 
diff --git a/src/libipsec/esp_context.c b/src/libipsec/esp_context.c
new file mode 100644 (file)
index 0000000..c7fb7ab
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * 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 <limits.h>
+
+#include "esp_context.h"
+
+#include <library.h>
+#include <debug.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+
+/**
+ * Should be a multiple of 8
+ */
+#define ESP_DEFAULT_WINDOW_SIZE 128
+
+typedef struct private_esp_context_t private_esp_context_t;
+
+/**
+ * Private additions to esp_context_t.
+ */
+struct private_esp_context_t {
+
+       /**
+        * Public members
+        */
+       esp_context_t public;
+
+       /**
+        * Crypter used to encrypt/decrypt ESP packets
+        */
+       crypter_t *crypter;
+
+       /**
+        * Signer to authenticate ESP packets
+        */
+       signer_t *signer;
+
+       /**
+        * The highest sequence number that was successfully verified
+        * and authenticated, or assigned in an outbound context
+        */
+       u_int32_t last_seqno;
+
+       /**
+        * The bit in the window of the highest authenticated sequence number
+        */
+       u_int seqno_index;
+
+       /**
+        * The size of the anti-replay window (in bits)
+        */
+       u_int window_size;
+
+       /**
+        * The anti-replay window buffer
+        */
+       chunk_t window;
+
+       /**
+        * TRUE in case of an inbound ESP context
+        */
+       bool inbound;
+};
+
+/**
+ * Set or unset a bit in the window.
+ */
+static inline void set_window_bit(private_esp_context_t *this,
+                                                                 u_int index, bool set)
+{
+       u_int i = index / CHAR_BIT;
+
+       if (set)
+       {
+               this->window.ptr[i] |= 1 << (index % CHAR_BIT);
+       }
+       else
+       {
+               this->window.ptr[i] &= ~(1 << (index % CHAR_BIT));
+       }
+}
+
+/**
+ * Get a bit from the window.
+ */
+static inline bool get_window_bit(private_esp_context_t *this, u_int index)
+{
+       u_int i = index / CHAR_BIT;
+
+       return this->window.ptr[i] & (1 << index % CHAR_BIT);
+}
+
+/**
+ * Returns TRUE if the supplied seqno is not already marked in the window
+ */
+static bool check_window(private_esp_context_t *this, u_int32_t seqno)
+{
+       u_int offset;
+
+       offset = this->last_seqno - seqno;
+       offset = (this->seqno_index - offset) % this->window_size;
+       return !get_window_bit(this, offset);
+}
+
+METHOD(esp_context_t, verify_seqno, bool,
+       private_esp_context_t *this, u_int32_t seqno)
+{
+       if (!this->inbound)
+       {
+               return FALSE;
+       }
+
+       if (seqno > this->last_seqno)
+       {       /*       |----------------------------------------|
+                *  <---------^   ^   or    <---------^     ^
+                *     WIN    H   S            WIN    H     S
+                */
+               return TRUE;
+       }
+       else if (seqno > 0 && this->window_size > this->last_seqno - seqno)
+       {       /*       |----------------------------------------|
+                *  <---------^      or     <---------^
+                *     WIN ^  H                WIN ^  H
+                *         S                       S
+                */
+               return check_window(this, seqno);
+       }
+       else
+       {       /*       |----------------------------------------|
+                *                       ^  <---------^
+                *                       S     WIN    H
+                */
+               return FALSE;
+       }
+}
+
+METHOD(esp_context_t, set_authenticated_seqno, void,
+       private_esp_context_t *this, u_int32_t seqno)
+{
+       u_int i, shift;
+
+       if (!this->inbound)
+       {
+               return;
+       }
+
+       if (seqno > this->last_seqno)
+       {       /* shift the window to the new highest authenticated seqno */
+               shift = seqno - this->last_seqno;
+               shift = shift < this->window_size ? shift : this->window_size;
+               for (i = 0; i < shift; ++i)
+               {
+                       this->seqno_index = (this->seqno_index + 1) % this->window_size;
+                       set_window_bit(this, this->seqno_index, FALSE);
+               }
+               set_window_bit(this, this->seqno_index, TRUE);
+               this->last_seqno = seqno;
+       }
+       else
+       {       /* seqno is inside the window, set the corresponding window bit */
+               i = this->last_seqno - seqno;
+               set_window_bit(this, (this->seqno_index - i) % this->window_size, TRUE);
+       }
+}
+
+METHOD(esp_context_t, get_seqno, u_int32_t,
+       private_esp_context_t *this)
+{
+       return this->last_seqno;
+}
+
+METHOD(esp_context_t, next_seqno, bool,
+       private_esp_context_t *this, u_int32_t *seqno)
+{
+       if (this->inbound || this->last_seqno == UINT32_MAX)
+       {       /* inbound or segno would cycle */
+               return FALSE;
+       }
+       *seqno = ++this->last_seqno;
+       return TRUE;
+}
+
+METHOD(esp_context_t, get_signer, signer_t *,
+               private_esp_context_t *this)
+{
+       return this->signer;
+}
+
+METHOD(esp_context_t, get_crypter, crypter_t *,
+               private_esp_context_t *this)
+{
+       return this->crypter;
+}
+
+METHOD(esp_context_t, destroy, void,
+               private_esp_context_t *this)
+{
+       chunk_free(&this->window);
+       DESTROY_IF(this->crypter);
+       DESTROY_IF(this->signer);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key,
+                                                                 int int_alg, chunk_t int_key, bool inbound)
+{
+       private_esp_context_t *this;
+
+       INIT(this,
+               .public = {
+                       .get_crypter = _get_crypter,
+                       .get_signer = _get_signer,
+                       .get_seqno = _get_seqno,
+                       .next_seqno = _next_seqno,
+                       .verify_seqno = _verify_seqno,
+                       .set_authenticated_seqno = _set_authenticated_seqno,
+                       .destroy = _destroy,
+               },
+               .inbound = inbound,
+               .window_size = ESP_DEFAULT_WINDOW_SIZE,
+       );
+
+       switch(enc_alg)
+       {
+               case ENCR_AES_CBC:
+                       this->crypter = lib->crypto->create_crypter(lib->crypto, enc_alg,
+                                                                                                               enc_key.len);
+                       break;
+               default:
+                       break;
+       }
+       if (!this->crypter)
+       {
+               DBG1(DBG_ESP, "failed to create ESP context: unsupported encryption "
+                        "algorithm");
+               destroy(this);
+               return NULL;
+       }
+       if (!this->crypter->set_key(this->crypter, enc_key))
+       {
+               DBG1(DBG_ESP, "failed to create ESP context: setting encryption key "
+                        "failed");
+               destroy(this);
+               return NULL;
+       }
+
+       switch(int_alg)
+       {
+               case AUTH_HMAC_SHA1_96:
+               case AUTH_HMAC_SHA2_256_128:
+               case AUTH_HMAC_SHA2_384_192:
+               case AUTH_HMAC_SHA2_512_256:
+                       this->signer = lib->crypto->create_signer(lib->crypto, int_alg);
+                       break;
+               default:
+                       break;
+       }
+       if (!this->signer)
+       {
+               DBG1(DBG_ESP, "failed to create ESP context: unsupported integrity "
+                        "algorithm");
+               destroy(this);
+               return NULL;
+       }
+       if (!this->signer->set_key(this->signer, int_key))
+       {
+               DBG1(DBG_ESP, "failed to create ESP context: setting signature key "
+                        "failed");
+               destroy(this);
+               return NULL;
+       }
+
+       if (inbound)
+       {
+               this->window = chunk_alloc(this->window_size / CHAR_BIT + 1);
+               memset(this->window.ptr, 0, this->window.len);
+       }
+       return &this->public;
+}
+
+
diff --git a/src/libipsec/esp_context.h b/src/libipsec/esp_context.h
new file mode 100644 (file)
index 0000000..db247dc
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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_context esp_context
+ * @{ @ingroup libipsec
+ */
+
+#ifndef ESP_CONTEXT_H_
+#define ESP_CONTEXT_H_
+
+#include <library.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+
+typedef struct esp_context_t esp_context_t;
+
+/**
+ *  ESP context, handles sequence numbers and maintains cryptographic primitives
+ */
+struct esp_context_t {
+
+       /**
+        * Get the crypter.
+        *
+        * @return                              crypter
+        */
+       crypter_t *(*get_crypter)(esp_context_t *this);
+
+       /**
+        * Get the signer.
+        *
+        * @return                              signer
+        */
+       signer_t *(*get_signer)(esp_context_t *this);
+
+       /**
+        * Get the current outbound ESP sequence number or the highest authenticated
+        * inbound sequence number.
+        *
+        * @return                      current sequence number, in host byte order
+        */
+       u_int32_t (*get_seqno)(esp_context_t *this);
+
+       /**
+        * Allocate the next outbound ESP sequence number.
+        *
+        * @param seqno         the sequence number, in host byte order
+        * @return                      FALSE if the sequence number cycled or inbound context
+        */
+       bool (*next_seqno)(esp_context_t *this, u_int32_t *seqno);
+
+       /**
+        * Verify an ESP sequence number.  Checks whether a packet with this
+        * sequence number was already received, using the anti-replay window.
+        * This operation does not modify the internal state.  After the sequence
+        * number is successfully verified and the ESP packet is authenticated,
+        * set_authenticated_seqno() should be called.
+        *
+        * @param seqno         the sequence number to verify, in host byte order
+        * @return                      TRUE when sequence number is valid
+        */
+       bool (*verify_seqno)(esp_context_t *this, u_int32_t seqno);
+
+       /**
+        * Adds a sequence number that was successfully verified and
+        * authenticated.  A user MUST call verify_seqno() immediately before
+        * calling this method.
+        *
+        * @param seqno         verified and authenticated seq number in host byte order
+        */
+       void (*set_authenticated_seqno)(esp_context_t *this,
+                                                                       u_int32_t seqno);
+
+       /**
+        * Destroy an esp_context_t
+        */
+       void (*destroy)(esp_context_t *this);
+
+};
+
+/**
+ * Create an esp_context_t instance
+ *
+ * @param enc_alg              encryption algorithm
+ * @param enc_key              encryption key
+ * @param int_alg              integrity protection algorithm
+ * @param int_key              integrity protection key
+ * @param inbound              TRUE to create an inbound ESP context
+ * @return                             ESP context instance, or NULL if creation fails
+ */
+esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key, int int_alg,
+                                                                 chunk_t int_key, bool inbound);
+
+#endif /** ESP_CONTEXT_H_ @}*/
+