Added a simple AEAD wrapper for IKEv1 encryption/decryption.
authorTobias Brunner <tobias@strongswan.org>
Mon, 21 Nov 2011 10:43:43 +0000 (11:43 +0100)
committerTobias Brunner <tobias@strongswan.org>
Tue, 20 Mar 2012 16:30:46 +0000 (17:30 +0100)
src/libcharon/sa/keymat_v1.c

index 59791e2..3592a3e 100644 (file)
@@ -45,6 +45,11 @@ struct private_keymat_v1_t {
        pseudo_random_function_t prf_alg;
 
        /**
+        * Crypter wrapped in an aead_t interface
+        */
+       aead_t *aead;
+
+       /**
         * Key used for authentication during main mode
         */
        chunk_t skeyid;
@@ -67,6 +72,148 @@ static const chunk_t octet_0 = chunk_from_chars(0x00);
 static const chunk_t octet_1 = chunk_from_chars(0x01);
 static const chunk_t octet_2 = chunk_from_chars(0x02);
 
+/**
+ * Simple aead_t implementation without support for authentication.
+ */
+typedef struct {
+       /** implements aead_t interface */
+       aead_t aead;
+       /** crypter to be used */
+       crypter_t *crypter;
+} private_aead_t;
+
+
+METHOD(aead_t, encrypt, void,
+       private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
+       chunk_t *encrypted)
+{
+       this->crypter->encrypt(this->crypter, plain, iv, encrypted);
+}
+
+METHOD(aead_t, decrypt, bool,
+       private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv,
+       chunk_t *plain)
+{
+       this->crypter->decrypt(this->crypter, encrypted, iv, plain);
+       return TRUE;
+}
+
+METHOD(aead_t, get_block_size, size_t,
+       private_aead_t *this)
+{
+       return this->crypter->get_block_size(this->crypter);
+}
+
+METHOD(aead_t, get_icv_size, size_t,
+       private_aead_t *this)
+{
+       return 0;
+}
+
+METHOD(aead_t, get_iv_size, size_t,
+       private_aead_t *this)
+{
+       /* in order to create the messages properly we return 0 here */
+       return 0;
+}
+
+METHOD(aead_t, get_key_size, size_t,
+       private_aead_t *this)
+{
+       return this->crypter->get_key_size(this->crypter);
+}
+
+METHOD(aead_t, set_key, void,
+       private_aead_t *this, chunk_t key)
+{
+       this->crypter->set_key(this->crypter, key);
+}
+
+METHOD(aead_t, aead_destroy, void,
+       private_aead_t *this)
+{
+       this->crypter->destroy(this->crypter);
+       free(this);
+}
+
+/**
+ * Expand SKEYID_e according to Appendix B in RFC 2409.
+ * TODO-IKEv1: verify keys (e.g. for weak keys, see Appendix B)
+ */
+static chunk_t expand_skeyid_e(chunk_t skeyid_e, size_t key_size, prf_t *prf)
+{
+       size_t block_size;
+       chunk_t seed, ka;
+       int i;
+
+       if (skeyid_e.len >= key_size)
+       {       /* no expansion required, reduce to key_size */
+               skeyid_e.len = key_size;
+               return skeyid_e;
+       }
+       block_size = prf->get_block_size(prf);
+       ka = chunk_alloc((key_size / block_size + 1) * block_size);
+       ka.len = key_size;
+
+       /* Ka = K1 | K2 | ..., K1 = prf(SKEYID_e, 0), K2 = prf(SKEYID_e, K1) ... */
+       prf->set_key(prf, skeyid_e);
+       seed = octet_0;
+       for (i = 0; i < key_size; i += block_size)
+       {
+               prf->get_bytes(prf, seed, ka.ptr + i);
+               seed = chunk_create(ka.ptr + i, block_size);
+       }
+       chunk_clear(&skeyid_e);
+       return ka;
+}
+
+/**
+ * Create a simple implementation of the aead_t interface which only encrypts
+ * or decrypts data.
+ */
+static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e)
+{
+       private_aead_t *this;
+       u_int16_t alg, key_size;
+       crypter_t *crypter;
+       chunk_t ka;
+
+       if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg,
+                                                                &key_size))
+       {
+               DBG1(DBG_IKE, "no %N selected",
+                        transform_type_names, ENCRYPTION_ALGORITHM);
+               return NULL;
+       }
+       crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8);
+       if (!crypter)
+       {
+               DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
+                        transform_type_names, ENCRYPTION_ALGORITHM,
+                        encryption_algorithm_names, alg, key_size);
+               return NULL;
+       }
+       key_size = crypter->get_key_size(crypter);
+       ka = expand_skeyid_e(skeyid_e, crypter->get_key_size(crypter), prf);
+       DBG4(DBG_IKE, "encryption key Ka %B", &ka);
+       crypter->set_key(crypter, ka);
+       chunk_clear(&ka);
+
+       INIT(this,
+               .aead = {
+                       .encrypt = _encrypt,
+                       .decrypt = _decrypt,
+                       .get_block_size = _get_block_size,
+                       .get_icv_size = _get_icv_size,
+                       .get_iv_size = _get_iv_size,
+                       .get_key_size = _get_key_size,
+                       .set_key = _set_key,
+                       .destroy = _aead_destroy,
+               },
+               .crypter = crypter,
+       );
+       return &this->aead;
+}
 
 /**
  * Converts integrity algorithm to PRF algorithm
@@ -214,6 +361,12 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
 
        chunk_clear(&g_xy);
 
+       this->aead = create_aead(proposal, this->prf, skeyid_e);
+       if (!this->aead)
+       {
+               return FALSE;
+       }
+
        return TRUE;
 }
 
@@ -226,13 +379,14 @@ METHOD(keymat_t, create_dh, diffie_hellman_t*,
 METHOD(keymat_t, get_aead, aead_t*,
        private_keymat_v1_t *this, bool in)
 {
-       return NULL;
+       return this->aead;
 }
 
 METHOD(keymat_t, destroy, void,
        private_keymat_v1_t *this)
 {
        DESTROY_IF(this->prf);
+       DESTROY_IF(this->aead);
        chunk_clear(&this->skeyid);
        chunk_clear(&this->skeyid_d);
        chunk_clear(&this->skeyid_a);