Implemented a tls_writer class to simplify TLS data generation

This commit is contained in:
Martin Willi 2010-02-01 15:12:18 +01:00
parent 4ef946dd64
commit 3a1640dea1
7 changed files with 452 additions and 75 deletions

View File

@ -12,6 +12,7 @@ libstrongswan_eap_tls_la_SOURCES = eap_tls_plugin.h eap_tls_plugin.c \
tls/tls_fragmentation.h tls/tls_fragmentation.c \
tls/tls_crypto.h tls/tls_crypto.c \
tls/tls_reader.h tls/tls_reader.c \
tls/tls_writer.h tls/tls_writer.c \
tls/tls_peer.h tls/tls_peer.c \
tls/tls_server.h tls/tls_server.c \
tls/tls_handshake.h

View File

@ -50,6 +50,11 @@ struct private_tls_fragmentation_t {
* Currently processed handshake message type
*/
tls_handshake_type_t type;
/**
* Handshake output buffer
*/
chunk_t output;
};
/**
@ -62,15 +67,6 @@ struct private_tls_fragmentation_t {
*/
#define MAX_TLS_HANDSHAKE_LEN 65536
/**
* TLS handshake message header
*/
typedef union {
u_int8_t type;
/* 24bit length field */
u_int32_t length;
} tls_handshake_header_t;
/**
* Process TLS handshake protocol data
*/
@ -171,27 +167,61 @@ METHOD(tls_fragmentation_t, process, status_t,
METHOD(tls_fragmentation_t, build, status_t,
private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
{
tls_handshake_header_t header;
tls_handshake_type_t hs_type;
chunk_t hs_data;
tls_writer_t *writer, *msg;
status_t status;
status = this->handshake->build(this->handshake, &hs_type, &hs_data);
if (status != NEED_MORE)
if (!this->output.len)
{
return status;
msg = tls_writer_create(64);
do
{
writer = tls_writer_create(64);
status = this->handshake->build(this->handshake, &hs_type, writer);
switch (status)
{
case NEED_MORE:
msg->write_uint8(msg, hs_type);
msg->write_data24(msg, writer->get_buf(writer));
break;
case INVALID_STATE:
this->output = chunk_clone(msg->get_buf(msg));
break;
default:
break;
}
writer->destroy(writer);
}
while (status == NEED_MORE);
msg->destroy(msg);
if (status != INVALID_STATE)
{
return status;
}
}
htoun32(&header.length, hs_data.len);
header.type |= hs_type;
*data = chunk_cat("cm", chunk_from_thing(header), hs_data);
*type = TLS_HANDSHAKE;
return NEED_MORE;
if (this->output.len)
{
*type = TLS_HANDSHAKE;
if (this->output.len <= MAX_TLS_FRAGMENT_LEN)
{
*data = this->output;
this->output = chunk_empty;
return NEED_MORE;
}
*data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN);
this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN));
return NEED_MORE;
}
return status;
}
METHOD(tls_fragmentation_t, destroy, void,
private_tls_fragmentation_t *this)
{
free(this->input.ptr);
free(this->output.ptr);
free(this);
}

View File

@ -25,6 +25,7 @@ typedef struct tls_handshake_t tls_handshake_t;
#include "tls.h"
#include "tls_reader.h"
#include "tls_writer.h"
/**
* TLS handshake state machine interface.
@ -48,7 +49,7 @@ struct tls_handshake_t {
* Build TLS handshake messages to send out.
*
* @param type type of created handshake message
* @param data allocated TLS handshake message data
* @param writer TLS data buffer to write to
* @return
* - SUCCESS if handshake complete
* - FAILED if handshake failed
@ -56,7 +57,7 @@ struct tls_handshake_t {
* - INVALID_STATE if more input to process() required
*/
status_t (*build)(tls_handshake_t *this,
tls_handshake_type_t *type, chunk_t *data);
tls_handshake_type_t *type, tls_writer_t *writer);
/**
* Destroy a tls_handshake_t.

View File

@ -185,82 +185,54 @@ METHOD(tls_handshake_t, process, status_t,
return NEED_MORE;
}
/**
* Build the Client Hello using a given set of ciphers
*/
static chunk_t build_hello(private_tls_peer_t *this,
int count, tls_cipher_suite_t *suite, rng_t *rng)
{
int i;
struct __attribute__((packed)) {
u_int16_t version;
struct __attribute__((packed)) {
u_int32_t gmt;
u_int8_t bytes[28];
} random;
struct __attribute__((packed)) {
/* never send a session identifier */
u_int8_t len;
u_int8_t id[0];
} session;
struct __attribute__((packed)) {
u_int16_t len;
u_int16_t suite[count];
} cipher;
struct __attribute__((packed)) {
/* currently NULL compression only */
u_int8_t len;
u_int8_t method[1];
} compression;
u_int8_t extensions[0];
} hello;
htoun16(&hello.session.len, 0);
htoun16(&hello.version, this->tls->get_version(this->tls));
htoun32(&hello.random.gmt, time(NULL));
rng->get_bytes(rng, sizeof(hello.random.bytes), (char*)&hello.random.bytes);
htoun16(&hello.cipher.len, count * 2);
for (i = 0; i < count; i++)
{
htoun16(&hello.cipher.suite[i], suite[i]);
}
hello.compression.len = 1;
hello.compression.method[0] = 0;
return chunk_clone(chunk_create((char*)&hello, sizeof(hello)));
}
/**
* Send a client hello
*/
static status_t send_hello(private_tls_peer_t *this,
tls_handshake_type_t *type, chunk_t *data)
tls_handshake_type_t *type, tls_writer_t *writer)
{
tls_cipher_suite_t *suite;
int count;
int count, i;
rng_t *rng;
char random[28];
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
return FAILED;
}
count = this->crypto->get_cipher_suites(this->crypto, &suite);
*data = build_hello(this, count, suite, rng);
*type = TLS_CLIENT_HELLO;
free(suite);
rng->get_bytes(rng, sizeof(random), random);
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));
/* session identifier => none */
writer->write_data8(writer, chunk_empty);
count = this->crypto->get_cipher_suites(this->crypto, &suite);
writer->write_uint16(writer, count * 2);
for (i = 0; i < count; i++)
{
writer->write_uint16(writer, suite[i]);
}
free(suite);
/* NULL compression only */
writer->write_uint8(writer, 1);
writer->write_uint8(writer, 0);
*type = TLS_CLIENT_HELLO;
this->state = STATE_HELLO_SENT;
return NEED_MORE;
}
METHOD(tls_handshake_t, build, status_t,
private_tls_peer_t *this, tls_handshake_type_t *type, chunk_t *data)
private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer)
{
switch (this->state)
{
case STATE_INIT:
return send_hello(this, type, data);
return send_hello(this, type, writer);
default:
return INVALID_STATE;
}

View File

@ -48,7 +48,7 @@ METHOD(tls_handshake_t, process, status_t,
}
METHOD(tls_handshake_t, build, status_t,
private_tls_server_t *this, tls_handshake_type_t *type, chunk_t *data)
private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer)
{
return INVALID_STATE;
}

View File

@ -0,0 +1,237 @@
/*
* Copyright (C) 2010 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "tls_writer.h"
typedef struct private_tls_writer_t private_tls_writer_t;
/**
* Private data of an tls_writer_t object.
*/
struct private_tls_writer_t {
/**
* Public tls_writer_t interface.
*/
tls_writer_t public;
/**
* Allocated buffer
*/
chunk_t buf;
/**
* Used bytes in buffer
*/
size_t used;
/**
* Number of bytes to increase buffer size
*/
size_t increase;
};
/**
* Increase buffer size
*/
static void increase(private_tls_writer_t *this)
{
this->buf.len += this->increase;
this->buf.ptr = realloc(this->buf.ptr, this->buf.len);
}
METHOD(tls_writer_t, write_uint8, void,
private_tls_writer_t *this, u_int8_t value)
{
if (this->used + 1 > this->buf.len)
{
increase(this);
}
this->buf.ptr[this->used] = value;
this->used += 1;
}
METHOD(tls_writer_t, write_uint16, void,
private_tls_writer_t *this, u_int16_t value)
{
if (this->used + 2 > this->buf.len)
{
increase(this);
}
htoun16(this->buf.ptr + this->used, value);
this->used += 2;
}
METHOD(tls_writer_t, write_uint24, void,
private_tls_writer_t *this, u_int32_t value)
{
if (this->used + 3 > this->buf.len)
{
increase(this);
}
value = htonl(value);
memcpy(this->buf.ptr + this->used, ((char*)&value) + 1, 3);
this->used += 3;
}
METHOD(tls_writer_t, write_uint32, void,
private_tls_writer_t *this, u_int32_t value)
{
if (this->used + 4 > this->buf.len)
{
increase(this);
}
htoun32(this->buf.ptr + this->used, value);
this->used += 4;
}
METHOD(tls_writer_t, write_data, void,
private_tls_writer_t *this, chunk_t value)
{
while (this->used + value.len > this->buf.len)
{
increase(this);
}
memcpy(this->buf.ptr + this->used, value.ptr, value.len);
this->used += value.len;
}
METHOD(tls_writer_t, write_data8, void,
private_tls_writer_t *this, chunk_t value)
{
write_uint8(this, value.len);
write_data(this, value);
}
METHOD(tls_writer_t, write_data16, void,
private_tls_writer_t *this, chunk_t value)
{
write_uint16(this, value.len);
write_data(this, value);
}
METHOD(tls_writer_t, write_data24, void,
private_tls_writer_t *this, chunk_t value)
{
write_uint24(this, value.len);
write_data(this, value);
}
METHOD(tls_writer_t, write_data32, void,
private_tls_writer_t *this, chunk_t value)
{
write_uint32(this, value.len);
write_data(this, value);
}
METHOD(tls_writer_t, wrap8, void,
private_tls_writer_t *this)
{
if (this->used + 1 > this->buf.len)
{
increase(this);
}
memmove(this->buf.ptr + 1, this->buf.ptr, 1);
this->buf.ptr[0] = this->used;
this->used += 1;
}
METHOD(tls_writer_t, wrap16, void,
private_tls_writer_t *this)
{
if (this->used + 2 > this->buf.len)
{
increase(this);
}
memmove(this->buf.ptr + 2, this->buf.ptr, 2);
htoun16(this->buf.ptr, this->used);
this->used += 2;
}
METHOD(tls_writer_t, wrap24, void,
private_tls_writer_t *this)
{
u_int32_t len;
if (this->used + 3 > this->buf.len)
{
increase(this);
}
memmove(this->buf.ptr + 3, this->buf.ptr, 3);
len = htonl(this->used);
memcpy(this->buf.ptr, ((char*)&len) + 1, 3);
this->used += 3;
}
METHOD(tls_writer_t, wrap32, void,
private_tls_writer_t *this)
{
if (this->used + 4 > this->buf.len)
{
increase(this);
}
memmove(this->buf.ptr + 4, this->buf.ptr, 4);
htoun32(this->buf.ptr, this->used);
this->used += 4;
}
METHOD(tls_writer_t, get_buf, chunk_t,
private_tls_writer_t *this)
{
return chunk_create(this->buf.ptr, this->used);
}
METHOD(tls_writer_t, destroy, void,
private_tls_writer_t *this)
{
free(this->buf.ptr);
free(this);
}
/**
* See header
*/
tls_writer_t *tls_writer_create(u_int32_t bufsize)
{
private_tls_writer_t *this;
INIT(this,
.public = {
.write_uint8 = _write_uint8,
.write_uint16 = _write_uint16,
.write_uint24 = _write_uint24,
.write_uint32 = _write_uint32,
.write_data = _write_data,
.write_data8 = _write_data8,
.write_data16 = _write_data16,
.write_data24 = _write_data24,
.write_data32 = _write_data32,
.wrap8 = _wrap8,
.wrap16 = _wrap16,
.wrap24 = _wrap24,
.wrap32 = _wrap32,
.get_buf = _get_buf,
.destroy = _destroy,
},
.increase = bufsize ?: 32,
);
if (bufsize)
{
this->buf = chunk_alloc(bufsize);
}
return &this->public;
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (C) 2010 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup tls_writer tls_writer
* @{ @ingroup tls
*/
#ifndef TLS_WRITER_H_
#define TLS_WRITER_H_
typedef struct tls_writer_t tls_writer_t;
#include <library.h>
/**
* TLS record generator.
*/
struct tls_writer_t {
/**
* Append a 8-bit integer to the buffer.
*
* @param value value to append
*/
void (*write_uint8)(tls_writer_t *this, u_int8_t value);
/**
* Append a 16-bit integer to the buffer.
*
* @param value value to append
*/
void (*write_uint16)(tls_writer_t *this, u_int16_t value);
/**
* Append a 24-bit integer to the buffer.
*
* @param value value to append
*/
void (*write_uint24)(tls_writer_t *this, u_int32_t value);
/**
* Append a 32-bit integer to the buffer.
*
* @param value value to append
*/
void (*write_uint32)(tls_writer_t *this, u_int32_t value);
/**
* Append a chunk of data without a length header.
*
* @param value value to append
*/
void (*write_data)(tls_writer_t *this, chunk_t value);
/**
* Append a chunk of data with a 16-bit length header.
*
* @param value value to append
*/
void (*write_data8)(tls_writer_t *this, chunk_t value);
/**
* Append a chunk of data with a 8-bit length header.
*
* @param value value to append
*/
void (*write_data16)(tls_writer_t *this, chunk_t value);
/**
* Append a chunk of data with a 24-bit length header.
*
* @param value value to append
*/
void (*write_data24)(tls_writer_t *this, chunk_t value);
/**
* Append a chunk of data with a 32-bit length header.
*
* @param value value to append
*/
void (*write_data32)(tls_writer_t *this, chunk_t value);
/**
* Prepend a 8-bit length header to existing data.
*/
void (*wrap8)(tls_writer_t *this);
/**
* Prepend a 16-bit length header to existing data.
*/
void (*wrap16)(tls_writer_t *this);
/**
* Prepend a 24-bit length header to existing data.
*/
void (*wrap24)(tls_writer_t *this);
/**
* Prepend a 32-bit length header to existing data.
*/
void (*wrap32)(tls_writer_t *this);
/**
* Get the encoded data buffer.
*
* @return chunk to internal buffer
*/
chunk_t (*get_buf)(tls_writer_t *this);
/**
* Destroy a tls_writer_t.
*/
void (*destroy)(tls_writer_t *this);
};
/**
* Create a tls_writer instance.
*
* @param bufsize initially allocated buffer size
*/
tls_writer_t *tls_writer_create(u_int32_t bufsize);
#endif /** TLS_WRITER_H_ @}*/