.destroy = _destroy,
},
.is_server = is_server,
- .crypto = tls_crypto_create(),
.version = TLS_1_2,
);
+ this->crypto = tls_crypto_create(&this->public);
if (is_server)
{
this->handshake = &tls_server_create(&this->public, this->crypto,
#include "tls_crypto.h"
+#include <daemon.h>
+
typedef struct private_tls_crypto_t private_tls_crypto_t;
/**
* Public tls_crypto_t interface.
*/
tls_crypto_t public;
+
+ /**
+ * List of supported/acceptable cipher suites
+ */
+ tls_cipher_suite_t *suites;
+
+ /**
+ * Number of supported suites
+ */
+ int suite_count;
+
+ /**
+ * Selected cipher suite
+ */
+ tls_cipher_suite_t suite;
+
+ /**
+ * TLS context
+ */
+ tls_t *tls;
+
+ /**
+ * Connection state TLS PRF
+ */
+ tls_prf_t *prf;
};
-METHOD(tls_crypto_t, get_cipher_suites, int,
- private_tls_crypto_t *this, tls_cipher_suite_t **suites)
+/**
+ * Initialize the cipher suite list
+ */
+static void build_cipher_suite_list(private_tls_crypto_t *this)
{
encryption_algorithm_t encr;
integrity_algorithm_t mac;
enumerator_t *encrs, *macs;
- tls_cipher_suite_t buf[64];
- int count = 0, i, j, res = 0;
+ tls_cipher_suite_t supported[64], unique[64];
+ int count = 0, i, j;
/* we assume that we support RSA, but no DHE yet */
macs = lib->crypto->create_signer_enumerator(lib->crypto);
switch (mac)
{
case AUTH_HMAC_SHA1_160:
- buf[count++] = TLS_RSA_WITH_NULL_SHA;
+ supported[count++] = TLS_RSA_WITH_NULL_SHA;
break;
case AUTH_HMAC_SHA2_256_256:
- buf[count++] = TLS_RSA_WITH_NULL_SHA256;
+ supported[count++] = TLS_RSA_WITH_NULL_SHA256;
break;
case AUTH_HMAC_MD5_128:
- buf[count++] = TLS_RSA_WITH_NULL_MD5;
+ supported[count++] = TLS_RSA_WITH_NULL_MD5;
break;
default:
break;
switch (mac)
{
case AUTH_HMAC_SHA1_160:
- buf[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
- buf[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+ supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ supported[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
break;
case AUTH_HMAC_SHA2_256_256:
- buf[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
- buf[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+ supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+ supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
break;
default:
break;
switch (mac)
{
case AUTH_HMAC_SHA1_160:
- buf[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ supported[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
break;
default:
break;
macs->destroy(macs);
/* remove duplicates */
- *suites = malloc(sizeof(tls_cipher_suite_t) * count);
+ this->suite_count = 0;
for (i = 0; i < count; i++)
{
bool match = FALSE;
- for (j = 0; j < res; j++)
+ for (j = 0; j < this->suite_count; j++)
{
- if (buf[i] == (*suites)[j])
+ if (supported[i] == unique[j])
{
match = TRUE;
break;
}
if (!match)
{
- (*suites)[res++] = buf[i];
+ unique[this->suite_count++] = supported[i];
+ }
+ }
+ free(this->suites);
+ this->suites = malloc(sizeof(tls_cipher_suite_t) * this->suite_count);
+ memcpy(this->suites, unique, sizeof(tls_cipher_suite_t) * this->suite_count);
+}
+
+METHOD(tls_crypto_t, get_cipher_suites, int,
+ private_tls_crypto_t *this, tls_cipher_suite_t **suites)
+{
+ *suites = this->suites;
+ return this->suite_count;
+}
+
+METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t,
+ private_tls_crypto_t *this, tls_cipher_suite_t *suites, int count)
+{
+ int i, j;
+
+ for (i = 0; i < this->suite_count; i++)
+ {
+ for (j = 0; j < count; j++)
+ {
+ if (this->suites[i] == suites[j])
+ {
+ this->suite = this->suites[i];
+ return this->suite;
+ }
+ }
+ }
+ return 0;
+}
+
+METHOD(tls_crypto_t, derive_master_secret, void,
+ private_tls_crypto_t *this, chunk_t premaster,
+ chunk_t client_random, chunk_t server_random)
+{
+ if (!this->prf)
+ {
+ if (this->tls->get_version(this->tls) < TLS_1_2)
+ {
+ this->prf = tls_prf_create_10();
+ }
+ else
+ {
+ switch (this->suite)
+ {
+ case TLS_RSA_WITH_NULL_MD5:
+ this->prf = tls_prf_create_12(PRF_HMAC_MD5);
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case TLS_RSA_WITH_NULL_SHA:
+ this->prf = tls_prf_create_12(PRF_HMAC_SHA1);
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_RSA_WITH_NULL_SHA256:
+ this->prf = tls_prf_create_12(PRF_HMAC_SHA2_256);
+ break;
+ default:
+ DBG1(DBG_IKE, "PRF for cipher suite unknown");
+ break;
+ }
}
}
- return res;
+ if (this->prf)
+ {
+ char master[48];
+ chunk_t seed;
+
+ seed = chunk_cata("cc", client_random, server_random);
+ this->prf->set_key(this->prf, premaster);
+ this->prf->get_bytes(this->prf, "master secret", seed,
+ sizeof(master), master);
+
+ this->prf->set_key(this->prf, chunk_from_thing(master));
+ memset(master, 0, sizeof(master));
+ }
}
+METHOD(tls_crypto_t, get_prf, tls_prf_t*,
+ private_tls_crypto_t *this)
+{
+
+ return this->prf;
+}
METHOD(tls_crypto_t, destroy, void,
private_tls_crypto_t *this)
{
+ free(this->suites);
+ DESTROY_IF(this->prf);
free(this);
}
/**
* See header
*/
-tls_crypto_t *tls_crypto_create()
+tls_crypto_t *tls_crypto_create(tls_t *tls)
{
private_tls_crypto_t *this;
INIT(this,
.public = {
.get_cipher_suites = _get_cipher_suites,
+ .select_cipher_suite = _select_cipher_suite,
+ .derive_master_secret = _derive_master_secret,
+ .get_prf = _get_prf,
.destroy = _destroy,
},
+ .tls = tls,
);
+ build_cipher_suite_list(this);
+
return &this->public;
}
typedef struct tls_crypto_t tls_crypto_t;
#include "tls.h"
+#include "tls_prf.h"
/**
* TLS crypto helper functions.
/**
* Get a list of supported TLS cipher suites.
*
- * @param suites allocated list of suites
+ * @param suites list of suites, points to internal data
* @return number of suites returned
*/
int (*get_cipher_suites)(tls_crypto_t *this, tls_cipher_suite_t **suites);
/**
+ * Select and store a cipher suite from a given list of candidates.
+ *
+ * @param suites list of candidates to select from
+ * @param count number of suites
+ * @return selected suite, 0 if none acceptable
+ */
+ tls_cipher_suite_t (*select_cipher_suite)(tls_crypto_t *this,
+ tls_cipher_suite_t *suites, int count);
+
+ /**
+ * Derive the master secret and load it into the PRF.
+ *
+ * @param premaster premaster secret
+ * @param client_random random data from client hello
+ * @param server_random random data from server hello
+ */
+ void (*derive_master_secret)(tls_crypto_t *this, chunk_t premaster,
+ chunk_t client_random, chunk_t server_random);
+
+ /**
+ * Get the connection state PRF.
+ *
+ * @return PRF, NULL if not supported
+ */
+ tls_prf_t* (*get_prf)(tls_crypto_t *this);
+
+ /**
* Destroy a tls_crypto_t.
*/
void (*destroy)(tls_crypto_t *this);
/**
* Create a tls_crypto instance.
*/
-tls_crypto_t *tls_crypto_create();
+tls_crypto_t *tls_crypto_create(tls_t *tls);
#endif /** TLS_CRYPTO_H_ @}*/
chunk_t handshake;
/**
+ * Hello random data selected by client
+ */
+ char client_random[32];
+
+ /**
+ * Hello random data selected by server
+ */
+ char server_random[32];
+
+ /**
* Auth helper for peer authentication
*/
auth_cfg_t *peer_auth;
{
u_int8_t compression;
u_int16_t version, cipher;
- u_int32_t gmt;
chunk_t random, session, ext = chunk_empty;
+ tls_cipher_suite_t suite;
append_handshake(this, TLS_SERVER_HELLO, reader->peek(reader));
if (!reader->read_uint16(reader, &version) ||
- !reader->read_uint32(reader, &gmt) ||
- !reader->read_data(reader, 28, &random) ||
+ !reader->read_data(reader, sizeof(this->server_random), &random) ||
!reader->read_data8(reader, &session) ||
!reader->read_uint16(reader, &cipher) ||
!reader->read_uint8(reader, &compression) ||
DBG1(DBG_IKE, "received invalid ServerHello");
return FAILED;
}
+
+ memcpy(this->server_random, random.ptr, sizeof(this->server_random));
+
if (version < this->tls->get_version(this->tls))
{
this->tls->set_version(this->tls, version);
}
+ suite = cipher;
+ if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1))
+ {
+ DBG1(DBG_IKE, "received cipher suite inacceptable");
+ return FAILED;
+ }
return NEED_MORE;
}
tls_cipher_suite_t *suite;
int count, i;
rng_t *rng;
- char random[28];
+ htoun32(&this->client_random, time(NULL));
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
return FAILED;
}
- rng->get_bytes(rng, sizeof(random), random);
+ rng->get_bytes(rng, sizeof(this->client_random) - 4, this->client_random + 4);
rng->destroy(rng);
writer->write_uint16(writer, this->tls->get_version(this->tls));
- writer->write_uint32(writer, time(NULL));
- writer->write_data(writer, chunk_from_thing(random));
+ writer->write_data(writer, chunk_from_thing(this->client_random));
/* session identifier => none */
writer->write_data8(writer, chunk_empty);
{
writer->write_uint16(writer, suite[i]);
}
- free(suite);
/* NULL compression only */
writer->write_uint8(writer, 1);
writer->write_uint8(writer, 0);
rng->destroy(rng);
htoun16(premaster, TLS_1_2);
+ this->crypto->derive_master_secret(this->crypto, chunk_from_thing(premaster),
+ chunk_from_thing(this->client_random),
+ chunk_from_thing(this->server_random));
+
enumerator = charon->credentials->create_public_enumerator(
charon->credentials, KEY_ANY, this->server, this->server_auth);
while (enumerator->enumerate(enumerator, ¤t, &auth))
static status_t send_finished(private_tls_peer_t *this,
tls_handshake_type_t *type, tls_writer_t *writer)
{
+ chunk_t seed;
+ tls_prf_t *prf;
+ char data[12];
+
+ if (this->tls->get_version(this->tls) >= TLS_1_2)
+ {
+ /* TODO: use hash of cipher suite only */
+ seed = chunk_empty;
+ }
+ else
+ {
+ hasher_t *md5, *sha1;
+ char buf[HASH_SIZE_MD5 + HASH_SIZE_SHA1];
+
+ md5 = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+ if (!md5)
+ {
+ DBG1(DBG_IKE, "unable to create %N Finished, MD5 not supported",
+ tls_version_names, this->tls->get_version(this->tls));
+ return FAILED;
+ }
+ md5->get_hash(md5, this->handshake, buf);
+ md5->destroy(md5);
+ sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ if (!sha1)
+ {
+ DBG1(DBG_IKE, "unable to sign %N Finished, SHA1 not supported",
+ tls_version_names, this->tls->get_version(this->tls));
+ return FAILED;
+ }
+ sha1->get_hash(sha1, this->handshake, buf + HASH_SIZE_MD5);
+ sha1->destroy(sha1);
+
+ seed = chunk_clonea(chunk_from_thing(buf));
+ }
+
+ prf = this->crypto->get_prf(this->crypto);
+ if (!prf)
+ {
+ return FAILED;
+ }
+ prf->get_bytes(prf, "client finished", seed, sizeof(data), data);
+
+ writer->write_data(writer, chunk_from_thing(data));
+
*type = TLS_FINISHED;
this->state = STATE_FINISHED_SENT;
- /* TODO: finished message */
return NEED_MORE;
}