123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731 |
- /*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /**
- * @file
- *
- * Transport Layer Security Protocol
- */
-
- #include <stdint.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include <errno.h>
- #include <byteswap.h>
- #include <gpxe/hmac.h>
- #include <gpxe/md5.h>
- #include <gpxe/sha1.h>
- #include <gpxe/aes.h>
- #include <gpxe/rsa.h>
- #include <gpxe/xfer.h>
- #include <gpxe/open.h>
- #include <gpxe/filter.h>
- #include <gpxe/tls.h>
-
- static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
- const void *data, size_t len );
- static void tls_clear_cipher ( struct tls_session *tls,
- struct tls_cipherspec *cipherspec );
-
- /**
- * Free TLS session
- *
- * @v refcnt Reference counter
- */
- static void free_tls ( struct refcnt *refcnt ) {
- struct tls_session *tls =
- container_of ( refcnt, struct tls_session, refcnt );
-
- /* Free dynamically-allocated resources */
- tls_clear_cipher ( tls, &tls->tx_cipherspec );
- tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
- tls_clear_cipher ( tls, &tls->rx_cipherspec );
- tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
- free ( tls->rsa_mod );
- free ( tls->rsa_pub_exp );
- free ( tls->rx_data );
-
- /* Free TLS structure itself */
- free ( tls );
- }
-
- /**
- * Finish with TLS session
- *
- * @v tls TLS session
- * @v rc Status code
- */
- static void tls_close ( struct tls_session *tls, int rc ) {
-
- /* Remove process */
- process_del ( &tls->process );
-
- /* Close ciphertext and plaintext streams */
- xfer_nullify ( &tls->cipherstream.xfer );
- xfer_close ( &tls->cipherstream.xfer, rc );
- xfer_nullify ( &tls->plainstream.xfer );
- xfer_close ( &tls->plainstream.xfer, rc );
- }
-
- /******************************************************************************
- *
- * Random number generation
- *
- ******************************************************************************
- */
-
- /**
- * Generate random data
- *
- * @v data Buffer to fill
- * @v len Length of buffer
- */
- static void tls_generate_random ( void *data, size_t len ) {
- /* FIXME: Some real random data source would be nice... */
- memset ( data, 0x01, len );
- }
-
- /**
- * Update HMAC with a list of ( data, len ) pairs
- *
- * @v digest Hash function to use
- * @v digest_ctx Digest context
- * @v args ( data, len ) pairs of data, terminated by NULL
- */
- static void tls_hmac_update_va ( struct crypto_algorithm *digest,
- void *digest_ctx, va_list args ) {
- void *data;
- size_t len;
-
- while ( ( data = va_arg ( args, void * ) ) ) {
- len = va_arg ( args, size_t );
- hmac_update ( digest, digest_ctx, data, len );
- }
- }
-
- /**
- * Generate secure pseudo-random data using a single hash function
- *
- * @v tls TLS session
- * @v digest Hash function to use
- * @v secret Secret
- * @v secret_len Length of secret
- * @v out Output buffer
- * @v out_len Length of output buffer
- * @v seeds ( data, len ) pairs of seed data, terminated by NULL
- */
- static void tls_p_hash_va ( struct tls_session *tls,
- struct crypto_algorithm *digest,
- void *secret, size_t secret_len,
- void *out, size_t out_len,
- va_list seeds ) {
- uint8_t secret_copy[secret_len];
- uint8_t digest_ctx[digest->ctxsize];
- uint8_t digest_ctx_partial[digest->ctxsize];
- uint8_t a[digest->digestsize];
- uint8_t out_tmp[digest->digestsize];
- size_t frag_len = digest->digestsize;
- va_list tmp;
-
- /* Copy the secret, in case HMAC modifies it */
- memcpy ( secret_copy, secret, secret_len );
- secret = secret_copy;
- DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name );
- DBGC2_HD ( tls, secret, secret_len );
-
- /* Calculate A(1) */
- hmac_init ( digest, digest_ctx, secret, &secret_len );
- va_copy ( tmp, seeds );
- tls_hmac_update_va ( digest, digest_ctx, tmp );
- va_end ( tmp );
- hmac_final ( digest, digest_ctx, secret, &secret_len, a );
- DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name );
- DBGC2_HD ( tls, &a, sizeof ( a ) );
-
- /* Generate as much data as required */
- while ( out_len ) {
- /* Calculate output portion */
- hmac_init ( digest, digest_ctx, secret, &secret_len );
- hmac_update ( digest, digest_ctx, a, sizeof ( a ) );
- memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize );
- va_copy ( tmp, seeds );
- tls_hmac_update_va ( digest, digest_ctx, tmp );
- va_end ( tmp );
- hmac_final ( digest, digest_ctx,
- secret, &secret_len, out_tmp );
-
- /* Copy output */
- if ( frag_len > out_len )
- frag_len = out_len;
- memcpy ( out, out_tmp, frag_len );
- DBGC2 ( tls, "TLS %p %s output:\n", tls, digest->name );
- DBGC2_HD ( tls, out, frag_len );
-
- /* Calculate A(i) */
- hmac_final ( digest, digest_ctx_partial,
- secret, &secret_len, a );
- DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name );
- DBGC2_HD ( tls, &a, sizeof ( a ) );
-
- out += frag_len;
- out_len -= frag_len;
- }
- }
-
- /**
- * Generate secure pseudo-random data
- *
- * @v tls TLS session
- * @v secret Secret
- * @v secret_len Length of secret
- * @v out Output buffer
- * @v out_len Length of output buffer
- * @v ... ( data, len ) pairs of seed data, terminated by NULL
- */
- static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len,
- void *out, size_t out_len, ... ) {
- va_list seeds;
- va_list tmp;
- size_t subsecret_len;
- void *md5_secret;
- void *sha1_secret;
- uint8_t out_md5[out_len];
- uint8_t out_sha1[out_len];
- unsigned int i;
-
- va_start ( seeds, out_len );
-
- /* Split secret into two, with an overlap of up to one byte */
- subsecret_len = ( ( secret_len + 1 ) / 2 );
- md5_secret = secret;
- sha1_secret = ( secret + secret_len - subsecret_len );
-
- /* Calculate MD5 portion */
- va_copy ( tmp, seeds );
- tls_p_hash_va ( tls, &md5_algorithm, md5_secret, subsecret_len,
- out_md5, out_len, seeds );
- va_end ( tmp );
-
- /* Calculate SHA1 portion */
- va_copy ( tmp, seeds );
- tls_p_hash_va ( tls, &sha1_algorithm, sha1_secret, subsecret_len,
- out_sha1, out_len, seeds );
- va_end ( tmp );
-
- /* XOR the two portions together into the final output buffer */
- for ( i = 0 ; i < out_len ; i++ ) {
- *( ( uint8_t * ) out + i ) = ( out_md5[i] ^ out_sha1[i] );
- }
-
- va_end ( seeds );
- }
-
- /**
- * Generate secure pseudo-random data
- *
- * @v secret Secret
- * @v secret_len Length of secret
- * @v out Output buffer
- * @v out_len Length of output buffer
- * @v label String literal label
- * @v ... ( data, len ) pairs of seed data
- */
- #define tls_prf_label( tls, secret, secret_len, out, out_len, label, ... ) \
- tls_prf ( (tls), (secret), (secret_len), (out), (out_len), \
- label, ( sizeof ( label ) - 1 ), __VA_ARGS__, NULL )
-
- /******************************************************************************
- *
- * Secret management
- *
- ******************************************************************************
- */
-
- /**
- * Generate master secret
- *
- * @v tls TLS session
- *
- * The pre-master secret and the client and server random values must
- * already be known.
- */
- static void tls_generate_master_secret ( struct tls_session *tls ) {
- DBGC ( tls, "TLS %p pre-master-secret:\n", tls );
- DBGC_HD ( tls, &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ) );
- DBGC ( tls, "TLS %p client random bytes:\n", tls );
- DBGC_HD ( tls, &tls->client_random, sizeof ( tls->server_random ) );
- DBGC ( tls, "TLS %p server random bytes:\n", tls );
- DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) );
-
- tls_prf_label ( tls, tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ),
- tls->master_secret, sizeof ( tls->master_secret ),
- "master secret",
- tls->client_random, sizeof ( tls->client_random ),
- tls->server_random, sizeof ( tls->server_random ) );
-
- DBGC ( tls, "TLS %p generated master secret:\n", tls );
- DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) );
- }
-
- /**
- * Generate key material
- *
- * @v tls TLS session
- *
- * The master secret must already be known.
- */
- static int tls_generate_keys ( struct tls_session *tls ) {
- struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending;
- struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending;
- size_t hash_size = tx_cipherspec->digest->digestsize;
- size_t key_size = tx_cipherspec->key_len;
- size_t iv_size = tx_cipherspec->cipher->blocksize;
- size_t total = ( 2 * ( hash_size + key_size + iv_size ) );
- uint8_t key_block[total];
- uint8_t *key;
- int rc;
-
- /* Generate key block */
- tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
- key_block, sizeof ( key_block ), "key expansion",
- tls->server_random, sizeof ( tls->server_random ),
- tls->client_random, sizeof ( tls->client_random ) );
-
- /* Split key block into portions */
- key = key_block;
-
- /* TX MAC secret */
- memcpy ( tx_cipherspec->mac_secret, key, hash_size );
- DBGC ( tls, "TLS %p TX MAC secret:\n", tls );
- DBGC_HD ( tls, key, hash_size );
- key += hash_size;
-
- /* RX MAC secret */
- memcpy ( rx_cipherspec->mac_secret, key, hash_size );
- DBGC ( tls, "TLS %p RX MAC secret:\n", tls );
- DBGC_HD ( tls, key, hash_size );
- key += hash_size;
-
- /* TX key */
- if ( ( rc = cipher_setkey ( tx_cipherspec->cipher,
- tx_cipherspec->cipher_ctx,
- key, key_size ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not set TX key: %s\n",
- tls, strerror ( rc ) );
- return rc;
- }
- DBGC ( tls, "TLS %p TX key:\n", tls );
- DBGC_HD ( tls, key, key_size );
- key += key_size;
-
- /* RX key */
- if ( ( rc = cipher_setkey ( rx_cipherspec->cipher,
- rx_cipherspec->cipher_ctx,
- key, key_size ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not set TX key: %s\n",
- tls, strerror ( rc ) );
- return rc;
- }
-
- /* FIXME: AES needs to be fixed to not require this */
- AES_convert_key ( rx_cipherspec->cipher_ctx );
-
- DBGC ( tls, "TLS %p RX key:\n", tls );
- DBGC_HD ( tls, key, key_size );
- key += key_size;
-
- /* TX initialisation vector */
- cipher_setiv ( tx_cipherspec->cipher, tx_cipherspec->cipher_ctx, key );
- DBGC ( tls, "TLS %p TX IV:\n", tls );
- DBGC_HD ( tls, key, iv_size );
- key += iv_size;
-
- /* RX initialisation vector */
- cipher_setiv ( rx_cipherspec->cipher, rx_cipherspec->cipher_ctx, key );
- DBGC ( tls, "TLS %p RX IV:\n", tls );
- DBGC_HD ( tls, key, iv_size );
- key += iv_size;
-
- assert ( ( key_block + total ) == key );
-
- return 0;
- }
-
- /******************************************************************************
- *
- * Cipher suite management
- *
- ******************************************************************************
- */
-
- /**
- * Clear cipher suite
- *
- * @v cipherspec TLS cipher specification
- */
- static void tls_clear_cipher ( struct tls_session *tls __unused,
- struct tls_cipherspec *cipherspec ) {
- free ( cipherspec->dynamic );
- memset ( cipherspec, 0, sizeof ( cipherspec ) );
- cipherspec->pubkey = &crypto_null;
- cipherspec->cipher = &crypto_null;
- cipherspec->digest = &crypto_null;
- }
-
- /**
- * Set cipher suite
- *
- * @v tls TLS session
- * @v cipherspec TLS cipher specification
- * @v pubkey Public-key encryption elgorithm
- * @v cipher Bulk encryption cipher algorithm
- * @v digest MAC digest algorithm
- * @v key_len Key length
- * @ret rc Return status code
- */
- static int tls_set_cipher ( struct tls_session *tls,
- struct tls_cipherspec *cipherspec,
- struct crypto_algorithm *pubkey,
- struct crypto_algorithm *cipher,
- struct crypto_algorithm *digest,
- size_t key_len ) {
- size_t total;
- void *dynamic;
-
- /* Clear out old cipher contents, if any */
- tls_clear_cipher ( tls, cipherspec );
-
- /* Allocate dynamic storage */
- total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize );
- dynamic = malloc ( total );
- if ( ! dynamic ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
- "context\n", tls, total );
- return -ENOMEM;
- }
- memset ( dynamic, 0, total );
-
- /* Assign storage */
- cipherspec->dynamic = dynamic;
- cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize;
- cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize;
- cipherspec->cipher_next_ctx = dynamic; dynamic += cipher->ctxsize;
- cipherspec->mac_secret = dynamic; dynamic += digest->digestsize;
- assert ( ( cipherspec->dynamic + total ) == dynamic );
-
- /* Store parameters */
- cipherspec->pubkey = pubkey;
- cipherspec->cipher = cipher;
- cipherspec->digest = digest;
- cipherspec->key_len = key_len;
-
- return 0;
- }
-
- /**
- * Select next cipher suite
- *
- * @v tls TLS session
- * @v cipher_suite Cipher suite specification
- * @ret rc Return status code
- */
- static int tls_select_cipher ( struct tls_session *tls,
- unsigned int cipher_suite ) {
- struct crypto_algorithm *pubkey = &crypto_null;
- struct crypto_algorithm *cipher = &crypto_null;
- struct crypto_algorithm *digest = &crypto_null;
- unsigned int key_len = 0;
- int rc;
-
- switch ( cipher_suite ) {
- case htons ( TLS_RSA_WITH_AES_128_CBC_SHA ):
- key_len = ( 128 / 8 );
- cipher = &aes_algorithm;
- digest = &sha1_algorithm;
- break;
- case htons ( TLS_RSA_WITH_AES_256_CBC_SHA ):
- key_len = ( 256 / 8 );
- cipher = &aes_algorithm;
- digest = &sha1_algorithm;
- break;
- default:
- DBGC ( tls, "TLS %p does not support cipher %04x\n",
- tls, ntohs ( cipher_suite ) );
- return -ENOTSUP;
- }
-
- /* Set ciphers */
- if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, pubkey,
- cipher, digest, key_len ) ) != 0 )
- return rc;
- if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, pubkey,
- cipher, digest, key_len ) ) != 0 )
- return rc;
-
- DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls,
- pubkey->name, cipher->name, ( key_len * 8 ), digest->name );
-
- return 0;
- }
-
- /**
- * Activate next cipher suite
- *
- * @v tls TLS session
- * @v pending Pending cipher specification
- * @v active Active cipher specification to replace
- * @ret rc Return status code
- */
- static int tls_change_cipher ( struct tls_session *tls,
- struct tls_cipherspec *pending,
- struct tls_cipherspec *active ) {
-
- /* Sanity check */
- if ( /* FIXME (when pubkey is not hard-coded to RSA):
- * ( pending->pubkey == &crypto_null ) || */
- ( pending->cipher == &crypto_null ) ||
- ( pending->digest == &crypto_null ) ) {
- DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
- return -ENOTSUP;
- }
-
- tls_clear_cipher ( tls, active );
- memswap ( active, pending, sizeof ( *active ) );
- return 0;
- }
-
- /******************************************************************************
- *
- * Handshake verification
- *
- ******************************************************************************
- */
-
- /**
- * Add handshake record to verification hash
- *
- * @v tls TLS session
- * @v data Handshake record
- * @v len Length of handshake record
- */
- static void tls_add_handshake ( struct tls_session *tls,
- const void *data, size_t len ) {
-
- digest_update ( &md5_algorithm, tls->handshake_md5_ctx, data, len );
- digest_update ( &sha1_algorithm, tls->handshake_sha1_ctx, data, len );
- }
-
- /**
- * Calculate handshake verification hash
- *
- * @v tls TLS session
- * @v out Output buffer
- *
- * Calculates the MD5+SHA1 digest over all handshake messages seen so
- * far.
- */
- static void tls_verify_handshake ( struct tls_session *tls, void *out ) {
- struct crypto_algorithm *md5 = &md5_algorithm;
- struct crypto_algorithm *sha1 = &sha1_algorithm;
- uint8_t md5_ctx[md5->ctxsize];
- uint8_t sha1_ctx[sha1->ctxsize];
- void *md5_digest = out;
- void *sha1_digest = ( out + md5->digestsize );
-
- memcpy ( md5_ctx, tls->handshake_md5_ctx, sizeof ( md5_ctx ) );
- memcpy ( sha1_ctx, tls->handshake_sha1_ctx, sizeof ( sha1_ctx ) );
- digest_final ( md5, md5_ctx, md5_digest );
- digest_final ( sha1, sha1_ctx, sha1_digest );
- }
-
- /******************************************************************************
- *
- * Record handling
- *
- ******************************************************************************
- */
-
- /**
- * Transmit Handshake record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_send_handshake ( struct tls_session *tls,
- void *data, size_t len ) {
-
- /* Add to handshake digest */
- tls_add_handshake ( tls, data, len );
-
- /* Send record */
- return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len );
- }
-
- /**
- * Transmit Client Hello record
- *
- * @v tls TLS session
- * @ret rc Return status code
- */
- static int tls_send_client_hello ( struct tls_session *tls ) {
- struct {
- uint32_t type_length;
- uint16_t version;
- uint8_t random[32];
- uint8_t session_id_len;
- uint16_t cipher_suite_len;
- uint16_t cipher_suites[2];
- uint8_t compression_methods_len;
- uint8_t compression_methods[1];
- } __attribute__ (( packed )) hello;
-
- memset ( &hello, 0, sizeof ( hello ) );
- hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
- htonl ( sizeof ( hello ) -
- sizeof ( hello.type_length ) ) );
- hello.version = htons ( TLS_VERSION_TLS_1_0 );
- memcpy ( &hello.random, tls->client_random, sizeof ( hello.random ) );
- hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
- hello.cipher_suites[0] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA );
- hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA );
- hello.compression_methods_len = sizeof ( hello.compression_methods );
-
- return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
- }
-
- /**
- * Transmit Client Key Exchange record
- *
- * @v tls TLS session
- * @ret rc Return status code
- */
- static int tls_send_client_key_exchange ( struct tls_session *tls ) {
- /* FIXME: Hack alert */
- RSA_CTX *rsa_ctx;
- RSA_pub_key_new ( &rsa_ctx, tls->rsa_mod, tls->rsa_mod_len,
- tls->rsa_pub_exp, tls->rsa_pub_exp_len );
- struct {
- uint32_t type_length;
- uint16_t encrypted_pre_master_secret_len;
- uint8_t encrypted_pre_master_secret[rsa_ctx->num_octets];
- } __attribute__ (( packed )) key_xchg;
-
- memset ( &key_xchg, 0, sizeof ( key_xchg ) );
- key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
- htonl ( sizeof ( key_xchg ) -
- sizeof ( key_xchg.type_length ) ) );
- key_xchg.encrypted_pre_master_secret_len
- = htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) );
-
- /* FIXME: Hack alert */
- DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" );
- DBGC_HD ( tls, &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ) );
- DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
- DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
- RSA_encrypt ( rsa_ctx, tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ),
- key_xchg.encrypted_pre_master_secret, 0 );
- DBGC ( tls, "RSA encrypt done. Ciphertext:\n" );
- DBGC_HD ( tls, &key_xchg.encrypted_pre_master_secret,
- sizeof ( key_xchg.encrypted_pre_master_secret ) );
- RSA_free ( rsa_ctx );
-
-
- return tls_send_handshake ( tls, &key_xchg, sizeof ( key_xchg ) );
- }
-
- /**
- * Transmit Change Cipher record
- *
- * @v tls TLS session
- * @ret rc Return status code
- */
- static int tls_send_change_cipher ( struct tls_session *tls ) {
- static const uint8_t change_cipher[1] = { 1 };
- return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER,
- change_cipher, sizeof ( change_cipher ) );
- }
-
- /**
- * Transmit Finished record
- *
- * @v tls TLS session
- * @ret rc Return status code
- */
- static int tls_send_finished ( struct tls_session *tls ) {
- struct {
- uint32_t type_length;
- uint8_t verify_data[12];
- } __attribute__ (( packed )) finished;
- uint8_t digest[MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE];
-
- memset ( &finished, 0, sizeof ( finished ) );
- finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) |
- htonl ( sizeof ( finished ) -
- sizeof ( finished.type_length ) ) );
- tls_verify_handshake ( tls, digest );
- tls_prf_label ( tls, tls->master_secret, sizeof ( tls->master_secret ),
- finished.verify_data, sizeof ( finished.verify_data ),
- "client finished", digest, sizeof ( digest ) );
-
- return tls_send_handshake ( tls, &finished, sizeof ( finished ) );
- }
-
- /**
- * Receive new Change Cipher record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_change_cipher ( struct tls_session *tls,
- void *data, size_t len ) {
- int rc;
-
- if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) {
- DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
- &tls->rx_cipherspec ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not activate RX cipher: %s\n",
- tls, strerror ( rc ) );
- return rc;
- }
- tls->rx_seq = ~( ( uint64_t ) 0 );
-
- return 0;
- }
-
- /**
- * Receive new Alert record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_alert ( struct tls_session *tls, void *data, size_t len ) {
- struct {
- uint8_t level;
- uint8_t description;
- char next[0];
- } __attribute__ (( packed )) *alert = data;
- void *end = alert->next;
-
- /* Sanity check */
- if ( end != ( data + len ) ) {
- DBGC ( tls, "TLS %p received overlength Alert\n", tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- switch ( alert->level ) {
- case TLS_ALERT_WARNING:
- DBGC ( tls, "TLS %p received warning alert %d\n",
- tls, alert->description );
- return 0;
- case TLS_ALERT_FATAL:
- DBGC ( tls, "TLS %p received fatal alert %d\n",
- tls, alert->description );
- return -EPERM;
- default:
- DBGC ( tls, "TLS %p received unknown alert level %d"
- "(alert %d)\n", tls, alert->level, alert->description );
- return -EIO;
- }
- }
-
- /**
- * Receive new Server Hello record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_server_hello ( struct tls_session *tls,
- void *data, size_t len ) {
- struct {
- uint32_t type_length;
- uint16_t version;
- uint8_t random[32];
- uint8_t session_id_len;
- char next[0];
- } __attribute__ (( packed )) *hello_a = data;
- struct {
- uint8_t session_id[hello_a->session_id_len];
- uint16_t cipher_suite;
- uint8_t compression_method;
- char next[0];
- } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next;
- void *end = hello_b->next;
- int rc;
-
- /* Sanity check */
- if ( end != ( data + len ) ) {
- DBGC ( tls, "TLS %p received overlength Server Hello\n", tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- /* Check protocol version */
- if ( ntohs ( hello_a->version ) < TLS_VERSION_TLS_1_0 ) {
- DBGC ( tls, "TLS %p does not support protocol version %d.%d\n",
- tls, ( ntohs ( hello_a->version ) >> 8 ),
- ( ntohs ( hello_a->version ) & 0xff ) );
- return -ENOTSUP;
- }
-
- /* Copy out server random bytes */
- memcpy ( tls->server_random, hello_a->random,
- sizeof ( tls->server_random ) );
-
- /* Select cipher suite */
- if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 )
- return rc;
-
- /* Generate secrets */
- tls_generate_master_secret ( tls );
- if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
- return rc;
-
- return 0;
- }
-
- /**
- * Receive new Certificate record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_certificate ( struct tls_session *tls,
- void *data, size_t len ) {
- struct {
- uint32_t type_length;
- uint8_t length[3];
- uint8_t first_cert_length[3];
- uint8_t asn1_start[0];
- } __attribute__ (( packed )) *certificate = data;
- uint8_t *cert = certificate->asn1_start;
- int offset = 0;
-
- /* FIXME */
- (void) len;
-
- if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
- asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
- asn1_skip_obj(cert, &offset, ASN1_EXPLICIT_TAG) ||
- asn1_skip_obj(cert, &offset, ASN1_INTEGER) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 ||
- asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) ||
- asn1_next_obj(cert, &offset, ASN1_BIT_STRING) < 0) {
- DBGC ( tls, "TLS %p invalid certificate\n", tls );
- DBGC_HD ( tls, cert + offset, 64 );
- return -EPERM;
- }
-
- offset++;
-
- if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) {
- DBGC ( tls, "TLS %p invalid certificate\n", tls );
- DBGC_HD ( tls, cert + offset, 64 );
- return -EPERM;
- }
-
- tls->rsa_mod_len = asn1_get_int(cert, &offset, &tls->rsa_mod);
- tls->rsa_pub_exp_len = asn1_get_int(cert, &offset, &tls->rsa_pub_exp);
-
- DBGC_HD ( tls, tls->rsa_mod, tls->rsa_mod_len );
- DBGC_HD ( tls, tls->rsa_pub_exp, tls->rsa_pub_exp_len );
-
- return 0;
- }
-
- /**
- * Receive new Server Hello Done record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_server_hello_done ( struct tls_session *tls,
- void *data, size_t len ) {
- struct {
- uint32_t type_length;
- char next[0];
- } __attribute__ (( packed )) *hello_done = data;
- void *end = hello_done->next;
-
- /* Sanity check */
- if ( end != ( data + len ) ) {
- DBGC ( tls, "TLS %p received overlength Server Hello Done\n",
- tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- /* Check that we are ready to send the Client Key Exchange */
- if ( tls->tx_state != TLS_TX_NONE ) {
- DBGC ( tls, "TLS %p received Server Hello Done while in "
- "TX state %d\n", tls, tls->tx_state );
- return -EIO;
- }
-
- /* Start sending the Client Key Exchange */
- tls->tx_state = TLS_TX_CLIENT_KEY_EXCHANGE;
-
- return 0;
- }
-
- /**
- * Receive new Finished record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_finished ( struct tls_session *tls,
- void *data, size_t len ) {
-
- /* FIXME: Handle this properly */
- tls->tx_state = TLS_TX_DATA;
- ( void ) data;
- ( void ) len;
- return 0;
- }
-
- /**
- * Receive new Handshake record
- *
- * @v tls TLS session
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_handshake ( struct tls_session *tls,
- void *data, size_t len ) {
- uint8_t *type = data;
- int rc;
-
- switch ( *type ) {
- case TLS_SERVER_HELLO:
- rc = tls_new_server_hello ( tls, data, len );
- break;
- case TLS_CERTIFICATE:
- rc = tls_new_certificate ( tls, data, len );
- break;
- case TLS_SERVER_HELLO_DONE:
- rc = tls_new_server_hello_done ( tls, data, len );
- break;
- case TLS_FINISHED:
- rc = tls_new_finished ( tls, data, len );
- break;
- default:
- DBGC ( tls, "TLS %p ignoring handshake type %d\n",
- tls, *type );
- rc = 0;
- break;
- }
-
- /* Add to handshake digest (except for Hello Requests, which
- * are explicitly excludede).
- */
- if ( *type != TLS_HELLO_REQUEST )
- tls_add_handshake ( tls, data, len );
-
- return rc;
- }
-
- /**
- * Receive new record
- *
- * @v tls TLS session
- * @v type Record type
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_new_record ( struct tls_session *tls,
- unsigned int type, void *data, size_t len ) {
-
- switch ( type ) {
- case TLS_TYPE_CHANGE_CIPHER:
- return tls_new_change_cipher ( tls, data, len );
- case TLS_TYPE_ALERT:
- return tls_new_alert ( tls, data, len );
- case TLS_TYPE_HANDSHAKE:
- return tls_new_handshake ( tls, data, len );
- case TLS_TYPE_DATA:
- return xfer_deliver_raw ( &tls->plainstream.xfer, data, len );
- default:
- /* RFC4346 says that we should just ignore unknown
- * record types.
- */
- DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
- return 0;
- }
- }
-
- /******************************************************************************
- *
- * Record encryption/decryption
- *
- ******************************************************************************
- */
-
- /**
- * Calculate HMAC
- *
- * @v tls TLS session
- * @v cipherspec Cipher specification
- * @v seq Sequence number
- * @v tlshdr TLS header
- * @v data Data
- * @v len Length of data
- * @v mac HMAC to fill in
- */
- static void tls_hmac ( struct tls_session *tls __unused,
- struct tls_cipherspec *cipherspec,
- uint64_t seq, struct tls_header *tlshdr,
- const void *data, size_t len, void *hmac ) {
- struct crypto_algorithm *digest = cipherspec->digest;
- uint8_t digest_ctx[digest->ctxsize];
-
- hmac_init ( digest, digest_ctx, cipherspec->mac_secret,
- &digest->digestsize );
- seq = cpu_to_be64 ( seq );
- hmac_update ( digest, digest_ctx, &seq, sizeof ( seq ) );
- hmac_update ( digest, digest_ctx, tlshdr, sizeof ( *tlshdr ) );
- hmac_update ( digest, digest_ctx, data, len );
- hmac_final ( digest, digest_ctx, cipherspec->mac_secret,
- &digest->digestsize, hmac );
- }
-
- /**
- * Allocate and assemble stream-ciphered record from data and MAC portions
- *
- * @v tls TLS session
- * @ret data Data
- * @ret len Length of data
- * @ret digest MAC digest
- * @ret plaintext_len Length of plaintext record
- * @ret plaintext Allocated plaintext record
- */
- static void * __malloc tls_assemble_stream ( struct tls_session *tls,
- const void *data, size_t len,
- void *digest, size_t *plaintext_len ) {
- size_t mac_len = tls->tx_cipherspec.digest->digestsize;
- void *plaintext;
- void *content;
- void *mac;
-
- /* Calculate stream-ciphered struct length */
- *plaintext_len = ( len + mac_len );
-
- /* Allocate stream-ciphered struct */
- plaintext = malloc ( *plaintext_len );
- if ( ! plaintext )
- return NULL;
- content = plaintext;
- mac = ( content + len );
-
- /* Fill in stream-ciphered struct */
- memcpy ( content, data, len );
- memcpy ( mac, digest, mac_len );
-
- return plaintext;
- }
-
- /**
- * Allocate and assemble block-ciphered record from data and MAC portions
- *
- * @v tls TLS session
- * @ret data Data
- * @ret len Length of data
- * @ret digest MAC digest
- * @ret plaintext_len Length of plaintext record
- * @ret plaintext Allocated plaintext record
- */
- static void * tls_assemble_block ( struct tls_session *tls,
- const void *data, size_t len,
- void *digest, size_t *plaintext_len ) {
- size_t blocksize = tls->tx_cipherspec.cipher->blocksize;
- size_t iv_len = blocksize;
- size_t mac_len = tls->tx_cipherspec.digest->digestsize;
- size_t padding_len;
- void *plaintext;
- void *iv;
- void *content;
- void *mac;
- void *padding;
-
- /* FIXME: TLSv1.1 has an explicit IV */
- iv_len = 0;
-
- /* Calculate block-ciphered struct length */
- padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) );
- *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 );
-
- /* Allocate block-ciphered struct */
- plaintext = malloc ( *plaintext_len );
- if ( ! plaintext )
- return NULL;
- iv = plaintext;
- content = ( iv + iv_len );
- mac = ( content + len );
- padding = ( mac + mac_len );
-
- /* Fill in block-ciphered struct */
- memset ( iv, 0, iv_len );
- memcpy ( content, data, len );
- memcpy ( mac, digest, mac_len );
- memset ( padding, padding_len, ( padding_len + 1 ) );
-
- return plaintext;
- }
-
- /**
- * Send plaintext record
- *
- * @v tls TLS session
- * @v type Record type
- * @v data Plaintext record
- * @v len Length of plaintext record
- * @ret rc Return status code
- */
- static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
- const void *data, size_t len ) {
- struct tls_header plaintext_tlshdr;
- struct tls_header *tlshdr;
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
- void *plaintext = NULL;
- size_t plaintext_len;
- struct io_buffer *ciphertext = NULL;
- size_t ciphertext_len;
- size_t mac_len = cipherspec->digest->digestsize;
- uint8_t mac[mac_len];
- int rc;
-
- /* Construct header */
- plaintext_tlshdr.type = type;
- plaintext_tlshdr.version = htons ( TLS_VERSION_TLS_1_0 );
- plaintext_tlshdr.length = htons ( len );
-
- /* Calculate MAC */
- tls_hmac ( tls, cipherspec, tls->tx_seq, &plaintext_tlshdr,
- data, len, mac );
-
- /* Allocate and assemble plaintext struct */
- if ( is_stream_cipher ( cipherspec->cipher ) ) {
- plaintext = tls_assemble_stream ( tls, data, len, mac,
- &plaintext_len );
- } else {
- plaintext = tls_assemble_block ( tls, data, len, mac,
- &plaintext_len );
- }
- if ( ! plaintext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "plaintext\n", tls, plaintext_len );
- rc = -ENOMEM;
- goto done;
- }
-
- DBGC2 ( tls, "Sending plaintext data:\n" );
- DBGC2_HD ( tls, plaintext, plaintext_len );
-
- /* Allocate ciphertext */
- ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len );
- ciphertext = xfer_alloc_iob ( &tls->cipherstream.xfer,
- ciphertext_len );
- if ( ! ciphertext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "ciphertext\n", tls, ciphertext_len );
- rc = -ENOMEM;
- goto done;
- }
-
- /* Assemble ciphertext */
- tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
- tlshdr->type = type;
- tlshdr->version = htons ( TLS_VERSION_TLS_1_0 );
- tlshdr->length = htons ( plaintext_len );
- memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx,
- cipherspec->cipher->ctxsize );
- if ( ( rc = cipher_encrypt ( cipherspec->cipher,
- cipherspec->cipher_next_ctx, plaintext,
- iob_put ( ciphertext, plaintext_len ),
- plaintext_len ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not encrypt: %s\n",
- tls, strerror ( rc ) );
- DBGC_HD ( tls, plaintext, plaintext_len );
- goto done;
- }
-
- /* Free plaintext as soon as possible to conserve memory */
- free ( plaintext );
- plaintext = NULL;
-
- /* Send ciphertext */
- rc = xfer_deliver_iob ( &tls->cipherstream.xfer, ciphertext );
- ciphertext = NULL;
- if ( rc != 0 ) {
- DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n",
- tls, strerror ( rc ) );
- goto done;
- }
-
- /* Update TX state machine to next record */
- tls->tx_seq += 1;
- memcpy ( tls->tx_cipherspec.cipher_ctx,
- tls->tx_cipherspec.cipher_next_ctx,
- tls->tx_cipherspec.cipher->ctxsize );
-
- done:
- free ( plaintext );
- free_iob ( ciphertext );
- return rc;
- }
-
- /**
- * Split stream-ciphered record into data and MAC portions
- *
- * @v tls TLS session
- * @v plaintext Plaintext record
- * @v plaintext_len Length of record
- * @ret data Data
- * @ret len Length of data
- * @ret digest MAC digest
- * @ret rc Return status code
- */
- static int tls_split_stream ( struct tls_session *tls,
- void *plaintext, size_t plaintext_len,
- void **data, size_t *len, void **digest ) {
- void *content;
- size_t content_len;
- void *mac;
- size_t mac_len;
-
- /* Decompose stream-ciphered data */
- mac_len = tls->rx_cipherspec.digest->digestsize;
- if ( plaintext_len < mac_len ) {
- DBGC ( tls, "TLS %p received underlength record\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- content_len = ( plaintext_len - mac_len );
- content = plaintext;
- mac = ( content + content_len );
-
- /* Fill in return values */
- *data = content;
- *len = content_len;
- *digest = mac;
-
- return 0;
- }
-
- /**
- * Split block-ciphered record into data and MAC portions
- *
- * @v tls TLS session
- * @v plaintext Plaintext record
- * @v plaintext_len Length of record
- * @ret data Data
- * @ret len Length of data
- * @ret digest MAC digest
- * @ret rc Return status code
- */
- static int tls_split_block ( struct tls_session *tls,
- void *plaintext, size_t plaintext_len,
- void **data, size_t *len,
- void **digest ) {
- void *iv;
- size_t iv_len;
- void *content;
- size_t content_len;
- void *mac;
- size_t mac_len;
- void *padding;
- size_t padding_len;
- unsigned int i;
-
- /* Decompose block-ciphered data */
- if ( plaintext_len < 1 ) {
- DBGC ( tls, "TLS %p received underlength record\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- iv_len = tls->rx_cipherspec.cipher->blocksize;
-
- /* FIXME: TLSv1.1 uses an explicit IV */
- iv_len = 0;
-
- mac_len = tls->rx_cipherspec.digest->digestsize;
- padding_len = *( ( uint8_t * ) ( plaintext + plaintext_len - 1 ) );
- if ( plaintext_len < ( iv_len + mac_len + padding_len + 1 ) ) {
- DBGC ( tls, "TLS %p received underlength record\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- content_len = ( plaintext_len - iv_len - mac_len - padding_len - 1 );
- iv = plaintext;
- content = ( iv + iv_len );
- mac = ( content + content_len );
- padding = ( mac + mac_len );
-
- /* Verify padding bytes */
- for ( i = 0 ; i < padding_len ; i++ ) {
- if ( *( ( uint8_t * ) ( padding + i ) ) != padding_len ) {
- DBGC ( tls, "TLS %p received bad padding\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- }
-
- /* Fill in return values */
- *data = content;
- *len = content_len;
- *digest = mac;
-
- return 0;
- }
-
- /**
- * Receive new ciphertext record
- *
- * @v tls TLS session
- * @v tlshdr Record header
- * @v ciphertext Ciphertext record
- * @ret rc Return status code
- */
- static int tls_new_ciphertext ( struct tls_session *tls,
- struct tls_header *tlshdr, void *ciphertext ) {
- struct tls_header plaintext_tlshdr;
- struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
- size_t record_len = ntohs ( tlshdr->length );
- void *plaintext = NULL;
- void *data;
- size_t len;
- void *mac;
- size_t mac_len = cipherspec->digest->digestsize;
- uint8_t verify_mac[mac_len];
- int rc;
-
- /* Allocate buffer for plaintext */
- plaintext = malloc ( record_len );
- if ( ! plaintext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "decryption buffer\n", tls, record_len );
- rc = -ENOMEM;
- goto done;
- }
-
- /* Decrypt the record */
- if ( ( rc = cipher_decrypt ( cipherspec->cipher,
- cipherspec->cipher_ctx, ciphertext,
- plaintext, record_len ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not decrypt: %s\n",
- tls, strerror ( rc ) );
- DBGC_HD ( tls, ciphertext, record_len );
- goto done;
- }
-
- /* Split record into content and MAC */
- if ( is_stream_cipher ( cipherspec->cipher ) ) {
- if ( ( rc = tls_split_stream ( tls, plaintext, record_len,
- &data, &len, &mac ) ) != 0 )
- goto done;
- } else {
- if ( ( rc = tls_split_block ( tls, plaintext, record_len,
- &data, &len, &mac ) ) != 0 )
- goto done;
- }
-
- /* Verify MAC */
- plaintext_tlshdr.type = tlshdr->type;
- plaintext_tlshdr.version = tlshdr->version;
- plaintext_tlshdr.length = htons ( len );
- tls_hmac ( tls, cipherspec, tls->rx_seq, &plaintext_tlshdr,
- data, len, verify_mac);
- if ( memcmp ( mac, verify_mac, mac_len ) != 0 ) {
- DBGC ( tls, "TLS %p failed MAC verification\n", tls );
- DBGC_HD ( tls, plaintext, record_len );
- goto done;
- }
-
- DBGC2 ( tls, "Received plaintext data:\n" );
- DBGC2_HD ( tls, data, len );
-
- /* Process plaintext record */
- if ( ( rc = tls_new_record ( tls, tlshdr->type, data, len ) ) != 0 )
- goto done;
-
- rc = 0;
- done:
- free ( plaintext );
- return rc;
- }
-
- /******************************************************************************
- *
- * Plaintext stream operations
- *
- ******************************************************************************
- */
-
- /**
- * Close interface
- *
- * @v xfer Plainstream data transfer interface
- * @v rc Reason for close
- */
- static void tls_plainstream_close ( struct xfer_interface *xfer, int rc ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, plainstream.xfer );
-
- tls_close ( tls, rc );
- }
-
- /**
- * Check flow control window
- *
- * @v xfer Plainstream data transfer interface
- * @ret len Length of window
- */
- static size_t tls_plainstream_window ( struct xfer_interface *xfer ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, plainstream.xfer );
-
- /* Block window unless we are ready to accept data */
- if ( tls->tx_state != TLS_TX_DATA )
- return 0;
-
- return filter_window ( xfer );
- }
-
- /**
- * Deliver datagram as raw data
- *
- * @v xfer Plainstream data transfer interface
- * @v data Data buffer
- * @v len Length of data buffer
- * @ret rc Return status code
- */
- static int tls_plainstream_deliver_raw ( struct xfer_interface *xfer,
- const void *data, size_t len ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, plainstream.xfer );
-
- /* Refuse unless we are ready to accept data */
- if ( tls->tx_state != TLS_TX_DATA )
- return -ENOTCONN;
-
- return tls_send_plaintext ( tls, TLS_TYPE_DATA, data, len );
- }
-
- /** TLS plaintext stream operations */
- static struct xfer_interface_operations tls_plainstream_operations = {
- .close = tls_plainstream_close,
- .vredirect = ignore_xfer_vredirect,
- .window = tls_plainstream_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = tls_plainstream_deliver_raw,
- };
-
- /******************************************************************************
- *
- * Ciphertext stream operations
- *
- ******************************************************************************
- */
-
- /**
- * Close interface
- *
- * @v xfer Plainstream data transfer interface
- * @v rc Reason for close
- */
- static void tls_cipherstream_close ( struct xfer_interface *xfer, int rc ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, cipherstream.xfer );
-
- tls_close ( tls, rc );
- }
-
- /**
- * Handle received TLS header
- *
- * @v tls TLS session
- * @ret rc Returned status code
- */
- static int tls_newdata_process_header ( struct tls_session *tls ) {
- size_t data_len = ntohs ( tls->rx_header.length );
-
- /* Allocate data buffer now that we know the length */
- assert ( tls->rx_data == NULL );
- tls->rx_data = malloc ( data_len );
- if ( ! tls->rx_data ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes "
- "for receive buffer\n", tls, data_len );
- return -ENOMEM;
- }
-
- /* Move to data state */
- tls->rx_state = TLS_RX_DATA;
-
- return 0;
- }
-
- /**
- * Handle received TLS data payload
- *
- * @v tls TLS session
- * @ret rc Returned status code
- */
- static int tls_newdata_process_data ( struct tls_session *tls ) {
- int rc;
-
- /* Process record */
- if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header,
- tls->rx_data ) ) != 0 )
- return rc;
-
- /* Increment RX sequence number */
- tls->rx_seq += 1;
-
- /* Free data buffer */
- free ( tls->rx_data );
- tls->rx_data = NULL;
-
- /* Return to header state */
- tls->rx_state = TLS_RX_HEADER;
-
- return 0;
- }
-
- /**
- * Receive new ciphertext
- *
- * @v app Stream application
- * @v data Data received
- * @v len Length of received data
- * @ret rc Return status code
- */
- static int tls_cipherstream_deliver_raw ( struct xfer_interface *xfer,
- const void *data, size_t len ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, cipherstream.xfer );
- size_t frag_len;
- void *buf;
- size_t buf_len;
- int ( * process ) ( struct tls_session *tls );
- int rc;
-
- while ( len ) {
- /* Select buffer according to current state */
- switch ( tls->rx_state ) {
- case TLS_RX_HEADER:
- buf = &tls->rx_header;
- buf_len = sizeof ( tls->rx_header );
- process = tls_newdata_process_header;
- break;
- case TLS_RX_DATA:
- buf = tls->rx_data;
- buf_len = ntohs ( tls->rx_header.length );
- process = tls_newdata_process_data;
- break;
- default:
- assert ( 0 );
- return -EINVAL;
- }
-
- /* Copy data portion to buffer */
- frag_len = ( buf_len - tls->rx_rcvd );
- if ( frag_len > len )
- frag_len = len;
- memcpy ( ( buf + tls->rx_rcvd ), data, frag_len );
- tls->rx_rcvd += frag_len;
- data += frag_len;
- len -= frag_len;
-
- /* Process data if buffer is now full */
- if ( tls->rx_rcvd == buf_len ) {
- if ( ( rc = process ( tls ) ) != 0 ) {
- tls_close ( tls, rc );
- return rc;
- }
- tls->rx_rcvd = 0;
- }
- }
-
- return 0;
- }
-
- /** TLS ciphertext stream operations */
- static struct xfer_interface_operations tls_cipherstream_operations = {
- .close = tls_cipherstream_close,
- .vredirect = xfer_vopen,
- .window = filter_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = tls_cipherstream_deliver_raw,
- };
-
- /******************************************************************************
- *
- * Controlling process
- *
- ******************************************************************************
- */
-
- /**
- * TLS TX state machine
- *
- * @v process TLS process
- */
- static void tls_step ( struct process *process ) {
- struct tls_session *tls =
- container_of ( process, struct tls_session, process );
- int rc;
-
- /* Wait for cipherstream to become ready */
- if ( ! xfer_window ( &tls->cipherstream.xfer ) )
- return;
-
- switch ( tls->tx_state ) {
- case TLS_TX_NONE:
- /* Nothing to do */
- break;
- case TLS_TX_CLIENT_HELLO:
- /* Send Client Hello */
- if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not send Client Hello: %s\n",
- tls, strerror ( rc ) );
- goto err;
- }
- tls->tx_state = TLS_TX_NONE;
- break;
- case TLS_TX_CLIENT_KEY_EXCHANGE:
- /* Send Client Key Exchange */
- if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) {
- DBGC ( tls, "TLS %p could send Client Key Exchange: "
- "%s\n", tls, strerror ( rc ) );
- goto err;
- }
- tls->tx_state = TLS_TX_CHANGE_CIPHER;
- break;
- case TLS_TX_CHANGE_CIPHER:
- /* Send Change Cipher, and then change the cipher in use */
- if ( ( rc = tls_send_change_cipher ( tls ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not send Change Cipher: "
- "%s\n", tls, strerror ( rc ) );
- goto err;
- }
- if ( ( rc = tls_change_cipher ( tls,
- &tls->tx_cipherspec_pending,
- &tls->tx_cipherspec )) != 0 ){
- DBGC ( tls, "TLS %p could not activate TX cipher: "
- "%s\n", tls, strerror ( rc ) );
- goto err;
- }
- tls->tx_seq = 0;
- tls->tx_state = TLS_TX_FINISHED;
- break;
- case TLS_TX_FINISHED:
- /* Send Finished */
- if ( ( rc = tls_send_finished ( tls ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not send Finished: %s\n",
- tls, strerror ( rc ) );
- goto err;
- }
- tls->tx_state = TLS_TX_NONE;
- break;
- case TLS_TX_DATA:
- /* Nothing to do */
- break;
- default:
- assert ( 0 );
- }
-
- return;
-
- err:
- tls_close ( tls, rc );
- }
-
- /******************************************************************************
- *
- * Instantiator
- *
- ******************************************************************************
- */
-
- int add_tls ( struct xfer_interface *xfer, struct xfer_interface **next ) {
- struct tls_session *tls;
-
- /* Allocate and initialise TLS structure */
- tls = malloc ( sizeof ( *tls ) );
- if ( ! tls )
- return -ENOMEM;
- memset ( tls, 0, sizeof ( *tls ) );
- tls->refcnt.free = free_tls;
- filter_init ( &tls->plainstream, &tls_plainstream_operations,
- &tls->cipherstream, &tls_cipherstream_operations,
- &tls->refcnt );
- tls_clear_cipher ( tls, &tls->tx_cipherspec );
- tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
- tls_clear_cipher ( tls, &tls->rx_cipherspec );
- tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
- *( ( uint32_t * ) tls->client_random ) = 0; /* GMT Unix time */
- tls_generate_random ( ( tls->client_random + 4 ),
- ( sizeof ( tls->client_random ) - 4 ) );
- *( ( uint16_t * ) tls->pre_master_secret )
- = htons ( TLS_VERSION_TLS_1_0 );
- tls_generate_random ( ( tls->pre_master_secret + 2 ),
- ( sizeof ( tls->pre_master_secret ) - 2 ) );
- digest_init ( &md5_algorithm, tls->handshake_md5_ctx );
- digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx );
- tls->tx_state = TLS_TX_CLIENT_HELLO;
- process_init ( &tls->process, tls_step, &tls->refcnt );
-
- /* Attach to parent interface, mortalise self, and return */
- xfer_plug_plug ( &tls->plainstream.xfer, xfer );
- *next = &tls->cipherstream.xfer;
- ref_put ( &tls->refcnt );
- return 0;
- }
|