Merge authors: Miika Komu (miika-iki) Xin (eric-nevup) Related merge proposals: https://code.launchpad.net/~hipl-core/hipl/hipv2-dh-ecdh/+merge/131786 proposed by: Miika Komu (miika-iki) review: Needs Information - Diego Biurrun (diego-biurrun) review: Approve - Christof Mroz (christof-mroz) ------------------------------------------------------------ revno: 6437 [merge] committer: Miika Komu <miika@xxxxxx> branch nick: hipl timestamp: Tue 2013-10-22 16:50:32 +0300 message: Merged lp:~hipl-core/hipl/hipv2-dh-ecdh modified: libcore/builder.c libcore/crypto.c libcore/crypto.h libcore/protodefs.h libhipl/cookie.c libhipl/cookie.h libhipl/dh.c libhipl/dh.h libhipl/hidb.c libhipl/hidb.h libhipl/input.c libhipl/netdev.c libhipl/output.c libhipl/output.h test/libcore/crypto.c -- lp:hipl https://code.launchpad.net/~hipl-core/hipl/trunk Your team HIPL core team is subscribed to branch lp:hipl. To unsubscribe from this branch go to https://code.launchpad.net/~hipl-core/hipl/trunk/+edit-subscription
=== modified file 'libcore/builder.c' --- libcore/builder.c 2013-08-19 18:30:29 +0000 +++ libcore/builder.c 2013-10-22 12:34:28 +0000 @@ -119,7 +119,8 @@ * @param content the buffer to hold all the items * @param count the number of items in the buffer * @param item_size the size of each item in bytes. The function only supports - * items in 2 bytes or 4 bytes. + * byte conversion for items in 2 bytes or 4 bytes. For items + * with other size, no conversion will be applied. * @param flag the flag to decide the byte conversion order */ static void convert_byte_order(void *content, unsigned count, @@ -128,14 +129,12 @@ uint32_t (*f32)(uint32_t) = (flag == CBO_HTON) ? htonl : ntohl; uint16_t (*f16)(uint16_t) = (flag == CBO_HTON) ? htons : ntohs; - HIP_ASSERT(item_size == sizeof(uint16_t) || item_size == sizeof(uint32_t)); - if (item_size == sizeof(uint16_t)) { uint16_t *p = content; for (unsigned i = 0; i < count; i++) { p[i] = f16(p[i]); } - } else { + } else if (item_size == sizeof(uint32_t)) { uint32_t *p = content; for (unsigned i = 0; i < count; i++) { p[i] = f32(p[i]); @@ -730,7 +729,8 @@ HIP_PARAM_ESP_PROT_ANCHOR, HIP_PARAM_ESP_PROT_BRANCH, HIP_PARAM_ESP_PROT_SECRET, - HIP_PARAM_ESP_PROT_ROOT + HIP_PARAM_ESP_PROT_ROOT, + HIP_PARAM_DH_GROUP_LIST }; hip_tlv type = hip_get_param_type(param); @@ -1200,6 +1200,7 @@ case HIP_PARAM_CERT: return "HIP_PARAM_CERT"; case HIP_PARAM_CERT_X509_REQ: return "HIP_PARAM_CERT_X509_REQ"; case HIP_PARAM_CERT_X509_RESP: return "HIP_PARAM_CERT_X509_RESP"; + case HIP_PARAM_DH_GROUP_LIST: return "HIP_PARAM_DH_GROUP_LIST"; case HIP_PARAM_DH_SHARED_KEY: return "HIP_PARAM_DH_SHARED_KEY"; case HIP_PARAM_DIFFIE_HELLMAN: return "HIP_PARAM_DIFFIE_HELLMAN"; case HIP_PARAM_DSA_SIGN_DATA: return "HIP_PARAM_DSA_SIGN_DATA"; === modified file 'libcore/crypto.c' --- libcore/crypto.c 2013-09-20 06:29:24 +0000 +++ libcore/crypto.c 2013-10-22 12:34:28 +0000 @@ -46,6 +46,7 @@ */ #include <errno.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -68,6 +69,12 @@ #include "crypto.h" +const uint8_t HIP_DH_GROUP_LIST[HIP_DH_GROUP_LIST_SIZE] = { + HIP_DH_NIST_P_384, + HIP_DH_OAKLEY_15, + HIP_DH_OAKLEY_5 +}; + /* * Diffie-Hellman primes */ @@ -325,6 +332,10 @@ sizeof(dhprime_modp_3072), sizeof(dhprime_modp_6144), sizeof(dhprime_modp_8192), + 64, /* NIST P-256 */ + 96, /* NIST P-384 */ + 132, /* NIST P-512 */ + 0, /* SECP 160R1, unsupported */ }; static unsigned char dhgen[HIP_MAX_DH_GROUP_ID] = { 0, @@ -701,6 +712,172 @@ return dh; } +#ifdef HAVE_EC_CRYPTO + +/** + * Test if the current DH group ID belongs to an ECDH group. + * + * @param group_id the Diffie-Hellman group ID + * @return True if the given group is an ECDH group, False otherwise. + */ +bool hip_is_ecdh_group(const int group_id) +{ + return group_id == HIP_DH_NIST_P_256 || + group_id == HIP_DH_NIST_P_384 || + group_id == HIP_DH_NIST_P_521; +} + +/** + * Generate a new Elliptic Curve Diffie-Hellman key. + * + * @param group_id the group ID of the DH_GROUP defined in HIPv2 + * @return a new ECDH key (caller deallocates), or NULL on error. + */ +EC_KEY *hip_generate_ecdh_key(const int group_id) +{ + char rnd_seed[20]; + struct timeval tv; + EC_KEY *key; + int nid; + + switch (group_id) { + case HIP_DH_NIST_P_256: + nid = NID_X9_62_prime256v1; + break; + case HIP_DH_NIST_P_384: + nid = NID_secp384r1; + break; + case HIP_DH_NIST_P_521: + nid = NID_secp521r1; + break; + default: + HIP_ERROR("Unsupported ECDH group: %d\n", group_id); + return NULL; + } + + gettimeofday(&tv, NULL); + sprintf(rnd_seed, "%x%x", (unsigned int) tv.tv_usec, + (unsigned int) tv.tv_sec); + RAND_seed(rnd_seed, sizeof(rnd_seed)); + + if ((key = EC_KEY_new_by_curve_name(nid)) == NULL) { + HIP_ERROR("Failed to create a new EC_KEY from nid: %d\n", nid); + return NULL; + } + + if (EC_KEY_generate_key(key) == 0) { + HIP_ERROR("Failed to generate parameters for the new EC_KEY.\n"); + EC_KEY_free(key); + return NULL; + } + + return key; +} + +/** + * Generate a shared key using Elliptic Curve Diffie-Hellman. + * This method only supports keys using Prime Curve. + * + * @param key the Elliptic Curve Diffie-Hellman key + * @param peer_pub_x the x coordinator of the peer's public key + * @param peer_pub_y the y coordinator of the peer's public key + * @param peer_len length of the @c peer_pub_x or @c peer_pub_y (these two + * length values are identical) + * @param shared_key shared key to generate + * @param outlen the length of the @c shared_key + * @return the length of the shared key on success, 0 otherwise + */ +int hip_gen_ecdh_shared_key(EC_KEY *const key, + const uint8_t *const peer_pub_x, + const uint8_t *const peer_pub_y, + const size_t peer_len, + uint8_t *const shared_key, + const size_t outlen) +{ + const EC_GROUP *group; + BIGNUM *peer_pubx = NULL; + BIGNUM *peer_puby = NULL; + EC_POINT *peer_pub = NULL; + unsigned int err = 1; + + if (EC_KEY_check_key(key) == 0) { + HIP_ERROR("Invalid input EC_KEY\n"); + return 0; + } + + group = EC_KEY_get0_group(key); + + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) + != NID_X9_62_prime_field) { + HIP_ERROR("Invalid group method, only prime curve is supported.\n"); + return 0; + } + + peer_pub = EC_POINT_new(group); + peer_pubx = BN_bin2bn(peer_pub_x, peer_len, NULL); + peer_puby = BN_bin2bn(peer_pub_y, peer_len, NULL); + + HIP_IFEL(EC_POINT_set_affine_coordinates_GFp(group, peer_pub, peer_pubx, + peer_puby, NULL) == 0, + 0, "Failed to create peer's public key.\n"); + + err = ECDH_compute_key(shared_key, outlen, peer_pub, key, NULL); + HIP_IFEL(err == 0 || err != peer_len, 0, + "Failed to compute the ECDH shared key\n"); + +out_err: + BN_free(peer_pubx); + BN_free(peer_puby); + EC_POINT_free(peer_pub); + return err; +} + +/** + * Encode an ECDH public key into a character array. + * + * @param key the ECDH key + * @param[out] out the character array + * @param outlen the length of @c out in bytes + * @return the number of bytes written + */ +int hip_encode_ecdh_publickey(EC_KEY *key, uint8_t *out, int outlen) +{ + BIGNUM *pubx = NULL; + BIGNUM *puby = NULL; + int len; + int err = 0; + + if (key == NULL || out == NULL || outlen < 0 || + EC_KEY_check_key(key) == 0) { + HIP_ERROR("Invalid input\n"); + return -1; + } + + pubx = BN_new(); + puby = BN_new(); + HIP_IFEL(pubx == NULL || puby == NULL, -1, "Failed to initialize Big Number\n"); + + err = EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key), + EC_KEY_get0_public_key(key), + pubx, puby, NULL); + HIP_IFEL(err == 0, -1, + "Failed to get x,y coordinates from the ECDH key\n"); + + len = BN_num_bytes(pubx); + HIP_IFEL(outlen < len * 2, -1, "Output buffer too small\n"); + + bn2bin_safe(pubx, out, outlen / 2); + bn2bin_safe(puby, out + outlen / 2, outlen / 2); + err = outlen; + +out_err: + BN_free(pubx); + BN_free(puby); + return err; +} + +#endif /* HAVE_EC_CRYPTO */ + /** * Determine the size for required to store DH shared secret. * @param hip_dh_group_type the group type from DIFFIE_HELLMAN parameter @@ -714,7 +891,7 @@ if (hip_dh_group_type == 0) { HIP_ERROR("Trying to use reserved DH group type 0\n"); } else if (hip_dh_group_type > ARRAY_SIZE(dhprime_len)) { - HIP_ERROR("Unknown/unsupported MODP group %d\n", hip_dh_group_type); + HIP_ERROR("Unknown/unsupported DH or ECDH group %d\n", hip_dh_group_type); } else { ret = dhprime_len[hip_dh_group_type]; } === modified file 'libcore/crypto.h' --- libcore/crypto.h 2012-05-12 10:21:32 +0000 +++ libcore/crypto.h 2012-07-28 21:18:08 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,6 +28,7 @@ #include "config.h" +#include <stdbool.h> #include <stdint.h> #include <netinet/in.h> #include <sys/types.h> @@ -51,15 +52,25 @@ /* These should be consistent with the table length in crypto.c and * crypto/dh.c */ -#define HIP_DH_384 1 /* 384-bit group */ -#define HIP_DH_OAKLEY_1 2 /* 768-bit OAKLEY well known group 1 */ +/* 384-bit group, DEPRECATED from HIPv2 */ +#define HIP_DH_384 1 +/* 768-bit OAKLEY well known group 1, DEPRECATED from HIPv2 */ +#define HIP_DH_OAKLEY_1 2 #define HIP_DH_OAKLEY_5 3 /* 1536-bit MODP group */ #define HIP_DH_OAKLEY_15 4 /* 3072-bit MODP group */ -#define HIP_DH_OAKLEY_17 5 /* 6144-bit MODP group */ -#define HIP_DH_OAKLEY_18 6 /* 8192-bit MODP group */ +/* 6144-bit MODP group, DEPRECATED from HIPv2 */ +#define HIP_DH_OAKLEY_17 5 +/* 8192-bit MODP group, DEPRECATED from HIPv2 */ +#define HIP_DH_OAKLEY_18 6 +/* Group 7 to 10 are new groups defined in HIPv2, among which group 7,8 and 9 + * are Ellipse Curve groups. */ +#define HIP_DH_NIST_P_256 7 +#define HIP_DH_NIST_P_384 8 +#define HIP_DH_NIST_P_521 9 +#define HIP_DH_SECP_160_R1 10 #define HIP_FIRST_DH_GROUP_ID HIP_DH_OAKLEY_5 #define HIP_SECOND_DH_GROUP_ID HIP_DH_384 -#define HIP_MAX_DH_GROUP_ID 7 +#define HIP_MAX_DH_GROUP_ID 11 #define DSA_KEY_DEFAULT_BITS 1024 #define RSA_KEY_DEFAULT_BITS 1024 @@ -86,6 +97,13 @@ #define HIP_SHA(buffer, total_len, hash) SHA((buffer), (total_len), (hash)) #endif +/* HIPv2: default value for DH_GROUP_LIST parameter */ +#define HIP_DH_GROUP_LIST_SIZE 3 +const uint8_t HIP_DH_GROUP_LIST[HIP_DH_GROUP_LIST_SIZE]; + +/* HIPv2: max acceptable size of DH group list, longer part will be ignored */ +#define HIP_DH_GROUP_MAX_RECV_SIZE 6 + int ssl_rsa_verify(uint8_t *digest, uint8_t *public_key, uint8_t *signature, int pub_klen); int ssl_dsa_verify(uint8_t *digest, uint8_t *public_key, uint8_t *signature); /* In kernel these come from crypto/dh.h, included above */ @@ -121,6 +139,15 @@ int impl_ecdsa_verify(const unsigned char *const digest, EC_KEY *const ecdsa, const unsigned char *const signature); +bool hip_is_ecdh_group(const int group_id); +EC_KEY *hip_generate_ecdh_key(const int group_id); +int hip_encode_ecdh_publickey(EC_KEY *key, uint8_t *out, int outlen); +int hip_gen_ecdh_shared_key(EC_KEY *const key, + const uint8_t *const peer_pub_x, + const uint8_t *const peer_pub_y, + const size_t peer_len, + uint8_t *const shared_key, + const size_t outlen); #endif /* HAVE_EC_CRYPTO */ #endif /* HIPL_LIBCORE_CRYPTO_H */ === modified file 'libcore/protodefs.h' --- libcore/protodefs.h 2012-07-12 11:32:14 +0000 +++ libcore/protodefs.h 2012-07-20 07:52:59 +0000 @@ -143,6 +143,7 @@ #define HIP_PARAM_SOLUTION 321 #define HIP_PARAM_SEQ 385 #define HIP_PARAM_ACK 449 +#define HIP_PARAM_DH_GROUP_LIST 511 #define HIP_PARAM_DIFFIE_HELLMAN 513 #define HIP_PARAM_HIP_TRANSFORM 577 #define HIP_PARAM_ENCRYPTED 641 === modified file 'libhipl/cookie.c' --- libhipl/cookie.c 2012-06-03 10:26:55 +0000 +++ libhipl/cookie.c 2012-11-01 15:06:33 +0000 @@ -177,83 +177,99 @@ } /** - * get a copy of R1entry structure + * Get a copy of R1entry structure. * * @param ip_i Initiator's IPv6 * @param ip_r Responder's IPv6 * @param our_hit Our HIT - * @param hip_version HIP message version - * - * @note Comments for the if 0 code are inlined below. - * - * Returns NULL if error. + * @param dh_group_id Diffie Hellman group ID. -1 for HIPv1, otherwise return + R1 for HIPv2 + * @return A R1 packet copy on success, NULL on error */ struct hip_common *hip_get_r1(struct in6_addr *ip_i, struct in6_addr *ip_r, - struct in6_addr *our_hit, uint8_t hip_version) + struct in6_addr *our_hit, const int dh_group_id) { - struct hip_common *err = NULL, *r1 = NULL; - struct hip_r1entry *hip_r1table = NULL; - struct local_host_id *hid = NULL; + struct hip_common *r1 = NULL; + struct hip_common *r1_matched = NULL; + struct local_host_id *hid = NULL; int idx, len; /* Find the proper R1 table and copy the R1 message from the table */ - HIP_IFEL(!(hid = hip_get_hostid_entry_by_lhi_and_algo(our_hit, HIP_ANY_ALGO, -1)), - NULL, "Unknown HIT\n"); + hid = hip_get_hostid_entry_by_lhi_and_algo(our_hit, HIP_ANY_ALGO, -1); + if (hid == NULL) { + HIP_ERROR("Unknown HIT\n"); + return NULL; + } - hip_r1table = hid->r1[hip_version]; - idx = calc_cookie_idx(ip_i, ip_r); + idx = calc_cookie_idx(ip_i, ip_r); HIP_DEBUG("Calculated index: %d\n", idx); + if (dh_group_id == -1) { + r1_matched = &hid->r1[idx].buf.msg; + } else { + r1_matched = &hid->r1_v2[dh_group_id][idx].buf.msg; + } /* Create a copy of the found entry */ - len = hip_get_msg_total_len(&hip_r1table[idx].buf.msg); - r1 = hip_msg_alloc(); - memcpy(r1, &hip_r1table[idx].buf.msg, len); - err = r1; - -out_err: - if (!err) { - free(r1); - } - - return err; + len = hip_get_msg_total_len(r1_matched); + if (len <= 0) { + HIP_ERROR("Invalid r1 entry\n"); + return NULL; + } + + if ((r1 = hip_msg_alloc()) == NULL) { + return NULL; + } + + memcpy(r1, r1_matched, len); + + return r1; } /** - * precreate an R1 packet + * HIPv1 & HIPv2: precreate R1 entries * - * @param r1table a pointer to R1 table structure + * @param id_entry a pointer to host ID entry * @param hit the local HIT * @param sign a signing callback function * @param privkey the private key to use for signing * @param pubkey the host id (public key) - * @param hip_version HIP message version * @return zero on success and non-zero on error */ -int hip_precreate_r1(struct hip_r1entry *const r1table, - const struct in6_addr *const hit, +int hip_precreate_r1(struct local_host_id *id_entry, + const hip_hit_t *const hit, int (*sign)(void *const key, struct hip_common *const m), void *const privkey, - const struct hip_host_id *const pubkey, - const uint8_t hip_version) + const struct hip_host_id *const pubkey) { - int i = 0; + const uint8_t cookie_k = get_cookie_difficulty(); + int i, j, group_id; + for (i = 0; i < HIP_R1TABLESIZE; i++) { - int cookie_k; - - cookie_k = get_cookie_difficulty(); - - hip_msg_init(&r1table[i].buf.msg); - - if (hip_create_r1(&r1table[i].buf.msg, hit, sign, privkey, pubkey, - cookie_k, hip_version)) { + hip_msg_init(&id_entry->r1[i].buf.msg); + + if (hip_create_r1(&id_entry->r1[i].buf.msg, hit, sign, privkey, + pubkey, cookie_k)) { HIP_ERROR("Unable to precreate R1s\n"); - return 0; - } - - HIP_DEBUG("Packet %d created\n", i); - } - - return 1; + return -1; + } + HIP_DEBUG("R1 Packet %d created\n", i); + } + + for (j = 0; j < HIP_DH_GROUP_LIST_SIZE; j++) { + group_id = HIP_DH_GROUP_LIST[j]; + for (i = 0; i < HIP_R1TABLESIZE; i++) { + hip_msg_init(&id_entry->r1_v2[group_id][i].buf.msg); + + if (hip_create_r1_v2(&id_entry->r1_v2[group_id][i].buf.msg, hit, sign, + privkey, pubkey, cookie_k, group_id)) { + HIP_ERROR("Unable to precreate R1_v2\n"); + return -1; + } + HIP_DEBUG("R1_v2 Packets %d created for group: %d\n", i, group_id); + } + } + + return 0; } /** @@ -266,13 +282,17 @@ * @param hdr a pointer to HIP packet common header * @param solution a pointer to a solution structure * @param hip_version HIP message version + * @param dh_group_id the Diffie-Hellman group ID of the R1 entry. This + * parameter is required for a HIPv2 cookie verification. + * For v1, this parameter will be ignored. * @return Zero if the cookie was verified successfully, negative * otherwise. */ int hip_verify_cookie(struct in6_addr *ip_i, struct in6_addr *ip_r, struct hip_common *hdr, const struct hip_solution *solution, - const uint8_t hip_version) + const uint8_t hip_version, + const int dh_group_id) { /* In a effort to conform the HIPL coding convention, the return value * of this function was inverted. I.e. This function now returns @@ -289,7 +309,12 @@ HIP_ANY_ALGO, -1)), -1, "Requested source HIT not (any more) available.\n"); - result = &hid->r1[hip_version][calc_cookie_idx(ip_i, ip_r)]; + + if (hip_version == HIP_V1) { + result = &hid->r1[calc_cookie_idx(ip_i, ip_r)]; + } else { + result = &hid->r1_v2[dh_group_id][calc_cookie_idx(ip_i, ip_r)]; + } puzzle = hip_get_param(&result->buf.msg, HIP_PARAM_PUZZLE); HIP_IFEL(!puzzle, -1, "Internal error: could not find the cookie\n"); @@ -344,7 +369,6 @@ static int recreate_r1s_for_entry_move(struct local_host_id *entry, UNUSED void *opaque) { - int i; int (*signature_func)(void *const key, struct hip_common *const m); switch (hip_get_host_id_algo(&entry->host_id)) { @@ -364,11 +388,10 @@ return -1; } - for (i = 1; i < HIP_MAX_VERSION; i++) { - if (!hip_precreate_r1(entry->r1[i], &entry->hit, signature_func, - entry->private_key, &entry->host_id, i)) { - return -1; - } + if (hip_precreate_r1(entry, &entry->hit, signature_func, + entry->private_key, &entry->host_id) < 0) { + HIP_ERROR("Precreate r1 failed\n"); + return -1; } return 0; === modified file 'libhipl/cookie.h' --- libhipl/cookie.h 2012-06-03 10:26:55 +0000 +++ libhipl/cookie.h 2012-11-01 15:06:33 +0000 @@ -30,32 +30,23 @@ #include <netinet/in.h> #include "libcore/protodefs.h" - -#define HIP_R1TABLESIZE 3 /* precreate only this many R1s */ - -struct hip_r1entry { - union hip_msg_bfr buf; - uint32_t generation; - uint8_t Ci[PUZZLE_LENGTH]; - uint8_t Ck; - uint8_t Copaque[HIP_PUZZLE_OPAQUE_LEN]; -}; +#include "libhipl/hidb.h" struct hip_common *hip_get_r1(struct in6_addr *ip_i, struct in6_addr *ip_r, - struct in6_addr *peer_hit, - uint8_t hip_version); + struct in6_addr *our_hit, + const int dh_group_id); int hip_recreate_all_precreated_r1_packets(void); -int hip_precreate_r1(struct hip_r1entry *const r1table, - const struct in6_addr *const hit, +int hip_precreate_r1(struct local_host_id *id_entry, + const hip_hit_t *const hit, int (*sign)(void *const key, struct hip_common *const m), void *const privkey, - const struct hip_host_id *const pubkey, - const uint8_t hip_version); + const struct hip_host_id *const pubkey); int hip_verify_cookie(struct in6_addr *ip_i, struct in6_addr *ip_r, struct hip_common *hdr, const struct hip_solution *cookie, - const uint8_t hip_version); + const uint8_t hip_version, + const int dh_group_id); int hip_inc_cookie_difficulty(void); int hip_dec_cookie_difficulty(void); int hip_get_puzzle_difficulty_msg(struct hip_common *msg); === modified file 'libhipl/dh.c' --- libhipl/dh.c 2012-05-12 10:21:32 +0000 +++ libhipl/dh.c 2012-10-31 17:00:21 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * Copyright (c) 2010, 2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -47,6 +47,8 @@ */ static DH *dh_table[HIP_MAX_DH_GROUP_ID] = { 0 }; +static EC_KEY *ecdh_table[HIP_MAX_DH_GROUP_ID] = { 0 }; + /** * insert the current DH-key into the buffer * @@ -95,66 +97,288 @@ } /** - * create a shared secret based on the public key of the peer + * Store the bytes of the current ECDH public key in the given buffer. + * + * A new ECDH key will be created if it doesn't exist, + * + * @param buffer buffer to store the public part of the ECDH key + * @param bufsize size of the @c buffer + * @param group_id group ID of the ECDH key + * @return the number of bytes written to the buffer, -1 on error. + */ +int hip_insert_ecdh(uint8_t *buffer, int bufsize, int group_id) +{ + EC_KEY *key; + int ret; + + if (!hip_is_ecdh_group(group_id)) { + HIP_ERROR("Invalid group id for ECDH: %d\n", group_id); + return -1; + } + + if (ecdh_table[group_id] == NULL) { + key = hip_generate_ecdh_key(group_id); + if (key == NULL) { + HIP_ERROR("Failed to generate an ECDH key for group: %d\n", + group_id); + return -1; + } + ecdh_table[group_id] = key; + } + + key = ecdh_table[group_id]; + if ((ret = hip_encode_ecdh_publickey(key, buffer, bufsize)) < 0) { + HIP_ERROR("Failed to encode the ECDH public key\n"); + return -1; + } + + return ret; +} + +/** + * HIPv2: Store the bytes of the current ECDH/DH public key to the given buffer. + * + * An ECDH/DH key will be created if it does not exist. + * + * @param buffer the buffer to store the ECDH/DH public key + * @param bufsize the size of the @c buffer + * @param group_id the group ID of the ECDH/DH key + * @return the number of bytes written to the buffer + */ +int hip_insert_dh_v2(uint8_t *buffer, int bufsize, int group_id) +{ + if (group_id <= 0 || group_id >= HIP_MAX_DH_GROUP_ID) { + HIP_ERROR("Invalid DH_GROUP_ID: %d\n", group_id); + return -1; + } + + if (hip_is_ecdh_group(group_id)) { + return hip_insert_ecdh(buffer, bufsize, group_id); + } else { + return hip_insert_dh(buffer, bufsize, group_id); + } +} + +/** + * Match the first identical DH group ID in local and peer's list. + * + * @param dh_group_list the DH_GROUP_LIST parameter sent from the peer + * @param our_dh_group the local DH list + * @param our_group_size the size of the @c our_dh_group + * @return ID of the matched group on success, -1 otherwise. + */ +int hip_match_dh_group_list(const struct hip_tlv_common *const dh_group_list, + const uint8_t *const our_dh_group, + const int our_group_size) +{ + int list_size = HIP_DH_GROUP_MAX_RECV_SIZE; + uint8_t list[list_size]; + int i, j; + + list_size = hip_get_list_from_param(dh_group_list, list, list_size, + sizeof(uint8_t)); + for (i = 0; i < list_size; i++) { + for (j = 0; j < our_group_size; j++) { + if (our_dh_group[j] == list[i]) { + return our_dh_group[j]; + } + } + } + + return -1; +} + +/** + * Calculate a Diffie-Hellman shared secret based on the public key of the peer * (passed as an argument) and own DH private key (created beforehand). * - * @param public_value Peer's Diffie-Hellman public key + * @param group_id the Diffie-Hellman group ID + * @param public_value the Diffie-Hellman public key of the peer + * @param len the length of the @c public_value + * @param buffer the buffer that holds enough space for the shared secret + * @param bufsize the size of the @c buffer + * + * @return the length of the shared secret in octets if successful, + * or -1 if an error occurred. + */ +static int hip_calculate_dh_shared_secret(const uint16_t group_id, + const uint8_t *const public_value, + const int len, + unsigned char *const buffer, + const int bufsize) +{ + DH *key; + int secret_len; + + if (group_id <= 0 || group_id >= HIP_MAX_DH_GROUP_ID) { + HIP_ERROR("Invalid Group ID: %d.\n", group_id); + return -1; + } + + if (dh_table[group_id] == NULL) { + if (NULL == (key = hip_generate_dh_key(group_id))) { + HIP_ERROR("Failed to generate a DH key for group: %d\n", group_id); + return -1; + } + dh_table[group_id] = key; + } + key = dh_table[group_id]; + + secret_len = hip_gen_dh_shared_key(dh_table[group_id], public_value, len, + buffer, bufsize); + if (secret_len < 0) { + HIP_ERROR("failed to create a DH shared secret\n"); + return -1; + } + + return secret_len; +} + +/** + * Calculate an Elliptic Curve Diffie-Hellman shared secret. + * + * The length of the public value should match the corresponding ECDH group; The + * buffer to hold the shared secret should be at least larger than the length of + * the public value divided by 2. + * + * @param group_id the ECDH group ID + * @param public_value Peer's ECDH public key + * @param pubkey_len the length of the @c public_value + * @param buffer Buffer that holds enough space for the shared secret + * @param bufsize size of the @c buffer + * + * @return the length of the shared secret in octets if successful, + * or -1 if an error occurred. + */ +static int hip_calculate_ecdh_shared_secret(const uint16_t group_id, + const uint8_t *const public_value, + const int pubkey_len, + unsigned char *const buffer, + const int bufsize) +{ + EC_KEY *key; + int key_len; + + if (ecdh_table[group_id] == NULL) { + if (NULL == (key = hip_generate_ecdh_key(group_id))) { + HIP_ERROR("Failed to generate an ECDH key for group: %d\n", + group_id); + return -1; + } + ecdh_table[group_id] = key; + } + key = ecdh_table[group_id]; + + key_len = hip_get_dh_size(group_id); + if (key_len != pubkey_len || key_len / 2 > bufsize) { + HIP_ERROR("Invalid public key length (%d) or buffer size (%d)\n", + pubkey_len, bufsize); + return -1; + } + int out = hip_gen_ecdh_shared_key(key, public_value, + public_value + key_len / 2, + key_len / 2, + buffer, + bufsize); + if (out <= 0) { + HIP_ERROR("Failed to generate a shared secret\n"); + return -1; + } + + return out; +} + +/** + * Calculate a shared secret for Diffie-Hellman exchange. + * + * This function supports both normal DH and ECDH groups. The DH private key + * is created beforehand. + * + * @param group_id the Diffie-Hellman group ID + * @param public_value the Diffie-Hellman public key of the peer + * @param len the length of the @c public_value + * @param buffer Buffer that holds the shared secret + * @param bufsize size of the @c buffer + * + * @return the length of the shared secret in octets if successful, + * or -1 if an error occurred. + */ +int hip_calculate_shared_secret(const uint16_t group_id, + const uint8_t *const public_value, + const int len, + unsigned char *const buffer, + const int bufsize) +{ + if (group_id <= 0 || group_id >= HIP_MAX_DH_GROUP_ID) { + HIP_ERROR("Invalid Diffie-Hellman group ID: %d\n", group_id); + return -1; + } + + if (hip_is_ecdh_group(group_id)) { + return hip_calculate_ecdh_shared_secret(group_id, public_value, len, + buffer, bufsize); + } else { + return hip_calculate_dh_shared_secret(group_id, public_value, len, + buffer, bufsize); + } +} + +/** + * Re-generate a DH key for a given group ID. + * * @param group_id the Diffie-Hellman group ID - * @param len the length of the public value - * @param buffer Buffer that holds enough space for the shared secret. - * @param bufsize size of the buffer - * - * @return the length of the shared secret in octets if successful, - * or -1 if an error occured. - */ -int hip_calculate_shared_secret(const uint8_t *public_value, - uint8_t group_id, - signed int len, - unsigned char *buffer, - int bufsize) -{ - int err = 0; - DH *tmp; - - if (group_id <= 0 || group_id >= HIP_MAX_DH_GROUP_ID) { - HIP_ERROR("The Group ID %d is invalid\n", group_id); - return -1; - } - - /* - * First check that we have the key available. - * Then encode it into the buffer - */ - - if (dh_table[group_id] == NULL) { - tmp = hip_generate_dh_key(group_id); - dh_table[group_id] = tmp; - - if (dh_table[group_id] == NULL) { - HIP_ERROR("Unsupported DH group: %d\n", group_id); - return -1; - } - } - - err = hip_gen_dh_shared_key(dh_table[group_id], public_value, - len, buffer, bufsize); - if (err < 0) { - HIP_ERROR("Could not create shared secret\n"); - return -1; - } - - return err; -} - -/** - * regenerate Diffie-Hellman keys for HIP - * @param bitmask Mask of groups to generate. - * - * @note Use only this function to generate DH keys. - */ -static void regen_dh_keys(uint32_t bitmask) + * @return 0 on success, -1 otherwise + */ +static int regen_dh_key(const int group_id) { DH *tmp, *okey; + + tmp = hip_generate_dh_key(group_id); + if (!tmp) { + HIP_INFO("Failed to generate a DH key for group: %d\n", group_id); + return -1; + } + + okey = dh_table[group_id]; + dh_table[group_id] = tmp; + + DH_free(okey); + return 0; +} + +#ifdef HAVE_EC_CRYPTO +/** + * Re-generate DH key for a given ECDH group ID. + * + * @param group_id the ECDH group ID + * @return 0 on success, -1 otherwise + */ +static int regen_ecdh_key(const int group_id) +{ + EC_KEY *tmp, *okey; + + tmp = hip_generate_ecdh_key(group_id); + if (!tmp) { + HIP_INFO("Failed to generate an ECDH key for group: %d\n", group_id); + return -1; + } + + okey = ecdh_table[group_id]; + ecdh_table[group_id] = tmp; + + EC_KEY_free(okey); + return 0; +} + +#endif /* HAVE_EC_CRYPTO */ + +/** + * HIPv2: regenerate Diffie-Hellman keys. + * + * @param bitmask the mask of groups to generate + */ +static void regen_dh_keys_v2(uint32_t bitmask) +{ int maxmask, i; int cnt = 0; @@ -164,19 +388,13 @@ for (i = 1; i < HIP_MAX_DH_GROUP_ID; i++) { if (bitmask & (1 << i)) { - tmp = hip_generate_dh_key(i); - if (!tmp) { - HIP_INFO("Error while generating group: %d\n", i); - continue; + if (hip_is_ecdh_group(i)) { + regen_ecdh_key(i); + } else { + regen_dh_key(i); } - okey = dh_table[i]; - dh_table[i] = tmp; - - DH_free(okey); - cnt++; - HIP_DEBUG("DH key for group %d generated\n", i); } } @@ -193,6 +411,14 @@ DH_free(dh_table[i]); dh_table[i] = NULL; } + +#ifdef HAVE_EC_CRYPTO + for (i = 1; i < HIP_MAX_DH_GROUP_ID; i++) { + EC_KEY_free(ecdh_table[i]); + ecdh_table[i] = NULL; + } +#endif /* HAVE_EC_CRYPTO */ + CRYPTO_cleanup_all_ex_data(); } @@ -203,12 +429,15 @@ { uint32_t supported_groups; - supported_groups = (1 << HIP_DH_OAKLEY_1 | - 1 << HIP_DH_OAKLEY_5 | - 1 << HIP_DH_384); + supported_groups = (1 << HIP_DH_OAKLEY_1 | + 1 << HIP_DH_OAKLEY_5 | + 1 << HIP_DH_384 | + 1 << HIP_DH_NIST_P_256 | + 1 << HIP_DH_NIST_P_384 | + 1 << HIP_DH_NIST_P_521); HIP_DEBUG("Generating DH keys\n"); - regen_dh_keys(supported_groups); + regen_dh_keys_v2(supported_groups); return 1; } === modified file 'libhipl/dh.h' --- libhipl/dh.h 2012-05-12 10:21:32 +0000 +++ libhipl/dh.h 2012-07-20 07:52:59 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * Copyright (c) 2010, 2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,13 +28,21 @@ #include <stdint.h> +#include "libcore/protodefs.h" + + int hip_insert_dh(uint8_t *buffer, int bufsize, int group_id); void hip_dh_uninit(void); -int hip_calculate_shared_secret(const uint8_t *public_value, - uint8_t group_id, - signed int len, - unsigned char *buffer, - int bufsize); +int hip_calculate_shared_secret(const uint16_t group_id, + const uint8_t *const pulic_value, + const int len, + unsigned char *const buffer, + const int bufsize); int hip_init_cipher(void); +int hip_insert_dh_v2(uint8_t *buffer, int bufsize, int group_id); +int hip_insert_ecdh(uint8_t *buffer, int bufsize, int group_id); +int hip_match_dh_group_list(const struct hip_tlv_common *const dh_group_list, + const uint8_t *our_dh_group, const int our_group_size); + #endif /* HIPL_LIBHIPL_DH_H */ === modified file 'libhipl/hidb.c' --- libhipl/hidb.c 2012-06-03 10:26:55 +0000 +++ libhipl/hidb.c 2012-07-20 07:52:59 +0000 @@ -429,7 +429,6 @@ hip_lsi_t *lsi, const struct hip_host_id_priv *host_id) { - int i; int err = 0; struct local_host_id *id_entry = NULL; struct local_host_id *old_entry; @@ -493,16 +492,12 @@ err = -1; goto out_err; } - for (i = 1; i < HIP_MAX_VERSION; i++) { - HIP_IFEL(!hip_precreate_r1(id_entry->r1[i], - &hit, - signature_func, - id_entry->private_key, - &id_entry->host_id, - i), - -ENOENT, - "Unable to precreate R1s.\n"); - } + + HIP_IFEL(hip_precreate_r1(id_entry, &hit, signature_func, + id_entry->private_key, + &id_entry->host_id) < 0, + -ENOENT, + "Unable to precreate R1s.\n"); out_err: if (err && id_entry) { === modified file 'libhipl/hidb.h' --- libhipl/hidb.h 2012-06-03 10:26:55 +0000 +++ libhipl/hidb.h 2012-07-28 21:18:08 +0000 @@ -35,9 +35,19 @@ #include <stdbool.h> #include <netinet/in.h> +#include "libcore/crypto.h" #include "libcore/protodefs.h" -#include "cookie.h" - + + +#define HIP_R1TABLESIZE 3 /* precreate only this many R1s */ + +struct hip_r1entry { + union hip_msg_bfr buf; + uint32_t generation; + uint8_t Ci[PUZZLE_LENGTH]; + uint8_t Ck; + uint8_t Copaque[HIP_PUZZLE_OPAQUE_LEN]; +}; struct local_host_id { hip_hit_t hit; @@ -45,7 +55,13 @@ hip_lsi_t lsi; struct hip_host_id host_id; void *private_key; /* RSA or DSA */ - struct hip_r1entry r1[HIP_MAX_VERSION][HIP_R1TABLESIZE]; /* precreated R1s */ + + /* precreated R1 entries. + * Due to the introduction of DH_GROUP_LIST in HIPv2, R1's DIFFIE_HELLMAN + * parameter must match one of the group ID of initiator's I1. Therefore we + * precreate R1 for all DH groups we support. */ + struct hip_r1entry r1[HIP_R1TABLESIZE]; + struct hip_r1entry r1_v2[HIP_MAX_DH_GROUP_ID][HIP_R1TABLESIZE]; }; struct local_host_id *hip_get_hostid_entry_by_lhi_and_algo(const struct in6_addr *const hit, === modified file 'libhipl/input.c' --- libhipl/input.c 2012-07-13 13:16:17 +0000 +++ libhipl/input.c 2012-10-31 17:00:21 +0000 @@ -110,6 +110,43 @@ } /** + * HIPv2: Check potential downgrade possibility for DH group selection by + * matching our DH list with the peer's DH list. If the peer doesn't select + * the strongest group, DH downgrade occurs. + * + * @param peer_group the DH group peer has selected + * @param peer_param the peer's DH group list parameter + * @return 0 if no downgrade detected, -1 otherwise. + */ +static int check_dh_downgrade_v2(const uint8_t peer_group, + const struct hip_tlv_common *const peer_param) +{ + int i, j; + int list_size = HIP_DH_GROUP_MAX_RECV_SIZE; + uint8_t list[list_size]; + + list_size = hip_get_list_from_param(peer_param, list, list_size, + sizeof(list[0])); + for (i = 0; i < HIP_DH_GROUP_LIST_SIZE; i++) { + for (j = 0; j < list_size; j++) { + if (HIP_DH_GROUP_LIST[i] == list[j]) { + if (list[j] == peer_group) { + return 0; + } else { + HIP_ERROR("Detect DH group downgrade. " + "Expect group: %d, Got: %d\n", + list[j], peer_group); + return -1; + } + } + } + } + + HIP_ERROR("No identical group in the DH group lists\n"); + return -1; +} + +/** * Verifies gerenal HMAC in HIP msg * * @param msg HIP packet @@ -325,11 +362,14 @@ HIP_DEBUG("Start PERF_DH_CREATE\n"); hip_perf_start_benchmark(perf_set, PERF_DH_CREATE); #endif - HIP_IFEL((dh_shared_len = hip_calculate_shared_secret(dhpv->public_value, dhpv->group_id, - ntohs(dhpv->pub_len), - (unsigned char *) dh_shared_key, - dh_shared_len)) < 0, - -EINVAL, "Calculation of shared secret failed.\n"); + + dh_shared_len = hip_calculate_shared_secret(dhpv->group_id, dhpv->public_value, + ntohs(dhpv->pub_len), + (unsigned char *) dh_shared_key, + dh_shared_len); + HIP_IFEL(dh_shared_len <= 0, -EINVAL, "Calculation of shared secret failed.\n"); + HIP_DEBUG("DH group %d, shared secret length is %d\n", + dhpv->group_id, dh_shared_len); hip_make_keymat(dh_shared_key, dh_shared_len, @@ -763,6 +803,24 @@ HIP_IFE(hip_init_peer(ctx->hadb_entry, &peer_host_id), -EINVAL); + /* HIPv2: check possible DH group downgrade */ + if (ctx->hadb_entry->hip_version == HIP_V2) { + const struct hip_tlv_common *dh_list_param; + const struct hip_diffie_hellman *dh_param; + + dh_list_param = hip_get_param(ctx->input_msg, HIP_PARAM_DH_GROUP_LIST); + HIP_IFEL(dh_list_param == NULL, -ENOENT, + "No DH_GROUP_LIST parameter found in R1\n"); + + dh_param = hip_get_param(ctx->input_msg, HIP_PARAM_DIFFIE_HELLMAN); + HIP_IFEL(dh_param == NULL, -ENOENT, + "No DIFFIE_HELLMAN parameter found in R1\n"); + + HIP_IFEL(check_dh_downgrade_v2(dh_param->pub_val.group_id, + dh_list_param) < 0, + -EINVAL, "DH group downgrade check failed.\n"); + } + #ifdef CONFIG_HIP_PERFORMANCE HIP_DEBUG("Start PERF_VERIFY\n"); hip_perf_start_benchmark(perf_set, PERF_VERIFY); @@ -945,7 +1003,8 @@ HIP_ERROR("Failed to allocate memory for public value\n"); return -ENOMEM; } - if ((pub_len = hip_insert_dh(public_value, pub_len, dhpv->group_id)) < 0) { + + if ((pub_len = hip_insert_dh_v2(public_value, pub_len, dhpv->group_id)) < 0) { HIP_ERROR("Could not extract the DH public key\n"); return -1; } @@ -1363,19 +1422,22 @@ UNUSED const enum hip_state ha_state, struct hip_packet_context *ctx) { - int hip_version = 0; - int err = 0, is_loopback = 0; - bool skip_key_creation = false; - uint16_t mask = HIP_PACKET_CTRL_ANON; - uint16_t crypto_len = 0; - char *tmp_enc = NULL; - const char *enc = NULL; - unsigned char *iv = NULL; - const struct hip_solution *solution = NULL; - const struct hip_r1_counter *r1cntr = NULL; - const struct hip_hip_transform *hip_transform = NULL; - struct hip_host_id *host_id_in_enc = NULL; - struct hip_host_id host_id; + int hip_version = 0; + int err = 0, is_loopback = 0; + bool skip_key_creation = false; + uint16_t mask = HIP_PACKET_CTRL_ANON; + uint16_t crypto_len = 0; + char *tmp_enc = NULL; + const char *enc = NULL; + unsigned char *iv = NULL; + const struct hip_solution *solution = NULL; + const struct hip_diffie_hellman *dh_param = NULL; + const struct hip_r1_counter *r1cntr = NULL; + const struct hip_hip_transform *hip_transform = NULL; + struct hip_host_id *host_id_in_enc = NULL; + struct hip_host_id host_id; + int dh_group_id, i; + #ifdef CONFIG_HIP_PERFORMANCE HIP_DEBUG("Start PERF_I2\n"); hip_perf_start_benchmark(perf_set, PERF_I2); @@ -1464,11 +1526,40 @@ -ENODATA, "SOLUTION parameter missing from I2 packet. Dropping\n"); - HIP_IFEL(hip_verify_cookie(&ctx->src_addr, - &ctx->dst_addr, - ctx->input_msg, - solution, - ctx->hadb_entry->hip_version), + /* Validate DIFFIE_HELLMAN parameter */ + if (NULL == (dh_param = hip_get_param(ctx->input_msg, + HIP_PARAM_DIFFIE_HELLMAN))) { + HIP_ERROR("DIFFIE_HELLMAN parameter missing from I2 packet\n"); + ctx->error = -ENODATA; + return ctx->error; + } + dh_group_id = dh_param->pub_val.group_id; + for (i = 0; i < HIP_DH_GROUP_LIST_SIZE; i++) { + if (dh_group_id == HIP_DH_GROUP_LIST[i]) { + break; + } + } + if (i == HIP_DH_GROUP_LIST_SIZE) { + HIP_ERROR("Invalid group %d for DIFFIE_HELLMAN parameter.\n", + dh_group_id); + ctx->error = -EINVAL; + return ctx->error; + } + if (hip_get_dh_size(dh_group_id) != ntohs(dh_param->pub_val.pub_len)) { + HIP_ERROR("Invalid public key length for DIFFIE_HELLMAN parameter. " + "Expect: %d, Got: %d\n", hip_get_dh_size(dh_group_id), + ntohs(dh_param->pub_val.pub_len)); + ctx->error = -EINVAL; + return ctx->error; + } + + /* Verify cookie */ + if (hip_version == HIP_V1) { + dh_group_id = 0; + } + HIP_IFEL(hip_verify_cookie(&ctx->src_addr, &ctx->dst_addr, + ctx->input_msg, solution, + hip_version, dh_group_id), -EPROTO, "Cookie solution rejected. Dropping the I2 packet.\n"); === modified file 'libhipl/netdev.c' --- libhipl/netdev.c 2012-06-03 10:26:55 +0000 +++ libhipl/netdev.c 2012-07-28 21:18:08 +0000 @@ -987,8 +987,25 @@ HIP_DEBUG("Using ifindex %d\n", if_index); - HIP_IFEL(hip_send_i1(&entry->hit_our, &entry->hit_peer, entry), -1, - "Sending of I1 failed\n"); + switch (entry->hip_version) { + case HIP_V1: + if (hip_send_i1(&entry->hit_our, &entry->hit_peer, entry) < 0) { + HIP_ERROR("Failed to send the I1(v1) message.\n"); + return -1; + } + break; + + case HIP_V2: + if (hip_send_i1_v2(&entry->hit_our, &entry->hit_peer, entry) < 0) { + HIP_ERROR("Failed to send the I1(v2) message.\n"); + return -1; + } + break; + + default: + HIP_ERROR("Unknown HIP version: %d\n", entry->hip_version); + return -1; + } out_err: === modified file 'libhipl/output.c' --- libhipl/output.c 2013-08-19 18:30:29 +0000 +++ libhipl/output.c 2013-10-22 12:34:28 +0000 @@ -161,7 +161,111 @@ } /** - * Send an I1 packet to the Responder + * Send an I1 packet to the responder. + * + * This method checks the shotgun status and decides whether to use shotgun + * mode or not for I1 sending. + * + * @param i1 the I1 packet to be sent + * @param entry a pointer to the current host association database state. + * @return zero on success, or negative error value on error. + */ +static int send_i1_internal(struct hip_common *const i1, + struct hip_hadb_state *const entry) +{ + struct in6_addr *local_addr = NULL; + struct in6_addr peer_addr; + LHASH_NODE *item = NULL, *tmp = NULL; + struct hip_peer_addr_list_item *addr; + int err = 0, i = 0; + + HIP_DEBUG("Sending I1 to the following addresses:\n"); + print_peer_addresses_to_be_added(entry); + + if (hip_shotgun_status == HIP_MSG_SHOTGUN_OFF || + (entry->peer_addr_list_to_be_added == NULL)) { + if (hip_hadb_get_peer_addr(entry, &peer_addr)) { + HIP_ERROR("No preferred IP address for the peer.\n"); + return -1; + } + + local_addr = &entry->our_addr; + return send_i1_pkt(i1, local_addr, &peer_addr, entry->local_udp_port, + entry->peer_udp_port, entry); + } else { + HIP_DEBUG("Number of items in the peer addr list: %d ", + ((struct lhash_st *) entry->peer_addr_list_to_be_added)->num_items); + list_for_each_safe(item, tmp, entry->peer_addr_list_to_be_added, i) + { + addr = list_entry(item); + ipv6_addr_copy(&peer_addr, &addr->address); + + err = send_i1_pkt(i1, NULL, &peer_addr, entry->local_udp_port, + entry->peer_udp_port, entry); + } + return err; + } +} + +/** + * HIPv2: send an I1 packet to the responder. + * + * @param src_hit a pointer to the source host identity tag. + * @param dst_hit a pointer to the destination host identity tag. + * @param entry a pointer to the host association database state reserved for + * the peer. + * @return zero on success, or negative value on error. + */ +int hip_send_i1_v2(hip_hit_t *const src_hit, const hip_hit_t *const dst_hit, + struct hip_hadb_state *const entry) +{ + struct hip_common *i1 = NULL; + int err = 0; + + int group_size = HIP_DH_GROUP_LIST_SIZE; + uint8_t dh_group[group_size]; + + memcpy(dh_group, HIP_DH_GROUP_LIST, group_size); + + if (entry->state == HIP_STATE_ESTABLISHED) { + HIP_DEBUG("HIP association established, not triggering bex\n"); + return 0; + } + + /* Assign a local private key, public key and HIT to HA */ + HIP_DEBUG_HIT("src_hit", src_hit); + HIP_DEBUG_HIT("entry->src_hit", &entry->hit_our); + HIP_IFEL(hip_init_us(entry, src_hit), -EINVAL, + "Could not assign a local host id\n"); + HIP_DEBUG_HIT("entry->src_hit", &entry->hit_our); + + if ((i1 = hip_msg_alloc()) == NULL) { + return -1; + } + + hip_build_network_hdr(i1, HIP_I1, 0, &entry->hit_our, + dst_hit, entry->hip_version); + + /* Calculate the HIP header length */ + hip_calc_hdr_len(i1); + + /* Build DH_GROUP_LIST */ + HIP_IFEL(hip_build_param_list(i1, HIP_PARAM_DH_GROUP_LIST, dh_group, + group_size, sizeof(uint8_t)), + -1, "Failed to build param: DH_GROUP_LIST\n"); + + HIP_DEBUG_HIT("HIT source", &i1->hit_sender); + HIP_DEBUG_HIT("HIT dest", &i1->hit_receiver); + + HIP_IFEL(send_i1_internal(i1, entry), -1, "send_i1_internal() failed\n"); + +out_err: + free(i1); + return err; +} + +/** + * HIPv1: send an I1 packet to the responder. * * @param src_hit a pointer to source host identity tag. * @param dst_hit a pointer to destination host identity tag. @@ -172,13 +276,8 @@ int hip_send_i1(hip_hit_t *src_hit, const hip_hit_t *dst_hit, struct hip_hadb_state *entry) { - struct hip_common *i1 = 0; - uint16_t mask = 0; - int err = 0, i = 0; - LHASH_NODE *item = NULL, *tmp = NULL; - struct hip_peer_addr_list_item *addr; - struct in6_addr *local_addr = NULL; - struct in6_addr peer_addr; + struct hip_common *i1 = 0; + int err = 0; #ifdef CONFIG_HIP_PERFORMANCE HIP_DEBUG("Start PERF_I1_SEND, PERF_BASE\n"); @@ -209,7 +308,7 @@ i1 = hip_msg_alloc(); - hip_build_network_hdr(i1, HIP_I1, mask, &entry->hit_our, + hip_build_network_hdr(i1, HIP_I1, 0, &entry->hit_our, dst_hit, entry->hip_version); /* Calculate the HIP header length */ @@ -218,30 +317,8 @@ HIP_DEBUG_HIT("HIT source", &i1->hit_sender); HIP_DEBUG_HIT("HIT dest", &i1->hit_receiver); - HIP_DEBUG("Sending I1 to the following addresses:\n"); - print_peer_addresses_to_be_added(entry); - - if (hip_shotgun_status == HIP_MSG_SHOTGUN_OFF || - (entry->peer_addr_list_to_be_added == NULL)) { - HIP_IFEL(hip_hadb_get_peer_addr(entry, &peer_addr), -1, - "No preferred IP address for the peer.\n"); - - local_addr = &entry->our_addr; - err = send_i1_pkt(i1, local_addr, &peer_addr, - entry->local_udp_port, - entry->peer_udp_port, entry); - } else { - HIP_DEBUG("Number of items in the peer addr list: %d ", - ((struct lhash_st *) entry->peer_addr_list_to_be_added)->num_items); - list_for_each_safe(item, tmp, entry->peer_addr_list_to_be_added, i) - { - addr = list_entry(item); - ipv6_addr_copy(&peer_addr, &addr->address); - - err = send_i1_pkt(i1, NULL, &peer_addr, entry->local_udp_port, - entry->peer_udp_port, entry); - } - } + HIP_IFEL(send_i1_internal(i1, entry), -1, "send_i1_internal() failed\n"); + #ifdef CONFIG_HIP_PERFORMANCE HIP_DEBUG("Stop and write PERF_I1_SEND\n"); hip_perf_stop_benchmark(perf_set, PERF_I1_SEND); @@ -263,8 +340,8 @@ */ static int add_echo_response(struct hip_packet_context *ctx, int sign) { - int param_type = sign ? HIP_PARAM_ECHO_REQUEST_SIGN - : HIP_PARAM_ECHO_REQUEST; + int param_type = + sign ? HIP_PARAM_ECHO_REQUEST_SIGN : HIP_PARAM_ECHO_REQUEST; const struct hip_echo_msg *ping = hip_get_param(ctx->input_msg, param_type); @@ -595,38 +672,25 @@ } /** - * Construct a new R1 packet payload + * Continue to build R1 packet after the Diffie-Hellman parameter. * - * @param msg points to a message object backed by HIP_MAX_PACKET bytes - * of memory to which the R1 message is written. - * @param src_hit a pointer to the source host identity tag used in the - * packet. + * @param msg pointer to a message backed by HIP_MAX_PACKET bytes of + * memory to which the R1 message is written. * @param sign a funtion pointer to a signature funtion. * @param private_key a pointer to the local host private key * @param host_id_pub a pointer to the public host id of the local host - * @param cookie_k the difficulty value for the puzzle - * @param hip_version HIP message version * @return 0 on success, a non-zero value on error. */ -int hip_create_r1(struct hip_common *const msg, - const struct in6_addr *const src_hit, - int (*sign)(void *const key, struct hip_common *const m), - void *const private_key, - const struct hip_host_id *const host_id_pub, - const int cookie_k, - const uint8_t hip_version) +static int build_r1_after_dh_param(struct hip_common *const msg, + int (*sign)(void *const key, struct hip_common *const m), + void *const private_key, + const struct hip_host_id *const host_id_pub) { - int err = 0; struct hip_srv service_list[HIP_TOTAL_EXISTING_SERVICES]; - uint8_t *dh_data1 = NULL, *dh_data2 = NULL; + unsigned int service_count = 0; + int err = 0; char order[] = "000"; - int dh_size1 = 0, dh_size2 = 0; - int mask = 0, i = 0, written1 = 0, written2 = 0; - unsigned int service_count = 0; - - enum number_dh_keys_t { ONE, TWO }; - enum number_dh_keys_t number_dh_keys = TWO; - + int i = 0; /* Supported HIP and ESP transforms. */ hip_transform_suite transform_hip_suite[] = { @@ -662,6 +726,102 @@ } } + /* Parameter HIP transform. */ + err = hip_build_param_hip_transform(msg, + transform_hip_suite, + sizeof(transform_hip_suite) / + sizeof(hip_transform_suite)); + if (err) { + HIP_ERROR("Building of HIP transform failed\n"); + return err; + } + + /* Parameter HOST_ID */ + if ((err = hip_build_param_host_id(msg, host_id_pub))) { + HIP_ERROR("Building of host id failed\n"); + return err; + } + + /* Parameter REG_INFO */ + hip_get_active_services(service_list, &service_count); + HIP_DEBUG("Found %d active service(s) \n", service_count); + hip_build_param_reg_info(msg, service_list, service_count); + + /* Parameter ESP-ENC transform. */ + err = hip_build_param_esp_transform(msg, transform_esp_suite, + sizeof(transform_esp_suite) / + sizeof(hip_transform_suite)); + if (err) { + HIP_ERROR("Building of ESP transform failed\n"); + return err; + } + + /********** ESP-PROT transform (OPTIONAL) **********/ + + if ((err = esp_prot_r1_add_transforms(msg))) { + HIP_ERROR("failed to add optional esp transform parameter\n"); + return err; + } + + /********** ECHO_REQUEST_SIGN (OPTIONAL) *********/ + + //HIP_HEXDUMP("Pubkey:", host_id_pub, hip_get_param_total_len(host_id_pub)); + + /* Parameter Signature 2 */ + + if ((err = sign(private_key, msg))) { + HIP_ERROR("Signing of R1 failed.\n"); + return err; + } + + /* Parameter ECHO_REQUEST (OPTIONAL) */ + + /* Fill puzzle parameters */ + { + struct hip_puzzle *pz; + + if (!(pz = hip_get_param_readwrite(msg, HIP_PARAM_PUZZLE))) { + HIP_ERROR("Internal error\n"); + return -1; + } + + /* hardcode kludge */ + pz->opaque[0] = 'H'; + pz->opaque[1] = 'I'; + get_random_bytes(pz->I, PUZZLE_LENGTH); + } + + return 0; +} + +/** + * Construct a new R1 packet payload + * + * @param msg pointer to a message backed by HIP_MAX_PACKET bytes of + * memory to which the R1 message is written. + * @param src_hit a pointer to the source host identity tag used in the + * packet. + * @param sign a funtion pointer to a signature funtion. + * @param private_key a pointer to the local host private key + * @param host_id_pub a pointer to the public host id of the local host + * @param cookie_k the difficulty value for the puzzle + * @return 0 on success, a non-zero value on error. + */ +int hip_create_r1(struct hip_common *const msg, + const struct in6_addr *const src_hit, + int (*sign)(void *const key, struct hip_common *const m), + void *const private_key, + const struct hip_host_id *const host_id_pub, + const int cookie_k) +{ + int err = 0; + uint8_t *dh_data1 = NULL, *dh_data2 = NULL; + int dh_size1 = 0, dh_size2 = 0; + int mask = 0, written1 = 0, written2 = 0; + + enum number_dh_keys_t { ONE, TWO }; + enum number_dh_keys_t number_dh_keys = TWO; + /* Initialize the message buffer as the message builder depends on it. */ hip_msg_init(msg); @@ -682,7 +842,7 @@ /** @todo TH: hip_build_network_hdr has to be replaced with an * appropriate function pointer */ HIP_DEBUG_HIT("src_hit used to build r1 network header", src_hit); - hip_build_network_hdr(msg, HIP_R1, mask, src_hit, NULL, hip_version); + hip_build_network_hdr(msg, HIP_R1, mask, src_hit, NULL, HIP_V1); /********** R1_COUNTER (OPTIONAL) *********/ @@ -714,58 +874,9 @@ err, "Building of DH failed.\n"); } - /* Parameter HIP transform. */ - HIP_IFEL((err = hip_build_param_hip_transform(msg, - transform_hip_suite, - sizeof(transform_hip_suite) / - sizeof(hip_transform_suite))), - err, "Building of HIP transform failed\n"); - - /* Parameter HOST_ID */ - HIP_IFEL((err = hip_build_param_host_id(msg, host_id_pub)), - err, "Building of host id failed\n"); - - /* Parameter REG_INFO */ - hip_get_active_services(service_list, &service_count); - HIP_DEBUG("Found %d active service(s) \n", service_count); - hip_build_param_reg_info(msg, service_list, service_count); - - /* Parameter ESP-ENC transform. */ - HIP_IFEL((err = hip_build_param_esp_transform(msg, - transform_esp_suite, - sizeof(transform_esp_suite) / - sizeof(hip_transform_suite))), - err, "Building of ESP transform failed\n"); - - /********** ESP-PROT transform (OPTIONAL) **********/ - - HIP_IFEL((err = esp_prot_r1_add_transforms(msg)), err, - "failed to add optional esp transform parameter\n"); - - /********** ECHO_REQUEST_SIGN (OPTIONAL) *********/ - - //HIP_HEXDUMP("Pubkey:", host_id_pub, hip_get_param_total_len(host_id_pub)); - - /* Parameter Signature 2 */ - - HIP_IFEL((err = sign(private_key, msg)), err, "Signing of R1 failed.\n"); - - /* Parameter ECHO_REQUEST (OPTIONAL) */ - - /* Fill puzzle parameters */ - { - struct hip_puzzle *pz; - - HIP_IFEL(!(pz = hip_get_param_readwrite(msg, HIP_PARAM_PUZZLE)), -1, - "Internal error\n"); - - /* hardcode kludge */ - pz->opaque[0] = 'H'; - pz->opaque[1] = 'I'; - //pz->opaque[2] = 'P'; - get_random_bytes(pz->I, PUZZLE_LENGTH); + if ((err = build_r1_after_dh_param(msg, sign, private_key, host_id_pub)) != 0) { + HIP_ERROR("Failed to build R1 after the DH parameter."); } - /* Packet ready */ out_err: @@ -776,6 +887,86 @@ } /** + * Construct a new R1 packet payload. + * + * @param msg pointer to a message backed by HIP_MAX_PACKET bytes + * of memory to which the R1 message is written + * @param src_hit a pointer to the source HIT used in the packet + * @param sign a function pointer to a signature function + * @param private_key a pointer to the local host private key + * @param host_id_pub a pointer to the public host id of the local host + * @param cookie_k the difficulty value for the puzzle + * @param dh_group_id the Diffie-Hellman group ID which will be used by this + * new R1 entry + * @return 0 on success, a non-zero value on error + */ +int hip_create_r1_v2(struct hip_common *const msg, + const struct in6_addr *const src_hit, + int (*sign)(void *const key, struct hip_common *const m), + void *const private_key, + const struct hip_host_id *const host_id_pub, + const int cookie_k, + const int dh_group_id) +{ + int err = 0; + uint8_t *dh_data = NULL; + int dh_size = 0; + int mask = 0, written = 0; + + /* Initialize the message buffer as the message builder depends on it. */ + hip_msg_init(msg); + + /* Allocate memory for writing the Diffie-Hellman public value */ + if ((dh_size = hip_get_dh_size(dh_group_id)) == 0) { + HIP_ERROR("Could not get dh_size\n"); + return -1; + } + if ((dh_data = calloc(1, dh_size)) == NULL) { + HIP_ERROR("Failed to allocate memory for dh_data\n"); + return -1; + } + + /* Ready to begin building of the R1 packet */ + HIP_DEBUG_HIT("src_hit used to build r1 network header", src_hit); + hip_build_network_hdr(msg, HIP_R1, mask, src_hit, NULL, HIP_V2); + + /********** R1_COUNTER (OPTIONAL) *********/ + + /********** Parameter PUZZLE ************/ + const uint8_t zero_i[PUZZLE_LENGTH] = { 0 }; + //2^(42-32) sec lifetime + err = hip_build_param_puzzle(msg, cookie_k, 42, 0, zero_i); + HIP_IFEL(err, err, "Cookies were burned. Bummer!\n"); + + /* Parameter DH_GROUP_LIST */ + uint8_t dh_group[HIP_DH_GROUP_LIST_SIZE]; + memcpy(dh_group, HIP_DH_GROUP_LIST, HIP_DH_GROUP_LIST_SIZE); + HIP_IFEL(hip_build_param_list(msg, HIP_PARAM_DH_GROUP_LIST, dh_group, + HIP_DH_GROUP_LIST_SIZE, sizeof(uint8_t)), + -1, "Failed to build param DH_GROUP_LIST.\n"); + + /******** Parameter DIFFIE_HELLMAN ********/ + HIP_IFEL((written = hip_insert_dh_v2(dh_data, dh_size, dh_group_id)) < 0, + written, "Could not extract DH public key\n"); + + HIP_IFEL((err = hip_build_param_diffie_hellman_contents(msg, dh_group_id, + dh_data, written, + HIP_MAX_DH_GROUP_ID, + NULL, 0)), + err, "Building of DH failed.\n"); + + if ((err = build_r1_after_dh_param(msg, sign, private_key, host_id_pub)) != 0) { + HIP_ERROR("Failed to build R1 after the DH parameter."); + } + /* Packet ready */ + +out_err: + free(dh_data); + + return err; +} + +/** * Transmit an R1 packet to the network. * * Send an R1 packet to the peer and store the cookie information that was @@ -866,10 +1057,29 @@ } } - HIP_IFEL(!(r1pkt = hip_get_r1(r1_dst_addr, &ctx->dst_addr, - &ctx->input_msg->hit_receiver, - hip_get_msg_version(ctx->input_msg))), - -ENOENT, "No precreated R1\n"); + if (hip_get_msg_version(ctx->input_msg) == HIP_V1) { + HIP_IFEL(!(r1pkt = hip_get_r1(r1_dst_addr, &ctx->dst_addr, + &ctx->input_msg->hit_receiver, -1)), + -ENOENT, "No precreated R1\n"); + } else { + const struct hip_tlv_common *dh_group_list; + int selected_group; + + dh_group_list = hip_get_param(ctx->input_msg, HIP_PARAM_DH_GROUP_LIST); + HIP_IFEL(dh_group_list == NULL, -1, "No DH_GROUP_LIST parameter in r1\n"); + + selected_group = hip_match_dh_group_list(dh_group_list, + HIP_DH_GROUP_LIST, + HIP_DH_GROUP_LIST_SIZE); + HIP_IFEL(selected_group == -1, -1, + "Cannot find a matchness for DH_GROUP_LIST\n"); + HIP_DEBUG("Selected DH group: %d\n", selected_group); + + r1pkt = hip_get_r1(r1_dst_addr, &ctx->dst_addr, + &ctx->input_msg->hit_receiver, selected_group); + HIP_IFEL(r1pkt == NULL, -ENOENT, "No precreated R1_v2 for group: %d\n", + selected_group); + } if (&ctx->input_msg->hit_sender) { ipv6_addr_copy(&r1pkt->hit_receiver, &ctx->input_msg->hit_sender); === modified file 'libhipl/output.h' --- libhipl/output.h 2012-06-03 10:26:55 +0000 +++ libhipl/output.h 2012-07-20 07:52:59 +0000 @@ -52,8 +52,15 @@ int (*sign)(void *const key, struct hip_common *const m), void *const private_key, const struct hip_host_id *const host_id_pub, - const int cookie_k, - const uint8_t hip_version); + const int cookie_k); + +int hip_create_r1_v2(struct hip_common *const msg, + const struct in6_addr *const src_hit, + int (*sign)(void *const key, struct hip_common *const m), + void *const private_key, + const struct hip_host_id *const host_id_pub, + const int cookie_k, + const int dh_group_id); int hip_send_r1(const uint8_t packet_type, const enum hip_state ha_state, @@ -81,6 +88,9 @@ int hip_send_i1(hip_hit_t *, const hip_hit_t *, struct hip_hadb_state *); +int hip_send_i1_v2(hip_hit_t *const src_hit, const hip_hit_t *const dst_hit, + struct hip_hadb_state *const entry); + int hip_add_signed_echo_response(const uint8_t packet_type, const enum hip_state ha_state, struct hip_packet_context *ctx); === modified file 'test/libcore/crypto.c' --- test/libcore/crypto.c 2012-05-12 06:54:33 +0000 +++ test/libcore/crypto.c 2012-10-31 17:00:21 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Aalto University and RWTH Aachen University. + * Copyright (c) 2011-2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,12 +28,225 @@ #include <string.h> #include <stdio.h> #include <unistd.h> +#ifdef HAVE_EC_CRYPTO +#include <openssl/ecdh.h> +#endif #include "libcore/crypto.h" #include "config.h" #include "test_suites.h" #ifdef HAVE_EC_CRYPTO + +static const int TEST_ECDH_PRIV_A = 0; +static const int TEST_ECDH_PUBX_A = 1; +static const int TEST_ECDH_PUBY_A = 2; +static const int TEST_ECDH_PRIV_B = 3; +static const int TEST_ECDH_PUBX_B = 4; +static const int TEST_ECDH_PUBY_B = 5; +static const int TEST_ECDH_SHARED = 6; + +/* An example for testing from RFC5903, section 8.1 */ +static unsigned char TEST_ECDH_NIST_P_256[] = { + 0xC8, 0x8F, 0x01, 0xF5, 0x10, 0xD9, 0xAC, 0x3F, 0x70, 0xA2, + 0x92, 0xDA, 0xA2, 0x31, 0x6D, 0xE5, 0x44, 0xE9, 0xAA, 0xB8, + 0xAF, 0xE8, 0x40, 0x49, 0xC6, 0x2A, 0x9C, 0x57, 0x86, 0x2D, + 0x14, 0x33, + + 0xDA, 0xD0, 0xB6, 0x53, 0x94, 0x22, 0x1C, 0xF9, 0xB0, 0x51, + 0xE1, 0xFE, 0xCA, 0x57, 0x87, 0xD0, 0x98, 0xDF, 0xE6, 0x37, + 0xFC, 0x90, 0xB9, 0xEF, 0x94, 0x5D, 0x0C, 0x37, 0x72, 0x58, + 0x11, 0x80, + + 0x52, 0x71, 0xA0, 0x46, 0x1C, 0xDB, 0x82, 0x52, 0xD6, 0x1F, + 0x1C, 0x45, 0x6F, 0xA3, 0xE5, 0x9A, 0xB1, 0xF4, 0x5B, 0x33, + 0xAC, 0xCF, 0x5F, 0x58, 0x38, 0x9E, 0x05, 0x77, 0xB8, 0x99, + 0x0B, 0xB3, + + 0xC6, 0xEF, 0x9C, 0x5D, 0x78, 0xAE, 0x01, 0x2A, 0x01, 0x11, + 0x64, 0xAC, 0xB3, 0x97, 0xCE, 0x20, 0x88, 0x68, 0x5D, 0x8F, + 0x06, 0xBF, 0x9B, 0xE0, 0xB2, 0x83, 0xAB, 0x46, 0x47, 0x6B, + 0xEE, 0x53, + + 0xD1, 0x2D, 0xFB, 0x52, 0x89, 0xC8, 0xD4, 0xF8, 0x12, 0x08, + 0xB7, 0x02, 0x70, 0x39, 0x8C, 0x34, 0x22, 0x96, 0x97, 0x0A, + 0x0B, 0xCC, 0xB7, 0x4C, 0x73, 0x6F, 0xC7, 0x55, 0x44, 0x94, + 0xBF, 0x63, + + 0x56, 0xFB, 0xF3, 0xCA, 0x36, 0x6C, 0xC2, 0x3E, 0x81, 0x57, + 0x85, 0x4C, 0x13, 0xC5, 0x8D, 0x6A, 0xAC, 0x23, 0xF0, 0x46, + 0xAD, 0xA3, 0x0F, 0x83, 0x53, 0xE7, 0x4F, 0x33, 0x03, 0x98, + 0x72, 0xAB, + + 0xD6, 0x84, 0x0F, 0x6B, 0x42, 0xF6, 0xED, 0xAF, 0xD1, 0x31, + 0x16, 0xE0, 0xE1, 0x25, 0x65, 0x20, 0x2F, 0xEF, 0x8E, 0x9E, + 0xCE, 0x7D, 0xCE, 0x03, 0x81, 0x24, 0x64, 0xD0, 0x4B, 0x94, + 0x42, 0xDE, +}; + +/* An example for testing from RFC5903, section 8.2 */ +static unsigned char TEST_ECDH_NIST_P_384[] = { + 0x09, 0x9F, 0x3C, 0x70, 0x34, 0xD4, 0xA2, 0xC6, 0x99, 0x88, + 0x4D, 0x73, 0xA3, 0x75, 0xA6, 0x7F, 0x76, 0x24, 0xEF, 0x7C, + 0x6B, 0x3C, 0x0F, 0x16, 0x06, 0x47, 0xB6, 0x74, 0x14, 0xDC, + 0xE6, 0x55, 0xE3, 0x5B, 0x53, 0x80, 0x41, 0xE6, 0x49, 0xEE, + 0x3F, 0xAE, 0xF8, 0x96, 0x78, 0x3A, 0xB1, 0x94, + + 0x66, 0x78, 0x42, 0xD7, 0xD1, 0x80, 0xAC, 0x2C, 0xDE, 0x6F, + 0x74, 0xF3, 0x75, 0x51, 0xF5, 0x57, 0x55, 0xC7, 0x64, 0x5C, + 0x20, 0xEF, 0x73, 0xE3, 0x16, 0x34, 0xFE, 0x72, 0xB4, 0xC5, + 0x5E, 0xE6, 0xDE, 0x3A, 0xC8, 0x08, 0xAC, 0xB4, 0xBD, 0xB4, + 0xC8, 0x87, 0x32, 0xAE, 0xE9, 0x5F, 0x41, 0xAA, + + 0x94, 0x82, 0xED, 0x1F, 0xC0, 0xEE, 0xB9, 0xCA, 0xFC, 0x49, + 0x84, 0x62, 0x5C, 0xCF, 0xC2, 0x3F, 0x65, 0x03, 0x21, 0x49, + 0xE0, 0xE1, 0x44, 0xAD, 0xA0, 0x24, 0x18, 0x15, 0x35, 0xA0, + 0xF3, 0x8E, 0xEB, 0x9F, 0xCF, 0xF3, 0xC2, 0xC9, 0x47, 0xDA, + 0xE6, 0x9B, 0x4C, 0x63, 0x45, 0x73, 0xA8, 0x1C, + + 0x41, 0xCB, 0x07, 0x79, 0xB4, 0xBD, 0xB8, 0x5D, 0x47, 0x84, + 0x67, 0x25, 0xFB, 0xEC, 0x3C, 0x94, 0x30, 0xFA, 0xB4, 0x6C, + 0xC8, 0xDC, 0x50, 0x60, 0x85, 0x5C, 0xC9, 0xBD, 0xA0, 0xAA, + 0x29, 0x42, 0xE0, 0x30, 0x83, 0x12, 0x91, 0x6B, 0x8E, 0xD2, + 0x96, 0x0E, 0x4B, 0xD5, 0x5A, 0x74, 0x48, 0xFC, + + 0xE5, 0x58, 0xDB, 0xEF, 0x53, 0xEE, 0xCD, 0xE3, 0xD3, 0xFC, + 0xCF, 0xC1, 0xAE, 0xA0, 0x8A, 0x89, 0xA9, 0x87, 0x47, 0x5D, + 0x12, 0xFD, 0x95, 0x0D, 0x83, 0xCF, 0xA4, 0x17, 0x32, 0xBC, + 0x50, 0x9D, 0x0D, 0x1A, 0xC4, 0x3A, 0x03, 0x36, 0xDE, 0xF9, + 0x6F, 0xDA, 0x41, 0xD0, 0x77, 0x4A, 0x35, 0x71, + + 0xDC, 0xFB, 0xEC, 0x7A, 0xAC, 0xF3, 0x19, 0x64, 0x72, 0x16, + 0x9E, 0x83, 0x84, 0x30, 0x36, 0x7F, 0x66, 0xEE, 0xBE, 0x3C, + 0x6E, 0x70, 0xC4, 0x16, 0xDD, 0x5F, 0x0C, 0x68, 0x75, 0x9D, + 0xD1, 0xFF, 0xF8, 0x3F, 0xA4, 0x01, 0x42, 0x20, 0x9D, 0xFF, + 0x5E, 0xAA, 0xD9, 0x6D, 0xB9, 0xE6, 0x38, 0x6C, + + 0x11, 0x18, 0x73, 0x31, 0xC2, 0x79, 0x96, 0x2D, 0x93, 0xD6, + 0x04, 0x24, 0x3F, 0xD5, 0x92, 0xCB, 0x9D, 0x0A, 0x92, 0x6F, + 0x42, 0x2E, 0x47, 0x18, 0x75, 0x21, 0x28, 0x7E, 0x71, 0x56, + 0xC5, 0xC4, 0xD6, 0x03, 0x13, 0x55, 0x69, 0xB9, 0xE9, 0xD0, + 0x9C, 0xF5, 0xD4, 0xA2, 0x70, 0xF5, 0x97, 0x46 +}; + +/* An example for testing from RFC5903, section 8.3 */ +static unsigned char TEST_ECDH_NIST_P_521[] = { + 0x00, 0x37, 0xAD, 0xE9, 0x31, 0x9A, 0x89, 0xF4, 0xDA, 0xBD, + 0xB3, 0xEF, 0x41, 0x1A, 0xAC, 0xCC, 0xA5, 0x12, 0x3C, 0x61, + 0xAC, 0xAB, 0x57, 0xB5, 0x39, 0x3D, 0xCE, 0x47, 0x60, 0x81, + 0x72, 0xA0, 0x95, 0xAA, 0x85, 0xA3, 0x0F, 0xE1, 0xC2, 0x95, + 0x2C, 0x67, 0x71, 0xD9, 0x37, 0xBA, 0x97, 0x77, 0xF5, 0x95, + 0x7B, 0x26, 0x39, 0xBA, 0xB0, 0x72, 0x46, 0x2F, 0x68, 0xC2, + 0x7A, 0x57, 0x38, 0x2D, 0x4A, 0x52, + + 0x00, 0x15, 0x41, 0x7E, 0x84, 0xDB, 0xF2, 0x8C, 0x0A, 0xD3, + 0xC2, 0x78, 0x71, 0x33, 0x49, 0xDC, 0x7D, 0xF1, 0x53, 0xC8, + 0x97, 0xA1, 0x89, 0x1B, 0xD9, 0x8B, 0xAB, 0x43, 0x57, 0xC9, + 0xEC, 0xBE, 0xE1, 0xE3, 0xBF, 0x42, 0xE0, 0x0B, 0x8E, 0x38, + 0x0A, 0xEA, 0xE5, 0x7C, 0x2D, 0x10, 0x75, 0x64, 0x94, 0x18, + 0x85, 0x94, 0x2A, 0xF5, 0xA7, 0xF4, 0x60, 0x17, 0x23, 0xC4, + 0x19, 0x5D, 0x17, 0x6C, 0xED, 0x3E, + + 0x01, 0x7C, 0xAE, 0x20, 0xB6, 0x64, 0x1D, 0x2E, 0xEB, 0x69, + 0x57, 0x86, 0xD8, 0xC9, 0x46, 0x14, 0x62, 0x39, 0xD0, 0x99, + 0xE1, 0x8E, 0x1D, 0x5A, 0x51, 0x4C, 0x73, 0x9D, 0x7C, 0xB4, + 0xA1, 0x0A, 0xD8, 0xA7, 0x88, 0x01, 0x5A, 0xC4, 0x05, 0xD7, + 0x79, 0x9D, 0xC7, 0x5E, 0x7B, 0x7D, 0x5B, 0x6C, 0xF2, 0x26, + 0x1A, 0x6A, 0x7F, 0x15, 0x07, 0x43, 0x8B, 0xF0, 0x1B, 0xEB, + 0x6C, 0xA3, 0x92, 0x6F, 0x95, 0x82, + + 0x01, 0x45, 0xBA, 0x99, 0xA8, 0x47, 0xAF, 0x43, 0x79, 0x3F, + 0xDD, 0x0E, 0x87, 0x2E, 0x7C, 0xDF, 0xA1, 0x6B, 0xE3, 0x0F, + 0xDC, 0x78, 0x0F, 0x97, 0xBC, 0xCC, 0x3F, 0x07, 0x83, 0x80, + 0x20, 0x1E, 0x9C, 0x67, 0x7D, 0x60, 0x0B, 0x34, 0x37, 0x57, + 0xA3, 0xBD, 0xBF, 0x2A, 0x31, 0x63, 0xE4, 0xC2, 0xF8, 0x69, + 0xCC, 0xA7, 0x45, 0x8A, 0xA4, 0xA4, 0xEF, 0xFC, 0x31, 0x1F, + 0x5C, 0xB1, 0x51, 0x68, 0x5E, 0xB9, + + 0x00, 0xD0, 0xB3, 0x97, 0x5A, 0xC4, 0xB7, 0x99, 0xF5, 0xBE, + 0xA1, 0x6D, 0x5E, 0x13, 0xE9, 0xAF, 0x97, 0x1D, 0x5E, 0x9B, + 0x98, 0x4C, 0x9F, 0x39, 0x72, 0x8B, 0x5E, 0x57, 0x39, 0x73, + 0x5A, 0x21, 0x9B, 0x97, 0xC3, 0x56, 0x43, 0x6A, 0xDC, 0x6E, + 0x95, 0xBB, 0x03, 0x52, 0xF6, 0xBE, 0x64, 0xA6, 0xC2, 0x91, + 0x2D, 0x4E, 0xF2, 0xD0, 0x43, 0x3C, 0xED, 0x2B, 0x61, 0x71, + 0x64, 0x00, 0x12, 0xD9, 0x46, 0x0F, + + 0x01, 0x5C, 0x68, 0x22, 0x63, 0x83, 0x95, 0x6E, 0x3B, 0xD0, + 0x66, 0xE7, 0x97, 0xB6, 0x23, 0xC2, 0x7C, 0xE0, 0xEA, 0xC2, + 0xF5, 0x51, 0xA1, 0x0C, 0x2C, 0x72, 0x4D, 0x98, 0x52, 0x07, + 0x7B, 0x87, 0x22, 0x0B, 0x65, 0x36, 0xC5, 0xC4, 0x08, 0xA1, + 0xD2, 0xAE, 0xBB, 0x8E, 0x86, 0xD6, 0x78, 0xAE, 0x49, 0xCB, + 0x57, 0x09, 0x1F, 0x47, 0x32, 0x29, 0x65, 0x79, 0xAB, 0x44, + 0xFC, 0xD1, 0x7F, 0x0F, 0xC5, 0x6A, + + 0x01, 0x14, 0x4C, 0x7D, 0x79, 0xAE, 0x69, 0x56, 0xBC, 0x8E, + 0xDB, 0x8E, 0x7C, 0x78, 0x7C, 0x45, 0x21, 0xCB, 0x08, 0x6F, + 0xA6, 0x44, 0x07, 0xF9, 0x78, 0x94, 0xE5, 0xE6, 0xB2, 0xD7, + 0x9B, 0x04, 0xD1, 0x42, 0x7E, 0x73, 0xCA, 0x4B, 0xAA, 0x24, + 0x0A, 0x34, 0x78, 0x68, 0x59, 0x81, 0x0C, 0x06, 0xB3, 0xC7, + 0x15, 0xA3, 0xA8, 0xCC, 0x31, 0x51, 0xF2, 0xBE, 0xE4, 0x17, + 0x99, 0x6D, 0x19, 0xF3, 0xDD, 0xEA, +}; + +enum ecdh_data { SIDE_A_KEY, SIDE_B_KEY, SHARED_SECRET }; +static void *generate_test_ecdh_data(const int group_id, + enum ecdh_data request_data) +{ + uint8_t *data_set; + int size; + EC_KEY *key; + const EC_GROUP *group; + const BIGNUM *k_priv = NULL; + const BIGNUM *k_pubx = NULL; + const BIGNUM *k_puby = NULL; + EC_POINT *k_pub = NULL; + + switch (group_id) { + case HIP_DH_NIST_P_256: + data_set = TEST_ECDH_NIST_P_256; + break; + case HIP_DH_NIST_P_384: + data_set = TEST_ECDH_NIST_P_384; + break; + case HIP_DH_NIST_P_521: + data_set = TEST_ECDH_NIST_P_521; + break; + default: + return NULL; + } + + size = hip_get_dh_size(group_id) / 2; + + switch (request_data) { + case SIDE_A_KEY: + key = hip_generate_ecdh_key(group_id); + group = EC_KEY_get0_group(key); + k_priv = BN_bin2bn(data_set + size * TEST_ECDH_PRIV_A, size, NULL); + k_pubx = BN_bin2bn(data_set + size * TEST_ECDH_PUBX_A, size, NULL); + k_puby = BN_bin2bn(data_set + size * TEST_ECDH_PUBY_A, size, NULL); + k_pub = EC_POINT_new(group); + EC_POINT_set_affine_coordinates_GFp(group, k_pub, k_pubx, k_puby, NULL); + EC_KEY_set_public_key(key, k_pub); + EC_KEY_set_private_key(key, k_priv); + return key; + case SIDE_B_KEY: + key = hip_generate_ecdh_key(group_id); + group = EC_KEY_get0_group(key); + k_priv = BN_bin2bn(data_set + size * TEST_ECDH_PRIV_B, size, NULL); + k_pubx = BN_bin2bn(data_set + size * TEST_ECDH_PUBX_B, size, NULL); + k_puby = BN_bin2bn(data_set + size * TEST_ECDH_PUBY_B, size, NULL); + k_pub = EC_POINT_new(group); + EC_POINT_set_affine_coordinates_GFp(group, k_pub, k_pubx, k_puby, NULL); + EC_KEY_set_public_key(key, k_pub); + EC_KEY_set_private_key(key, k_priv); + return key; + case SHARED_SECRET: + return data_set + size * TEST_ECDH_SHARED; + } + + return NULL; +} + START_TEST(test_create_ecdsa_key_invalid_id) { HIP_DEBUG("Trying to create some invalid ECDSA keys.\n"); @@ -244,6 +457,108 @@ } END_TEST +#define TEST_ECDH_GROUP_SIZE 3 +static int TEST_ECDH_GROUPS[TEST_ECDH_GROUP_SIZE] = { HIP_DH_NIST_P_256, + HIP_DH_NIST_P_384, + HIP_DH_NIST_P_521 }; + +START_TEST(test_generate_ecdh_key_invalid_group_id) +{ + fail_unless(hip_generate_ecdh_key(0) == NULL); + fail_unless(hip_generate_ecdh_key(-1) == NULL); + fail_unless(hip_generate_ecdh_key(HIP_MAX_DH_GROUP_ID) == NULL); +} +END_TEST + +START_TEST(test_generate_ecdh_key_valid_group_id) +{ + EC_KEY *key; + int i; + + for (i = 0; i < TEST_ECDH_GROUP_SIZE; i++) { + key = hip_generate_ecdh_key(TEST_ECDH_GROUPS[i]); + fail_if(key == NULL || EC_KEY_check_key(key) == 0); + } +} +END_TEST + +START_TEST(test_ecdh_generate_2_keys_and_share_secret) +{ + EC_KEY *key1; + EC_KEY *key2; + int out1, out2; + int len1, len2; + int i; + int group_id; + int pubkey_size; + + for (i = 0; i < TEST_ECDH_GROUP_SIZE; i++) { + group_id = TEST_ECDH_GROUPS[i]; + pubkey_size = hip_get_dh_size(group_id); + fail_if(pubkey_size <= 0); + + key1 = hip_generate_ecdh_key(group_id); + key2 = hip_generate_ecdh_key(group_id); + fail_if(key1 == NULL || key2 == NULL); + + const EC_POINT *k1pub = EC_KEY_get0_public_key(key1); + const EC_POINT *k2pub = EC_KEY_get0_public_key(key2); + + len1 = len2 = pubkey_size / 2; + unsigned char share1[len1]; + unsigned char share2[len2]; + + out1 = ECDH_compute_key(share1, len1, k2pub, key1, NULL); + out2 = ECDH_compute_key(share2, len2, k1pub, key2, NULL); + fail_if(out1 <= 0 || out2 <= 0); + fail_if(out1 != len1 && out2 != len2); + fail_if(memcmp(share1, share2, len1) != 0); + } +} +END_TEST + +START_TEST(test_ecdh_encode_publickey) +{ + int i, group_id, key_size; + + for (i = 0; i < TEST_ECDH_GROUP_SIZE; i++) { + group_id = TEST_ECDH_GROUPS[i]; + key_size = hip_get_dh_size(group_id); + uint8_t out[key_size]; + EC_KEY *key = hip_generate_ecdh_key(group_id); + fail_if(hip_encode_ecdh_publickey(key, out, key_size) != key_size); + } +} +END_TEST + +START_TEST(test_ecdh_gen_shared_key) +{ + int i, group_id, size, res; + + for (i = 0; i < TEST_ECDH_GROUP_SIZE; i++) { + group_id = TEST_ECDH_GROUPS[i]; + size = hip_get_dh_size(group_id) / 2; + uint8_t pub[size * 2]; + uint8_t out[size]; + EC_KEY *a = generate_test_ecdh_data(group_id, SIDE_A_KEY); + EC_KEY *b = generate_test_ecdh_data(group_id, SIDE_B_KEY); + void *secret = generate_test_ecdh_data(group_id, SHARED_SECRET); + fail_if(a == NULL || b == NULL || secret == NULL); + fail_if(EC_KEY_check_key(a) == 0 || EC_KEY_check_key(b) == 0); + + hip_encode_ecdh_publickey(b, pub, size * 2); + res = hip_gen_ecdh_shared_key(a, pub, pub + size, size, out, size); + fail_if(res != size); + fail_if(memcmp(secret, out, size) != 0); + + hip_encode_ecdh_publickey(a, pub, size * 2); + res = hip_gen_ecdh_shared_key(b, pub, pub + size, size, out, size); + fail_if(res != size); + fail_if(memcmp(secret, out, size) != 0); + } +} +END_TEST + Suite *libcore_crypto(void) { Suite *s = suite_create("libcore/crypto"); @@ -257,6 +572,11 @@ tcase_add_test(tc_core, test_load_invalid_ecdsa_key); tcase_add_test(tc_core, test_impl_ecdsa_sign_verify); tcase_add_test(tc_core, test_invalid_impl_ecdsa_sign_verify); + tcase_add_test(tc_core, test_generate_ecdh_key_invalid_group_id); + tcase_add_test(tc_core, test_generate_ecdh_key_valid_group_id); + tcase_add_test(tc_core, test_ecdh_generate_2_keys_and_share_secret); + tcase_add_test(tc_core, test_ecdh_encode_publickey); + tcase_add_test(tc_core, test_ecdh_gen_shared_key); suite_add_tcase(s, tc_core);