Added support for AES-CCM and AES-GCM (authenticated encryption algorithms) in charon.
authorTobias Brunner <tobias@strongswan.org>
Fri, 16 May 2008 13:27:21 +0000 (13:27 -0000)
committerTobias Brunner <tobias@strongswan.org>
Fri, 16 May 2008 13:27:21 +0000 (13:27 -0000)
src/charon/config/proposal.c
src/charon/encoding/payloads/transform_substructure.c
src/charon/kernel/kernel_interface.c
src/libstrongswan/crypto/crypters/crypter.c
src/libstrongswan/crypto/crypters/crypter.h

index 53f58f2..4e05bdc 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2008 Tobias Brunner
  * Copyright (C) 2006 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -264,6 +265,24 @@ static void strip_dh(private_proposal_t *this)
 }
 
 /**
+ * Returns true if the given alg is an authenticated encryption algorithm
+ */
+static bool is_authenticated_encryption(u_int16_t alg)
+{
+       switch(alg)
+       {
+               case ENCR_AES_CCM_ICV8:
+               case ENCR_AES_CCM_ICV12:
+               case ENCR_AES_CCM_ICV16:
+               case ENCR_AES_GCM_ICV8:
+               case ENCR_AES_GCM_ICV12:
+               case ENCR_AES_GCM_ICV16:
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+/**
  * Find a matching alg/keysize in two linked lists
  */
 static bool select_algo(linked_list_t *first, linked_list_t *second, bool *add,
@@ -343,18 +362,21 @@ static proposal_t *select_proposal(private_proposal_t *this, private_proposal_t
                return NULL;
        }
        /* select integrity algorithm */
-       if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size))
+       if (!is_authenticated_encryption(algo))
        {
-               if (add)
+               if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size))
                {
-                       selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size);
+                       if (add)
+                       {
+                               selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size);
+                       }
+               }
+               else
+               {
+                       selected->destroy(selected);
+                       DBG2(DBG_CFG, "  no acceptable INTEGRITY_ALGORITHM found, skipping");
+                       return NULL;
                }
-       }
-       else
-       {
-               selected->destroy(selected);
-               DBG2(DBG_CFG, "  no acceptable INTEGRITY_ALGORITHM found, skipping");
-               return NULL;
        }
        /* select prf algorithm */
        if (select_algo(this->prf_algos, other->prf_algos, &add, &algo, &key_size))
@@ -519,6 +541,37 @@ static proposal_t *clone_(private_proposal_t *this)
 }
 
 /**
+ * Checks the proposal read from a string.
+ */
+static void check_proposal(private_proposal_t *this)
+{
+       enumerator_t *e;
+       algorithm_t *alg;
+       bool all_aead = TRUE;
+       
+       e = this->encryption_algos->create_enumerator(this->encryption_algos);
+       while (e->enumerate(e, &alg))
+       {
+               if (!is_authenticated_encryption(alg->algorithm))
+               {
+                       all_aead = FALSE;
+                       break;
+               }
+       }
+       e->destroy(e);
+       
+       if (all_aead)
+       {
+               /* if all encryption algorithms in the proposal are authenticated encryption
+                * algorithms we MUST NOT propose any integrity algorithms */
+               while(this->integrity_algos->remove_last(this->integrity_algos, (void**)&alg) == SUCCESS)
+               {
+                       free(alg);
+               }
+       }
+}
+
+/**
  * add a algorithm identified by a string to the proposal.
  * TODO: we could use gperf here.
  */
@@ -540,6 +593,56 @@ static status_t add_string_algo(private_proposal_t *this, chunk_t alg)
        {
                add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256);
        }
+       else if (strstr(alg.ptr, "ccm"))
+       {
+               u_int16_t key_size, icv_size;
+               if (sscanf(alg.ptr, "aes%huccm%hu", &key_size, &icv_size) == 2)
+               {
+                       if (key_size == 128 || key_size == 192 || key_size == 256)
+                       {
+                               switch(icv_size)
+                               {
+                                       case 8:
+                                               add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV8, key_size);
+                                               break;
+                                       case 12:
+                                               add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, key_size);
+                                               break;
+                                       case 16:
+                                               add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, key_size);
+                                               break;
+                                       default:
+                                               /* invalid ICV size */
+                                               break;
+                               }
+                       }
+               }
+       }
+       else if (strstr(alg.ptr, "gcm"))
+       {
+               u_int16_t key_size, icv_size;
+               if (sscanf(alg.ptr, "aes%hugcm%hu", &key_size, &icv_size) == 2)
+               {
+                       if (key_size == 128 || key_size == 192 || key_size == 256)
+                       {
+                               switch(icv_size)
+                               {
+                                       case 8:
+                                               add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, key_size);
+                                               break;
+                                       case 12:
+                                               add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, key_size);
+                                               break;
+                                       case 16:
+                                               add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, key_size);
+                                               break;
+                                       default:
+                                               /* invalid ICV size */
+                                               break;
+                               }
+                       }
+               }
+       }
        else if (strncmp(alg.ptr, "3des", alg.len) == 0)
        {
                add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0);
@@ -774,6 +877,8 @@ proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs
                return NULL;
        }
        
+       check_proposal(this);
+       
        if (protocol == PROTO_AH || protocol == PROTO_ESP)
        {
                add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0);
index 6706cf8..27e921a 100644 (file)
@@ -390,14 +390,28 @@ transform_substructure_t *transform_substructure_create_type(transform_type_t tr
        transform->set_transform_id(transform,transform_id);
        
        /* a keylength attribute is only created for variable length algos */
-       if (transform_type == ENCRYPTION_ALGORITHM &&
-               (transform_id == ENCR_AES_CBC ||
-                transform_id == ENCR_IDEA ||
-                transform_id == ENCR_CAST ||
-                transform_id == ENCR_BLOWFISH))
+       if (transform_type == ENCRYPTION_ALGORITHM)
        {
-               transform_attribute_t *attribute = transform_attribute_create_key_length(key_length);
-               transform->add_transform_attribute(transform,attribute);
+               switch(transform_id)
+               {
+                       case ENCR_AES_CBC:
+                       case ENCR_IDEA:
+                       case ENCR_CAST:
+                       case ENCR_BLOWFISH:
+                       case ENCR_AES_CCM_ICV8:
+                       case ENCR_AES_CCM_ICV12:
+                       case ENCR_AES_CCM_ICV16:
+                       case ENCR_AES_GCM_ICV8:
+                       case ENCR_AES_GCM_ICV12:
+                       case ENCR_AES_GCM_ICV16:
+                       {
+                               transform_attribute_t *attribute = transform_attribute_create_key_length(key_length);
+                               transform->add_transform_attribute(transform,attribute);
+                               break;
+                       }
+                       default:
+                               break;
+               }
        }
 
        return transform;
index 43837a2..4d47907 100644 (file)
@@ -112,18 +112,24 @@ struct kernel_algorithm_t {
  * Algorithms for encryption
  */
 static kernel_algorithm_t encryption_algs[] = {
-/*     {ENCR_DES_IV64,         "***",                  0}, */
-       {ENCR_DES,                      "des",                  64},
-       {ENCR_3DES,             "des3_ede",             192},
-/*     {ENCR_RC5,                      "***",                  0}, */
-/*     {ENCR_IDEA,             "***",                  0}, */
-       {ENCR_CAST,             "cast128",              0},
-       {ENCR_BLOWFISH,         "blowfish",             0},
-/*     {ENCR_3IDEA,            "***",                  0}, */
-/*     {ENCR_DES_IV32,         "***",                  0}, */
-       {ENCR_NULL,             "cipher_null",  0},
-       {ENCR_AES_CBC,          "aes",                  0},
-/*     {ENCR_AES_CTR,          "***",                  0}, */
+/*     {ENCR_DES_IV64,                 "***",                                  0}, */
+       {ENCR_DES,                              "des",                                  64},
+       {ENCR_3DES,                     "des3_ede",                             192},
+/*     {ENCR_RC5,                              "***",                                  0}, */
+/*     {ENCR_IDEA,                     "***",                                  0}, */
+       {ENCR_CAST,                     "cast128",                              0},
+       {ENCR_BLOWFISH,                 "blowfish",                             0},
+/*     {ENCR_3IDEA,                    "***",                                  0}, */
+/*     {ENCR_DES_IV32,                 "***",                                  0}, */
+       {ENCR_NULL,                     "cipher_null",                  0},
+       {ENCR_AES_CBC,                  "aes",                                  0},
+/*     {ENCR_AES_CTR,                  "***",                                  0}, */
+       {ENCR_AES_CCM_ICV8,             "rfc4309(ccm(aes))",    64},    /* key_size = ICV size */
+       {ENCR_AES_CCM_ICV12,    "rfc4309(ccm(aes))",    96},    /* key_size = ICV size */
+       {ENCR_AES_CCM_ICV16,    "rfc4309(ccm(aes))",    128},   /* key_size = ICV size */
+       {ENCR_AES_GCM_ICV8,             "rfc4106(gcm(aes))",    64},    /* key_size = ICV size */
+       {ENCR_AES_GCM_ICV12,    "rfc4106(gcm(aes))",    96},    /* key_size = ICV size */
+       {ENCR_AES_GCM_ICV16,    "rfc4106(gcm(aes))",    128},   /* key_size = ICV size */
        {END_OF_LIST,           NULL,                   0},
 };
 
@@ -1966,6 +1972,7 @@ static status_t add_sa(private_kernel_interface_t *this,
 {
        unsigned char request[BUFFER_SIZE];
        char *alg_name;
+       u_int16_t add_keymat = 32; /* additional 4 octets KEYMAT required for AES-GCM as of RFC4106 8.1. */
        struct nlmsghdr *hdr;
        struct xfrm_usersa_info *sa;
        
@@ -2000,34 +2007,82 @@ static status_t add_sa(private_kernel_interface_t *this,
        
        struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info);
        
-       if (enc_alg != ENCR_UNDEFINED)
+       switch (enc_alg)
        {
-               rthdr->rta_type = XFRMA_ALG_CRYPT;
-               alg_name = lookup_algorithm(encryption_algs, enc_alg, &enc_size);
-               if (alg_name == NULL)
+               case ENCR_UNDEFINED:
+                       /* no encryption */
+                       break;
+               case ENCR_AES_CCM_ICV8:
+               case ENCR_AES_CCM_ICV12:
+               case ENCR_AES_CCM_ICV16:
+                       /* AES-CCM needs only 3 additional octets KEYMAT as of RFC 4309 7.1. */
+                       add_keymat = 24;
+                       /* fall-through */
+               case ENCR_AES_GCM_ICV8:
+               case ENCR_AES_GCM_ICV12:
+               case ENCR_AES_GCM_ICV16:
                {
-                       DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
-                                encryption_algorithm_names, enc_alg);
-                       return FAILED;
+                       u_int16_t icv_size = 0;
+                       rthdr->rta_type = XFRMA_ALG_AEAD;
+                       alg_name = lookup_algorithm(encryption_algs, enc_alg, &icv_size);
+                       if (alg_name == NULL)
+                       {
+                               DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
+                                        encryption_algorithm_names, enc_alg);
+                               return FAILED;
+                       }
+                       DBG2(DBG_KNL, "  using encryption algorithm %N with key size %d",
+                                encryption_algorithm_names, enc_alg, enc_size);
+                       
+                       /* additional KEYMAT required */
+                       enc_size += add_keymat;
+                       
+                       rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_aead) + enc_size / 8);
+                       hdr->nlmsg_len += rthdr->rta_len;
+                       if (hdr->nlmsg_len > sizeof(request))
+                       {
+                               return FAILED;
+                       }
+                       
+                       struct xfrm_algo_aead* algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr);
+                       algo->alg_key_len = enc_size;
+                       algo->alg_icv_len = icv_size;
+                       strcpy(algo->alg_name, alg_name);
+                       prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key);
+                       
+                       rthdr = XFRM_RTA_NEXT(rthdr);
+                       break;
                }
-               DBG2(DBG_KNL, "  using encryption algorithm %N with key size %d",
-                        encryption_algorithm_names, enc_alg, enc_size);
-               
-               rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_size);
-               hdr->nlmsg_len += rthdr->rta_len;
-               if (hdr->nlmsg_len > sizeof(request))
+               default:
                {
-                       return FAILED;
+                       rthdr->rta_type = XFRMA_ALG_CRYPT;
+                       alg_name = lookup_algorithm(encryption_algs, enc_alg, &enc_size);
+                       if (alg_name == NULL)
+                       {
+                               DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
+                                        encryption_algorithm_names, enc_alg);
+                               return FAILED;
+                       }
+                       DBG2(DBG_KNL, "  using encryption algorithm %N with key size %d",
+                                encryption_algorithm_names, enc_alg, enc_size);
+                       
+                       rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_size / 8);
+                       hdr->nlmsg_len += rthdr->rta_len;
+                       if (hdr->nlmsg_len > sizeof(request))
+                       {
+                               return FAILED;
+                       }
+                       
+                       struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr);
+                       algo->alg_key_len = enc_size;
+                       strcpy(algo->alg_name, alg_name);
+                       prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key);
+                       
+                       rthdr = XFRM_RTA_NEXT(rthdr);
+                       break;
                }
-               
-               struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr);
-               algo->alg_key_len = enc_size;
-               strcpy(algo->alg_name, alg_name);
-               prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key);
-               
-               rthdr = XFRM_RTA_NEXT(rthdr);
        }
-       
+               
        if (int_alg  != AUTH_UNDEFINED)
        {
                rthdr->rta_type = XFRMA_ALG_AUTH;
@@ -2041,7 +2096,7 @@ static status_t add_sa(private_kernel_interface_t *this,
                DBG2(DBG_KNL, "  using integrity algorithm %N with key size %d",
                         integrity_algorithm_names, int_alg, int_size);
                
-               rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_size);
+               rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_size / 8);
                hdr->nlmsg_len += rthdr->rta_len;
                if (hdr->nlmsg_len > sizeof(request))
                {
index 69bab02..879cf60 100644 (file)
@@ -30,9 +30,16 @@ ENUM_NEXT(encryption_algorithm_names, ENCR_DES_IV64, ENCR_DES_IV32, ENCR_UNDEFIN
        "BLOWFISH",
        "3IDEA",
        "DES_IV32");
-ENUM_NEXT(encryption_algorithm_names, ENCR_NULL, ENCR_AES_CTR, ENCR_DES_IV32,
+ENUM_NEXT(encryption_algorithm_names, ENCR_NULL, ENCR_AES_CCM_ICV16, ENCR_DES_IV32,
        "NULL",
        "AES_CBC",
-       "AES_CTR");
-ENUM_END(encryption_algorithm_names, ENCR_AES_CTR);
+       "AES_CTR",
+       "AES_CCM_8",
+       "AES_CCM_12",
+       "AES_CCM_16");
+ENUM_NEXT(encryption_algorithm_names, ENCR_AES_GCM_ICV8, ENCR_AES_GCM_ICV16, ENCR_AES_CCM_ICV16,
+       "AES_GCM_8",
+       "AES_GCM_12",
+       "AES_GCM_16");
+ENUM_END(encryption_algorithm_names, ENCR_AES_GCM_ICV16);
 
index 5a64b1c..345d7ab 100644 (file)
@@ -45,7 +45,13 @@ enum encryption_algorithm_t {
        ENCR_DES_IV32 = 9,
        ENCR_NULL = 11,
        ENCR_AES_CBC = 12,
-       ENCR_AES_CTR = 13
+       ENCR_AES_CTR = 13,
+       ENCR_AES_CCM_ICV8 = 14,
+    ENCR_AES_CCM_ICV12 = 15,
+    ENCR_AES_CCM_ICV16 = 16,
+    ENCR_AES_GCM_ICV8 = 18,
+    ENCR_AES_GCM_ICV12 = 19,
+    ENCR_AES_GCM_ICV16 = 20
 };
 
 /**