* Connection state TLS PRF
*/
tls_prf_t *prf;
+
+ /**
+ * Signer instance for inbound traffic
+ */
+ signer_t *signer_in;
+
+ /**
+ * Signer instance for outbound traffic
+ */
+ signer_t *signer_out;
+
+ /**
+ * Crypter instance for inbound traffic
+ */
+ crypter_t *crypter_in;
+
+ /**
+ * Crypter instance for outbound traffic
+ */
+ crypter_t *crypter_out;
+
+ /**
+ * IV for input decryption, if < TLSv1.2
+ */
+ chunk_t iv_in;
+
+ /**
+ * IV for output decryption, if < TLSv1.2
+ */
+ chunk_t iv_out;
};
+typedef struct {
+ tls_cipher_suite_t suite;
+ hash_algorithm_t hash;
+ pseudo_random_function_t prf;
+ integrity_algorithm_t mac;
+ encryption_algorithm_t encr;
+ size_t encr_size;
+} suite_algs_t;
+
+/**
+ * Mapping suites to a set of algorithms
+ */
+static suite_algs_t suite_algs[] = {
+ { TLS_RSA_WITH_NULL_MD5,
+ HASH_MD5,
+ PRF_HMAC_MD5,
+ AUTH_HMAC_MD5_128,
+ ENCR_NULL, 0
+ },
+ { TLS_RSA_WITH_NULL_SHA,
+ HASH_SHA1,
+ PRF_HMAC_SHA1,
+ AUTH_HMAC_SHA1_160,
+ ENCR_NULL, 0
+ },
+ { TLS_RSA_WITH_NULL_SHA256,
+ HASH_SHA256,
+ PRF_HMAC_SHA2_256,
+ AUTH_HMAC_SHA2_256_256,
+ ENCR_NULL, 0
+ },
+ { TLS_RSA_WITH_AES_128_CBC_SHA,
+ HASH_SHA1,
+ PRF_HMAC_SHA1,
+ AUTH_HMAC_SHA1_160,
+ ENCR_AES_CBC, 16
+ },
+ { TLS_RSA_WITH_AES_256_CBC_SHA,
+ HASH_SHA1,
+ PRF_HMAC_SHA1,
+ AUTH_HMAC_SHA1_160,
+ ENCR_AES_CBC, 32
+ },
+ { TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ HASH_SHA1,
+ PRF_HMAC_SHA1,
+ AUTH_HMAC_SHA1_160,
+ ENCR_3DES, 0
+ },
+ { TLS_RSA_WITH_AES_128_CBC_SHA256,
+ HASH_SHA256,
+ PRF_HMAC_SHA2_256,
+ AUTH_HMAC_SHA2_256_256,
+ ENCR_AES_CBC, 16
+ },
+};
+
+/**
+ * Look up algoritms by a suite
+ */
+static suite_algs_t *find_suite(tls_cipher_suite_t suite)
+{
+ int i;
+
+ for (i = 0; i < countof(suite_algs); i++)
+ {
+ if (suite_algs[i].suite == suite)
+ {
+ return &suite_algs[i];
+ }
+ }
+ return NULL;
+}
+
/**
* Initialize the cipher suite list
*/
return this->suite_count;
}
+/**
+ * Create crypto primitives
+ */
+static bool create_ciphers(private_tls_crypto_t *this, tls_cipher_suite_t suite)
+{
+ suite_algs_t *algs;
+
+ algs = find_suite(suite);
+ if (!algs)
+ {
+ DBG1(DBG_IKE, "selected TLS suite not supported");
+ return FALSE;
+ }
+
+ DESTROY_IF(this->prf);
+ if (this->tls->get_version(this->tls) < TLS_1_2)
+ {
+ this->prf = tls_prf_create_10();
+ }
+ else
+ {
+ this->prf = tls_prf_create_12(algs->prf);
+ }
+ if (!this->prf)
+ {
+ DBG1(DBG_IKE, "selected TLS PRF not supported");
+ return FALSE;
+ }
+
+ DESTROY_IF(this->signer_in);
+ DESTROY_IF(this->signer_out);
+ this->signer_in = lib->crypto->create_signer(lib->crypto, algs->mac);
+ this->signer_out = lib->crypto->create_signer(lib->crypto, algs->mac);
+ if (!this->signer_in || !this->signer_out)
+ {
+ DBG1(DBG_IKE, "selected TLS MAC %N not supported",
+ integrity_algorithm_names, algs->mac);
+ return FALSE;
+ }
+
+ DESTROY_IF(this->crypter_in);
+ DESTROY_IF(this->crypter_out);
+ if (algs->encr == ENCR_NULL)
+ {
+ this->crypter_in = this->crypter_out = NULL;
+ }
+ else
+ {
+ this->crypter_in = lib->crypto->create_crypter(lib->crypto,
+ algs->encr, algs->encr_size);
+ this->crypter_out = lib->crypto->create_crypter(lib->crypto,
+ algs->encr, algs->encr_size);
+ if (!this->crypter_in || !this->crypter_out)
+ {
+ DBG1(DBG_IKE, "selected TLS crypter %N not supported",
+ encryption_algorithm_names, algs->encr);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t,
private_tls_crypto_t *this, tls_cipher_suite_t *suites, int count)
{
{
if (this->suites[i] == suites[j])
{
- this->suite = this->suites[i];
- return this->suite;
+ if (create_ciphers(this, this->suites[i]))
+ {
+ this->suite = this->suites[i];
+ return this->suite;
+ }
}
}
}
private_tls_crypto_t *this, chunk_t premaster,
chunk_t client_random, chunk_t server_random)
{
- if (!this->prf)
+ char master[48];
+ chunk_t seed, block, client_write, server_write;
+ int mks, eks = 0, ivs = 0;
+
+ /* derive master secret */
+ 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));
+
+ /* derive key block for key expansion */
+ mks = this->signer_out->get_key_size(this->signer_out);
+ if (this->crypter_out)
{
+ eks = this->crypter_out->get_key_size(this->crypter_out);
if (this->tls->get_version(this->tls) < TLS_1_2)
{
- this->prf = tls_prf_create_10();
+ ivs = this->crypter_out->get_block_size(this->crypter_out);
+ }
+ }
+ seed = chunk_cata("cc", server_random, client_random);
+ block = chunk_alloca((mks + eks + ivs) * 2);
+ this->prf->get_bytes(this->prf, "key expansion", seed, block.len, block.ptr);
+
+ /* signer keys */
+ client_write = chunk_create(block.ptr, mks);
+ block = chunk_skip(block, mks);
+ server_write = chunk_create(block.ptr, mks);
+ block = chunk_skip(block, mks);
+ if (this->tls->is_server(this->tls))
+ {
+ this->signer_in->set_key(this->signer_in, client_write);
+ this->signer_out->set_key(this->signer_out, server_write);
+ }
+ else
+ {
+ this->signer_out->set_key(this->signer_out, client_write);
+ this->signer_in->set_key(this->signer_in, server_write);
+ }
+
+ /* crypter keys, and IVs if < TLSv1.2 */
+ if (this->crypter_out && this->crypter_in)
+ {
+ client_write = chunk_create(block.ptr, eks);
+ block = chunk_skip(block, eks);
+ server_write = chunk_create(block.ptr, eks);
+ block = chunk_skip(block, eks);
+
+ if (this->tls->is_server(this->tls))
+ {
+ this->crypter_in->set_key(this->crypter_in, client_write);
+ this->crypter_out->set_key(this->crypter_out, server_write);
}
else
{
- switch (this->suite)
+ this->crypter_out->set_key(this->crypter_out, client_write);
+ this->crypter_in->set_key(this->crypter_in, server_write);
+ }
+ if (ivs)
+ {
+ client_write = chunk_create(block.ptr, ivs);
+ block = chunk_skip(block, ivs);
+ server_write = chunk_create(block.ptr, ivs);
+ block = chunk_skip(block, ivs);
+
+ if (this->tls->is_server(this->tls))
{
- 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;
+ this->iv_in = chunk_clone(client_write);
+ this->iv_out = chunk_clone(server_write);
+ }
+ else
+ {
+ this->iv_out = chunk_clone(client_write);
+ this->iv_in = chunk_clone(server_write);
}
}
}
- 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, change_cipher, void,
+ private_tls_crypto_t *this, bool inbound)
+{
+ if (inbound)
+ {
+ this->tls->change_cipher(this->tls, TRUE, this->signer_in,
+ this->crypter_in, this->iv_in);
+ }
+ else
+ {
+ this->tls->change_cipher(this->tls, FALSE, this->signer_out,
+ this->crypter_out, this->iv_out);
}
}
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->signer_in);
+ DESTROY_IF(this->signer_out);
+ DESTROY_IF(this->crypter_in);
+ DESTROY_IF(this->crypter_out);
+ free(this->iv_in.ptr);
+ free(this->iv_out.ptr);
DESTROY_IF(this->prf);
+ free(this->suites);
free(this);
}
.get_cipher_suites = _get_cipher_suites,
.select_cipher_suite = _select_cipher_suite,
.derive_master_secret = _derive_master_secret,
+ .change_cipher = _change_cipher,
.get_prf = _get_prf,
.destroy = _destroy,
},
tls_protection_t public;
/**
+ * TLS context
+ */
+ tls_t *tls;
+
+ /**
* Upper layer, TLS record compression
*/
tls_compression_t *compression;
+
+ /**
+ * RNG if we generate IVs ourself
+ */
+ rng_t *rng;
+
+ /**
+ * Sequence number of incoming records
+ */
+ u_int32_t seq_in;
+
+ /**
+ * Sequence number for outgoing records
+ */
+ u_int32_t seq_out;
+
+ /**
+ * Signer instance for inbound traffic
+ */
+ signer_t *signer_in;
+
+ /**
+ * Signer instance for outbound traffic
+ */
+ signer_t *signer_out;
+
+ /**
+ * Crypter instance for inbound traffic
+ */
+ crypter_t *crypter_in;
+
+ /**
+ * Crypter instance for outbound traffic
+ */
+ crypter_t *crypter_out;
+
+ /**
+ * Current IV for input decryption
+ */
+ chunk_t iv_in;
+
+ /**
+ * Current IV for output decryption
+ */
+ chunk_t iv_out;
};
+/**
+ * Create the header to append to the record data to create the MAC
+ */
+static chunk_t sigheader(u_int32_t seq, u_int8_t type,
+ u_int16_t version, u_int16_t length)
+{
+ /* we only support 32 bit sequence numbers, but TLS uses 64 bit */
+ u_int32_t seq_high = 0;
+
+ seq = htonl(seq);
+ version = htons(version);
+ length = htons(length);
+
+ return chunk_cat("ccccc", chunk_from_thing(seq_high),
+ chunk_from_thing(seq), chunk_from_thing(type),
+ chunk_from_thing(version), chunk_from_thing(length));
+}
+
METHOD(tls_protection_t, process, status_t,
private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
{
+ this->seq_in++;
return this->compression->process(this->compression, type, data);
}
METHOD(tls_protection_t, build, status_t,
private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
{
- return this->compression->build(this->compression, type, data);
+ status_t status;
+
+ status = this->compression->build(this->compression, type, data);
+ if (*type == TLS_CHANGE_CIPHER_SPEC)
+ {
+ this->seq_out = 0;
+ return status;
+ }
+
+ if (status == NEED_MORE)
+ {
+ if (this->signer_out)
+ {
+ chunk_t mac, header;
+
+ header = sigheader(this->seq_out, *type,
+ this->tls->get_version(this->tls), data->len);
+ this->signer_out->get_signature(this->signer_out, header, NULL);
+ free(header.ptr);
+ this->signer_out->allocate_signature(this->signer_out, *data, &mac);
+ if (this->crypter_out)
+ {
+ chunk_t padding, iv;
+ u_int8_t bs, padding_length;
+
+ bs = this->crypter_out->get_block_size(this->crypter_out);
+ padding_length = bs - ((data->len + mac.len + 1) % bs);
+
+ padding = chunk_alloca(padding_length);
+ memset(padding.ptr, padding_length, padding.len);
+
+ if (this->iv_out.len)
+ { /* < TLSv1.2 uses IV from key derivation/last block */
+ iv = this->iv_out;
+ }
+ else
+ { /* TLSv1.2 uses random IVs, prepended to record */
+ if (!this->rng)
+ {
+ DBG1(DBG_IKE, "no RNG supported to generate TLS IV");
+ free(data->ptr);
+ return FAILED;
+ }
+ this->rng->allocate_bytes(this->rng, bs, &iv);
+ }
+
+ *data = chunk_cat("mmcc", *data, mac, padding,
+ chunk_from_thing(padding_length));
+ /* encrypt inline */
+ this->crypter_out->encrypt(this->crypter_out, *data,
+ this->iv_out, NULL);
+
+ if (this->iv_out.len)
+ { /* next record IV is last ciphertext block of this record */
+ memcpy(this->iv_out.ptr, data->ptr - this->iv_out.len,
+ this->iv_out.len);
+ }
+ else
+ { /* prepend IV */
+ *data = chunk_cat("mm", iv, *data);
+ }
+ }
+ else
+ { /* NULL encryption */
+ *data = chunk_cat("mm", *data, mac);
+ }
+ }
+ }
+ this->seq_out++;
+ return status;
+}
+
+METHOD(tls_protection_t, set_cipher, void,
+ private_tls_protection_t *this, bool inbound, signer_t *signer,
+ crypter_t *crypter, chunk_t iv)
+{
+ if (inbound)
+ {
+ this->signer_in = signer;
+ this->crypter_in = crypter;
+ this->iv_in = iv;
+ }
+ else
+ {
+ this->signer_out = signer;
+ this->crypter_out = crypter;
+ this->iv_out = iv;
+ if (!iv.len)
+ { /* generate IVs if none given */
+ this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ }
+ }
}
METHOD(tls_protection_t, destroy, void,
private_tls_protection_t *this)
{
+ DESTROY_IF(this->rng);
free(this);
}
/**
* See header
*/
-tls_protection_t *tls_protection_create(tls_compression_t *compression)
+tls_protection_t *tls_protection_create(tls_t *tls,
+ tls_compression_t *compression)
{
private_tls_protection_t *this;
.public = {
.process = _process,
.build = _build,
+ .set_cipher = _set_cipher,
.destroy = _destroy,
},
+ .tls = tls,
.compression = compression,
);