123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- * Copyright (C) 2012-2013 Tobias Brunner
- * Copyright (C) 2012 Giuliano Grassi
- * Copyright (C) 2012 Ralf Sager
- * HSR 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 <stdint.h>
- #include "esp_context.h"
- #include <library.h>
- #include <utils/debug.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;
- /**
- * AEAD wrapper or method to encrypt/decrypt/authenticate ESP packets
- */
- aead_t *aead;
- /**
- * The highest sequence number that was successfully verified
- * and authenticated, or assigned in an outbound context
- */
- uint32_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, uint32_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, uint32_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, uint32_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, uint32_t,
- private_esp_context_t *this)
- {
- return this->last_seqno;
- }
- METHOD(esp_context_t, next_seqno, bool,
- private_esp_context_t *this, uint32_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_aead, aead_t*,
- private_esp_context_t *this)
- {
- return this->aead;
- }
- METHOD(esp_context_t, destroy, void,
- private_esp_context_t *this)
- {
- chunk_free(&this->window);
- DESTROY_IF(this->aead);
- free(this);
- }
- /**
- * Create an AEAD algorithm
- */
- static bool create_aead(private_esp_context_t *this, int alg,
- chunk_t key)
- {
- size_t salt = 0;
- switch (alg)
- {
- case ENCR_AES_GCM_ICV8:
- case ENCR_AES_GCM_ICV12:
- case ENCR_AES_GCM_ICV16:
- case ENCR_CHACHA20_POLY1305:
- salt = 4;
- break;
- case ENCR_AES_CCM_ICV8:
- case ENCR_AES_CCM_ICV12:
- case ENCR_AES_CCM_ICV16:
- case ENCR_CAMELLIA_CCM_ICV8:
- case ENCR_CAMELLIA_CCM_ICV12:
- case ENCR_CAMELLIA_CCM_ICV16:
- salt = 3;
- break;
- default:
- break;
- }
- if (salt)
- {
- this->aead = lib->crypto->create_aead(lib->crypto, alg,
- key.len - salt, salt);
- }
- if (!this->aead)
- {
- DBG1(DBG_ESP, "failed to create ESP context: unsupported AEAD "
- "algorithm %N", encryption_algorithm_names, alg);
- return FALSE;
- }
- if (!this->aead->set_key(this->aead, key))
- {
- DBG1(DBG_ESP, "failed to create ESP context: setting AEAD key failed");
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Create AEAD wrapper around traditional encryption/integrity algorithms
- */
- static bool create_traditional(private_esp_context_t *this, int enc_alg,
- chunk_t enc_key, int int_alg, chunk_t int_key)
- {
- crypter_t *crypter = NULL;
- signer_t *signer = NULL;
- iv_gen_t *ivg;
- switch (enc_alg)
- {
- case ENCR_AES_CTR:
- case ENCR_CAMELLIA_CTR:
- /* the key includes a 4 byte salt */
- crypter = lib->crypto->create_crypter(lib->crypto, enc_alg,
- enc_key.len - 4);
- break;
- default:
- crypter = lib->crypto->create_crypter(lib->crypto, enc_alg,
- enc_key.len);
- break;
- }
- if (!crypter)
- {
- DBG1(DBG_ESP, "failed to create ESP context: unsupported encryption "
- "algorithm %N", encryption_algorithm_names, enc_alg);
- goto failed;
- }
- if (!crypter->set_key(crypter, enc_key))
- {
- DBG1(DBG_ESP, "failed to create ESP context: setting encryption key "
- "failed");
- goto failed;
- }
- signer = lib->crypto->create_signer(lib->crypto, int_alg);
- if (!signer)
- {
- DBG1(DBG_ESP, "failed to create ESP context: unsupported integrity "
- "algorithm %N", integrity_algorithm_names, int_alg);
- goto failed;
- }
- if (!signer->set_key(signer, int_key))
- {
- DBG1(DBG_ESP, "failed to create ESP context: setting signature key "
- "failed");
- goto failed;
- }
- ivg = iv_gen_create_for_alg(enc_alg);
- if (!ivg)
- {
- DBG1(DBG_ESP, "failed to create ESP context: creating iv gen failed");
- goto failed;
- }
- this->aead = aead_create(crypter, signer, ivg);
- return TRUE;
- failed:
- DESTROY_IF(crypter);
- DESTROY_IF(signer);
- return FALSE;
- }
- /**
- * 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_aead = _get_aead,
- .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,
- );
- if (encryption_algorithm_is_aead(enc_alg))
- {
- if (!create_aead(this, enc_alg, enc_key))
- {
- destroy(this);
- return NULL;
- }
- }
- else
- {
- if (!create_traditional(this, enc_alg, enc_key, int_alg, int_key))
- {
- 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;
- }
|