diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2000-04-09 11:49:42 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2000-04-09 11:49:42 +0000 |
commit | ccb26d30efc4aeda237196cab4cb88fb40dc9f25 (patch) | |
tree | 29ace3c191e2ce6acb203093d7a9ce23644d0245 /lib | |
parent | af145d777894217cc337ce6505566d6799d8831b (diff) | |
download | gnutls-ccb26d30efc4aeda237196cab4cb88fb40dc9f25.tar.gz |
Changed directory structure.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 5 | ||||
-rw-r--r-- | lib/debug.c | 91 | ||||
-rw-r--r-- | lib/debug.h | 5 | ||||
-rw-r--r-- | lib/defines.h | 75 | ||||
-rw-r--r-- | lib/gnutls.c | 663 | ||||
-rw-r--r-- | lib/gnutls.h | 42 | ||||
-rw-r--r-- | lib/gnutls_algorithms.c | 161 | ||||
-rw-r--r-- | lib/gnutls_algorithms.h | 76 | ||||
-rw-r--r-- | lib/gnutls_buffers.c | 88 | ||||
-rw-r--r-- | lib/gnutls_buffers.h | 5 | ||||
-rw-r--r-- | lib/gnutls_cipher.c | 589 | ||||
-rw-r--r-- | lib/gnutls_cipher.h | 15 | ||||
-rw-r--r-- | lib/gnutls_compress.c | 79 | ||||
-rw-r--r-- | lib/gnutls_compress.h | 11 | ||||
-rw-r--r-- | lib/gnutls_dh.c | 662 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 31 | ||||
-rw-r--r-- | lib/gnutls_errors.h | 19 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 710 | ||||
-rw-r--r-- | lib/gnutls_handshake.h | 9 | ||||
-rw-r--r-- | lib/gnutls_int.h | 254 | ||||
-rw-r--r-- | lib/gnutls_num.c | 21 | ||||
-rw-r--r-- | lib/gnutls_num.h | 2 | ||||
-rw-r--r-- | lib/gnutls_plaintext.c | 51 | ||||
-rw-r--r-- | lib/gnutls_plaintext.h | 3 |
24 files changed, 3667 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index e69de29bb2..96d22e33fe 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -0,0 +1,5 @@ +include_HEADERS = gnutls.h +EXTRA_DIST = debug.h gnutls_compress.h defines.h gnutls_plaintext.h gnutls_cipher.h gnutls_buffers.h gnutls_errors.h gnutls_int.h gnutls_handshake.h gnutls_num.h gnutls_algorithms.h +lib_LTLIBRARIES = libgnutls.la +libgnutls_la_SOURCES = gnutls.c gnutls_compress.c debug.c gnutls_plaintext.c gnutls_cipher.c gnutls_buffers.c gnutls_handshake.c gnutls_num.c gnutls_errors.c gnutls_algorithms.c +libgnutls_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/lib/debug.c b/lib/debug.c new file mode 100644 index 0000000000..9343de237d --- /dev/null +++ b/lib/debug.c @@ -0,0 +1,91 @@ +#include <stdio.h> +#include <stdlib.h> +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" + + +static char hexconvtab[] = "0123456789abcdef"; + +char * bin2hex(const unsigned char *old, const size_t oldlen) +{ + unsigned char *new = NULL; + int i, j; + + new = malloc(oldlen * 2 * sizeof(char) + 1); + if (!new) + return (new); + + for (i = j = 0; i < oldlen; i++) { + new[j++] = hexconvtab[old[i] >> 4]; + new[j++] = hexconvtab[old[i] & 15]; + } + new[j] = '\0'; + + return (new); +} + + +void _print_state(GNUTLS_STATE state) +{ + + fprintf(stderr, "GNUTLS State:\n"); + fprintf(stderr, "Connection End: %d\n", + state->security_parameters.entity); + fprintf(stderr, "Cipher Algorithm: %d\n", + state->security_parameters.bulk_cipher_algorithm); + fprintf(stderr, "Cipher Type: %d\n", + state->security_parameters.cipher_type); + fprintf(stderr, "Key Size: %d\n", + state->security_parameters.key_size); + fprintf(stderr, "Key Material: %d\n", + state->security_parameters.key_material_length); + fprintf(stderr, "Exportable: %d\n", + state->security_parameters.is_exportable); + fprintf(stderr, "MAC algorithm: %d\n", + state->security_parameters.mac_algorithm); + fprintf(stderr, "Hash size: %d\n", + state->security_parameters.hash_size); + fprintf(stderr, "Compression Algorithm: %d\n", + state->security_parameters.compression_algorithm); + fprintf(stderr, "\n"); + +} + +void _print_TLSCompressed(GNUTLSCompressed * compressed) +{ + fprintf(stderr, "TLSCompressed packet:\n"); + fprintf(stderr, "type: %d\n", compressed->type); + fprintf(stderr, "version: %d,%d\n", compressed->version.major, + compressed->version.minor); + fprintf(stderr, "length: %d\n", compressed->length); + fprintf(stderr, "fragment: %s\n", bin2hex(compressed->fragment, compressed->length)); + fprintf(stderr, "\n"); +} + + +void _print_TLSPlaintext(GNUTLSPlaintext * plaintext) +{ + fprintf(stderr, "TLSPlaintext packet:\n"); + fprintf(stderr, "type: %d\n", plaintext->type); + fprintf(stderr, "version: %d,%d\n", plaintext->version.major, + plaintext->version.minor); + fprintf(stderr, "length: %d\n", plaintext->length); + fprintf(stderr, "fragment: %s\n", bin2hex(plaintext->fragment, plaintext->length)); + fprintf(stderr, "\n"); +} + + +void _print_TLSCiphertext( GNUTLSCiphertext * ciphertext) +{ + + fprintf(stderr, "TLSCiphertext packet:\n"); + fprintf(stderr, "type: %d\n", ciphertext->type); + fprintf(stderr, "version: %d,%d\n", ciphertext->version.major, + ciphertext->version.minor); + fprintf(stderr, "length: %d\n", ciphertext->length); + + fprintf(stderr, "fragment: %s\n", bin2hex(ciphertext->fragment, ciphertext->length)); + fprintf(stderr, "\n"); +} diff --git a/lib/debug.h b/lib/debug.h new file mode 100644 index 0000000000..3c19e39e8d --- /dev/null +++ b/lib/debug.h @@ -0,0 +1,5 @@ +void _print_state(GNUTLS_STATE state); +void _print_TLSCompressed(GNUTLSCompressed * compressed); +void _print_TLSPlaintext(GNUTLSPlaintext * plaintext); +void _print_TLSCiphertext( GNUTLSCiphertext *); +char * bin2hex(const unsigned char *old, const size_t oldlen); diff --git a/lib/defines.h b/lib/defines.h new file mode 100644 index 0000000000..546aaed186 --- /dev/null +++ b/lib/defines.h @@ -0,0 +1,75 @@ +#include <config.h> + +#ifdef STDC_HEADERS +# include <string.h> +# include <stdlib.h> +# include <stdio.h> +#endif + +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif + +#include <time.h> + + +/* for open */ +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <gcrypt.h> + +#if SIZEOF_UNSIGNED_LONG_INT == 8 + typedef unsigned long int uint64; + typedef signed long int sint64; +#elif SIZEOF_UNSIGNED_LONG_LONG == 8 + typedef unsigned long long uint64; + typedef signed long long sint64; +#else +# error "Cannot find a 64 bit integer in your system, sorry." +#endif + + +#if SIZEOF_UNSIGNED_LONG_INT == 4 + typedef unsigned long int uint32; + typedef signed long int sint32; +#elif SIZEOF_UNSIGNED_INT == 4 + typedef unsigned int uint32; + typedef signed int sint32; +#else +# error "Cannot find a 32 bit integer in your system, sorry." +#endif + +#if SIZEOF_UNSIGNED_INT == 2 + typedef unsigned int uint16; + typedef signed int sint16; +#elif SIZEOF_UNSIGNED_SHORT_INT == 2 + typedef unsigned short int uint16; + typedef signed short int sint16; +#else +# error "Cannot find a 16 bit integer in your system, sorry." +#endif + +#if SIZEOF_UNSIGNED_CHAR == 1 + typedef unsigned char uint8; + typedef signed char int8; +#else +# error "Cannot find an 8 bit char in your system, sorry." +#endif + +#ifndef HAVE_MEMMOVE +# ifdef HAVE_BCOPY +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# else +# error "Neither memmove nor bcopy exists on your system." +# endif +#endif diff --git a/lib/gnutls.c b/lib/gnutls.c new file mode 100644 index 0000000000..d9a2b8c8aa --- /dev/null +++ b/lib/gnutls.c @@ -0,0 +1,663 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "debug.h" +#include "gnutls_compress.h" +#include "gnutls_plaintext.h" +#include "gnutls_cipher.h" +#include "gnutls_buffers.h" +#include "gnutls_handshake.h" + +int gnutls_init(GNUTLS_STATE * state, ConnectionEnd con_end) +{ + *state = gnutls_calloc(1, sizeof(GNUTLS_STATE_INT)); + memset(*state, 0, sizeof(GNUTLS_STATE)); + (*state)->security_parameters.entity = con_end; + +/* Set the defaults (only to remind me that they should be allocated ) */ + (*state)->security_parameters.bulk_cipher_algorithm = CIPHER_NULL; + (*state)->security_parameters.mac_algorithm = MAC_NULL; + (*state)->security_parameters.compression_algorithm = COMPRESSION_NULL; + + (*state)->connection_state.read_compression_state = NULL; + (*state)->connection_state.read_mac_secret = NULL; + (*state)->connection_state.write_compression_state = NULL; + (*state)->connection_state.write_mac_secret = NULL; + + (*state)->cipher_specs.server_write_mac_secret = NULL; + (*state)->cipher_specs.client_write_mac_secret = NULL; + (*state)->cipher_specs.server_write_IV = NULL; + (*state)->cipher_specs.client_write_IV = NULL; + (*state)->cipher_specs.server_write_key = NULL; + (*state)->cipher_specs.client_write_key = NULL; + + (*state)->gnutls_internals.buffer = NULL; + (*state)->gnutls_internals.client_md_md5 = NULL; + (*state)->gnutls_internals.client_md_sha1 = NULL; + (*state)->gnutls_internals.server_md_md5 = NULL; + (*state)->gnutls_internals.server_md_sha1 = NULL; + (*state)->gnutls_internals.server_hash = 0; + (*state)->gnutls_internals.client_hash = 0; + (*state)->gnutls_internals.resumable = RESUME_TRUE; +} + +int gnutls_deinit(GNUTLS_STATE * state) +{ + gnutls_free((*state)->connection_state.read_compression_state); + gnutls_free((*state)->connection_state.read_mac_secret); + gnutls_free((*state)->connection_state.write_compression_state); + gnutls_free((*state)->connection_state.write_mac_secret); + + gnutls_free((*state)->gnutls_internals.buffer); + + if ((*state)->connection_state.read_cipher_state != NULL) + gcry_cipher_close((*state)->connection_state. + read_cipher_state); + if ((*state)->connection_state.write_cipher_state != NULL) + gcry_cipher_close((*state)->connection_state. + write_cipher_state); + + secure_free((*state)->cipher_specs.server_write_mac_secret); + secure_free((*state)->cipher_specs.client_write_mac_secret); + secure_free((*state)->cipher_specs.server_write_IV); + secure_free((*state)->cipher_specs.client_write_IV); + secure_free((*state)->cipher_specs.server_write_key); + secure_free((*state)->cipher_specs.client_write_key); + + gnutls_free(*state); +} + +/* Produces "total_bytes" bytes using the hash algorithm specified. + * (used in the PRF function) + */ +svoid *gnutls_P_hash(hashid algorithm, opaque * secret, int secret_size, + opaque * seed, int seed_size, int total_bytes) +{ + + MHASH td1, td2; + char *ret = secure_malloc(total_bytes); + void *A; + int i = 0, times, copy_bytes = 0, how; + void *final; + + do { + i += mhash_get_block_size(algorithm); + } while (i < total_bytes); + + A = seed; + times = i / mhash_get_block_size(algorithm); + + for (i = 0; i < times; i++) { + td2 = + mhash_hmac_init(algorithm, secret, secret_size, + mhash_get_hash_pblock(algorithm)); + + td1 = + mhash_hmac_init(algorithm, secret, secret_size, + mhash_get_hash_pblock(algorithm)); + mhash(td1, A, seed_size); + + A = mhash_hmac_end(td1); + + mhash(td2, A, mhash_get_block_size(algorithm)); + mhash(td2, seed, seed_size); + final = mhash_hmac_end(td2); + + copy_bytes = mhash_get_block_size(algorithm); + if ((i + 1) * copy_bytes < total_bytes) { + how = mhash_get_block_size(algorithm); + } else { + how = total_bytes - (i) * copy_bytes; + } + + if (how > 0) { + memmove(&ret[i * copy_bytes], final, how); + } + free(final); + if (i > 0) + free(A); + } + + return ret; +} + + +/* The PRF function expands a given secret */ +svoid *gnutls_PRF(opaque * secret, int secret_size, uint8 * label, + int label_size, opaque * seed, int seed_size, + int total_bytes) +{ + int l_s1, l_s2, i, s_seed_size; + char *o1, *o2; + char *s1, *s2; + char *ret; + char *s_seed; + + /* label+seed = s_seed */ + s_seed_size = seed_size + label_size; + s_seed = gnutls_malloc(s_seed_size); + memmove(s_seed, label, label_size); + memmove(&s_seed[label_size], seed, seed_size); + + + if (secret_size % 2 == 0) { + l_s1 = l_s2 = secret_size / 2; + s1 = &secret[0]; + s2 = &secret[l_s1 + 1]; + } else { + l_s1 = l_s2 = (secret_size / 2) + 1; + s1 = &secret[0]; + s2 = &secret[l_s1]; + } + + o1 = + gnutls_P_hash(MHASH_MD5, s1, l_s1, s_seed, s_seed_size, + total_bytes); + o2 = + gnutls_P_hash(MHASH_SHA1, s2, l_s2, s_seed, s_seed_size, + total_bytes); + + ret = secure_malloc(total_bytes); + gnutls_free(s_seed); + for (i = 0; i < total_bytes; i++) { + ret[i] = o1[i] ^ o2[i]; + } + + secure_free(o1); + secure_free(o2); + + return ret; + +} + +/* if master_secret, client_random and server_random have been initialized, + * this function creates the keys and stores them into state->cipher_specs + */ +int _gnutls_set_keys(GNUTLS_STATE state) +{ + char *key_block; + char keyexp[] = "key expansion"; + char *random = gnutls_malloc(64); + int hash_size; + int IV_size; + int key_size; + + hash_size = state->security_parameters.hash_size; + IV_size = state->security_parameters.IV_size; + key_size = state->security_parameters.key_material_length; + + memmove(random, state->security_parameters.server_random, 32); + memmove(&random[32], state->security_parameters.client_random, 32); + + key_block = + gnutls_PRF(state->security_parameters.master_secret, 48, + keyexp, strlen(keyexp), random, 64, + 2 * hash_size + 2 * key_size + 2 * IV_size); + + state->cipher_specs.client_write_mac_secret = + secure_malloc(hash_size); + memmove(state->cipher_specs.client_write_mac_secret, &key_block[0], + hash_size); + + state->cipher_specs.server_write_mac_secret = + secure_malloc(hash_size); + memmove(state->cipher_specs.server_write_mac_secret, + &key_block[hash_size], hash_size); + + state->cipher_specs.client_write_key = secure_malloc(key_size); + memmove(state->cipher_specs.client_write_key, + &key_block[2 * hash_size], key_size); + + state->cipher_specs.server_write_key = secure_malloc(key_size); + memmove(state->cipher_specs.server_write_key, + &key_block[2 * hash_size + key_size], key_size); + + state->cipher_specs.client_write_IV = secure_malloc(IV_size); + memmove(state->cipher_specs.client_write_IV, + &key_block[2 * key_size + 2 * hash_size], IV_size); + + state->cipher_specs.server_write_IV = secure_malloc(IV_size); + memmove(state->cipher_specs.server_write_IV, + &key_block[2 * hash_size + 2 * key_size + IV_size], + IV_size); + + secure_free(key_block); + return 0; +} + +int _gnutls_send_alert(int cd, GNUTLS_STATE state, AlertLevel level, + AlertDescription desc) +{ + uint8 data[2]; + + memmove(&data[0], &level, 1); + memmove(&data[1], &desc, 1); + + return gnutls_send_int(cd, state, GNUTLS_ALERT, data, 2); + +} + +int gnutls_close(int cd, GNUTLS_STATE state) +{ + int ret; + + ret = + _gnutls_send_alert(cd, state, GNUTLS_WARNING, + GNUTLS_CLOSE_NOTIFY); + + /* receive pending data or the closure alert */ + gnutls_recv_int(cd, state, GNUTLS_ALERT, NULL, 0); + + state->gnutls_internals.valid_connection = VALID_FALSE; + return ret; + +} + +ssize_t gnutls_send_int(int cd, GNUTLS_STATE state, ContentType type, + char *data, size_t sizeofdata) +{ + GNUTLSPlaintext *gtxt; + GNUTLSCompressed *gcomp; + GNUTLSCiphertext *gcipher; + int iterations, i, err; + uint16 length; + int ret = 0, Size; + + + if (sizeofdata == 0) + return 0; + if (state->gnutls_internals.valid_connection == VALID_FALSE) + return GNUTLS_E_INVALID_SESSION; + + if (sizeofdata < 16384) { + iterations = 1; + Size = sizeofdata; + } else { + iterations = sizeofdata / 16384; + Size = 16384; + } + for (i = 0; i < iterations; i++) { + err = + _gnutls_text2TLSPlaintext(type, >xt, &data[i * Size], + Size); + if (err < 0) { + /*gnutls_perror(err); */ + return err; + } + + err = + _gnutls_TLSPlaintext2TLSCompressed(state, &gcomp, + gtxt); + if (err < 0) { + /*gnutls_perror(err); */ + return err; + } + + _gnutls_freeTLSPlaintext(gtxt); + + err = + _gnutls_TLSCompressed2TLSCiphertext(state, &gcipher, + gcomp); + if (err < 0) { + /*gnutls_perror(err); */ + return err; + } + + _gnutls_freeTLSCompressed(gcomp); + + if (Write(cd, &gcipher->type, sizeof(ContentType)) != + sizeof(ContentType)) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + + if (Write(cd, &gcipher->version.major, 1) != 1) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + + if (Write(cd, &gcipher->version.minor, 1) != 1) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } +#ifdef WORDS_BIGENDIAN + length = gcipher->length; +#else + length = byteswap16(gcipher->length); +#endif + if (Write(cd, &length, sizeof(uint16)) != sizeof(uint16)) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + + if (Write(cd, gcipher->fragment, gcipher->length) != + gcipher->length) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + state->connection_state.write_sequence_number++; + ret += Size; + + _gnutls_freeTLSCiphertext(gcipher); + } + /* rest data */ + if (iterations > 1) { + Size = sizeofdata % 16384; + err = + _gnutls_text2TLSPlaintext(type, >xt, &data[ret], + Size); + if (err < 0) { + /*gnutls_perror(err); */ + return err; + } + + err = + _gnutls_TLSPlaintext2TLSCompressed(state, &gcomp, + gtxt); + if (err < 0) { + /*gnutls_perror(err); */ + return err; + } + + _gnutls_freeTLSPlaintext(gtxt); + + err = + _gnutls_TLSCompressed2TLSCiphertext(state, &gcipher, + gcomp); + if (err < 0) { + /*gnutls_perror(err); */ + return err; + } + _gnutls_freeTLSCompressed(gcomp); +#ifdef WORDS_BIGENDIAN + length = gcipher->length; +#else + length = byteswap16(gcipher->length); +#endif + if (Write(cd, &gcipher->type, sizeof(ContentType)) != + sizeof(ContentType)) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + if (Write(cd, &gcipher->version.major, 1) != 1) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + if (Write(cd, &gcipher->version.minor, 1) != 1) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + if (Write(cd, &length, sizeof(uint16)) != sizeof(uint16)) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + if (Write(cd, gcipher->fragment, gcipher->length) != + gcipher->length) { + state->gnutls_internals.valid_connection = + VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNABLE_SEND_DATA; + } + state->connection_state.write_sequence_number++; + ret += Size; + + _gnutls_freeTLSCiphertext(gcipher); + } + + return ret; +} + + +ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, + char *data, size_t sizeofdata) +{ + GNUTLSPlaintext *gtxt; + GNUTLSCompressed *gcomp; + GNUTLSCiphertext gcipher; + int iterations, i, err; + uint8 *tmpdata; + int tmplen; + int ret = 0; + + /* If we have enough data in the cache do not bother receiving + * a new packet. (in order to flush the cache) + */ + if (type == GNUTLS_APPLICATION_DATA && gnutls_getDataBufferSize(type, state) > 0) { + ret = gnutls_getDataFromBuffer(state, data, sizeofdata); + return ret; + } + + if (state->gnutls_internals.valid_connection == VALID_FALSE) + return GNUTLS_E_INVALID_SESSION; + + if (Read(cd, &gcipher.type, sizeof(ContentType)) != + sizeof(ContentType)) { + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (Read(cd, &gcipher.version.major, 1) != 1) { + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (Read(cd, &gcipher.version.minor, 1) != 1) { + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (gcipher.version.major != GNUTLS_VERSION_MAJOR + || gcipher.version.minor != GNUTLS_VERSION_MINOR) { + + _gnutls_send_alert(cd, state, GNUTLS_FATAL, + GNUTLS_PROTOCOL_VERSION); + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + + if (Read(cd, &gcipher.length, 2) != 2) { + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } +#ifndef WORDS_BIGENDIAN + gcipher.length = byteswap16(gcipher.length); +#endif + + if (gcipher.length > 18432) { /* 2^14+2048 */ +#ifdef DEBUG + fprintf(stderr, + "Received packet with length: %d\n", + gcipher.length); +#endif + _gnutls_send_alert(cd, state, GNUTLS_FATAL, + GNUTLS_RECORD_OVERFLOW); + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + gcipher.fragment = gnutls_malloc(gcipher.length); + + /* read ciphertext */ + + ret = Read(cd, gcipher.fragment, gcipher.length); + + if (ret != gcipher.length) { +#ifdef DEBUG + fprintf(stderr, + "Received packet with length: %d\nExpected %d\n", + ret, gcipher.length); +#endif + gnutls_free(gcipher.fragment); + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (ret = + _gnutls_TLSCiphertext2TLSCompressed(state, + &gcomp, &gcipher) < 0) { + gnutls_free(gcipher.fragment); + if (ret == GNUTLS_E_MAC_FAILED) { + _gnutls_send_alert(cd, state, + GNUTLS_FATAL, + GNUTLS_BAD_RECORD_MAC); + } else { + _gnutls_send_alert(cd, state, + GNUTLS_FATAL, + GNUTLS_DECRYPTION_FAILED); + } + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return ret; + } + gnutls_free(gcipher.fragment); + + if (ret = + _gnutls_TLSCompressed2TLSPlaintext(state, >xt, gcomp) < 0) { + _gnutls_send_alert(cd, state, GNUTLS_FATAL, + GNUTLS_DECOMPRESSION_FAILURE); + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return ret; + } + _gnutls_freeTLSCompressed(gcomp); + + if (ret = _gnutls_TLSPlaintext2text((void *) &tmpdata, gtxt) < 0) { + _gnutls_send_alert(cd, state, GNUTLS_FATAL, + GNUTLS_INTERNAL_ERROR); + state->gnutls_internals.valid_connection = VALID_FALSE; + state->gnutls_internals.resumable = RESUME_FALSE; + return ret; + } + tmplen = gtxt->length; + + _gnutls_freeTLSPlaintext(gtxt); + + if (gcipher.type == type && type == GNUTLS_APPLICATION_DATA) { + gnutls_insertDataBuffer(state, tmpdata, tmplen); + } else { + switch (gcipher.type) { + case GNUTLS_ALERT: +#ifdef DEBUG + fprintf(stderr, + "Alert[%d|%d] was received\n", + tmpdata[0], tmpdata[1]); +#endif + state->gnutls_internals.last_alert = tmpdata[1]; + + if (tmpdata[1] == + GNUTLS_CLOSE_NOTIFY + && tmpdata[0] != GNUTLS_FATAL) { + /* If we have been expecting for an alert do not call close() */ + if (type != GNUTLS_ALERT) + gnutls_close(cd, state); + return GNUTLS_E_CLOSURE_ALERT_RECEIVED; + } else { + if (tmpdata[0] == GNUTLS_FATAL) { + state->gnutls_internals. + valid_connection = VALID_FALSE; + + state->gnutls_internals.resumable + = RESUME_FALSE; + return + GNUTLS_E_FATAL_ALERT_RECEIVED; + } + return GNUTLS_E_WARNING_ALERT_RECEIVED; + } + break; + + case GNUTLS_CHANGE_CIPHER_SPEC: + + if (type != GNUTLS_CHANGE_CIPHER_SPEC) { + return GNUTLS_E_UNEXPECTED_PACKET; + } + if (((ChangeCipherSpecType) + tmpdata[0]) == GNUTLS_TYPE_CHANGE_CIPHER_SPEC && tmplen == 1) { + ret = 0; // _gnutls_connection_state_init(state); + + } else { + state->gnutls_internals.valid_connection + = VALID_FALSE; + state->gnutls_internals.resumable = + RESUME_FALSE; + ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + state->connection_state.read_sequence_number++; + return ret; + + case GNUTLS_HANDSHAKE: + + if (type == GNUTLS_HANDSHAKE) { + ret = + _gnutls_recv_handshake_int + (cd, state, tmpdata, tmplen, data, sizeofdata); + + gnutls_free(tmpdata); + state-> + connection_state.read_sequence_number++; + } else { + + ret = GNUTLS_E_RECEIVED_BAD_MESSAGE; + } + return ret; + } + } + + + + /* Incread sequence number */ + state->connection_state.read_sequence_number++; + + + + /* Insert Application data to buffer */ + if (type == GNUTLS_APPLICATION_DATA && gcipher.type==type) { + ret = gnutls_getDataFromBuffer(state, data, sizeofdata); + gnutls_free(tmpdata); + } else { + if (gcipher.type != type) { + return GNUTLS_E_RECEIVED_BAD_MESSAGE; +#ifdef DEBUG + fprintf(stderr, + "Received unexpected packet type\n"); +#endif + } + /* this is an error because we have messages of fixed + * length */ + + ret = GNUTLS_E_RECEIVED_BAD_MESSAGE; + } + + return ret; +} + +int _gnutls_send_change_cipher_spec(int cd, GNUTLS_STATE state) +{ + ChangeCipherSpecType x = GNUTLS_TYPE_CHANGE_CIPHER_SPEC; + + return gnutls_send_int(cd, state, GNUTLS_CHANGE_CIPHER_SPEC, + (void *) &x, 1); +} diff --git a/lib/gnutls.h b/lib/gnutls.h new file mode 100644 index 0000000000..fd8f4247af --- /dev/null +++ b/lib/gnutls.h @@ -0,0 +1,42 @@ +enum ContentType { GNUTLS_APPLICATION_DATA=23 }; +typedef enum ContentType ContentType; +enum BulkCipherAlgorithm { CIPHER_NULL, CIPHER_3DES = 4 }; +typedef enum BulkCipherAlgorithm BulkCipherAlgorithm; +enum MACAlgorithm { MAC_NULL, MAC_MD5, MAC_SHA }; +typedef enum MACAlgorithm MACAlgorithm; +enum CompressionMethod { COMPRESSION_NULL }; +typedef enum CompressionMethod CompressionMethod; +enum ConnectionEnd { GNUTLS_SERVER, GNUTLS_CLIENT }; +typedef enum ConnectionEnd ConnectionEnd; + +struct GNUTLS_STATE_INT; +typedef struct GNUTLS_STATE_INT* GNUTLS_STATE; + +int gnutls_init(GNUTLS_STATE * state, ConnectionEnd con_end); +int gnutls_deinit(GNUTLS_STATE * state); +ssize_t gnutls_send_int(int cd, GNUTLS_STATE state, ContentType type, char* data, size_t sizeofdata); +ssize_t gnutls_recv_int(int cd, GNUTLS_STATE state, ContentType type, char* data, size_t sizeofdata); +int gnutls_close(int cd, GNUTLS_STATE state); +int gnutls_handshake(int cd, GNUTLS_STATE state); + +#define gnutls_send( x, y, z, w) gnutls_send_int( x, y, GNUTLS_APPLICATION_DATA, z, w) +#define gnutls_recv( x, y, z, w) gnutls_recv_int( x, y, GNUTLS_APPLICATION_DATA, z, w) + + +#define GNUTLS_E_MAC_FAILED -1 +#define GNUTLS_E_UNKNOWN_CIPHER -2 +#define GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM -3 +#define GNUTLS_E_UNKNOWN_MAC_ALGORITHM -4 +#define GNUTLS_E_UNKNOWN_ERROR -5 +#define GNUTLS_E_UNKNOWN_CIPHER_TYPE -6 +#define GNUTLS_E_LARGE_PACKET -7 +#define GNUTLS_E_UNSUPPORTED_VERSION_PACKET -8 +#define GNUTLS_E_UNEXPECTED_PACKET_LENGTH -9 +#define GNUTLS_E_INVALID_SESSION -10 +#define GNUTLS_E_UNABLE_SEND_DATA -11 +#define GNUTLS_E_FATAL_ALERT_RECEIVED -12 +#define GNUTLS_E_RECEIVED_BAD_MESSAGE -13 +#define GNUTLS_E_RECEIVED_MORE_DATA -14 +#define GNUTLS_E_UNEXPECTED_PACKET -15 +#define GNUTLS_E_WARNING_ALERT_RECEIVED -16 +#define GNUTLS_E_CLOSURE_ALERT_RECEIVED -17 diff --git a/lib/gnutls_algorithms.c b/lib/gnutls_algorithms.c new file mode 100644 index 0000000000..d3de151836 --- /dev/null +++ b/lib/gnutls_algorithms.c @@ -0,0 +1,161 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_algorithms.h" + + +void tolow(char *str, int size) +{ + int i; + + for (i = 0; i < size; i++) { + str[i] = tolower(str[i]); + } +} + + +int _gnutls_cipher_get_block_size(BulkCipherAlgorithm algorithm) +{ + size_t ret = 0; + GNUTLS_ALG_LOOP(ret = p->blocksize); + return ret; + +} + +int _gnutls_cipher_is_block(BulkCipherAlgorithm algorithm) +{ + size_t ret = 0; + + GNUTLS_ALG_LOOP(ret = p->block); + return ret; + +} + +int _gnutls_cipher_get_key_size(BulkCipherAlgorithm algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_ALG_LOOP(ret = p->keysize); + return ret; + +} + +int _gnutls_cipher_get_iv_size(BulkCipherAlgorithm algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_ALG_LOOP(ret = p->iv); + return ret; + +} + +char *_gnutls_cipher_get_name(BulkCipherAlgorithm algorithm) +{ + char *ret = NULL; + char *pointerTo_; + + /* avoid prefix */ + GNUTLS_ALG_LOOP(ret = strdup(p->name + sizeof("CIPHER_") - 1)); + + + if (ret != NULL) { + tolow(ret, strlen(ret)); + pointerTo_ = strchr(ret, '_'); + + while (pointerTo_ != NULL) { + *pointerTo_ = '-'; + pointerTo_ = strchr(ret, '_'); + } + } + return ret; +} + + +int _gnutls_cipher_is_ok(BulkCipherAlgorithm algorithm) +{ + char *y = _gnutls_cipher_get_name(algorithm); + + if (y != NULL) { + free(y); + return 0; + } else { + return 1; + } + +} + + + +int _gnutls_kx_algo_server_certificate(KX_Algorithm algorithm) +{ + size_t ret = 0; + GNUTLS_KX_ALG_LOOP(ret = p->server_cert); + return ret; + +} + +int _gnutls_kx_algo_server_key_exchange(KX_Algorithm algorithm) +{ + size_t ret = 0; + + GNUTLS_KX_ALG_LOOP(ret = p->server_kx); + return ret; + +} + +int _gnutls_kx_algo_client_certificate(KX_Algorithm algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_KX_ALG_LOOP(ret = p->client_cert); + return ret; + +} + +int _gnutls_kx_algo_RSA_premaster(KX_Algorithm algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_KX_ALG_LOOP(ret = p->RSA_premaster); + return ret; + +} + +int _gnutls_kx_algo_DH_public_value(KX_Algorithm algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_KX_ALG_LOOP(ret = p->DH_public_value); + return ret; + +} + +char *_gnutls_kx_algo_get_name(KX_Algorithm algorithm) +{ + char *ret = NULL; + char *pointerTo_; + + /* avoid prefix */ + GNUTLS_KX_ALG_LOOP(ret = strdup(p->name + sizeof("KX_") - 1)); + + + if (ret != NULL) { + tolow(ret, strlen(ret)); + pointerTo_ = strchr(ret, '_'); + + while (pointerTo_ != NULL) { + *pointerTo_ = '-'; + pointerTo_ = strchr(ret, '_'); + } + } + return ret; +} + + +int _gnutls_kx_algo_is_ok(KX_Algorithm algorithm) +{ + char *y = _gnutls_kx_algo_get_name(algorithm); + + if (y != NULL) { + free(y); + return 0; + } else { + return 1; + } + +} diff --git a/lib/gnutls_algorithms.h b/lib/gnutls_algorithms.h new file mode 100644 index 0000000000..7bc613ad16 --- /dev/null +++ b/lib/gnutls_algorithms.h @@ -0,0 +1,76 @@ +#define GNUTLS_CIPHER_ENTRY(name, blksize, keysize, block, iv) \ + { #name, name, blksize, keysize, block, iv } + +struct gnutls_cipher_entry { + char *name; + BulkCipherAlgorithm id; + size_t blocksize; + size_t keysize; + size_t block; + size_t iv; +}; +typedef struct gnutls_cipher_entry gnutls_cipher_entry; + +static gnutls_cipher_entry algorithms[] = { + GNUTLS_CIPHER_ENTRY(CIPHER_3DES, 8, 24, 1, 8), + GNUTLS_CIPHER_ENTRY(CIPHER_NULL, 1, 0, 0, 0), + {0} +}; + +#define GNUTLS_LOOP(b) \ + gnutls_cipher_entry *p; \ + for(p = algorithms; p->name != NULL; p++) { b ; } + +#define GNUTLS_ALG_LOOP(a) \ + GNUTLS_LOOP( if(p->id == algorithm) { a; break; } ) + + + + +#define GNUTLS_KX_ALGO_ENTRY(name, server_cert, server_kx, client_cert, RSA_premaster, DH_public_value) \ + { #name, name, server_cert, server_kx, client_cert, RSA_premaster, DH_public_value } + +struct gnutls_kx_algo_entry { + char *name; + KX_Algorithm algorithm; + int server_cert; + int server_kx; + int client_cert; + int RSA_premaster; + int DH_public_value; +}; +typedef struct gnutls_kx_algo_entry gnutls_kx_algo_entry; + +static gnutls_kx_algo_entry kx_algorithms[] = { + GNUTLS_KX_ALGO_ENTRY( KX_ANON_DH, 0, 1, 0, 0, 1), + GNUTLS_KX_ALGO_ENTRY( KX_RSA , 1, 0, 1, 1, 0), + GNUTLS_KX_ALGO_ENTRY( KX_DHE_DSS, 1, 1, 1, 0, 0), + GNUTLS_KX_ALGO_ENTRY( KX_DHE_RSA, 1, 1, 1, 0, 0), + GNUTLS_KX_ALGO_ENTRY( KX_DH_DSS , 1, 0, 1, 0, 0), + GNUTLS_KX_ALGO_ENTRY( KX_DH_RSA , 1, 0, 1, 0, 0), + {0} +}; + +#define GNUTLS_KX_LOOP(b) \ + gnutls_kx_algo_entry *p; \ + for(p = kx_algorithms; p->name != NULL; p++) { b ; } + +#define GNUTLS_KX_ALG_LOOP(a) \ + GNUTLS_KX_LOOP( if(p->algorithm == algorithm) { a; break; } ) + + +int _gnutls_cipher_get_block_size(BulkCipherAlgorithm algorithm); +int _gnutls_cipher_is_block(BulkCipherAlgorithm algorithm); +int _gnutls_cipher_is_ok(BulkCipherAlgorithm algorithm); +int _gnutls_cipher_get_key_size(BulkCipherAlgorithm algorithm); +int _gnutls_cipher_get_iv_size(BulkCipherAlgorithm algorithm); +char *_gnutls_cipher_get_name(BulkCipherAlgorithm algorithm); + + +int _gnutls_kx_get_block_size(KX_Algorithm algorithm); +int _gnutls_kx_is_block(KX_Algorithm algorithm); +int _gnutls_kx_is_ok(KX_Algorithm algorithm); +int _gnutls_kx_get_key_size(KX_Algorithm algorithm); +int _gnutls_kx_get_iv_size(KX_Algorithm algorithm); +char *_gnutls_kx_get_name(KX_Algorithm algorithm); + diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c new file mode 100644 index 0000000000..9b8b3fd6f1 --- /dev/null +++ b/lib/gnutls_buffers.c @@ -0,0 +1,88 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" + +int gnutls_insertDataBuffer(GNUTLS_STATE state, char *data, int length) +{ + int old_buffer = state->gnutls_internals.bufferSize; + + state->gnutls_internals.bufferSize += length; + state->gnutls_internals.buffer = + gnutls_realloc(state->gnutls_internals.buffer, + state->gnutls_internals.bufferSize); + memmove(&state->gnutls_internals.buffer[old_buffer], data, length); + + return 0; + +} + +int gnutls_getDataBufferSize(ContentType type, GNUTLS_STATE state) +{ + if (type == GNUTLS_APPLICATION_DATA) + return state->gnutls_internals.bufferSize; + return 0; +} + +int gnutls_getDataFromBuffer(GNUTLS_STATE state, char *data, int length) +{ + if (length > state->gnutls_internals.bufferSize) { + length = state->gnutls_internals.bufferSize; + } + + state->gnutls_internals.bufferSize -= length; + memmove(data, state->gnutls_internals.buffer, length); + + /* overwrite buffer */ + memmove(state->gnutls_internals.buffer, + &state->gnutls_internals.buffer[length], + state->gnutls_internals.bufferSize); + state->gnutls_internals.buffer = + gnutls_realloc(state->gnutls_internals.buffer, + state->gnutls_internals.bufferSize); + + return length; +} + +ssize_t Read(int fd, void *iptr, size_t sizeOfPtr) +{ + size_t left; + ssize_t i=0; + char *ptr = iptr; + + left = sizeOfPtr; + while (left > 0) { + i = read(fd, &ptr[i], left); + if (i < 0) { + return -1; + } else { + if (i == 0) + break; /* EOF */ + } + + left -= i; + + } + + return (sizeOfPtr - left); +} + + +ssize_t Write(int fd, const void *iptr, size_t n) +{ + size_t left; + ssize_t i = 0; + const char *ptr = iptr; + + left = n; + while (left > 0) { + i = write(fd, &ptr[i], left); + if (i <= 0) { + return -1; + } + left -= i; + } + + return n; + +} diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h new file mode 100644 index 0000000000..24ad57b878 --- /dev/null +++ b/lib/gnutls_buffers.h @@ -0,0 +1,5 @@ +int gnutls_insertDataBuffer(GNUTLS_STATE state, char *data, int length); +int gnutls_getDataBufferSize(ContentType type, GNUTLS_STATE state); +int gnutls_getDataFromBuffer(GNUTLS_STATE state, char *data, int length); +ssize_t Read(int fd, void *iptr, size_t n); +ssize_t Write(int fd, const void *iptr, size_t n); diff --git a/lib/gnutls_cipher.c b/lib/gnutls_cipher.c new file mode 100644 index 0000000000..855ab27647 --- /dev/null +++ b/lib/gnutls_cipher.c @@ -0,0 +1,589 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_compress.h" +#include "gnutls_cipher.h" +#include "gnutls_algorithms.h" + +#define MD5_DIGEST 16 +#define SHA_DIGEST 20 + +/* Sets the specified cipher into the pending state */ +int _gnutls_set_cipher(GNUTLS_STATE state, BulkCipherAlgorithm algo) +{ + + if (_gnutls_cipher_is_ok(algo) == 0) { + state->security_parameters.bulk_cipher_algorithm = algo; + if (_gnutls_cipher_is_block(algo) == 0) { + state->security_parameters.cipher_type = + CIPHER_BLOCK; + } else { + state->security_parameters.cipher_type = + CIPHER_STREAM; + } + state->security_parameters.is_exportable = + EXPORTABLE_FALSE; + state->security_parameters.key_material_length = + state->security_parameters.key_size = + _gnutls_cipher_get_key_size(algo); + state->security_parameters.IV_size = + _gnutls_cipher_get_iv_size(algo); + } else { + return GNUTLS_E_UNKNOWN_CIPHER; + } + + return 0; + +} + +/* Sets the specified algorithm into pending compression state */ +int _gnutls_set_compression(GNUTLS_STATE state, CompressionMethod algo) +{ + + switch (algo) { + case COMPRESSION_NULL: + break; + + default: + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + return 0; + +} + +/* Sets the specified mac algorithm into pending state */ +int _gnutls_set_mac(GNUTLS_STATE state, MACAlgorithm algo) +{ + + switch (algo) { + case MAC_NULL: + state->security_parameters.mac_algorithm = MAC_NULL; + state->security_parameters.hash_size = 0; + break; + + case MAC_MD5: + state->security_parameters.mac_algorithm = MAC_MD5; + state->security_parameters.hash_size = MD5_DIGEST; + break; + + case MAC_SHA: + state->security_parameters.mac_algorithm = MAC_SHA; + state->security_parameters.hash_size = SHA_DIGEST; + break; + + default: + return GNUTLS_E_UNKNOWN_MAC_ALGORITHM; + } + + return 0; + +} + +/* Sets the current connection state to conform with the + * Security parameters(pending state), and initializes encryption. + */ +int _gnutls_connection_state_init(GNUTLS_STATE state) +{ + int rc; + + gnutls_free(state->connection_state.write_mac_secret); + gnutls_free(state->connection_state.read_mac_secret); + + if (state->connection_state.read_cipher_state != NULL) + gcry_cipher_close(state->connection_state. + read_cipher_state); + + if (state->connection_state.write_cipher_state != NULL) + gcry_cipher_close(state->connection_state. + write_cipher_state); + + gnutls_free(state->connection_state.read_compression_state); + gnutls_free(state->connection_state.write_compression_state); + + switch (state->security_parameters.compression_algorithm) { + case COMPRESSION_NULL: + state->connection_state.read_compression_state = NULL; + state->connection_state.write_compression_state = NULL; + break; + default: + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + switch (state->security_parameters.mac_algorithm) { + case MAC_NULL: + state->connection_state.read_mac_secret = NULL; + state->connection_state.write_mac_secret = NULL; + state->connection_state.mac_secret_size = 0; + break; + case MAC_MD5: + state->connection_state.read_mac_secret = + gnutls_malloc(MD5_DIGEST); + state->connection_state.write_mac_secret = + gnutls_malloc(MD5_DIGEST); + state->connection_state.mac_secret_size = MD5_DIGEST; + break; + case MAC_SHA: + state->connection_state.read_mac_secret = + gnutls_malloc(SHA_DIGEST); + state->connection_state.write_mac_secret = + gnutls_malloc(SHA_DIGEST); + state->connection_state.mac_secret_size = SHA_DIGEST; + break; + default: + return GNUTLS_E_UNKNOWN_MAC_ALGORITHM; + } + + switch (state->security_parameters.bulk_cipher_algorithm) { + case CIPHER_NULL: + state->connection_state.read_cipher_state = NULL; + state->connection_state.write_cipher_state = NULL; + break; + case CIPHER_3DES: + state->connection_state.read_cipher_state = + gcry_cipher_open(GCRY_CIPHER_3DES, + GCRY_CIPHER_MODE_CBC, 0); + state->connection_state.write_cipher_state = + gcry_cipher_open(GCRY_CIPHER_3DES, + GCRY_CIPHER_MODE_CBC, 0); + break; + default: + return GNUTLS_E_UNKNOWN_CIPHER; + } + + + switch (state->security_parameters.entity) { + case GNUTLS_SERVER: + if (state->connection_state.write_cipher_state != NULL) { + rc = + gcry_cipher_setkey(state->connection_state. + write_cipher_state, + state->cipher_specs. + server_write_key, + state->security_parameters. + key_size); + gcry_cipher_setiv(state->connection_state. + write_cipher_state, + state->cipher_specs. + server_write_IV, + state->security_parameters. + IV_size); + } + if (state->connection_state.mac_secret_size > 0) { + memmove(state->connection_state.read_mac_secret, + state->cipher_specs. + client_write_mac_secret, + state->connection_state.mac_secret_size); + memmove(state->connection_state.write_mac_secret, + state->cipher_specs. + server_write_mac_secret, + state->connection_state.mac_secret_size); + } + + if (state->connection_state.read_cipher_state != NULL) { + rc = + gcry_cipher_setkey(state->connection_state. + read_cipher_state, + state->cipher_specs. + client_write_key, + state->security_parameters. + key_size); + gcry_cipher_setiv(state->connection_state. + read_cipher_state, + state->cipher_specs. + client_write_IV, + state->security_parameters. + IV_size); + } + break; + + case GNUTLS_CLIENT: + if (state->connection_state.read_cipher_state != NULL) { + rc = + gcry_cipher_setkey(state->connection_state. + read_cipher_state, + state->cipher_specs. + server_write_key, + state->security_parameters. + key_size); + gcry_cipher_setiv(state->connection_state. + read_cipher_state, + state->cipher_specs. + server_write_IV, + state->security_parameters. + IV_size); + } + if (state->connection_state.mac_secret_size > 0) { + memmove(state->connection_state.read_mac_secret, + state->cipher_specs. + server_write_mac_secret, + state->connection_state.mac_secret_size); + memmove(state->connection_state.write_mac_secret, + state->cipher_specs. + client_write_mac_secret, + state->connection_state.mac_secret_size); + } + + if (state->connection_state.write_cipher_state != NULL) { + gcry_cipher_setiv(state->connection_state. + write_cipher_state, + state->cipher_specs. + client_write_IV, + state->security_parameters. + IV_size); + rc = + gcry_cipher_setkey(state->connection_state. + write_cipher_state, + state->cipher_specs. + client_write_key, + state->security_parameters. + key_size); + } + break; + + default: + return GNUTLS_E_UNKNOWN_ERROR; + } + + return 0; +} + +int _gnutls_TLSCompressed2TLSCiphertext(GNUTLS_STATE state, + GNUTLSCiphertext ** + cipher, + GNUTLSCompressed * compressed) +{ + GNUTLSCiphertext *ciphertext; + uint8 *padding, *content, *MAC; + uint8 padding_length; + uint16 c_length; + int rc; + uint8 *data; + uint8 *macpointer; + uint8 pad; + uint8 *rand; + uint64 seq_num; + int length; + MHASH td; + + + content = gnutls_malloc(compressed->length); + memmove(content, compressed->fragment, compressed->length); + +/* if (state->connection_state.mac_secret_size>0) { + MAC = gnutls_malloc(state->connection_state.mac_secret_size); + }*/ + + *cipher = gnutls_malloc(sizeof(GNUTLSCiphertext)); + ciphertext = *cipher; + + switch (state->security_parameters.mac_algorithm) { + case MAC_NULL: + td = MHASH_FAILED; + break; + case MAC_SHA: + td = + mhash_hmac_init(MHASH_SHA1, + state->connection_state. + write_mac_secret, + state->connection_state. + mac_secret_size, + mhash_get_hash_pblock(MHASH_SHA1)); + break; + case MAC_MD5: + td = + mhash_hmac_init(MHASH_MD5, + state->connection_state. + write_mac_secret, + state->connection_state. + mac_secret_size, + mhash_get_hash_pblock(MHASH_MD5)); + break; + default: + gnutls_free(*cipher); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_MAC_ALGORITHM; + } + +#ifdef WORDS_BIGENDIAN + seq_num = state->connection_state.write_sequence_number; + c_length = compressed->length; +#else + seq_num = + byteswap64(state->connection_state.write_sequence_number); + c_length = byteswap16(compressed->length); +#endif + if (td != MHASH_FAILED) { + mhash(td, &seq_num, 8); + mhash(td, &compressed->type, 1); + mhash(td, &compressed->version.major, 1); + mhash(td, &compressed->version.minor, 1); + mhash(td, &c_length, 2); + mhash(td, &compressed->fragment, compressed->length); + MAC = mhash_hmac_end(td); + } + switch (state->security_parameters.cipher_type) { + case CIPHER_STREAM: + switch (state->security_parameters.bulk_cipher_algorithm) { + case CIPHER_NULL: + length = + compressed->length + + state->connection_state.mac_secret_size; + data = gnutls_malloc(length); + memmove(data, content, compressed->length); + memmove(&data[compressed->length], MAC, + state->connection_state.mac_secret_size); + ciphertext->fragment = data; + ciphertext->length = length; + ciphertext->type = compressed->type; + ciphertext->version.major = + compressed->version.major; + ciphertext->version.minor = + compressed->version.minor; + break; + default: + gnutls_free(*cipher); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_CIPHER; + + } + break; + case CIPHER_BLOCK: + switch (state->security_parameters.bulk_cipher_algorithm) { + case CIPHER_3DES: + + rand = gcry_random_bytes(1, GCRY_STRONG_RANDOM); + length = + compressed->length + + state->connection_state.mac_secret_size + + rand[0] + 1; + length = + (length / + _gnutls_cipher_get_block_size(CIPHER_3DES)) * + _gnutls_cipher_get_block_size(CIPHER_3DES); + pad = + length - compressed->length - + state->connection_state.mac_secret_size - 1; + + /* set pad bytes pad */ + padding = gnutls_malloc(pad); + memset(padding, pad, pad); + padding_length = pad; + + data = gnutls_malloc(length); + memmove(data, content, compressed->length); + memmove(&data[compressed->length], MAC, + state->connection_state.mac_secret_size); + memmove(&data + [state->connection_state.mac_secret_size + + compressed->length], padding, pad); + memmove(&data + [pad + + state->connection_state.mac_secret_size + + compressed->length], &padding_length, 1); + + gnutls_free(padding); + + gcry_cipher_encrypt(state->connection_state. + write_cipher_state, data, + length, data, length); + + ciphertext->fragment = data; + ciphertext->length = length; + ciphertext->type = compressed->type; + ciphertext->version.major = + compressed->version.major; + ciphertext->version.minor = + compressed->version.minor; + + gcry_free(rand); + break; + default: + gnutls_free(*cipher); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_CIPHER; + } + break; + default: + gnutls_free(*cipher); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + +// gnutls_free( MAC); + if (td != MHASH_FAILED) + free(MAC); + gnutls_free(content); + + return 0; +} + +int _gnutls_TLSCiphertext2TLSCompressed(GNUTLS_STATE state, + GNUTLSCompressed ** + compress, + GNUTLSCiphertext * ciphertext) +{ + GNUTLSCompressed *compressed; + uint8 *content, *MAC; + uint16 c_length; + int rc; + uint8 *data; + uint8 *macpointer; + uint8 pad; + uint64 seq_num; + int length; + MHASH td; + + + content = gnutls_malloc(ciphertext->length); + memmove(content, ciphertext->fragment, ciphertext->length); + +/* if (state->connection_state.mac_secret_size>0) { + MAC = gnutls_malloc(state->connection_state.mac_secret_size); + }*/ + + *compress = gnutls_malloc(sizeof(GNUTLSCompressed)); + compressed = *compress; + + + switch (state->security_parameters.mac_algorithm) { + case MAC_NULL: + td = MHASH_FAILED; + break; + case MAC_SHA: + td = + mhash_hmac_init(MHASH_SHA1, + state->connection_state. + read_mac_secret, + state->connection_state. + mac_secret_size, + mhash_get_hash_pblock(MHASH_SHA1)); + break; + case MAC_MD5: + td = + mhash_hmac_init(MHASH_MD5, + state->connection_state. + read_mac_secret, + state->connection_state. + mac_secret_size, + mhash_get_hash_pblock(MHASH_MD5)); + break; + default: + gnutls_free(*compress); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_MAC_ALGORITHM; + } + +#ifdef WORDS_BIGENDIAN + seq_num = state->connection_state.read_sequence_number; + c_length = ciphertext->length; +#else + seq_num = byteswap64(state->connection_state.read_sequence_number); + c_length = byteswap16(ciphertext->length); +#endif + if (td != MHASH_FAILED) { + mhash(td, &seq_num, 8); + mhash(td, &ciphertext->type, 1); + mhash(td, &ciphertext->version.major, 1); + mhash(td, &ciphertext->version.minor, 1); + mhash(td, &c_length, 2); + mhash(td, &ciphertext->fragment, ciphertext->length); + MAC = mhash_hmac_end(td); + } + switch (state->security_parameters.cipher_type) { + case CIPHER_STREAM: + switch (state->security_parameters.bulk_cipher_algorithm) { + case CIPHER_NULL: + length = + ciphertext->length - + state->connection_state.mac_secret_size; + data = gnutls_malloc(length); + memmove(data, content, length); + + /* HMAC was not the same. */ + if (memcmp + (MAC, &data[length], + state->connection_state.mac_secret_size) != 0) + return GNUTLS_E_MAC_FAILED; + + compressed->fragment = data; + compressed->length = length; + compressed->type = ciphertext->type; + compressed->version.major = + ciphertext->version.major; + compressed->version.minor = + ciphertext->version.minor; + break; + default: + gnutls_free(*compress); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_CIPHER; + + } + break; + case CIPHER_BLOCK: + switch (state->security_parameters.bulk_cipher_algorithm) { + case CIPHER_3DES: + + gcry_cipher_decrypt(state->connection_state. + read_cipher_state, content, + ciphertext->length, content, + ciphertext->length); + + pad = content[ciphertext->length - 1]; /* pad */ + length = + ciphertext->length - + state->connection_state.mac_secret_size - pad - + 1; + + /* HMAC was not the same. */ + if (memcmp + (MAC, &data[length], + state->connection_state.mac_secret_size) != 0) + return GNUTLS_E_MAC_FAILED; + + data = gnutls_malloc(length); + memmove(data, content, length); + + compressed->fragment = data; + compressed->length = length; + compressed->type = ciphertext->type; + compressed->version.major = + ciphertext->version.major; + compressed->version.minor = + ciphertext->version.minor; + + break; + default: + gnutls_free(*compress); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_CIPHER; + } + break; + default: + gnutls_free(*compress); + gnutls_free(content); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + +// gnutls_free( MAC); + if (td != MHASH_FAILED) + free(MAC); + gnutls_free(content); + + return 0; +} + + + + +int _gnutls_freeTLSCiphertext(GNUTLSCiphertext * ciphertext) +{ + if (ciphertext == NULL) + return 0; + + gnutls_free(ciphertext->fragment); + gnutls_free(ciphertext); + + return 0; +} diff --git a/lib/gnutls_cipher.h b/lib/gnutls_cipher.h new file mode 100644 index 0000000000..1ba32edab2 --- /dev/null +++ b/lib/gnutls_cipher.h @@ -0,0 +1,15 @@ +int _gnutls_TLSCompressed2TLSCiphertext(GNUTLS_STATE state, + GNUTLSCiphertext** + cipher, + GNUTLSCompressed * + compressed); +int _gnutls_freeTLSCiphertext(GNUTLSCiphertext * ciphertext); +int _gnutls_set_cipher( GNUTLS_STATE state, BulkCipherAlgorithm algo); +int _gnutls_set_mac( GNUTLS_STATE state, MACAlgorithm algo); +int _gnutls_set_compression( GNUTLS_STATE state, CompressionMethod algo); +int _gnutls_connection_state_init(GNUTLS_STATE state); +int _gnutls_TLSCiphertext2TLSCompressed(GNUTLS_STATE state, + GNUTLSCompressed** + compress, + GNUTLSCiphertext * + ciphertext); diff --git a/lib/gnutls_compress.c b/lib/gnutls_compress.c new file mode 100644 index 0000000000..fa0d2bb11b --- /dev/null +++ b/lib/gnutls_compress.c @@ -0,0 +1,79 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_compress.h" +#include "gnutls_errors.h" + +int _gnutls_TLSPlaintext2TLSCompressed(GNUTLS_STATE state, + GNUTLSCompressed ** + compress, + GNUTLSPlaintext * + plaintext) +{ + GNUTLSCompressed *compressed; + + *compress = gnutls_malloc(sizeof(GNUTLSCompressed)); + compressed = *compress; + + switch (state->security_parameters.compression_algorithm) { + case COMPRESSION_NULL: + + compressed->fragment = gnutls_malloc(plaintext->length); + + memmove(compressed->fragment, plaintext->fragment, + plaintext->length); + compressed->length = plaintext->length; + compressed->type = plaintext->type; + compressed->version.major = plaintext->version.major; + compressed->version.minor = plaintext->version.minor; + break; + default: + gnutls_free(*compress); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + return 0; +} + +int _gnutls_TLSCompressed2TLSPlaintext(GNUTLS_STATE state, + GNUTLSPlaintext** + plain, + GNUTLSCompressed * + compressed) +{ + GNUTLSPlaintext *plaintext; + + *plain = gnutls_malloc(sizeof(GNUTLSPlaintext)); + plaintext = *plain; + + switch (state->security_parameters.compression_algorithm) { + case COMPRESSION_NULL: + plaintext->fragment = gnutls_malloc(compressed->length); + memmove(plaintext->fragment, compressed->fragment, + compressed->length); + plaintext->length = compressed->length; + plaintext->type = compressed->type; + plaintext->version.major = compressed->version.major; + plaintext->version.minor = compressed->version.minor; + break; + default: + gnutls_free(*plain); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + return 0; +} + + + + +int _gnutls_freeTLSCompressed(GNUTLSCompressed * compressed) +{ + if (compressed == NULL) + return 0; + + gnutls_free(compressed->fragment); + gnutls_free(compressed); + + return 0; +} diff --git a/lib/gnutls_compress.h b/lib/gnutls_compress.h new file mode 100644 index 0000000000..ef15cac254 --- /dev/null +++ b/lib/gnutls_compress.h @@ -0,0 +1,11 @@ +int _gnutls_freeTLSCompressed(GNUTLSCompressed * compressed); +int _gnutls_TLSPlaintext2TLSCompressed(GNUTLS_STATE state, + GNUTLSCompressed ** + compress, + GNUTLSPlaintext * + plaintext); +int _gnutls_TLSCompressed2TLSPlaintext(GNUTLS_STATE state, + GNUTLSPlaintext** + plain, + GNUTLSCompressed * + compressed); diff --git a/lib/gnutls_dh.c b/lib/gnutls_dh.c new file mode 100644 index 0000000000..754f363dc7 --- /dev/null +++ b/lib/gnutls_dh.c @@ -0,0 +1,662 @@ +#include <defines.h> +#include <gcrypt.h> + +static const byte diffie_hellman_group1_prime[130] = { 0x04, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + +/**************** + * Choose a random value x and calculate e = g^x mod p. + * Return: e and if ret_x is not NULL x. + */ +static MPI +calc_dh_secret( MPI *ret_x ) +{ + MPI e, g, x, prime; + size_t n = sizeof diffie_hellman_group1_prime; + + if( gcry_mpi_scan( &prime, GCRYMPI_FMT_STD, + diffie_hellman_group1_prime, &n ) ) + abort(); + /*dump_mpi(stderr, "prime=", prime );*/ + + g = mpi_set_ui( NULL, 2 ); + x = mpi_new( 200 ); /* FIXME: allocate in secure memory */ + gcry_mpi_randomize( x, 200, GCRY_STRONG_RANDOM ); + /* fixme: set high bit of x and select a larger one */ + + e = mpi_new(1024); + mpi_powm( e, g, x, prime ); + + if( ret_x ) + *ret_x = x; + else + mpi_release(x); + mpi_release(g); + mpi_release(prime); + return e; +} + + + +static void +hash_mpi( GCRY_MD_HD md, MPI a ) +{ + char buf[400]; + size_t n = sizeof buf; + + if( gcry_mpi_print( GCRYMPI_FMT_SSH, buf, &n, a ) ) + fprintf(stderr,"Oops: MPI too large for hashing\n"); + else + gcry_md_write( md, buf, n ); +} + + +static void +hash_bstring( GCRY_MD_HD md, BSTRING a ) +{ + byte buf[4]; + size_t n = a->len; + + buf[0] = n >> 24; + buf[1] = n >> 16; + buf[2] = n >> 8; + buf[3] = n; + gcry_md_write( md, buf, 4 ); + gcry_md_write( md, a->d, n ); +} + + + +static MPI +calc_dh_key( MPI f, MPI x ) +{ + MPI k, prime; + size_t n = sizeof diffie_hellman_group1_prime; + + if( gcry_mpi_scan( &prime, GCRYMPI_FMT_STD, + diffie_hellman_group1_prime, &n ) ) + abort(); + + k = mpi_new( 1024 ); /* FIXME: allocate in secure memory */ + mpi_powm( k, f, x, prime ); + mpi_release(prime); + return k; +} + + + + +/**************** + * calculate the exchange hash value and put it into the handle. + */ +static int +calc_exchange_hash( GSTIHD hd, BSTRING i_c, BSTRING i_s, + BSTRING k_s, MPI e, MPI f ) +{ + GCRY_MD_HD md; + int algo = GCRY_MD_SHA1; + + md = gcry_md_open( algo, 0 ); + if( !md ) + return map_gcry_rc( gcry_errno() ); + + if( hd->we_are_server ) { + BSTRING pp; + hash_bstring( md, hd->peer_version_string ); + pp = make_bstring( host_version_string, strlen(host_version_string) ); + hash_bstring( md, pp ); + gsti_free(pp); + } + else { + BSTRING pp; + pp = make_bstring( host_version_string, strlen(host_version_string) ); + hash_bstring( md, pp ); + gsti_free(pp); + hash_bstring( md, hd->peer_version_string ); + } + hash_bstring( md, i_c ); + hash_bstring( md, i_s ); + hash_bstring( md, k_s ); + hash_mpi( md, e ); + hash_mpi( md, f ); + hash_mpi( md, hd->kex.k ); + + hd->kex.h = make_bstring( gcry_md_read( md, algo ), + gcry_md_get_algo_dlen(algo) ); + if( !hd->session_id ) /* initialize the session id the first time */ + hd->session_id = make_bstring( gcry_md_read( md, algo ), + gcry_md_get_algo_dlen(algo)); + gcry_md_close( md ); +/* dump_hexbuf( stderr, "SesID=", hd->session_id->d, hd->session_id->len );*/ + return 0; +} + + +/* Hmm. We need to have a new_kex structure so that the old + * kex data can be used untip we have send the NEWKEYs msg + * Well, doesn't matter for now. + */ +static BSTRING +construct_one_key( GSTIHD hd, GCRY_MD_HD md1, int algo, + const byte *letter, size_t size ) +{ + BSTRING hash; + GCRY_MD_HD md = gcry_md_copy( md1 ); + size_t n, n1; + + hash = make_bstring( NULL, size ); + gcry_md_write( md, letter, 1 ); + gcry_md_write( md, hd->session_id->d, hd->session_id->len ); + n = gcry_md_get_algo_dlen(algo); + if( n > size ) + n = size; + memcpy( hash->d, gcry_md_read(md, algo), n ); + while( n < size ) { + gcry_md_close( md ); + md = gcry_md_copy( md1 ); + gcry_md_write( md, hash->d, n ); + n1 = gcry_md_get_algo_dlen(algo); + if( n1 > size-n ) + n1 = size-n; + memcpy( hash->d+n, gcry_md_read(md, algo), n1 ); + n += n1; + } + gcry_md_close( md ); + + return hash; +} + + +static int +construct_keys( GSTIHD hd ) +{ + GCRY_MD_HD md; + int algo = GCRY_MD_SHA1; + + if( hd->kex.key_a ) + return 0; /* already constructed */ + + md = gcry_md_open( algo, 0 ); + if( !md ) + return map_gcry_rc( gcry_errno() ); + + hash_mpi( md, hd->kex.k ); + gcry_md_write( md, hd->kex.h->d, hd->kex.h->len ); + + hd->kex.key_a = construct_one_key( hd, md, algo, "\x41", 8 ); + hd->kex.key_b = construct_one_key( hd, md, algo, "\x42", 8 ); + hd->kex.key_c = construct_one_key( hd, md, algo, "\x43", 24 ); /* des*/ + hd->kex.key_d = construct_one_key( hd, md, algo, "\x44", 24 ); + hd->kex.key_e = construct_one_key( hd, md, algo, "\x45", 20 ); + hd->kex.key_f = construct_one_key( hd, md, algo, "\x46", 20 ); + gcry_md_close( md ); + #if 0 + dump_hexbuf( stderr, "key A=", hd->kex.key_a->d, hd->kex.key_a->len ); + dump_hexbuf( stderr, "key B=", hd->kex.key_b->d, hd->kex.key_b->len ); + dump_hexbuf( stderr, "key C=", hd->kex.key_c->d, hd->kex.key_c->len ); + dump_hexbuf( stderr, "key D=", hd->kex.key_d->d, hd->kex.key_d->len ); + dump_hexbuf( stderr, "key E=", hd->kex.key_e->d, hd->kex.key_e->len ); + dump_hexbuf( stderr, "key F=", hd->kex.key_f->d, hd->kex.key_f->len ); + #endif + return 0; +} + + +static int +prepare_hmac_pad( GCRY_MD_HD *rhd, BSTRING key, int pad ) +{ + int algo = GCRY_MD_SHA1; + int i; + byte buf[64]; /* fixme */ + + assert( key->len <= sizeof buf ); + *rhd = gcry_md_open( algo, 0 ); + if( !*rhd ) + return map_gcry_rc( gcry_errno() ); + + memset( buf, 0, sizeof buf ); + memcpy( buf, key->d, key->len ); + for(i=0; i < sizeof buf; i++ ) + buf[i] ^= pad; + gcry_md_write( *rhd, buf, sizeof buf ); + return 0; +} + +/**************** + * Prepare a HMAC + */ +static int +prepare_mac( GCRY_MD_HD *inner_hd, GCRY_MD_HD *outer_hd, BSTRING key ) +{ + int rc; + + rc = prepare_hmac_pad( inner_hd, key, '\x36'); + if( !rc ) + rc = prepare_hmac_pad( outer_hd, key, '\x5c'); + return rc; +} + + +/* fixme: must release some data in case of error or when at end */ +int +kex_send_init_packet( GSTIHD hd ) +{ + MSG_kexinit kex; + int rc=0; + + /* first send our kexinit packet */ + memset( &kex, 0, sizeof kex ); + memset( kex.cookie, 'w', 16 ); /* fixme: send a random one */ + kex.kex_algorithm = insert_strlist( NULL, "diffie-hellman-group1-sha1" ); + kex.server_host_key_algorithms = insert_strlist( NULL, "ssh-dss"); + kex.encryption_algorithms_client_to_server = insert_strlist( NULL, "3des-cbc"); + kex.encryption_algorithms_server_to_client = insert_strlist( NULL, "3des-cbc"); + kex.mac_algorithms_client_to_server = insert_strlist( NULL, "hmac-sha1"); + kex.mac_algorithms_server_to_client = insert_strlist( NULL, "hmac-sha1"); + kex.compression_algorithms_client_to_server = insert_strlist( NULL, "none"); + kex.compression_algorithms_server_to_client = insert_strlist( NULL, "none"); + rc = build_msg_kexinit( &kex, &hd->pkt ); + if( rc ) + return rc; + rc = write_packet( hd ); + if( rc ) + return rc; + /* must do it here because write_packet fills in the packet type */ + hd->host_kexinit_data = make_bstring( hd->pkt.payload, hd->pkt.payload_len ); + rc = flush_packet( hd ); + return rc; +} + + +/**************** + * Process a received keyinit packet. + */ +int +kex_proc_init_packet( GSTIHD hd ) +{ + MSG_kexinit kex; + int rc; + + if( hd->pkt.type != SSH_MSG_KEXINIT ) + return GSTI_BUG; /* oops */ + rc = parse_msg_kexinit( &kex, hd->pkt.payload, hd->pkt.payload_len ); + if( rc ) + return rc; + /* make a copy of the received payload which we will need later */ + hd->peer_kexinit_data = make_bstring( hd->pkt.payload, + hd->pkt.payload_len ); + + dump_msg_kexinit( &kex ); + return 0; +} + + +/**************** + * Send a KEX init packet (we are in the client role) + */ +int +kex_send_kexdh_init( GSTIHD hd ) +{ + MSG_kexdh_init kexdh; + int rc=0; + + memset( &kexdh, 0, sizeof kexdh ); + /* FIXME: move secret_x it to secure memory */ + kexdh.e = hd->kexdh_e = calc_dh_secret( &hd->secret_x ); + rc = build_msg_kexdh_init( &kexdh, &hd->pkt ); + if( rc ) + return rc; + rc = write_packet( hd ); + if( rc ) + return rc; + rc = flush_packet( hd ); + return rc; +} + + +/**************** + * Process the received DH init (we are in the server role) + */ +int +kex_proc_kexdh_init( GSTIHD hd ) +{ + int rc; + MSG_kexdh_init kexdh; + + if( hd->pkt.type != SSH_MSG_KEXDH_INIT ) + return GSTI_BUG; /* oops */ + + rc = parse_msg_kexdh_init( &kexdh, hd->pkt.payload, hd->pkt.payload_len ); + if( rc ) + return rc; + + /* we need the received e later */ + hd->kexdh_e = kexdh.e; + + dump_msg_kexdh_init( &kexdh ); + return 0; +} + + +/**************** + * Send a DH init packet (we are in the server role) + */ +int +kex_send_kexdh_reply( GSTIHD hd ) +{ + MSG_kexdh_reply dhr; + int rc; + MPI y; + + memset( &dhr, 0, sizeof dhr ); + dhr.k_s = make_bstring( "servers-pk", 10 ); /* fixme: Get from a DB */ + + /* generate our secret and the public value for it */ + dhr.f = calc_dh_secret( &y ); + /* now we can calculate the shared secret */ + hd->kex.k = calc_dh_key( hd->kexdh_e, y ); + mpi_release( y ); + /* and the hash */ + rc = calc_exchange_hash( hd, hd->host_kexinit_data, hd->peer_kexinit_data, + dhr.k_s, hd->kexdh_e, dhr.f ); + mpi_release( hd->kexdh_e ); + if( rc ) + return rc; + dhr.sig_h = make_bstring("signature_of_H", 14 ); /*FIXME*/ + + rc = build_msg_kexdh_reply( &dhr, &hd->pkt ); + if( !rc ) + dump_msg_kexdh_reply( &dhr ); + if( !rc ) + rc = write_packet( hd ); + if( !rc ) + rc = flush_packet( hd ); + return rc; +} + +/**************** + * Process the received DH value and take the encryption kes into use. + * (we are in the client role) + */ +int +kex_proc_kexdh_reply( GSTIHD hd ) +{ + int rc; + MSG_kexdh_reply dhr; + + if( hd->pkt.type != SSH_MSG_KEXDH_REPLY ) + return GSTI_BUG; /* oops */ + + rc = parse_msg_kexdh_reply( &dhr, hd->pkt.payload, hd->pkt.payload_len ); + if( rc ) + return rc; + + dump_msg_kexdh_reply( &dhr ); + + hd->kex.k = calc_dh_key( dhr.f, hd->secret_x ); + mpi_release( hd->secret_x ); + + rc = calc_exchange_hash( hd, hd->host_kexinit_data, hd->peer_kexinit_data, + dhr.k_s, hd->kexdh_e, dhr.f ); + mpi_release( hd->kexdh_e ); + if( rc ) + return rc; + + /* FIXME: check that this is the real host */ + + return rc; +} + + +int +kex_send_newkeys( GSTIHD hd ) +{ + int rc; + + rc = construct_keys( hd ); + if( rc ) + return rc; + + hd->pkt.type = SSH_MSG_NEWKEYS; + hd->pkt.payload_len = 1; + rc = write_packet( hd ); + if( !rc ) + rc = flush_packet( hd ); + if( rc ) + return rc; + + /* now we have to take the encryption keys into use */ + hd->encrypt_hd = gcry_cipher_open( GCRY_CIPHER_3DES, + GCRY_CIPHER_MODE_CBC, 0 ); + if( !hd->encrypt_hd ) + rc = map_gcry_rc( gcry_errno() ); + else if( hd->we_are_server ) { + if( !rc ) + rc = gcry_cipher_setkey( hd->encrypt_hd, hd->kex.key_d->d, + hd->kex.key_d->len ); + if( !rc ) + rc = gcry_cipher_setiv( hd->encrypt_hd, hd->kex.key_b->d, + hd->kex.key_b->len ); + rc = map_gcry_rc(rc); + if( !rc ) + rc = prepare_mac( &hd->send_mac_inner_hd, &hd->send_mac_outer_hd, + hd->kex.key_f ); + } + else { + if( !rc ) + rc = gcry_cipher_setkey( hd->encrypt_hd, hd->kex.key_c->d, + hd->kex.key_c->len ); + if( !rc ) + rc = gcry_cipher_setiv( hd->encrypt_hd, hd->kex.key_a->d, + hd->kex.key_a->len ); + rc = map_gcry_rc(rc); + if( !rc ) + rc = prepare_mac( &hd->send_mac_inner_hd, &hd->send_mac_outer_hd, + hd->kex.key_e ); + } + if( rc ) + return debug_rc(rc,"setup encryption keys failed"); + return rc; +} + +/**************** + * Process a received newkeys message and take the decryption keys in use + */ +int +kex_proc_newkeys( GSTIHD hd ) +{ + int rc; + + if( hd->pkt.type != SSH_MSG_NEWKEYS ) + return GSTI_BUG; /* ooops */ + + rc = construct_keys( hd ); + if( rc ) + return rc; + + hd->decrypt_hd = gcry_cipher_open( GCRY_CIPHER_3DES, + GCRY_CIPHER_MODE_CBC, 0 ); + if( !hd->encrypt_hd ) + rc = map_gcry_rc( gcry_errno() ); + else if( hd->we_are_server ) { + if( !rc ) + rc = gcry_cipher_setkey( hd->decrypt_hd, hd->kex.key_c->d, + hd->kex.key_c->len ); + if( !rc ) + rc = gcry_cipher_setiv( hd->decrypt_hd, hd->kex.key_a->d, + hd->kex.key_a->len ); + rc = map_gcry_rc(rc); + if( !rc ) + rc = prepare_mac( &hd->recv_mac_inner_hd, &hd->recv_mac_outer_hd, + hd->kex.key_e ); + } + else { + if( !rc ) + rc = gcry_cipher_setkey( hd->decrypt_hd, hd->kex.key_d->d, + hd->kex.key_d->len ); + if( !rc ) + rc = gcry_cipher_setiv( hd->decrypt_hd, hd->kex.key_b->d, + hd->kex.key_b->len ); + rc = map_gcry_rc(rc); + if( !rc ) + rc = prepare_mac( &hd->recv_mac_inner_hd, &hd->recv_mac_outer_hd, + hd->kex.key_f ); + } + + if( rc ) + return debug_rc(rc,"setup decryption keys failed"); + return rc; +} + + + +/**************** + * Parse a SSH_MSG_SERVICE_{ACCEPT,REQUEST} and return the service name. + * Returns: 0 on success or an errorcode. + */ +static int +parse_msg_service( BSTRING *svcname, const byte *msg, size_t msglen, int type ) +{ + size_t n; + int rc = 0; + + *svcname = NULL; + if( msglen < (1+4) ) + return GSTI_TOO_SHORT; + if( *msg != type ) + return GSTI_BUG; + msg++; msglen--; + + n = msglen; + *svcname = parse_bstring( msg, &n ); + if( !*svcname ) + return GSTI_TOO_SHORT; + msg += n; msglen -= n; + + /* make sure the msg length matches */ + if( msglen ) { + fprintf(stderr,"parse_msg_service: hmmm, %lu bytes remaining\n", + (ulong)msglen ); + } + return rc; +} + +/**************** + * Build a SERVICE_{Accept,REQUEST} packet. + */ +static int +build_msg_service( BSTRING svcname, struct packet_buffer_s *pkt, int type ) +{ + size_t n; + byte *p = pkt->payload; + size_t length = pkt->size; + + assert( pkt->size > 100 ); + if( !svcname ) { + fprintf(stderr,"build_msg_service: no service name\n"); + return GSTI_BUG; + } + + pkt->type = type; + p++; pkt->payload_len = 1; + + n = build_bstring( p, length, svcname ); + if( !n ) + return GSTI_TOO_SHORT; /* provided buffer is too short */ + p += n; length -= n; pkt->payload_len += n; + return 0; +} + + + +int +kex_send_service_request( GSTIHD hd, const char *name ) +{ + int rc; + + hd->service_name = make_bstring( name, strlen(name) ); + rc = build_msg_service( hd->service_name, &hd->pkt,SSH_MSG_SERVICE_REQUEST); + if( !rc ) + rc = write_packet( hd ); + if( !rc ) + rc = flush_packet( hd ); + + if( rc ) { + gsti_free( hd->service_name ); + hd->service_name = NULL; + } + return rc; +} + +int +kex_proc_service_request( GSTIHD hd ) +{ + int rc; + BSTRING svcname; + + if( hd->pkt.type != SSH_MSG_SERVICE_REQUEST ) + return GSTI_BUG; /* oops */ + rc = parse_msg_service( &svcname, hd->pkt.payload, hd->pkt.payload_len, + SSH_MSG_SERVICE_REQUEST ); + if( rc ) + return rc; + + /* FIXME: here we should whether we provide this service */ + + /* store the servicename, so that it can later be answered */ + if( hd->service_name ) + return debug_rc( GSTI_BUG, "a service is already in use"); + + hd->service_name = svcname; + return rc; +} + + + +int +kex_send_service_accept( GSTIHD hd ) +{ + int rc; + + rc = build_msg_service( hd->service_name, &hd->pkt, SSH_MSG_SERVICE_ACCEPT); + if( !rc ) + rc = write_packet( hd ); + if( !rc ) + rc = flush_packet( hd ); + return rc; +} + +int +kex_proc_service_accept( GSTIHD hd ) +{ + int rc; + BSTRING svcname; + + if( hd->pkt.type != SSH_MSG_SERVICE_ACCEPT ) + return GSTI_BUG; /* oops */ + rc = parse_msg_service( &svcname, hd->pkt.payload, hd->pkt.payload_len, + SSH_MSG_SERVICE_ACCEPT ); + if( rc ) + return rc; + + if( !hd->service_name ) + return debug_rc( GSTI_BUG, "no service request sent"); + rc = cmp_bstring( hd->service_name, svcname ); + gsti_free( svcname ); + if( rc ) + return debug_rc( GSTI_PROT_VIOL, + "service name does not match requested one" ); + return 0; +} + diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c new file mode 100644 index 0000000000..b571eb7adf --- /dev/null +++ b/lib/gnutls_errors.c @@ -0,0 +1,31 @@ +#include "gnutls_errors.h" + +int gnutls_is_fatal_error( int error) { + + switch (error) { + case GNUTLS_E_MAC_FAILED: + case GNUTLS_E_UNKNOWN_CIPHER: + case GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM: + case GNUTLS_E_UNKNOWN_MAC_ALGORITHM: + case GNUTLS_E_UNKNOWN_ERROR: + case GNUTLS_E_UNKNOWN_CIPHER_TYPE: + case GNUTLS_E_LARGE_PACKET: + case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: + case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: + case GNUTLS_E_INVALID_SESSION: + case GNUTLS_E_UNABLE_SEND_DATA: + case GNUTLS_E_FATAL_ALERT_RECEIVED: + case GNUTLS_E_RECEIVED_BAD_MESSAGE: + case GNUTLS_E_RECEIVED_MORE_DATA: + case GNUTLS_E_UNEXPECTED_PACKET: + case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: + case GNUTLS_E_CLOSURE_ALERT_RECEIVED: + case GNUTLS_E_ERROR_IN_FINISHED_PACKET: + return 1; + case GNUTLS_E_WARNING_ALERT_RECEIVED: + return 0; + default: + return 0; + + } +} diff --git a/lib/gnutls_errors.h b/lib/gnutls_errors.h new file mode 100644 index 0000000000..7a251a27b9 --- /dev/null +++ b/lib/gnutls_errors.h @@ -0,0 +1,19 @@ +#define GNUTLS_E_MAC_FAILED -1 +#define GNUTLS_E_UNKNOWN_CIPHER -2 +#define GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM -3 +#define GNUTLS_E_UNKNOWN_MAC_ALGORITHM -4 +#define GNUTLS_E_UNKNOWN_ERROR -5 +#define GNUTLS_E_UNKNOWN_CIPHER_TYPE -6 +#define GNUTLS_E_LARGE_PACKET -7 +#define GNUTLS_E_UNSUPPORTED_VERSION_PACKET -8 +#define GNUTLS_E_UNEXPECTED_PACKET_LENGTH -9 +#define GNUTLS_E_INVALID_SESSION -10 +#define GNUTLS_E_UNABLE_SEND_DATA -11 +#define GNUTLS_E_FATAL_ALERT_RECEIVED -12 +#define GNUTLS_E_RECEIVED_BAD_MESSAGE -13 +#define GNUTLS_E_RECEIVED_MORE_DATA -14 +#define GNUTLS_E_UNEXPECTED_PACKET -15 +#define GNUTLS_E_WARNING_ALERT_RECEIVED -16 +#define GNUTLS_E_CLOSURE_ALERT_RECEIVED -17 +#define GNUTLS_E_ERROR_IN_FINISHED_PACKET -18 +#define GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET -19 diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c new file mode 100644 index 0000000000..5c44c4b027 --- /dev/null +++ b/lib/gnutls_handshake.c @@ -0,0 +1,710 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "debug.h" +#include "gnutls_compress.h" +#include "gnutls_plaintext.h" +#include "gnutls_cipher.h" +#include "gnutls_buffers.h" +#include "gnutls_handshake.h" +#include "gnutls_num.h" + +#ifdef DEBUG +#define ERR(x, y) fprintf(stderr, "GNUTLS Error: %s (%d)\n", x,y) +#else +#define ERR(x, y) +#endif + +#define HASH_TRUE 1 +#define HASH_FALSE 0 + + +#define SERVER_MSG "server finished" +#define CLIENT_MSG "client finished" +_gnutls_send_finished( int cd, GNUTLS_STATE state) { +uint8* data; +uint8 concat[36]; /* md5+sha1 */ +int ret; + + + memset(concat, 0, 36); + + if (state->security_parameters.entity == GNUTLS_CLIENT) { + memmove( concat, state->gnutls_internals.client_md_md5, 16); + memmove( &concat[16], state->gnutls_internals.client_md_sha1, 20); + + data = gnutls_PRF(state->security_parameters.master_secret, 48, + CLIENT_MSG, strlen(CLIENT_MSG), concat, 36, + 12); + } else { /* server */ + memmove( concat, state->gnutls_internals.server_md_md5, 16); + memmove( &concat[16], state->gnutls_internals.server_md_sha1, 20); + + data = gnutls_PRF(state->security_parameters.master_secret, 48, + SERVER_MSG, strlen(SERVER_MSG), concat, 36, + 12); + } + + ret = _gnutls_send_handshake( cd, state, data, 12, GNUTLS_FINISHED); + gnutls_free(data); + + return ret; +} + +int _gnutls_recv_handshake( int cd, GNUTLS_STATE state, void* data, uint32 datalen, HandshakeType type) { +int ret; + state->gnutls_internals.next_handshake_type = type; + ret = gnutls_recv_int( cd, state, GNUTLS_HANDSHAKE, data, datalen); + state->gnutls_internals.next_handshake_type = GNUTLS_NONE; + + return ret; +} + +_gnutls_recv_finished( int cd, GNUTLS_STATE state) { +uint8* data, vrfy[12]; +uint8 concat[36]; /* md5+sha1 */ +int ret=0; + + memset( concat, 0, 36); + memset( vrfy, 0, 12); + + ret = _gnutls_recv_handshake( cd, state, vrfy, 12, GNUTLS_FINISHED); + if (ret<0) { + ERR("recv finished int", ret); + return ret; + } + if (ret!=12) return GNUTLS_E_ERROR_IN_FINISHED_PACKET; + + if (state->security_parameters.entity == GNUTLS_CLIENT) { + memmove( concat, state->gnutls_internals.server_md_md5, 16); + memmove( &concat[16], state->gnutls_internals.server_md_sha1, 20); + + data = gnutls_PRF(state->security_parameters.master_secret, 48, + SERVER_MSG, strlen(SERVER_MSG), concat, 36, + 12); + } else { /* server */ + memmove( concat, state->gnutls_internals.client_md_md5, 16); + memmove( &concat[16], state->gnutls_internals.client_md_sha1, 20); + + data = gnutls_PRF(state->security_parameters.master_secret, 48, + CLIENT_MSG, strlen(CLIENT_MSG), concat, 36, + 12); + } + + if ( memcmp( vrfy, data, 12) != 0) ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET; + + gnutls_free(data); + + return ret; +} + + + +int SelectSuite( opaque ret[2], char* data, int datalen) { +int x, pos=0, i,j; +GNUTLS_CipherSuite *ciphers; + + x = _gnutls_supported_ciphersuites(&ciphers); + memset( ret, '\0', sizeof(GNUTLS_CipherSuite)); + + for ( j=0;j<datalen;j+=2) { + for (i=0;i<x;i++) { + if ( memcmp( &ciphers[i].CipherSuite, &data[j], 2) == 0) { + memmove( ret, &ciphers[i].CipherSuite, 2); + gnutls_free(ciphers); + return 0; + } + } + } + + + gnutls_free(ciphers); + return GNUTLS_E_UNKNOWN_CIPHER; + +} + +int SelectCompMethod( CompressionMethod* ret, char* data, int datalen) { +int x, pos=0, i,j; +CompressionMethod *ciphers; + + x = _gnutls_supported_compression_methods(&ciphers); + memset( ret, '\0', sizeof(CompressionMethod)); + + for ( j=0;j<datalen;j++) { + for (i=0;i<x;i++) { + if ( memcmp( &ciphers[i], &data[j], 1) == 0) { + memmove( ret, &ciphers[i], 1); + gnutls_free(ciphers); + return 0; + } + } + } + + + gnutls_free(ciphers); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + +} + + +#define SUPPORTED_CIPHERSUITES 1 +int _gnutls_supported_ciphersuites(GNUTLS_CipherSuite **ciphers) { + + int i; + + *ciphers = gnutls_malloc( SUPPORTED_CIPHERSUITES * sizeof(GNUTLS_CipherSuite)); + + for (i=0;i<SUPPORTED_CIPHERSUITES;i++) { + (*ciphers)[i].CipherSuite[0] = 0x00; + } + + /* GNUTLS_DH_anon_WITH_3DES_EDE_CBC_SHA */ +// (*ciphers)[0].CipherSuite[1] = 0x1B; + + /* GNUTLS_NULL_WITH_NULL_NULL */ + (*ciphers)[0].CipherSuite[1] = 0x0; + + return SUPPORTED_CIPHERSUITES; +} + + +#define SUPPORTED_COMPRESSION_METHODS 1 +int _gnutls_supported_compression_methods(CompressionMethod **comp) { + + int i; + + *comp = gnutls_malloc( SUPPORTED_COMPRESSION_METHODS * sizeof(CompressionMethod)); + +/* NULL Compression */ + (*comp)[0] = COMPRESSION_NULL; + + return SUPPORTED_COMPRESSION_METHODS; +} + +int _gnutls_send_handshake(int cd, GNUTLS_STATE state, void* i_data, uint32 i_datasize, HandshakeType type) { + int ret; + uint8* data; + uint24 length; + uint32 datasize; + int pos=0; + +#ifdef WORDS_BIGENDIAN + datasize = i_datasize; +#else + datasize = byteswap32(i_datasize); +#endif + + length = uint32touint24( datasize); + + i_datasize += 4; + data = gnutls_malloc( i_datasize); + memmove( &data[pos++], &type, 1); + memmove( &data[pos++], &length.pint[0], 1); + memmove( &data[pos++], &length.pint[1], 1); + memmove( &data[pos++], &length.pint[2], 1); + + if (i_datasize > 0) memmove( &data[pos], i_data, i_datasize-4); + + if (state->gnutls_internals.client_hash==HASH_TRUE) { + mhash( state->gnutls_internals.client_td_md5, data ,i_datasize); + mhash( state->gnutls_internals.client_td_sha1, data, i_datasize); + } + if (state->gnutls_internals.server_hash==HASH_TRUE) { + mhash( state->gnutls_internals.server_td_md5, data ,i_datasize); + mhash( state->gnutls_internals.server_td_sha1, data, i_datasize); + } + + ret = gnutls_send_int( cd, state, GNUTLS_HANDSHAKE, data, i_datasize); + + return ret; +} + +int _gnutls_recv_handshake_int( int cd, GNUTLS_STATE state, void* data, uint32 datasize, void* output_data, uint32 output_datasize) { + int ret; + uint32 length32=0; + int pos=0; + uint8 *dataptr=data; + uint24 num; + + + num.pint[0] = dataptr[1]; + num.pint[1] = dataptr[2]; + num.pint[2] = dataptr[3]; + length32 = uint24touint32( num); + + +#ifndef WORDS_BIGENDIAN + length32 = byteswap32(length32); +#endif + + if (state->gnutls_internals.client_hash == HASH_TRUE) { + mhash( state->gnutls_internals.client_td_md5, dataptr, length32 + 4); + mhash( state->gnutls_internals.client_td_sha1, dataptr, length32 + 4); + } + + if (state->gnutls_internals.server_hash == HASH_TRUE) { + mhash( state->gnutls_internals.server_td_md5, dataptr, length32 + 4); + mhash( state->gnutls_internals.server_td_sha1, dataptr, length32 + 4); + } + + ret = GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; + switch(dataptr[0]) { + case GNUTLS_CLIENT_HELLO: + case GNUTLS_SERVER_HELLO: + if (state->gnutls_internals.next_handshake_type == dataptr[0]) + ret = _gnutls_recv_hello( cd, state, &dataptr[4], length32, NULL, 0); + break; + case GNUTLS_SERVER_HELLO_DONE: + ret = 0; + break; + case GNUTLS_FINISHED: + if (output_datasize > length32) output_datasize=length32; + memmove( output_data, &dataptr[4], length32); + ret = length32; + break; + } + + return ret; +} + +int _gnutls_send_hello_request(int cd, GNUTLS_STATE state) { + return _gnutls_send_handshake( cd, state, NULL, 0, GNUTLS_HELLO_REQUEST); +} + + +int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque* SessionID, uint8 SessionIDLen) { + char* rand; + char *data=NULL; + uint8 session_id_len, z; + uint32 cur_time; + int pos=0; + GNUTLS_CipherSuite* cipher_suites; + CompressionMethod* compression_methods; + int i, datalen, ret=0; + uint16 x; + + session_id_len = SessionIDLen; + if (SessionID==NULL) session_id_len=0; + rand=gcry_random_bytes( 28, GCRY_STRONG_RANDOM); + + if (state->security_parameters.entity == GNUTLS_CLIENT) { + + datalen = 2 + 4 + (session_id_len+1) + 28 + 3; + data = gnutls_malloc ( datalen); + + data[pos++] = GNUTLS_VERSION_MAJOR; + data[pos++] = GNUTLS_VERSION_MINOR; +#ifdef WORDS_BIGENDIAN + cur_time = time(NULL); +#else + cur_time = byteswap32(time(NULL)); +#endif + memmove( state->security_parameters.client_random, &cur_time, 4); + memmove( &state->security_parameters.client_random[4], rand, 28); + + memmove( &data[pos], &cur_time, 4); + pos += 4; + memmove( &data[pos], rand, 28); + pos+=28; + + memmove( &data[pos++], &session_id_len, 1); + + if (session_id_len>0) { + memmove( &data[pos], SessionID, session_id_len); + } + pos+=session_id_len; + + x = _gnutls_supported_ciphersuites( &cipher_suites); + +#ifdef WORDS_BIGENDIAN + memmove( &data[pos], &x, sizeof(uint16)); +#else + x=byteswap16(x); + memmove( &data[pos], &x, sizeof(uint16)); + x=byteswap16(x); +#endif + pos+=2; + + datalen += 2*x; + data = gnutls_realloc( data, datalen); + + for (i=0;i<x;i++) { + memmove( &data[pos], &cipher_suites[i].CipherSuite, 2); + pos+=2; + } + + z = _gnutls_supported_compression_methods( &compression_methods); + memmove( &data[pos++], &z, sizeof(uint8)); + datalen += z; + data = gnutls_realloc( data, datalen); + + for (i=0;i<z;i++) { + memmove( &data[pos], &compression_methods[i], 1); + pos++; + } + + gcry_free(rand); + gnutls_free(cipher_suites); + gnutls_free(compression_methods); + + ret = _gnutls_send_handshake( cd, state, data, datalen, GNUTLS_CLIENT_HELLO); + gnutls_free(data); + + + } else { /* SERVER */ + datalen = 2 + sizeof(uint32) + session_id_len+1 + 28; + data = gnutls_malloc ( datalen); + + data[pos++] = GNUTLS_VERSION_MAJOR; + data[pos++] = GNUTLS_VERSION_MINOR; +#ifdef WORDS_BIGENDIAN + cur_time = time(NULL); +#else + cur_time = byteswap32(time(NULL)); +#endif + memmove( state->security_parameters.server_random, &cur_time, 4); + memmove( &state->security_parameters.server_random[4], rand, 28); + + memmove( &data[pos], &cur_time, sizeof(uint32)); + pos += sizeof(uint32); + memmove( &data[pos], rand, 28); + pos+=28; + + memmove( &data[pos++], &session_id_len, sizeof(uint8)); + if (session_id_len>0) { + memmove( &data[pos], SessionID, session_id_len); + } + pos+=session_id_len; + + datalen += 2; + data = gnutls_realloc( data, datalen); + memmove( &data[pos], &state->gnutls_internals.current_cipher_suite.CipherSuite, 2); + pos+=2; + + datalen += 1; + data = gnutls_realloc( data, datalen); + memmove( &data[pos++], &state->gnutls_internals.compression_method, 1); + + gcry_free(rand); + ret = _gnutls_send_handshake( cd, state, data, datalen, GNUTLS_SERVER_HELLO); + gnutls_free(data); + + } + + return ret; +} + + +/* RECEIVE A HELLO MESSAGE. This should be called from gnutls_recv_handshake_int only if a + * hello message is expected. It uses the gnutls_internals.current_cipher_suite + * and gnutls_internals.compression_method. + */ +int _gnutls_recv_hello(int cd, GNUTLS_STATE state, char* data, int datalen, opaque** SessionID, int SessionIDnum) { + uint8 session_id_len=0, z; + uint32 cur_time; + int pos=0; + GNUTLS_CipherSuite cipher_suite, *cipher_suites; + CompressionMethod compression_method, *compression_methods; + int i, ret; + uint16 x, sizeOfSuites; + + if (state->security_parameters.entity == GNUTLS_CLIENT) { + if (datalen < 38) return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + + if (data[pos++] != GNUTLS_VERSION_MAJOR) + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + + if (data[pos++] != GNUTLS_VERSION_MINOR) + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + + memmove( state->security_parameters.server_random, &data[pos], 32); + pos+=32; + + memmove( &session_id_len, &data[pos++], 1); + + if (datalen < 38+session_id_len) return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; +#ifdef DEBUG + fprintf(stderr, "SessionID length: %d\n", session_id_len); + fprintf(stderr, "SessionID: %s\n", bin2hex(&data[pos], session_id_len)); +#endif + pos+=session_id_len; + + /* We should resume an old connection here. This is not + * implemented yet. + */ + + memmove( &cipher_suite.CipherSuite, &data[pos], 2); + pos+=2; + + z=1; + x = _gnutls_supported_ciphersuites( &cipher_suites); + for (i=0;i<x;i++) { + if ( memcmp( &cipher_suites[i], cipher_suite.CipherSuite, 2) == 0) { + z=0; + + } + } + if (z!=0) return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + memmove( state->gnutls_internals.current_cipher_suite.CipherSuite, cipher_suite.CipherSuite, 2); + + z=1; + memmove( &compression_method, &data[pos++], 1); + z = _gnutls_supported_compression_methods( &compression_methods); + for (i=0;i<z;i++) { + if (memcmp( &compression_methods[i], &compression_method, 1)==0) { + z=0; + + } + } + if (z!=0) return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + memmove( &state->gnutls_internals.compression_method, &compression_method, 1); + + + gnutls_free(cipher_suites); + gnutls_free(compression_methods); + + } else { /* Server side reading a client hello */ + if (datalen < 35) return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + + if (data[pos++] != GNUTLS_VERSION_MAJOR) + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + + if (data[pos++] != GNUTLS_VERSION_MINOR) + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + + memmove( state->security_parameters.client_random, &data[pos], 32); + pos+=32; + + memmove( &session_id_len, &data[pos++], 1); + pos+=session_id_len; + + /* We should resume an old connection here. This is not + * implemented yet. + */ + + + /* Select a ciphersuite */ + memmove( &sizeOfSuites, &data[pos], 2); + pos+=2; +#ifndef WORDS_BIGENDIAN + sizeOfSuites=byteswap16(sizeOfSuites); +#endif + SelectSuite( state->gnutls_internals.current_cipher_suite.CipherSuite, &data[pos], sizeOfSuites); + pos+=sizeOfSuites; + + memmove( &z, &data[pos++], 1); + SelectCompMethod( &state->gnutls_internals.compression_method, &data[pos], z); + + } + + return ret; +} + +#define HASH(x) state->gnutls_internals.x=HASH_TRUE +#define NOT_HASH(x) state->gnutls_internals.x=HASH_FALSE + +int gnutls_handshake(int cd, GNUTLS_STATE state) { +int ret; +char* session_id; +uint8 session_id_size; + + state->gnutls_internals.client_td_md5 = mhash_init(MHASH_MD5); + state->gnutls_internals.client_td_sha1 = mhash_init(MHASH_SHA1); + state->gnutls_internals.server_td_md5 = mhash_init(MHASH_MD5); + state->gnutls_internals.server_td_sha1 = mhash_init(MHASH_SHA1); + + if (state->security_parameters.entity == GNUTLS_CLIENT) { + HASH(client_hash); + HASH(server_hash); + ret = _gnutls_send_hello( cd, state, NULL, 0); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("send hello", ret); + return ret; + } + + /* receive the server hello */ + HASH(client_hash); + HASH(server_hash); + ret = _gnutls_recv_handshake( cd, state, NULL, 0, GNUTLS_SERVER_HELLO); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("recv hello", ret); + return ret; + } + + /* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */ + + /* receive the server hello done */ + HASH(client_hash); + HASH(server_hash); + ret = _gnutls_recv_handshake( cd, state, NULL, 0, GNUTLS_SERVER_HELLO_DONE); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("recv server hello done", ret); + return ret; + } + + /* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */ + + /* Send the CHANGE CIPHER SPEC PACKET */ + ret = _gnutls_send_change_cipher_spec( cd, state); + if (ret<0) { + ERR("send ChangeCipherSpec", ret); + return ret; + } + + + state->gnutls_internals.client_md_md5 = mhash_end( state->gnutls_internals.client_td_md5); + state->gnutls_internals.client_md_sha1 = mhash_end( state->gnutls_internals.client_td_sha1); + + /* Initialize the connection state (start encryption) */ + _gnutls_connection_state_init(state); + + /* send the finished message */ + + NOT_HASH(client_hash); + HASH(server_hash); + ret = _gnutls_send_finished( cd, state); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("send Finished", ret); + return ret; + } + + + ret = gnutls_recv_int( cd, state, GNUTLS_CHANGE_CIPHER_SPEC, NULL, 0); + if (ret<0) { + ERR("recv ChangeCipherSpec", ret); + return ret; + } + + state->gnutls_internals.server_md_md5 = mhash_end( state->gnutls_internals.server_td_md5); + state->gnutls_internals.server_md_sha1 = mhash_end( state->gnutls_internals.server_td_sha1); + + NOT_HASH(client_hash); + NOT_HASH(server_hash); + ret = _gnutls_recv_finished( cd, state); + if (ret<0) { + ERR("recv finished", ret); + return ret; + } + + mhash_free( state->gnutls_internals.client_md_md5); + mhash_free( state->gnutls_internals.client_md_sha1); + mhash_free( state->gnutls_internals.server_md_md5); + mhash_free( state->gnutls_internals.server_md_sha1); + + } else { /* SERVER */ + + HASH(client_hash); + HASH(server_hash); + ret = _gnutls_recv_handshake( cd, state, NULL, 0, GNUTLS_CLIENT_HELLO); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("recv hello", ret); + return ret; + } + + _gnutls_generate_session_id( &session_id, &session_id_size); + HASH(client_hash); + HASH(server_hash); + ret = _gnutls_send_hello( cd, state, session_id, session_id_size); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("send hello", ret); + return ret; + } + gnutls_free(session_id); + + /* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */ + + /* send the server hello done */ + HASH(client_hash); + HASH(server_hash); + ret = _gnutls_send_handshake( cd, state, NULL, 0, GNUTLS_SERVER_HELLO_DONE); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("send server hello done", ret); + return ret; + } + + /* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */ + + + /* Initialize the connection state (start encryption) */ + + + + ret = gnutls_recv_int( cd, state, GNUTLS_CHANGE_CIPHER_SPEC, NULL, 0); + if (ret<0) { + ERR("recv ChangeCipherSpec", ret); + return ret; + } + + _gnutls_connection_state_init(state); + + state->gnutls_internals.client_md_md5 = mhash_end( state->gnutls_internals.client_td_md5); + state->gnutls_internals.client_md_sha1 = mhash_end( state->gnutls_internals.client_td_sha1); + + NOT_HASH(client_hash); + HASH(server_hash); + ret = _gnutls_recv_finished( cd, state); + NOT_HASH(client_hash); + NOT_HASH(server_hash); + if (ret<0) { + ERR("recv finished", ret); + return ret; + } + + ret = _gnutls_send_change_cipher_spec( cd, state); + if (ret<0) { + ERR("send ChangeCipherSpec", ret); + return ret; + } + + state->gnutls_internals.server_md_md5 = mhash_end( state->gnutls_internals.server_td_md5); + state->gnutls_internals.server_md_sha1 = mhash_end( state->gnutls_internals.server_td_sha1); + + NOT_HASH(client_hash); + NOT_HASH(server_hash); + ret = _gnutls_send_finished( cd, state); + if (ret<0) { + ERR("recv finished", ret); + return ret; + } + + mhash_free( state->gnutls_internals.server_md_md5); + mhash_free( state->gnutls_internals.server_md_sha1); + mhash_free( state->gnutls_internals.client_md_md5); + mhash_free( state->gnutls_internals.client_md_sha1); + + } + + return ret; + +} + +int _gnutls_generate_session_id( char** session_id, uint8* len) { +char* rand; + *session_id=gnutls_malloc(32); + rand = gcry_random_bytes(32, GCRY_WEAK_RANDOM); + + memmove( *session_id, rand, 32); + gcry_free(rand); + *len=32; + +#ifdef DEBUG + fprintf(stderr, "SessionID: %s\n", bin2hex( *session_id, 32)); +#endif + return 0; +} + diff --git a/lib/gnutls_handshake.h b/lib/gnutls_handshake.h new file mode 100644 index 0000000000..b1fbd1c48b --- /dev/null +++ b/lib/gnutls_handshake.h @@ -0,0 +1,9 @@ +int _gnutls_supported_ciphersuites(GNUTLS_CipherSuite **ciphers); +int _gnutls_supported_compression_methods(CompressionMethod **comp); +int _gnutls_send_handshake(int cd, GNUTLS_STATE state, void* i_data, uint32 i_datasize, HandshakeType type); +int _gnutls_send_hello_request(int cd, GNUTLS_STATE state); +int _gnutls_send_hello(int cd, GNUTLS_STATE state, opaque* SessionID, uint8 SessionIDLen); +int _gnutls_recv_hello(int cd, GNUTLS_STATE state, char* data, int datalen, opaque** SessionID, int SessionIDnum); +int gnutls_handshake(int cd, GNUTLS_STATE state); +int _gnutls_recv_handshake_int( int cd, GNUTLS_STATE state, void*, uint32, void*, uint32); +int _gnutls_generate_session_id( char** session_id, uint8* len); diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h new file mode 100644 index 0000000000..1700ad1b56 --- /dev/null +++ b/lib/gnutls_int.h @@ -0,0 +1,254 @@ +#define DEBUG + +#define svoid void /* for functions that allocate using secure_free */ +#define secure_free free +#define secure_malloc malloc +#define secure_realloc realloc +#define secure_calloc calloc +#define gnutls_malloc malloc +#define gnutls_realloc realloc +#define gnutls_calloc calloc +#define gnutls_free free + +typedef struct { + uint8 pint[3]; +} uint24; + +#define rotl64(x,n) (((x) << ((uint16)(n))) | ((x) >> (64 - (uint16)(n)))) +#define rotr64(x,n) (((x) >> ((uint16)(n))) | ((x) << (64 - (uint16)(n)))) +#define rotl32(x,n) (((x) << ((uint16)(n))) | ((x) >> (32 - (uint16)(n)))) +#define rotr32(x,n) (((x) >> ((uint16)(n))) | ((x) << (32 - (uint16)(n)))) +#define rotl16(x,n) (((x) << ((uint16)(n))) | ((x) >> (16 - (uint16)(n)))) +#define rotr16(x,n) (((x) >> ((uint16)(n))) | ((x) << (16 - (uint16)(n)))) + +#define byteswap16(x) ((rotl16(x, 8) & 0x00ff) | (rotr16(x, 8) & 0xff00)) +#define byteswap32(x) ((rotl32(x, 8) & 0x00ff00ff) | (rotr32(x, 8) & 0xff00ff00)) +#define byteswap64(x) ((rotl64(x, 8) & 0x00ff00ff00ff00ff) | (rotr64(x, 8) & 0xff00ff00ff00ff00)) + +typedef unsigned char opaque; + + +enum ChangeCipherSpecType { GNUTLS_TYPE_CHANGE_CIPHER_SPEC=1 }; +enum AlertLevel { GNUTLS_WARNING, GNUTLS_FATAL }; +enum AlertDescription { GNUTLS_CLOSE_NOTIFY, GNUTLS_UNEXPECTED_MESSAGE=10, GNUTLS_BAD_RECORD_MAC=20, + GNUTLS_DECRYPTION_FAILED, GNUTLS_RECORD_OVERFLOW, GNUTLS_DECOMPRESSION_FAILURE=30, + GNUTLS_HANDSHAKE_FAILURE=40, GNUTLS_BAD_CERTIFICATE=42, GNUTLS_UNSUPPORTED_CERTIFICATE, + GNUTLS_CERTIFICATE_REVOKED, GNUTLS_CERTIFICATE_EXPIRED, GNUTLS_CERTIFICATE_UNKNOWN, + GNUTLS_ILLEGAL_PARAMETER, GNUTLS_UNKNOWN_CA, GNUTLS_ACCESS_DENIED, GNUTLS_DECODE_ERROR=50, + GNUTLS_DECRYPT_ERROR, GNUTLS_EXPORT_RESTRICTION=60, GNUTLS_PROTOCOL_VERSION=70, + GNUTLS_INSUFFICIENT_SECURITY, GNUTLS_INTERNAL_ERROR=80, GNUTLS_USER_CANCELED=90, + GNUTLS_NO_RENEGOTIATION=100 + }; + +typedef enum AlertDescription AlertDescription; +typedef enum AlertLevel AlertLevel; +typedef enum ChangeCipherSpecType ChangeCipherSpecType; + +enum HandshakeType { GNUTLS_HELLO_REQUEST, GNUTLS_CLIENT_HELLO, GNUTLS_SERVER_HELLO, + GNUTLS_CERTIFICATE=11, GNUTLS_SERVER_KEY_EXCHANGE, + GNUTLS_CERTIFICATE_REQUEST, GNUTLS_SERVER_HELLO_DONE, + GNUTLS_CERTIFICATE_VERIFY, GNUTLS_CLIENT_KEY_EXCHANGE, + GNUTLS_FINISHED=20, GNUTLS_NONE=255 }; + +typedef enum HandshakeType HandshakeType; + + +typedef struct { + ChangeCipherSpecType type; +} ChangeCipherSpec; + +typedef struct { + AlertLevel level; + AlertDescription description; +} Alert; + + +/* STATE */ +enum ConnectionEnd { GNUTLS_SERVER, GNUTLS_CLIENT }; +enum BulkCipherAlgorithm { CIPHER_NULL, CIPHER_3DES = 4 }; +enum KX_Algorithm { KX_RSA, KX_DHE_DSS, KX_DHE_RSA, KX_DH_DSS, KX_DH_RSA, KX_ANON_DH }; +enum CipherType { CIPHER_STREAM, CIPHER_BLOCK }; +enum IsExportable { EXPORTABLE_TRUE, EXPORTABLE_FALSE }; +enum MACAlgorithm { MAC_NULL, MAC_MD5, MAC_SHA }; +enum CompressionMethod { COMPRESSION_NULL }; + +enum ValidSession { VALID_TRUE, VALID_FALSE }; +enum ResumableSession { RESUME_TRUE, RESUME_FALSE }; + +typedef enum KX_Algorithm KX_Algorithm; +typedef enum ValidSession ValidSession; +typedef enum ResumableSession ResumableSession; +typedef enum ConnectionEnd ConnectionEnd; +typedef enum BulkCipherAlgorithm BulkCipherAlgorithm; +typedef enum CipherType CipherType; +typedef enum IsExportable IsExportable; +typedef enum MACAlgorithm MACAlgorithm; +typedef enum CompressionMethod CompressionMethod; + +typedef struct { + ConnectionEnd entity; + BulkCipherAlgorithm bulk_cipher_algorithm; + CipherType cipher_type; + uint8 IV_size; /* not specified in the protocol, but later it + * uses it */ + uint8 key_size; + uint8 key_material_length; + IsExportable is_exportable; + MACAlgorithm mac_algorithm; + uint8 hash_size; + CompressionMethod compression_algorithm; + opaque master_secret[48]; + opaque client_random[32]; + opaque server_random[32]; +} SecurityParameters; + +typedef struct { + opaque* server_write_mac_secret; + opaque* client_write_mac_secret; + opaque* server_write_IV; + opaque* client_write_IV; + opaque* server_write_key; + opaque* client_write_key; +} CipherSpecs; + +typedef struct { + opaque* read_compression_state; + opaque* write_compression_state; + GCRY_CIPHER_HD write_cipher_state; + GCRY_CIPHER_HD read_cipher_state; + opaque* read_mac_secret; + opaque* write_mac_secret; + uint8 mac_secret_size; + uint64 read_sequence_number; + uint64 write_sequence_number; +} ConnectionState; + +typedef struct { + uint8 CipherSuite[2]; +} GNUTLS_CipherSuite; + + +typedef struct { + char* buffer; + uint32 bufferSize; + ResumableSession resumable; + ValidSession valid_connection; + AlertDescription last_alert; + GNUTLS_CipherSuite current_cipher_suite; + CompressionMethod compression_method; + /* for the handshake protocol */ + HandshakeType next_handshake_type; + MHASH client_td_md5; + MHASH client_td_sha1; + void* client_md_md5; + void* client_md_sha1; + MHASH server_td_md5; + MHASH server_td_sha1; + void* server_md_md5; + void* server_md_sha1; + int server_hash; + int client_hash; +} GNUTLS_INTERNALS; + +typedef struct { + SecurityParameters security_parameters; + CipherSpecs cipher_specs; + ConnectionState connection_state; + GNUTLS_INTERNALS gnutls_internals; +} GNUTLS_STATE_INT; + +typedef GNUTLS_STATE_INT *GNUTLS_STATE; + + +/* Record Protocol */ +enum ContentType { GNUTLS_CHANGE_CIPHER_SPEC=20, GNUTLS_ALERT, GNUTLS_HANDSHAKE, + GNUTLS_APPLICATION_DATA }; +typedef enum ContentType ContentType; + +#define GNUTLS_VERSION_MAJOR 3 +#define GNUTLS_VERSION_MINOR 1 + +typedef struct { + uint8 major; + uint8 minor; +} ProtocolVersion; + +typedef struct { + ContentType type; + ProtocolVersion version; + uint16 length; + opaque* fragment; +} GNUTLSPlaintext; + +typedef struct { + ContentType type; + ProtocolVersion version; + uint16 length; + opaque* fragment; +} GNUTLSCompressed; + +/* This is used for both block ciphers and stream ciphers. In stream ciphers + * the padding is just ignored. + */ +typedef struct { + opaque* content; + opaque* MAC; + uint8* padding; + uint8 padding_length; +} GNUTLS_GenericBlockCipher; + +typedef struct { + opaque* content; + opaque* MAC; +} GNUTLS_GenericStreamCipher; + +typedef struct { + ContentType type; + ProtocolVersion version; + uint16 length; + void* fragment; /* points GenericStreamCipher + * or GenericBlockCipher + */ +} GNUTLSCiphertext; + + +/* Handshake protocol */ + + +typedef struct { + HandshakeType msg_type; + uint24 length; + void* body; +} GNUTLS_Handshake; + +typedef struct { + uint32 gmt_unix_time; + opaque random_bytes[28]; +} GNUTLS_random; + + +typedef struct { + ProtocolVersion client_version; + GNUTLS_random random; + opaque* session_id; + GNUTLS_CipherSuite* cipher_suites; + CompressionMethod* compression_methods; +} GNUTLS_ClientHello; + +typedef struct { + ProtocolVersion server_version; + GNUTLS_random random; + opaque* session_id; + GNUTLS_CipherSuite cipher_suite; + CompressionMethod compression_method; +} GNUTLS_ServerHello; + +#define GNUTLS_DH_anon_WITH_3DES_EDE_CBC_SHA { 0x00, 0x1B } + +/* functions */ +int _gnutls_send_alert( int cd, GNUTLS_STATE state, AlertLevel level, AlertDescription desc); +int gnutls_close(int cd, GNUTLS_STATE state); +svoid *gnutls_PRF(opaque * secret, int secret_size, uint8 * label, + int label_size, opaque * seed, int seed_size, + int total_bytes); +
\ No newline at end of file diff --git a/lib/gnutls_num.c b/lib/gnutls_num.c new file mode 100644 index 0000000000..4374d32ecb --- /dev/null +++ b/lib/gnutls_num.c @@ -0,0 +1,21 @@ +#include <defines.h> +#include <mhash.h> +#include <gnutls_int.h> + +uint32 uint24touint32( uint24 num) { +uint32 ret=0; + + ((uint8*)&ret)[1] = num.pint[0]; + ((uint8*)&ret)[2] = num.pint[1]; + ((uint8*)&ret)[3] = num.pint[2]; + return ret; +} +uint24 uint32touint24( uint32 num) { +uint24 ret; + + ret.pint[0] = ((uint8*)&num)[1]; + ret.pint[1] = ((uint8*)&num)[2]; + ret.pint[2] = ((uint8*)&num)[3]; + return ret; + +} diff --git a/lib/gnutls_num.h b/lib/gnutls_num.h new file mode 100644 index 0000000000..a1d755d3f4 --- /dev/null +++ b/lib/gnutls_num.h @@ -0,0 +1,2 @@ +uint32 uint24touint32( uint24 num); +uint24 uint32touint24( uint32 num); diff --git a/lib/gnutls_plaintext.c b/lib/gnutls_plaintext.c new file mode 100644 index 0000000000..b57ad97911 --- /dev/null +++ b/lib/gnutls_plaintext.c @@ -0,0 +1,51 @@ +#include <defines.h> +#include <mhash.h> +#include "gnutls_int.h" +#include "gnutls_errors.h" + +/* Plaintext Handling */ +int _gnutls_text2TLSPlaintext(ContentType type, GNUTLSPlaintext** plain, char *text, uint16 length) +{ + GNUTLSPlaintext *plaintext; + + if (length > 16384) + return GNUTLS_E_LARGE_PACKET; + + *plain = gnutls_malloc(sizeof(GNUTLSPlaintext)); + plaintext = *plain; + + plaintext->fragment = gnutls_malloc(length); + memmove(plaintext->fragment, text, length); + plaintext->length = length; + plaintext->type = type; + plaintext->version.major = GNUTLS_VERSION_MAJOR; + plaintext->version.minor = GNUTLS_VERSION_MINOR; + + return 0; +} + +int _gnutls_TLSPlaintext2text( char** txt, GNUTLSPlaintext* plaintext) +{ + char *text; + + if (plaintext->length > 16384) + return GNUTLS_E_LARGE_PACKET; + + *txt = gnutls_malloc(plaintext->length); + text = *txt; + + memmove(text, plaintext->fragment, plaintext->length); + + return 0; +} + +int _gnutls_freeTLSPlaintext(GNUTLSPlaintext * plaintext) +{ + if (plaintext == NULL) + return 0; + + gnutls_free(plaintext->fragment); + gnutls_free(plaintext); + + return 0; +} diff --git a/lib/gnutls_plaintext.h b/lib/gnutls_plaintext.h new file mode 100644 index 0000000000..9e3e931de3 --- /dev/null +++ b/lib/gnutls_plaintext.h @@ -0,0 +1,3 @@ +int _gnutls_text2TLSPlaintext(ContentType type, GNUTLSPlaintext**, char *text, uint16 length); +int _gnutls_freeTLSPlaintext(GNUTLSPlaintext* plaintext); +int _gnutls_TLSPlaintext2text( char**, GNUTLSPlaintext* plaintext); |