]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - src/libcharon/encoding/payloads/proposal_substructure.c
proposal-substructure: Fix incorrect type for IKEv2 proposals
[thirdparty/strongswan.git] / src / libcharon / encoding / payloads / proposal_substructure.c
index 7f075f1035a609be7b3b35dee949c4847670050d..2d0cb1f829f0f89be820a80eb4d618cb9dd4c921 100644 (file)
@@ -1,7 +1,8 @@
 /*
+ * Copyright (C) 2012-2014 Tobias Brunner
  * Copyright (C) 2005-2010 Martin Willi
  * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -21,7 +22,7 @@
 #include <encoding/payloads/encodings.h>
 #include <encoding/payloads/transform_substructure.h>
 #include <library.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
 #include <daemon.h>
 
 /**
@@ -44,37 +45,37 @@ struct private_proposal_substructure_t {
        /**
         * Next payload type.
         */
-       u_int8_t  next_payload;
+       uint8_t  next_payload;
 
        /**
         * reserved byte
         */
-       u_int8_t reserved;
+       uint8_t reserved;
 
        /**
         * Length of this payload.
         */
-       u_int16_t proposal_length;
+       uint16_t proposal_length;
 
        /**
         * Proposal number.
         */
-       u_int8_t proposal_number;
+       uint8_t proposal_number;
 
        /**
         * Protocol ID.
         */
-       u_int8_t protocol_id;
+       uint8_t protocol_id;
 
        /**
         * SPI size of the following SPI.
         */
-       u_int8_t  spi_size;
+       uint8_t  spi_size;
 
        /**
         * Number of transforms.
         */
-       u_int8_t  transforms_count;
+       uint8_t  transforms_count;
 
        /**
         * SPI is stored as chunk.
@@ -87,7 +88,7 @@ struct private_proposal_substructure_t {
        linked_list_t *transforms;
 
        /**
-        * Type of this payload, PROPOSAL_SUBSTRUCTURE or PROPOSAL_SUBSTRUCTURE_V1
+        * Type of this payload, PLV2_PROPOSAL_SUBSTRUCTURE or PLV1_PROPOSAL_SUBSTRUCTURE
         */
        payload_type_t type;
 };
@@ -113,7 +114,7 @@ static encoding_rule_t encodings_v1[] = {
        /* SPI is a chunk of variable size*/
        { SPI,                          offsetof(private_proposal_substructure_t, spi)                          },
        /* Transforms are stored in a transform substructure list */
-       { PAYLOAD_LIST + TRANSFORM_SUBSTRUCTURE_V1,
+       { PAYLOAD_LIST + PLV1_TRANSFORM_SUBSTRUCTURE,
                                                offsetof(private_proposal_substructure_t, transforms)           },
 };
 
@@ -138,7 +139,7 @@ static encoding_rule_t encodings_v2[] = {
        /* SPI is a chunk of variable size*/
        { SPI,                          offsetof(private_proposal_substructure_t, spi)                          },
        /* Transforms are stored in a transform substructure list */
-       { PAYLOAD_LIST + TRANSFORM_SUBSTRUCTURE,
+       { PAYLOAD_LIST + PLV2_TRANSFORM_SUBSTRUCTURE,
                                                offsetof(private_proposal_substructure_t, transforms)           },
 };
 
@@ -170,7 +171,9 @@ typedef enum {
        IKEV1_ENCR_CAST_CBC = 6,
        IKEV1_ENCR_AES_CBC = 7,
        IKEV1_ENCR_CAMELLIA_CBC = 8,
-       IKEV1_ENCR_LAST = 9,
+       /* FreeS/WAN proprietary */
+       IKEV1_ENCR_SERPENT_CBC = 65004,
+       IKEV1_ENCR_TWOFISH_CBC = 65005,
 } ikev1_encryption_t;
 
 /**
@@ -193,23 +196,73 @@ typedef enum {
 } ikev1_ike_transid_t;
 
 /**
- * IKEv1 Transform ID ESP.
+ * IKEv1 Transform ID ESP encryption algorithm.
  */
 typedef enum {
-       IKEV1_TRANSID_ESP_DES_IV64 = 1,
-       IKEV1_TRANSID_ESP_DES = 2,
-       IKEV1_TRANSID_ESP_3DES = 3,
-       IKEV1_TRANSID_ESP_RC5 = 4,
-       IKEV1_TRANSID_ESP_IDEA = 5,
-       IKEV1_TRANSID_ESP_CAST = 6,
-       IKEV1_TRANSID_ESP_BLOWFISH = 7,
-       IKEV1_TRANSID_ESP_3IDEA = 8,
-       IKEV1_TRANSID_ESP_DES_IV32 = 9,
-       IKEV1_TRANSID_ESP_RC4 = 10,
-       IKEV1_TRANSID_ESP_NULL = 11,
-       IKEV1_TRANSID_ESP_AES_CBC = 12,
+       IKEV1_ESP_ENCR_DES_IV64 = 1,
+       IKEV1_ESP_ENCR_DES = 2,
+       IKEV1_ESP_ENCR_3DES = 3,
+       IKEV1_ESP_ENCR_RC5 = 4,
+       IKEV1_ESP_ENCR_IDEA = 5,
+       IKEV1_ESP_ENCR_CAST = 6,
+       IKEV1_ESP_ENCR_BLOWFISH = 7,
+       IKEV1_ESP_ENCR_3IDEA = 8,
+       IKEV1_ESP_ENCR_DES_IV32 = 9,
+       IKEV1_ESP_ENCR_RC4 = 10,
+       IKEV1_ESP_ENCR_NULL = 11,
+       IKEV1_ESP_ENCR_AES_CBC = 12,
+       IKEV1_ESP_ENCR_AES_CTR = 13,
+       IKEV1_ESP_ENCR_AES_CCM_8 = 14,
+       IKEV1_ESP_ENCR_AES_CCM_12 = 15,
+       IKEV1_ESP_ENCR_AES_CCM_16 = 16,
+       IKEV1_ESP_ENCR_AES_GCM_8 = 18,
+       IKEV1_ESP_ENCR_AES_GCM_12 = 19,
+       IKEV1_ESP_ENCR_AES_GCM_16 = 20,
+       IKEV1_ESP_ENCR_SEED_CBC = 21,
+       IKEV1_ESP_ENCR_CAMELLIA = 22,
+       IKEV1_ESP_ENCR_NULL_AUTH_AES_GMAC = 23,
+       /* FreeS/WAN proprietary */
+       IKEV1_ESP_ENCR_SERPENT = 252,
+       IKEV1_ESP_ENCR_TWOFISH = 253,
 } ikev1_esp_transid_t;
 
+/**
+ * IKEv1 Transform ID AH authentication algorithm.
+ */
+typedef enum {
+       IKEV1_AH_HMAC_MD5 = 2,
+       IKEV1_AH_HMAC_SHA = 3,
+       IKEV1_AH_DES_MAC = 4,
+       IKEV1_AH_HMAC_SHA2_256 = 5,
+       IKEV1_AH_HMAC_SHA2_384 = 6,
+       IKEV1_AH_HMAC_SHA2_512 = 7,
+       IKEV1_AH_RIPEMD = 8,
+       IKEV1_AH_AES_XCBC_MAC = 9,
+       IKEV1_AH_RSA = 10,
+       IKEV1_AH_AES_128_GMAC = 11,
+       IKEV1_AH_AES_192_GMAC = 12,
+       IKEV1_AH_AES_256_GMAC = 13,
+} ikev1_ah_transid_t;
+
+/**
+ * IKEv1 authentication algorithm.
+ */
+typedef enum {
+       IKEV1_AUTH_HMAC_MD5 = 1,
+       IKEV1_AUTH_HMAC_SHA = 2,
+       IKEV1_AUTH_DES_MAC = 3,
+       IKEV1_AUTH_KPDK = 4,
+       IKEV1_AUTH_HMAC_SHA2_256 = 5,
+       IKEV1_AUTH_HMAC_SHA2_384 = 6,
+       IKEV1_AUTH_HMAC_SHA2_512 = 7,
+       IKEV1_AUTH_HMAC_RIPEMD = 8,
+       IKEV1_AUTH_AES_XCBC_MAC = 9,
+       IKEV1_AUTH_SIG_RSA = 10,
+       IKEV1_AUTH_AES_128_GMAC = 11,
+       IKEV1_AUTH_AES_192_GMAC = 12,
+       IKEV1_AUTH_AES_256_GMAC = 13,
+} ikev1_auth_algo_t;
+
 /**
  * IKEv1 ESP Encapsulation mode.
  */
@@ -218,6 +271,8 @@ typedef enum {
   IKEV1_ENCAP_TRANSPORT = 2,
   IKEV1_ENCAP_UDP_TUNNEL = 3,
   IKEV1_ENCAP_UDP_TRANSPORT = 4,
+  IKEV1_ENCAP_UDP_TUNNEL_DRAFT_00_03 = 61443,
+  IKEV1_ENCAP_UDP_TRANSPORT_DRAFT_00_03 = 61444,
 } ikev1_esp_encap_t;
 
 /**
@@ -229,7 +284,7 @@ typedef enum {
 } ikev1_life_type_t;
 
 /**
- * IKEv1 authenticaiton methods
+ * IKEv1 authentication methods
  */
 typedef enum {
        IKEV1_AUTH_PSK = 1,
@@ -237,6 +292,10 @@ typedef enum {
        IKEV1_AUTH_RSA_SIG = 3,
        IKEV1_AUTH_RSA_ENC = 4,
        IKEV1_AUTH_RSA_ENC_REV = 5,
+       IKEV1_AUTH_ECDSA_256 = 9,
+       IKEV1_AUTH_ECDSA_384 = 10,
+       IKEV1_AUTH_ECDSA_521 = 11,
+       /* XAuth Modes */
        IKEV1_AUTH_XAUTH_INIT_PSK = 65001,
        IKEV1_AUTH_XAUTH_RESP_PSK = 65002,
        IKEV1_AUTH_XAUTH_INIT_DSS = 65003,
@@ -247,8 +306,22 @@ typedef enum {
        IKEV1_AUTH_XAUTH_RESP_RSA_ENC = 65008,
        IKEV1_AUTH_XAUTH_INIT_RSA_ENC_REV = 65009,
        IKEV1_AUTH_XAUTH_RESP_RSA_ENC_REV = 65010,
+       /* Hybrid Modes */
+       IKEV1_AUTH_HYBRID_INIT_RSA = 64221,
+       IKEV1_AUTH_HYBRID_RESP_RSA = 64222,
+       IKEV1_AUTH_HYBRID_INIT_DSS = 64223,
+       IKEV1_AUTH_HYBRID_RESP_DSS = 64224,
 } ikev1_auth_method_t;
 
+/**
+ * IKEv1 IPComp transform IDs
+ */
+typedef enum {
+       IKEV1_IPCOMP_OUI = 1,
+       IKEV1_IPCOMP_DEFLATE = 2,
+       IKEV1_IPCOMP_LZS = 3,
+} ikev1_ipcomp_transform_t;
+
 METHOD(payload_t, verify, status_t,
        private_proposal_substructure_t *this)
 {
@@ -256,7 +329,7 @@ METHOD(payload_t, verify, status_t,
        enumerator_t *enumerator;
        payload_t *current;
 
-       if (this->next_payload != NO_PAYLOAD && this->next_payload != 2)
+       if (this->next_payload != PL_NONE && this->next_payload != 2)
        {
                /* must be 0 or 2 */
                DBG1(DBG_ENC, "inconsistent next payload");
@@ -271,22 +344,37 @@ METHOD(payload_t, verify, status_t,
 
        switch (this->protocol_id)
        {
+               case PROTO_IPCOMP:
+                       if (this->spi.len != 2 && this->spi.len != 4)
+                       {
+                               DBG1(DBG_ENC, "invalid CPI length in IPCOMP proposal");
+                               return FAILED;
+                       }
+                       break;
                case PROTO_AH:
                case PROTO_ESP:
                        if (this->spi.len != 4)
                        {
                                DBG1(DBG_ENC, "invalid SPI length in %N proposal",
-                                                                 protocol_id_names, this->protocol_id);
+                                        protocol_id_names, this->protocol_id);
                                return FAILED;
                        }
                        break;
                case PROTO_IKE:
-                       if (this->spi.len != 0 && this->spi.len  != 8)
+                       if (this->type == PLV1_PROPOSAL_SUBSTRUCTURE)
                        {
-                               DBG1(DBG_ENC, "invalid SPI length in IKE proposal");
-                               return FAILED;
+                               if (this->spi.len <= 16)
+                               {       /* according to RFC 2409, section 3.5 anything between
+                                        * 0 and 16 is fine */
+                                       break;
+                               }
                        }
-                       break;
+                       else if (this->spi.len == 0 || this->spi.len  == 8)
+                       {
+                               break;
+                       }
+                       DBG1(DBG_ENC, "invalid SPI length in IKE proposal");
+                       return FAILED;
                default:
                        break;
        }
@@ -309,7 +397,7 @@ METHOD(payload_t, verify, status_t,
 METHOD(payload_t, get_encoding_rules, int,
        private_proposal_substructure_t *this, encoding_rule_t **rules)
 {
-       if (this->type == PROPOSAL_SUBSTRUCTURE)
+       if (this->type == PLV2_PROPOSAL_SUBSTRUCTURE)
        {
                *rules = encodings_v2;
                return countof(encodings_v2);
@@ -391,24 +479,24 @@ METHOD(proposal_substructure_t, set_is_last_proposal, void,
 }
 
 METHOD(proposal_substructure_t, set_proposal_number, void,
-       private_proposal_substructure_t *this,u_int8_t proposal_number)
+       private_proposal_substructure_t *this,uint8_t proposal_number)
 {
        this->proposal_number = proposal_number;
 }
 
-METHOD(proposal_substructure_t, get_proposal_number, u_int8_t,
+METHOD(proposal_substructure_t, get_proposal_number, uint8_t,
        private_proposal_substructure_t *this)
 {
        return this->proposal_number;
 }
 
 METHOD(proposal_substructure_t, set_protocol_id, void,
-       private_proposal_substructure_t *this,u_int8_t protocol_id)
+       private_proposal_substructure_t *this,uint8_t protocol_id)
 {
        this->protocol_id = protocol_id;
 }
 
-METHOD(proposal_substructure_t, get_protocol_id, u_int8_t,
+METHOD(proposal_substructure_t, get_protocol_id, uint8_t,
        private_proposal_substructure_t *this)
 {
        return this->protocol_id;
@@ -429,6 +517,35 @@ METHOD(proposal_substructure_t, get_spi, chunk_t,
        return this->spi;
 }
 
+METHOD(proposal_substructure_t, get_cpi, bool,
+       private_proposal_substructure_t *this, uint16_t *cpi)
+{
+
+       transform_substructure_t *transform;
+       enumerator_t *enumerator;
+
+       if (this->protocol_id != PROTO_IPCOMP)
+       {
+               return FALSE;
+       }
+
+       enumerator = this->transforms->create_enumerator(this->transforms);
+       while (enumerator->enumerate(enumerator, &transform))
+       {
+               if (transform->get_transform_id(transform) == IKEV1_IPCOMP_DEFLATE)
+               {
+                       if (cpi)
+                       {
+                               *cpi = htons(untoh16(this->spi.ptr + this->spi.len - 2));
+                       }
+                       enumerator->destroy(enumerator);
+                       return TRUE;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return FALSE;
+}
+
 /**
  * Add a transform to a proposal for IKEv2
  */
@@ -437,7 +554,7 @@ static void add_to_proposal_v2(proposal_t *proposal,
 {
        transform_attribute_t *tattr;
        enumerator_t *enumerator;
-       u_int16_t key_length = 0;
+       uint16_t key_length = 0;
 
        enumerator = transform->create_attribute_enumerator(transform);
        while (enumerator->enumerate(enumerator, &tattr))
@@ -459,8 +576,8 @@ static void add_to_proposal_v2(proposal_t *proposal,
  * Map IKEv1 to IKEv2 algorithms
  */
 typedef struct {
-       u_int16_t ikev1;
-       u_int16_t ikev2;
+       uint16_t ikev1;
+       uint16_t ikev2;
 } algo_map_t;
 
 /**
@@ -474,6 +591,8 @@ static algo_map_t map_encr[] = {
        { IKEV1_ENCR_CAST_CBC,          ENCR_CAST },
        { IKEV1_ENCR_AES_CBC,           ENCR_AES_CBC },
        { IKEV1_ENCR_CAMELLIA_CBC,      ENCR_CAMELLIA_CBC },
+       { IKEV1_ENCR_SERPENT_CBC,       ENCR_SERPENT_CBC },
+       { IKEV1_ENCR_TWOFISH_CBC,       ENCR_TWOFISH_CBC },
 };
 
 /**
@@ -499,34 +618,74 @@ static algo_map_t map_prf[] = {
 };
 
 /**
- * Get IKEv2 algorithm from IKEv1 identifier
+ * ESP encryption algorithm mapping
+ */
+static algo_map_t map_esp[] = {
+       { IKEV1_ESP_ENCR_DES_IV64,                              ENCR_DES_IV64 },
+       { IKEV1_ESP_ENCR_DES,                                   ENCR_DES },
+       { IKEV1_ESP_ENCR_3DES,                                  ENCR_3DES },
+       { IKEV1_ESP_ENCR_RC5,                                   ENCR_RC5 },
+       { IKEV1_ESP_ENCR_IDEA,                                  ENCR_IDEA },
+       { IKEV1_ESP_ENCR_CAST,                                  ENCR_CAST },
+       { IKEV1_ESP_ENCR_BLOWFISH,                              ENCR_BLOWFISH },
+       { IKEV1_ESP_ENCR_3IDEA,                                 ENCR_3IDEA },
+       { IKEV1_ESP_ENCR_DES_IV32,                              ENCR_DES_IV32 },
+       { IKEV1_ESP_ENCR_NULL,                                  ENCR_NULL },
+       { IKEV1_ESP_ENCR_AES_CBC,                               ENCR_AES_CBC },
+       { IKEV1_ESP_ENCR_AES_CTR,                               ENCR_AES_CTR },
+       { IKEV1_ESP_ENCR_AES_CCM_8,                             ENCR_AES_CCM_ICV8 },
+       { IKEV1_ESP_ENCR_AES_CCM_12,                    ENCR_AES_CCM_ICV12 },
+       { IKEV1_ESP_ENCR_AES_CCM_16,                    ENCR_AES_CCM_ICV16 },
+       { IKEV1_ESP_ENCR_AES_GCM_8,                             ENCR_AES_GCM_ICV8 },
+       { IKEV1_ESP_ENCR_AES_GCM_12,                    ENCR_AES_GCM_ICV12 },
+       { IKEV1_ESP_ENCR_AES_GCM_16,                    ENCR_AES_GCM_ICV16 },
+       { IKEV1_ESP_ENCR_CAMELLIA,                              ENCR_CAMELLIA_CBC },
+       { IKEV1_ESP_ENCR_NULL_AUTH_AES_GMAC,    ENCR_NULL_AUTH_AES_GMAC },
+       { IKEV1_ESP_ENCR_SERPENT,                               ENCR_SERPENT_CBC },
+       { IKEV1_ESP_ENCR_TWOFISH,                               ENCR_TWOFISH_CBC },
+};
+
+/**
+ * AH authentication algorithm mapping
+ */
+static algo_map_t map_ah[] = {
+       { IKEV1_AH_HMAC_MD5,            AUTH_HMAC_MD5_96 },
+       { IKEV1_AH_HMAC_SHA,            AUTH_HMAC_SHA1_96 },
+       { IKEV1_AH_DES_MAC,                     AUTH_DES_MAC },
+       { IKEV1_AH_HMAC_SHA2_256,       AUTH_HMAC_SHA2_256_128 },
+       { IKEV1_AH_HMAC_SHA2_384,       AUTH_HMAC_SHA2_384_192 },
+       { IKEV1_AH_HMAC_SHA2_512,       AUTH_HMAC_SHA2_512_256 },
+       { IKEV1_AH_AES_XCBC_MAC,        AUTH_AES_XCBC_96 },
+       { IKEV1_AH_AES_128_GMAC,        AUTH_AES_128_GMAC },
+       { IKEV1_AH_AES_192_GMAC,        AUTH_AES_192_GMAC },
+       { IKEV1_AH_AES_256_GMAC,        AUTH_AES_256_GMAC },
+};
+
+/**
+ * ESP/AH authentication algorithm mapping
+ */
+static algo_map_t map_auth[] = {
+       { IKEV1_AUTH_HMAC_MD5,                  AUTH_HMAC_MD5_96 },
+       { IKEV1_AUTH_HMAC_SHA,                  AUTH_HMAC_SHA1_96 },
+       { IKEV1_AUTH_DES_MAC,                   AUTH_DES_MAC },
+       { IKEV1_AUTH_KPDK,                              AUTH_KPDK_MD5 },
+       { IKEV1_AUTH_HMAC_SHA2_256,             AUTH_HMAC_SHA2_256_128 },
+       { IKEV1_AUTH_HMAC_SHA2_384,             AUTH_HMAC_SHA2_384_192 },
+       { IKEV1_AUTH_HMAC_SHA2_512,             AUTH_HMAC_SHA2_512_256 },
+       { IKEV1_AUTH_AES_XCBC_MAC,              AUTH_AES_XCBC_96 },
+       { IKEV1_AUTH_AES_128_GMAC,              AUTH_AES_128_GMAC },
+       { IKEV1_AUTH_AES_192_GMAC,              AUTH_AES_192_GMAC },
+       { IKEV1_AUTH_AES_256_GMAC,              AUTH_AES_256_GMAC },
+};
+
+/**
+ * Map an IKEv1 to an IKEv2 identifier
  */
-static u_int16_t get_alg_from_ikev1(transform_type_t type, u_int16_t value)
+static uint16_t ikev2_from_ikev1(algo_map_t *map, int count, uint16_t def,
+                                                                 uint16_t value)
 {
-       algo_map_t *map;
-       u_int16_t def;
-       int i, count;
+       int i;
 
-       switch (type)
-       {
-               case ENCRYPTION_ALGORITHM:
-                       map = map_encr;
-                       count = countof(map_encr);
-                       def = ENCR_UNDEFINED;
-                       break;
-               case INTEGRITY_ALGORITHM:
-                       map = map_integ;
-                       count = countof(map_integ);
-                       def = AUTH_UNDEFINED;
-                       break;
-               case PSEUDO_RANDOM_FUNCTION:
-                       map = map_prf;
-                       count = countof(map_prf);
-                       def = PRF_UNDEFINED;
-                       break;
-               default:
-                       return 0;
-       }
        for (i = 0; i < count; i++)
        {
                if (map[i].ikev1 == value)
@@ -538,44 +697,117 @@ static u_int16_t get_alg_from_ikev1(transform_type_t type, u_int16_t value)
 }
 
 /**
- * Get IKEv1 algorithm from IKEv2 identifier
+ * Map an IKEv2 to an IKEv1 identifier
+ */
+static uint16_t ikev1_from_ikev2(algo_map_t *map, int count, uint16_t value)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+       {
+               if (map[i].ikev2 == value)
+               {
+                       return map[i].ikev1;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Get IKEv2 algorithm from IKEv1 identifier
  */
-static u_int16_t get_ikev1_from_alg(transform_type_t type, u_int16_t value)
+static uint16_t get_alg_from_ikev1(transform_type_t type, uint16_t value)
 {
-       algo_map_t *map;
-       int i, count;
+       switch (type)
+       {
+               case ENCRYPTION_ALGORITHM:
+                       return ikev2_from_ikev1(map_encr, countof(map_encr),
+                                                                       ENCR_UNDEFINED, value);
+               case INTEGRITY_ALGORITHM:
+                       return ikev2_from_ikev1(map_integ, countof(map_integ),
+                                                                       AUTH_UNDEFINED, value);
+               case PSEUDO_RANDOM_FUNCTION:
+                       return ikev2_from_ikev1(map_prf, countof(map_prf),
+                                                                       PRF_UNDEFINED, value);
+               default:
+                       return 0;
+       }
+}
 
+/**
+ * Get IKEv1 algorithm from IKEv2 identifier
+ */
+static uint16_t get_ikev1_from_alg(transform_type_t type, uint16_t value)
+{
        switch (type)
        {
                case ENCRYPTION_ALGORITHM:
-                       map = map_encr;
-                       count = countof(map_encr);
-                       break;
+                       return ikev1_from_ikev2(map_encr, countof(map_encr), value);
                case INTEGRITY_ALGORITHM:
-                       map = map_integ;
-                       count = countof(map_integ);
-                       break;
+                       return ikev1_from_ikev2(map_integ, countof(map_integ), value);
                case PSEUDO_RANDOM_FUNCTION:
-                       map = map_prf;
-                       count = countof(map_prf);
-                       break;
+                       return ikev1_from_ikev2(map_prf, countof(map_prf), value);
                default:
                        return 0;
        }
-       for (i = 0; i < count; i++)
+}
+
+/**
+ * Get IKEv2 algorithm from IKEv1 ESP/AH transform ID
+ */
+static uint16_t get_alg_from_ikev1_transid(transform_type_t type,
+                                                                                       uint16_t value)
+{
+       switch (type)
        {
-               if (map[i].ikev2 == value)
-               {
-                       return map[i].ikev1;
-               }
+               case ENCRYPTION_ALGORITHM:
+                       return ikev2_from_ikev1(map_esp, countof(map_esp),
+                                                                       ENCR_UNDEFINED, value);
+               case INTEGRITY_ALGORITHM:
+                       return ikev2_from_ikev1(map_ah, countof(map_ah),
+                                                                       AUTH_UNDEFINED, value);
+               default:
+                       return 0;
+       }
+}
+
+/**
+ * Get IKEv1 ESP/AH transform ID from IKEv2 identifier
+ */
+static uint16_t get_ikev1_transid_from_alg(transform_type_t type,
+                                                                                       uint16_t value)
+{
+       switch (type)
+       {
+               case ENCRYPTION_ALGORITHM:
+                       return ikev1_from_ikev2(map_esp, countof(map_esp), value);
+               case INTEGRITY_ALGORITHM:
+                       return ikev1_from_ikev2(map_ah, countof(map_ah), value);
+               default:
+                       return 0;
        }
-       return 0;
+}
+
+/**
+ * Get IKEv1 authentication algorithm from IKEv2 identifier
+ */
+static uint16_t get_alg_from_ikev1_auth(uint16_t value)
+{
+       return ikev2_from_ikev1(map_auth, countof(map_auth), AUTH_UNDEFINED, value);
+}
+
+/**
+ * Get IKEv1 authentication algorithm from IKEv2 identifier
+ */
+static uint16_t get_ikev1_auth_from_alg(uint16_t value)
+{
+       return ikev1_from_ikev2(map_auth, countof(map_auth), value);
 }
 
 /**
  * Get IKEv1 authentication attribute from auth_method_t
  */
-static u_int16_t get_ikev1_auth(auth_method_t method)
+static uint16_t get_ikev1_auth(auth_method_t method)
 {
        switch (method)
        {
@@ -585,12 +817,24 @@ static u_int16_t get_ikev1_auth(auth_method_t method)
                        return IKEV1_AUTH_DSS_SIG;
                case AUTH_XAUTH_INIT_PSK:
                        return IKEV1_AUTH_XAUTH_INIT_PSK;
+               case AUTH_XAUTH_RESP_PSK:
+                       return IKEV1_AUTH_XAUTH_RESP_PSK;
                case AUTH_XAUTH_INIT_RSA:
                        return IKEV1_AUTH_XAUTH_INIT_RSA;
-               default:
-                       /* TODO-IKEv1: Handle other XAUTH methods */
-                       /* TODO-IKEv1: Handle ECDSA methods */
+               case AUTH_XAUTH_RESP_RSA:
+                       return IKEV1_AUTH_XAUTH_RESP_RSA;
+               case AUTH_HYBRID_INIT_RSA:
+                       return IKEV1_AUTH_HYBRID_INIT_RSA;
+               case AUTH_HYBRID_RESP_RSA:
+                       return IKEV1_AUTH_HYBRID_RESP_RSA;
+               case AUTH_ECDSA_256:
+                       return IKEV1_AUTH_ECDSA_256;
+               case AUTH_ECDSA_384:
+                       return IKEV1_AUTH_ECDSA_384;
+               case AUTH_ECDSA_521:
+                       return IKEV1_AUTH_ECDSA_521;
                case AUTH_PSK:
+               default:
                        return IKEV1_AUTH_PSK;
        }
 }
@@ -598,14 +842,30 @@ static u_int16_t get_ikev1_auth(auth_method_t method)
 /**
  * Get IKEv1 encapsulation mode
  */
-static u_int16_t get_ikev1_mode(ipsec_mode_t mode, bool udp)
+static uint16_t get_ikev1_mode(ipsec_mode_t mode, encap_t udp)
 {
        switch (mode)
        {
                case MODE_TUNNEL:
-                       return udp ? IKEV1_ENCAP_UDP_TUNNEL : IKEV1_ENCAP_TUNNEL;
+                       switch (udp)
+                       {
+                               case ENCAP_UDP:
+                                       return IKEV1_ENCAP_UDP_TUNNEL;
+                               case ENCAP_UDP_DRAFT_00_03:
+                                       return IKEV1_ENCAP_UDP_TUNNEL_DRAFT_00_03;
+                               default:
+                                       return IKEV1_ENCAP_TUNNEL;
+                       }
                case MODE_TRANSPORT:
-                       return udp ? IKEV1_ENCAP_UDP_TRANSPORT : IKEV1_ENCAP_TRANSPORT;
+                       switch (udp)
+                       {
+                               case ENCAP_UDP:
+                                       return IKEV1_ENCAP_UDP_TRANSPORT;
+                               case ENCAP_UDP_DRAFT_00_03:
+                                       return IKEV1_ENCAP_UDP_TRANSPORT_DRAFT_00_03;
+                               default:
+                                       return IKEV1_ENCAP_TRANSPORT;
+                       }
                default:
                        return IKEV1_ENCAP_TUNNEL;
        }
@@ -620,8 +880,8 @@ static void add_to_proposal_v1_ike(proposal_t *proposal,
        transform_attribute_type_t type;
        transform_attribute_t *tattr;
        enumerator_t *enumerator;
-       u_int16_t value, key_length = 0;
-       u_int16_t encr = ENCR_UNDEFINED;
+       uint16_t value, key_length = 0;
+       uint16_t encr = ENCR_UNDEFINED;
 
        enumerator = transform->create_attribute_enumerator(transform);
        while (enumerator->enumerate(enumerator, &tattr))
@@ -647,7 +907,6 @@ static void add_to_proposal_v1_ike(proposal_t *proposal,
                                                value, 0);
                                break;
                        default:
-                               /* TODO-IKEv1: lifetimes, authentication and other attributes */
                                break;
                }
        }
@@ -655,20 +914,26 @@ static void add_to_proposal_v1_ike(proposal_t *proposal,
 
        if (encr != ENCR_UNDEFINED)
        {
+               if (encr == ENCR_AES_CBC && !key_length)
+               {       /* some implementations don't send a Key Length attribute for
+                        * AES-128, early drafts of RFC 3602 allowed that */
+                       key_length = 128;
+               }
                proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, key_length);
        }
 }
 
 /**
- * Add an ESP transform to a proposal for IKEv1
+ * Add an ESP/AH transform to a proposal for IKEv1
  */
-static void add_to_proposal_v1_esp(proposal_t *proposal,
-                                                                  transform_substructure_t *transform)
+static void add_to_proposal_v1(proposal_t *proposal,
+                                       transform_substructure_t *transform, protocol_id_t proto)
 {
        transform_attribute_type_t type;
        transform_attribute_t *tattr;
        enumerator_t *enumerator;
-       u_int16_t value, key_length = 0;
+       uint16_t encr, value, key_length = 0;
+       extended_sequence_numbers_t esn = NO_EXT_SEQ_NUMBERS;
 
        enumerator = transform->create_attribute_enumerator(transform);
        while (enumerator->enumerate(enumerator, &tattr))
@@ -682,21 +947,38 @@ static void add_to_proposal_v1_esp(proposal_t *proposal,
                                break;
                        case TATTR_PH2_AUTH_ALGORITHM:
                                proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM,
-                                               get_alg_from_ikev1(INTEGRITY_ALGORITHM, value), 0);
+                                                                               get_alg_from_ikev1_auth(value), 0);
+                               break;
+                       case TATTR_PH2_GROUP:
+                               proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
+                                               value, 0);
+                               break;
+                       case TATTR_PH2_EXT_SEQ_NUMBER:
+                               esn = EXT_SEQ_NUMBERS;
                                break;
                        default:
-                               /* TODO-IKEv1: lifetimes other attributes */
                                break;
                }
        }
        enumerator->destroy(enumerator);
 
-       /* TODO-IKEv1: handle ESN attribute */
-       proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS,
-                                                       NO_EXT_SEQ_NUMBERS, 0);
-
-       proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM,
-                                                       transform->get_transform_id(transform), key_length);
+       proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, esn, 0);
+       if (proto == PROTO_ESP)
+       {
+               encr = get_alg_from_ikev1_transid(ENCRYPTION_ALGORITHM,
+                                                                       transform->get_transform_id(transform));
+               if (encr)
+               {
+                       if (encr == ENCR_AES_CBC && !key_length)
+                       {       /* some implementations don't send a Key Length attribute for
+                                * AES-128, early drafts of RFC 3602 allowed that for IKE, some
+                                * also seem to do it for ESP */
+                               key_length = 128;
+                       }
+                       proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr,
+                                                                       key_length);
+               }
+       }
 }
 
 METHOD(proposal_substructure_t, get_proposals, void,
@@ -705,15 +987,15 @@ METHOD(proposal_substructure_t, get_proposals, void,
        transform_substructure_t *transform;
        enumerator_t *enumerator;
        proposal_t *proposal = NULL;
-       u_int64_t spi = 0;
+       uint64_t spi = 0;
 
        switch (this->spi.len)
        {
                case 4:
-                       spi =  *((u_int32_t*)this->spi.ptr);
+                       spi =  *((uint32_t*)this->spi.ptr);
                        break;
                case 8:
-                       spi = *((u_int64_t*)this->spi.ptr);
+                       spi = *((uint64_t*)this->spi.ptr);
                        break;
                default:
                        break;
@@ -728,7 +1010,7 @@ METHOD(proposal_substructure_t, get_proposals, void,
                        proposal->set_spi(proposal, spi);
                        proposals->insert_last(proposals, proposal);
                }
-               if (this->type == PROPOSAL_SUBSTRUCTURE)
+               if (this->type == PLV2_PROPOSAL_SUBSTRUCTURE)
                {
                        add_to_proposal_v2(proposal, transform);
                }
@@ -740,7 +1022,8 @@ METHOD(proposal_substructure_t, get_proposals, void,
                                        add_to_proposal_v1_ike(proposal, transform);
                                        break;
                                case PROTO_ESP:
-                                       add_to_proposal_v1_esp(proposal, transform);
+                               case PROTO_AH:
+                                       add_to_proposal_v1(proposal, transform, this->protocol_id);
                                        break;
                                default:
                                        break;
@@ -759,121 +1042,114 @@ METHOD(proposal_substructure_t, create_substructure_enumerator, enumerator_t*,
 }
 
 /**
- * Get an attribute from a selected transform
+ * Get an attribute from any transform, 0 if not found
  */
-static u_int64_t get_attr_tfrm(transform_substructure_t *transform,
-                                                          transform_attribute_type_t type)
+static uint64_t get_attr(private_proposal_substructure_t *this,
+                                                 transform_attribute_type_t type)
 {
-       enumerator_t *enumerator;
+       enumerator_t *transforms, *attributes;
+       transform_substructure_t *transform;
        transform_attribute_t *attr;
-       u_int64_t value = 0;
 
-       enumerator = transform->create_attribute_enumerator(transform);
-       while (enumerator->enumerate(enumerator, &attr))
+       transforms = this->transforms->create_enumerator(this->transforms);
+       while (transforms->enumerate(transforms, &transform))
        {
-               if (attr->get_attribute_type(attr) == type)
+               attributes = transform->create_attribute_enumerator(transform);
+               while (attributes->enumerate(attributes, &attr))
                {
-                       value = attr->get_value(attr);
-                       break;
+                       if (attr->get_attribute_type(attr) == type)
+                       {
+                               attributes->destroy(attributes);
+                               transforms->destroy(transforms);
+                               return attr->get_value(attr);
+                       }
                }
+               attributes->destroy(attributes);
        }
-       enumerator->destroy(enumerator);
-       return value;
+       transforms->destroy(transforms);
+       return 0;
 }
 
-
 /**
- * Get an attribute from any transform, 0 if not found
+ * Look up a lifetime duration of a given kind in all transforms
  */
-static u_int64_t get_attr(private_proposal_substructure_t *this,
-                               transform_attribute_type_t type, transform_substructure_t **sel)
+static uint64_t get_life_duration(private_proposal_substructure_t *this,
+                               transform_attribute_type_t type_attr, ikev1_life_type_t type,
+                               transform_attribute_type_t dur_attr)
 {
+       enumerator_t *transforms, *attributes;
        transform_substructure_t *transform;
-       enumerator_t *enumerator;
-       u_int64_t value = 0;
+       transform_attribute_t *attr;
 
-       enumerator = this->transforms->create_enumerator(this->transforms);
-       while (enumerator->enumerate(enumerator, &transform))
+       transforms = this->transforms->create_enumerator(this->transforms);
+       while (transforms->enumerate(transforms, &transform))
        {
-               value = get_attr_tfrm(transform, type);
-               if (value)
+               attributes = transform->create_attribute_enumerator(transform);
+               while (attributes->enumerate(attributes, &attr))
                {
-                       if (sel)
-                       {
-                               *sel = transform;
+                       if (attr->get_attribute_type(attr) == type_attr &&
+                               attr->get_value(attr) == type)
+                       {       /* got type attribute, look for duration following next */
+                               while (attributes->enumerate(attributes, &attr))
+                               {
+                                       if (attr->get_attribute_type(attr) == dur_attr)
+                                       {
+                                               attributes->destroy(attributes);
+                                               transforms->destroy(transforms);
+                                               return attr->get_value(attr);
+                                       }
+                               }
                        }
-                       break;
                }
+               attributes->destroy(attributes);
        }
-       enumerator->destroy(enumerator);
-       return value;
+       transforms->destroy(transforms);
+       return 0;
 }
 
-METHOD(proposal_substructure_t, get_lifetime, u_int32_t,
+METHOD(proposal_substructure_t, get_lifetime, uint32_t,
        private_proposal_substructure_t *this)
 {
-       transform_substructure_t *transform;
-       transform_attribute_type_t type;
+       uint32_t duration;
 
        switch (this->protocol_id)
        {
                case PROTO_IKE:
-                       type = get_attr(this, TATTR_PH1_LIFE_TYPE, &transform);
-                       if (type == IKEV1_LIFE_TYPE_SECONDS)
-                       {
-                               return get_attr_tfrm(transform, TATTR_PH1_LIFE_DURATION);
-                       }
-                       break;
+                       return get_life_duration(this, TATTR_PH1_LIFE_TYPE,
+                                               IKEV1_LIFE_TYPE_SECONDS, TATTR_PH1_LIFE_DURATION);
                case PROTO_ESP:
-                       type = get_attr(this, TATTR_PH2_SA_LIFE_TYPE, &transform);
-                       if (type == IKEV1_LIFE_TYPE_SECONDS)
-                       {
-                               return get_attr_tfrm(transform, TATTR_PH2_SA_LIFE_DURATION);
-                       }
-                       else if (type != IKEV1_LIFE_TYPE_KILOBYTES)
+               case PROTO_AH:
+                       duration = get_life_duration(this, TATTR_PH2_SA_LIFE_TYPE,
+                                               IKEV1_LIFE_TYPE_SECONDS, TATTR_PH2_SA_LIFE_DURATION);
+                       if (!duration)
                        {       /* default to 8 hours, RFC 2407 */
                                return 28800;
                        }
-                       break;
+                       return duration;
                default:
-                       break;
+                       return 0;
        }
-       return 0;
 }
 
-METHOD(proposal_substructure_t, get_lifebytes, u_int64_t,
+METHOD(proposal_substructure_t, get_lifebytes, uint64_t,
        private_proposal_substructure_t *this)
 {
-       transform_substructure_t *transform;
-       transform_attribute_type_t type;
-
        switch (this->protocol_id)
        {
-               case PROTO_IKE:
-                       type = get_attr(this, TATTR_PH1_LIFE_TYPE, &transform);
-                       if (type == IKEV1_LIFE_TYPE_KILOBYTES)
-                       {
-                               return get_attr_tfrm(transform, TATTR_PH1_LIFE_DURATION);
-                       }
-                       break;
                case PROTO_ESP:
-                       type = get_attr(this, TATTR_PH2_SA_LIFE_TYPE, &transform);
-                       if (type == IKEV1_LIFE_TYPE_KILOBYTES)
-                       {
-                               return get_attr_tfrm(transform, TATTR_PH1_LIFE_DURATION);
-                       }
-                       break;
+               case PROTO_AH:
+                       return 1000 * get_life_duration(this, TATTR_PH2_SA_LIFE_TYPE,
+                                       IKEV1_LIFE_TYPE_KILOBYTES, TATTR_PH2_SA_LIFE_DURATION);
+               case PROTO_IKE:
                default:
-                       break;
+                       return 0;
        }
-       return 0;
-
 }
 
 METHOD(proposal_substructure_t, get_auth_method, auth_method_t,
        private_proposal_substructure_t *this)
 {
-       switch (get_attr(this, TATTR_PH1_AUTH_METHOD, NULL))
+       switch (get_attr(this, TATTR_PH1_AUTH_METHOD))
        {
                case IKEV1_AUTH_PSK:
                        return AUTH_PSK;
@@ -883,10 +1159,23 @@ METHOD(proposal_substructure_t, get_auth_method, auth_method_t,
                        return AUTH_DSS;
                case IKEV1_AUTH_XAUTH_INIT_PSK:
                        return AUTH_XAUTH_INIT_PSK;
+               case IKEV1_AUTH_XAUTH_RESP_PSK:
+                       return AUTH_XAUTH_RESP_PSK;
                case IKEV1_AUTH_XAUTH_INIT_RSA:
                        return AUTH_XAUTH_INIT_RSA;
+               case IKEV1_AUTH_XAUTH_RESP_RSA:
+                       return AUTH_XAUTH_RESP_RSA;
+               case IKEV1_AUTH_HYBRID_INIT_RSA:
+                       return AUTH_HYBRID_INIT_RSA;
+               case IKEV1_AUTH_HYBRID_RESP_RSA:
+                       return AUTH_HYBRID_RESP_RSA;
+               case IKEV1_AUTH_ECDSA_256:
+                       return AUTH_ECDSA_256;
+               case IKEV1_AUTH_ECDSA_384:
+                       return AUTH_ECDSA_384;
+               case IKEV1_AUTH_ECDSA_521:
+                       return AUTH_ECDSA_521;
                default:
-                       /* TODO-IKEv1: other XAUTH, ECDSA sigs */
                        return AUTH_NONE;
        }
 }
@@ -895,16 +1184,18 @@ METHOD(proposal_substructure_t, get_encap_mode, ipsec_mode_t,
        private_proposal_substructure_t *this, bool *udp)
 {
        *udp = FALSE;
-       switch (get_attr(this, TATTR_PH2_ENCAP_MODE, NULL))
+       switch (get_attr(this, TATTR_PH2_ENCAP_MODE))
        {
                case IKEV1_ENCAP_TRANSPORT:
                        return MODE_TRANSPORT;
                case IKEV1_ENCAP_TUNNEL:
-                       return MODE_TRANSPORT;
+                       return MODE_TUNNEL;
                case IKEV1_ENCAP_UDP_TRANSPORT:
+               case IKEV1_ENCAP_UDP_TRANSPORT_DRAFT_00_03:
                        *udp = TRUE;
                        return MODE_TRANSPORT;
                case IKEV1_ENCAP_UDP_TUNNEL:
+               case IKEV1_ENCAP_UDP_TUNNEL_DRAFT_00_03:
                        *udp = TRUE;
                        return MODE_TUNNEL;
                default:
@@ -950,13 +1241,14 @@ proposal_substructure_t *proposal_substructure_create(payload_type_t type)
                        .create_substructure_enumerator = _create_substructure_enumerator,
                        .set_spi = _set_spi,
                        .get_spi = _get_spi,
+                       .get_cpi = _get_cpi,
                        .get_lifetime = _get_lifetime,
                        .get_lifebytes = _get_lifebytes,
                        .get_auth_method = _get_auth_method,
                        .get_encap_mode = _get_encap_mode,
                        .destroy = _destroy,
                },
-               .next_payload = NO_PAYLOAD,
+               .next_payload = PL_NONE,
                .transforms = linked_list_create(),
                .type = type,
        );
@@ -969,45 +1261,47 @@ proposal_substructure_t *proposal_substructure_create(payload_type_t type)
  * Add an IKEv1 IKE proposal to the substructure
  */
 static void set_from_proposal_v1_ike(private_proposal_substructure_t *this,
-                                                                        proposal_t *proposal, u_int32_t lifetime,
+                                                                        proposal_t *proposal, uint32_t lifetime,
                                                                         auth_method_t method, int number)
 {
        transform_substructure_t *transform;
-       u_int16_t alg, key_size;
+       uint16_t alg, key_size;
        enumerator_t *enumerator;
 
-       transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE_V1,
+       transform = transform_substructure_create_type(PLV1_TRANSFORM_SUBSTRUCTURE,
                                                                                                number, IKEV1_TRANSID_KEY_IKE);
 
        enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM);
-       if (enumerator->enumerate(enumerator, &alg, &key_size))
+       while (enumerator->enumerate(enumerator, &alg, &key_size))
        {
                alg = get_ikev1_from_alg(ENCRYPTION_ALGORITHM, alg);
                if (alg)
                {
                        transform->add_transform_attribute(transform,
-                               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                                        TATTR_PH1_ENCRYPTION_ALGORITHM, alg));
                        if (key_size)
                        {
                                transform->add_transform_attribute(transform,
-                                       transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                                                TATTR_PH1_KEY_LENGTH, key_size));
                        }
+                       break;
                }
        }
        enumerator->destroy(enumerator);
 
        /* encode the integrity algorithm as hash and assume use the same PRF */
        enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM);
-       if (enumerator->enumerate(enumerator, &alg, &key_size))
+       while (enumerator->enumerate(enumerator, &alg, &key_size))
        {
                alg = get_ikev1_from_alg(INTEGRITY_ALGORITHM, alg);
                if (alg)
                {
                        transform->add_transform_attribute(transform,
-                               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                                        TATTR_PH1_HASH_ALGORITHM, alg));
+                       break;
                }
        }
        enumerator->destroy(enumerator);
@@ -1016,88 +1310,123 @@ static void set_from_proposal_v1_ike(private_proposal_substructure_t *this,
        if (enumerator->enumerate(enumerator, &alg, &key_size))
        {
                transform->add_transform_attribute(transform,
-                       transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                                TATTR_PH1_GROUP, alg));
        }
        enumerator->destroy(enumerator);
 
        transform->add_transform_attribute(transform,
-               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH1_AUTH_METHOD, get_ikev1_auth(method)));
        transform->add_transform_attribute(transform,
-               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH1_LIFE_TYPE, IKEV1_LIFE_TYPE_SECONDS));
        transform->add_transform_attribute(transform,
-               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH1_LIFE_DURATION, lifetime));
 
        add_transform_substructure(this, transform);
 }
 
 /**
- * Add an IKEv1 ESP proposal to the substructure
+ * Add an IKEv1 ESP/AH proposal to the substructure
  */
-static void set_from_proposal_v1_esp(private_proposal_substructure_t *this,
-                               proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes,
-                               ipsec_mode_t mode, bool udp, int number)
+static void set_from_proposal_v1(private_proposal_substructure_t *this,
+                               proposal_t *proposal, uint32_t lifetime, uint64_t lifebytes,
+                               ipsec_mode_t mode, encap_t udp, int number)
 {
        transform_substructure_t *transform = NULL;
-       u_int16_t alg, key_size;
+       uint16_t alg, transid, key_size;
        enumerator_t *enumerator;
 
        enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM);
        if (enumerator->enumerate(enumerator, &alg, &key_size))
        {
-               transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE_V1,
-                                                                                                  number, alg);
-               if (key_size)
+               transid = get_ikev1_transid_from_alg(ENCRYPTION_ALGORITHM, alg);
+               if (transid)
                {
-                       transform->add_transform_attribute(transform,
-                               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
-                                                                       TATTR_PH2_KEY_LENGTH, key_size));
+                       transform = transform_substructure_create_type(
+                                                               PLV1_TRANSFORM_SUBSTRUCTURE, number, transid);
+                       if (key_size)
+                       {
+                               transform->add_transform_attribute(transform,
+                                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                                                       TATTR_PH2_KEY_LENGTH, key_size));
+                       }
                }
        }
        enumerator->destroy(enumerator);
-       if (!transform)
-       {
-               return;
-       }
 
        enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM);
        if (enumerator->enumerate(enumerator, &alg, &key_size))
        {
-               alg = get_ikev1_from_alg(INTEGRITY_ALGORITHM, alg);
+               transid = get_ikev1_transid_from_alg(INTEGRITY_ALGORITHM, alg);
+               alg = get_ikev1_auth_from_alg(alg);
                if (alg)
                {
-                       transform->add_transform_attribute(transform,
-                               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
-                                                                       TATTR_PH2_AUTH_ALGORITHM, alg));
+                       if (!transform && transid)
+                       {
+                               transform = transform_substructure_create_type(
+                                                               PLV1_TRANSFORM_SUBSTRUCTURE, number, transid);
+                       }
+                       if (transform)
+                       {
+                               transform->add_transform_attribute(transform,
+                                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                                               TATTR_PH2_AUTH_ALGORITHM, alg));
+                       }
                }
        }
        enumerator->destroy(enumerator);
 
+       if (!transform)
+       {
+               return;
+       }
+
+       enumerator = proposal->create_enumerator(proposal, DIFFIE_HELLMAN_GROUP);
+       if (enumerator->enumerate(enumerator, &alg, &key_size))
+       {
+               transform->add_transform_attribute(transform,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                                       TATTR_PH2_GROUP, alg));
+       }
+       enumerator->destroy(enumerator);
+
        transform->add_transform_attribute(transform,
-               transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH2_ENCAP_MODE, get_ikev1_mode(mode, udp)));
        if (lifetime)
        {
                transform->add_transform_attribute(transform,
-                       transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH2_SA_LIFE_TYPE, IKEV1_LIFE_TYPE_SECONDS));
                transform->add_transform_attribute(transform,
-                       transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH2_SA_LIFE_DURATION, lifetime));
        }
-       else if (lifebytes)
+       if (lifebytes)
        {
                transform->add_transform_attribute(transform,
-                       transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH2_SA_LIFE_TYPE, IKEV1_LIFE_TYPE_KILOBYTES));
                transform->add_transform_attribute(transform,
-                       transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
                                                        TATTR_PH2_SA_LIFE_DURATION, lifebytes / 1000));
        }
 
+       enumerator = proposal->create_enumerator(proposal,
+                       EXTENDED_SEQUENCE_NUMBERS);
+       while (enumerator->enumerate(enumerator, &alg, NULL))
+       {
+               if (alg == EXT_SEQ_NUMBERS)
+               {
+                       transform->add_transform_attribute(transform,
+                               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                               TATTR_PH2_EXT_SEQ_NUMBER, alg));
+               }
+       }
+       enumerator->destroy(enumerator);
        add_transform_substructure(this, transform);
 }
 
@@ -1108,19 +1437,19 @@ static void set_from_proposal_v2(private_proposal_substructure_t *this,
                                                                 proposal_t *proposal)
 {
        transform_substructure_t *transform;
-       u_int16_t alg, key_size;
+       uint16_t alg, key_size;
        enumerator_t *enumerator;
 
        /* encryption algorithm is only available in ESP */
        enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM);
        while (enumerator->enumerate(enumerator, &alg, &key_size))
        {
-               transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE,
+               transform = transform_substructure_create_type(PLV2_TRANSFORM_SUBSTRUCTURE,
                                                                                                ENCRYPTION_ALGORITHM, alg);
                if (key_size)
                {
                        transform->add_transform_attribute(transform,
-                               transform_attribute_create_value(TRANSFORM_ATTRIBUTE,
+                               transform_attribute_create_value(PLV2_TRANSFORM_ATTRIBUTE,
                                                                                        TATTR_IKEV2_KEY_LENGTH, key_size));
                }
                add_transform_substructure(this, transform);
@@ -1131,7 +1460,7 @@ static void set_from_proposal_v2(private_proposal_substructure_t *this,
        enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM);
        while (enumerator->enumerate(enumerator, &alg, &key_size))
        {
-               transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE,
+               transform = transform_substructure_create_type(PLV2_TRANSFORM_SUBSTRUCTURE,
                                                                                                INTEGRITY_ALGORITHM, alg);
                add_transform_substructure(this, transform);
        }
@@ -1141,7 +1470,7 @@ static void set_from_proposal_v2(private_proposal_substructure_t *this,
        enumerator = proposal->create_enumerator(proposal, PSEUDO_RANDOM_FUNCTION);
        while (enumerator->enumerate(enumerator, &alg, &key_size))
        {
-               transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE,
+               transform = transform_substructure_create_type(PLV2_TRANSFORM_SUBSTRUCTURE,
                                                                                                PSEUDO_RANDOM_FUNCTION, alg);
                add_transform_substructure(this, transform);
        }
@@ -1151,7 +1480,7 @@ static void set_from_proposal_v2(private_proposal_substructure_t *this,
        enumerator = proposal->create_enumerator(proposal, DIFFIE_HELLMAN_GROUP);
        while (enumerator->enumerate(enumerator, &alg, NULL))
        {
-               transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE,
+               transform = transform_substructure_create_type(PLV2_TRANSFORM_SUBSTRUCTURE,
                                                                                                DIFFIE_HELLMAN_GROUP, alg);
                add_transform_substructure(this, transform);
        }
@@ -1161,7 +1490,7 @@ static void set_from_proposal_v2(private_proposal_substructure_t *this,
        enumerator = proposal->create_enumerator(proposal, EXTENDED_SEQUENCE_NUMBERS);
        while (enumerator->enumerate(enumerator, &alg, NULL))
        {
-               transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE,
+               transform = transform_substructure_create_type(PLV2_TRANSFORM_SUBSTRUCTURE,
                                                                                                EXTENDED_SEQUENCE_NUMBERS, alg);
                add_transform_substructure(this, transform);
        }
@@ -1173,8 +1502,8 @@ static void set_from_proposal_v2(private_proposal_substructure_t *this,
  */
 static void set_data(private_proposal_substructure_t *this, proposal_t *proposal)
 {
-       u_int64_t spi64;
-       u_int32_t spi32;
+       uint64_t spi64;
+       uint32_t spi32;
 
        /* add SPI, if necessary */
        switch (proposal->get_protocol(proposal))
@@ -1210,7 +1539,7 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v2(
        private_proposal_substructure_t *this;
 
        this = (private_proposal_substructure_t*)
-                                                       proposal_substructure_create(SECURITY_ASSOCIATION);
+                                               proposal_substructure_create(PLV2_PROPOSAL_SUBSTRUCTURE);
        set_from_proposal_v2(this, proposal);
        set_data(this, proposal);
 
@@ -1221,21 +1550,22 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v2(
  * See header.
  */
 proposal_substructure_t *proposal_substructure_create_from_proposal_v1(
-                       proposal_t *proposal,  u_int32_t lifetime, u_int64_t lifebytes,
-                       auth_method_t auth, ipsec_mode_t mode, bool udp)
+                       proposal_t *proposal, uint32_t lifetime, uint64_t lifebytes,
+                       auth_method_t auth, ipsec_mode_t mode, encap_t udp)
 {
        private_proposal_substructure_t *this;
 
        this = (private_proposal_substructure_t*)
-                                               proposal_substructure_create(PROPOSAL_SUBSTRUCTURE_V1);
+                                               proposal_substructure_create(PLV1_PROPOSAL_SUBSTRUCTURE);
        switch (proposal->get_protocol(proposal))
        {
                case PROTO_IKE:
-                       set_from_proposal_v1_ike(this, proposal, lifetime, auth, 0);
+                       set_from_proposal_v1_ike(this, proposal, lifetime, auth, 1);
                        break;
                case PROTO_ESP:
-                       set_from_proposal_v1_esp(this, proposal, lifetime,
-                                                                        lifebytes, mode, udp, 0);
+               case PROTO_AH:
+                       set_from_proposal_v1(this, proposal, lifetime,
+                                                                lifebytes, mode, udp, 1);
                        break;
                default:
                        break;
@@ -1249,8 +1579,8 @@ proposal_substructure_t *proposal_substructure_create_from_proposal_v1(
  * See header.
  */
 proposal_substructure_t *proposal_substructure_create_from_proposals_v1(
-                       linked_list_t *proposals, u_int32_t lifetime, u_int64_t lifebytes,
-                       auth_method_t auth, ipsec_mode_t mode, bool udp)
+                       linked_list_t *proposals, uint32_t lifetime, uint64_t lifebytes,
+                       auth_method_t auth, ipsec_mode_t mode, encap_t udp)
 {
        private_proposal_substructure_t *this = NULL;
        enumerator_t *enumerator;
@@ -1265,6 +1595,7 @@ proposal_substructure_t *proposal_substructure_create_from_proposals_v1(
                        this = (private_proposal_substructure_t*)
                                                proposal_substructure_create_from_proposal_v1(
                                                                proposal, lifetime, lifebytes, auth, mode, udp);
+                       ++number;
                }
                else
                {
@@ -1275,8 +1606,9 @@ proposal_substructure_t *proposal_substructure_create_from_proposals_v1(
                                                                                         auth, ++number);
                                        break;
                                case PROTO_ESP:
-                                       set_from_proposal_v1_esp(this, proposal, lifetime,
-                                                                                        lifebytes, mode, udp, ++number);
+                               case PROTO_AH:
+                                       set_from_proposal_v1(this, proposal, lifetime,
+                                                                                lifebytes, mode, udp, ++number);
                                        break;
                                default:
                                        break;
@@ -1287,3 +1619,55 @@ proposal_substructure_t *proposal_substructure_create_from_proposals_v1(
 
        return &this->public;
 }
+
+/**
+ * See header.
+ */
+proposal_substructure_t *proposal_substructure_create_for_ipcomp_v1(
+                       uint32_t lifetime, uint64_t lifebytes, uint16_t cpi,
+                       ipsec_mode_t mode, encap_t udp, uint8_t proposal_number)
+{
+       private_proposal_substructure_t *this;
+       transform_substructure_t *transform;
+
+
+       this = (private_proposal_substructure_t*)
+                                               proposal_substructure_create(PLV1_PROPOSAL_SUBSTRUCTURE);
+
+       /* we currently support DEFLATE only */
+       transform = transform_substructure_create_type(PLV1_TRANSFORM_SUBSTRUCTURE,
+                                                                                                  1, IKEV1_IPCOMP_DEFLATE);
+
+       transform->add_transform_attribute(transform,
+               transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                       TATTR_PH2_ENCAP_MODE, get_ikev1_mode(mode, udp)));
+       if (lifetime)
+       {
+               transform->add_transform_attribute(transform,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                       TATTR_PH2_SA_LIFE_TYPE, IKEV1_LIFE_TYPE_SECONDS));
+               transform->add_transform_attribute(transform,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                       TATTR_PH2_SA_LIFE_DURATION, lifetime));
+       }
+       if (lifebytes)
+       {
+               transform->add_transform_attribute(transform,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                       TATTR_PH2_SA_LIFE_TYPE, IKEV1_LIFE_TYPE_KILOBYTES));
+               transform->add_transform_attribute(transform,
+                       transform_attribute_create_value(PLV1_TRANSFORM_ATTRIBUTE,
+                                                       TATTR_PH2_SA_LIFE_DURATION, lifebytes / 1000));
+       }
+
+       add_transform_substructure(this, transform);
+
+       this->spi = chunk_clone(chunk_from_thing(cpi));
+       this->spi_size = this->spi.len;
+       this->protocol_id = PROTO_IPCOMP;
+       this->proposal_number = proposal_number;
+
+       compute_length(this);
+
+       return &this->public;
+}