]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[tls] Formalise the definition of a TLS cipher suite
authorMichael Brown <mcb30@ipxe.org>
Mon, 5 Mar 2012 23:12:47 +0000 (23:12 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 5 Mar 2012 23:13:52 +0000 (23:13 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/tls.h
src/net/tls.c

index 8d4e709db9c524b3f63925c8ee156984eaeb38ea..804e1a1fb8035e30fe201911c4ec2cafd7987769 100644 (file)
@@ -99,8 +99,8 @@ enum tls_tx_pending {
        TLS_TX_FINISHED = 0x0010,
 };
 
-/** A TLS cipher specification */
-struct tls_cipherspec {
+/** A TLS cipher suite */
+struct tls_cipher_suite {
        /** Public-key encryption algorithm */
        struct pubkey_algorithm *pubkey;
        /** Bulk encryption cipher algorithm */
@@ -108,7 +108,15 @@ struct tls_cipherspec {
        /** MAC digest algorithm */
        struct digest_algorithm *digest;
        /** Key length */
-       size_t key_len;
+       uint16_t key_len;
+       /** Numeric code (in network-endian order) */
+       uint16_t code;
+};
+
+/** A TLS cipher specification */
+struct tls_cipherspec {
+       /** Cipher suite */
+       struct tls_cipher_suite *suite;
        /** Dynamically-allocated storage */
        void *dynamic;
        /** Public key encryption context */
index 4195e5c7709477a44d750e6bc5ce8ff747f29205..8940613c3a57d5496f4697b8dddc3e3ecc7c2b71 100644 (file)
@@ -346,9 +346,9 @@ static void tls_generate_master_secret ( struct tls_session *tls ) {
 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 hash_size = tx_cipherspec->suite->digest->digestsize;
+       size_t key_size = tx_cipherspec->suite->key_len;
+       size_t iv_size = tx_cipherspec->suite->cipher->blocksize;
        size_t total = ( 2 * ( hash_size + key_size + iv_size ) );
        uint8_t key_block[total];
        uint8_t *key;
@@ -376,7 +376,7 @@ static int tls_generate_keys ( struct tls_session *tls ) {
        key += hash_size;
 
        /* TX key */
-       if ( ( rc = cipher_setkey ( tx_cipherspec->cipher,
+       if ( ( rc = cipher_setkey ( tx_cipherspec->suite->cipher,
                                    tx_cipherspec->cipher_ctx,
                                    key, key_size ) ) != 0 ) {
                DBGC ( tls, "TLS %p could not set TX key: %s\n",
@@ -388,7 +388,7 @@ static int tls_generate_keys ( struct tls_session *tls ) {
        key += key_size;
 
        /* RX key */
-       if ( ( rc = cipher_setkey ( rx_cipherspec->cipher,
+       if ( ( rc = cipher_setkey ( rx_cipherspec->suite->cipher,
                                    rx_cipherspec->cipher_ctx,
                                    key, key_size ) ) != 0 ) {
                DBGC ( tls, "TLS %p could not set TX key: %s\n",
@@ -400,13 +400,15 @@ static int tls_generate_keys ( struct tls_session *tls ) {
        key += key_size;
 
        /* TX initialisation vector */
-       cipher_setiv ( tx_cipherspec->cipher, tx_cipherspec->cipher_ctx, key );
+       cipher_setiv ( tx_cipherspec->suite->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 );
+       cipher_setiv ( rx_cipherspec->suite->cipher,
+                      rx_cipherspec->cipher_ctx, key );
        DBGC ( tls, "TLS %p RX IV:\n", tls );
        DBGC_HD ( tls, key, iv_size );
        key += iv_size;
@@ -423,6 +425,70 @@ static int tls_generate_keys ( struct tls_session *tls ) {
  ******************************************************************************
  */
 
+/** Null cipher suite */
+struct tls_cipher_suite tls_cipher_suite_null = {
+       .pubkey = &pubkey_null,
+       .cipher = &cipher_null,
+       .digest = &digest_null,
+};
+
+/** Supported cipher suites, in order of preference */
+struct tls_cipher_suite tls_cipher_suites[] = {
+       {
+               .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ),
+               .key_len = ( 256 / 8 ),
+               .pubkey = &pubkey_null, /* FIXME */
+               .cipher = &aes_cbc_algorithm,
+               .digest = &sha256_algorithm,
+       },
+       {
+               .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ),
+               .key_len = ( 128 / 8 ),
+               .pubkey = &pubkey_null, /* FIXME */
+               .cipher = &aes_cbc_algorithm,
+               .digest = &sha256_algorithm,
+       },
+       {
+               .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ),
+               .key_len = ( 256 / 8 ),
+               .pubkey = &pubkey_null, /* FIXME */
+               .cipher = &aes_cbc_algorithm,
+               .digest = &sha1_algorithm,
+       },
+       {
+               .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ),
+               .key_len = ( 128 / 8 ),
+               .pubkey = &pubkey_null, /* FIXME */
+               .cipher = &aes_cbc_algorithm,
+               .digest = &sha1_algorithm,
+       },
+};
+
+/** Number of supported cipher suites */
+#define TLS_NUM_CIPHER_SUITES \
+       ( sizeof ( tls_cipher_suites ) / sizeof ( tls_cipher_suites[0] ) )
+
+/**
+ * Identify cipher suite
+ *
+ * @v cipher_suite     Cipher suite specification
+ * @ret suite          Cipher suite, or NULL
+ */
+static struct tls_cipher_suite *
+tls_find_cipher_suite ( unsigned int cipher_suite ) {
+       struct tls_cipher_suite *suite;
+       unsigned int i;
+
+       /* Identify cipher suite */
+       for ( i = 0 ; i < TLS_NUM_CIPHER_SUITES ; i++ ) {
+               suite = &tls_cipher_suites[i];
+               if ( suite->code == cipher_suite )
+                       return suite;
+       }
+
+       return NULL;
+}
+
 /**
  * Clear cipher suite
  *
@@ -432,9 +498,7 @@ static void tls_clear_cipher ( struct tls_session *tls __unused,
                               struct tls_cipherspec *cipherspec ) {
        free ( cipherspec->dynamic );
        memset ( cipherspec, 0, sizeof ( cipherspec ) );
-       cipherspec->pubkey = &pubkey_null;
-       cipherspec->cipher = &cipher_null;
-       cipherspec->digest = &digest_null;
+       cipherspec->suite = &tls_cipher_suite_null;
 }
 
 /**
@@ -442,18 +506,15 @@ static void tls_clear_cipher ( struct tls_session *tls __unused,
  *
  * @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
+ * @v suite            Cipher suite
  * @ret rc             Return status code
  */
 static int tls_set_cipher ( struct tls_session *tls,
                            struct tls_cipherspec *cipherspec,
-                           struct pubkey_algorithm *pubkey,
-                           struct cipher_algorithm *cipher,
-                           struct digest_algorithm *digest,
-                           size_t key_len ) {
+                           struct tls_cipher_suite *suite ) {
+       struct pubkey_algorithm *pubkey = suite->pubkey;
+       struct cipher_algorithm *cipher = suite->cipher;
+       struct digest_algorithm *digest = suite->digest;
        size_t total;
        void *dynamic;
 
@@ -479,10 +540,7 @@ static int tls_set_cipher ( struct tls_session *tls,
        assert ( ( cipherspec->dynamic + total ) == dynamic );
 
        /* Store parameters */
-       cipherspec->pubkey = pubkey;
-       cipherspec->cipher = cipher;
-       cipherspec->digest = digest;
-       cipherspec->key_len = key_len;
+       cipherspec->suite = suite;
 
        return 0;
 }
@@ -496,49 +554,28 @@ static int tls_set_cipher ( struct tls_session *tls,
  */
 static int tls_select_cipher ( struct tls_session *tls,
                               unsigned int cipher_suite ) {
-       struct pubkey_algorithm *pubkey = &pubkey_null;
-       struct cipher_algorithm *cipher = &cipher_null;
-       struct digest_algorithm *digest = &digest_null;
-       unsigned int key_len = 0;
+       struct tls_cipher_suite *suite;
        int rc;
 
-       switch ( cipher_suite ) {
-       case htons ( TLS_RSA_WITH_AES_128_CBC_SHA ):
-               key_len = ( 128 / 8 );
-               cipher = &aes_cbc_algorithm;
-               digest = &sha1_algorithm;
-               break;
-       case htons ( TLS_RSA_WITH_AES_256_CBC_SHA ):
-               key_len = ( 256 / 8 );
-               cipher = &aes_cbc_algorithm;
-               digest = &sha1_algorithm;
-               break;
-       case htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ):
-               key_len = ( 128 / 8 );
-               cipher = &aes_cbc_algorithm;
-               digest = &sha256_algorithm;
-               break;
-       case htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ):
-               key_len = ( 256 / 8 );
-               cipher = &aes_cbc_algorithm;
-               digest = &sha256_algorithm;
-               break;
-       default:
+       /* Identify cipher suite */
+       suite = tls_find_cipher_suite ( cipher_suite );
+       if ( ! suite ) {
                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 )
+       if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending,
+                                    suite ) ) != 0 )
                return rc;
-       if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, pubkey,
-                                    cipher, digest, key_len ) ) != 0 )
+       if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending,
+                                    suite ) ) != 0 )
                return rc;
 
-       DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls,
-              pubkey->name, cipher->name, ( key_len * 8 ), digest->name );
+       DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls, suite->pubkey->name,
+              suite->cipher->name, ( suite->key_len * 8 ),
+              suite->digest->name );
 
        return 0;
 }
@@ -556,10 +593,7 @@ static int tls_change_cipher ( struct tls_session *tls,
                               struct tls_cipherspec *active ) {
 
        /* Sanity check */
-       if ( /* FIXME (when pubkey is not hard-coded to RSA):
-             * ( pending->pubkey == &pubkey_null ) || */
-            ( pending->cipher == &cipher_null ) ||
-            ( pending->digest == &digest_null ) ) {
+       if ( pending->suite == &tls_cipher_suite_null ) {
                DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
                return -ENOTSUP;
        }
@@ -687,7 +721,7 @@ static int tls_send_client_hello ( struct tls_session *tls ) {
                uint8_t random[32];
                uint8_t session_id_len;
                uint16_t cipher_suite_len;
-               uint16_t cipher_suites[4];
+               uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
                uint8_t compression_methods_len;
                uint8_t compression_methods[1];
                uint16_t extensions_len;
@@ -704,6 +738,7 @@ static int tls_send_client_hello ( struct tls_session *tls ) {
                        } __attribute__ (( packed )) server_name;
                } __attribute__ (( packed )) extensions;
        } __attribute__ (( packed )) hello;
+       unsigned int i;
 
        memset ( &hello, 0, sizeof ( hello ) );
        hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
@@ -712,10 +747,8 @@ static int tls_send_client_hello ( struct tls_session *tls ) {
        hello.version = htons ( tls->version );
        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_256_CBC_SHA256 );
-       hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 );
-       hello.cipher_suites[2] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA );
-       hello.cipher_suites[3] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA );
+       for ( i = 0 ; i < TLS_NUM_CIPHER_SUITES ; i++ )
+               hello.cipher_suites[i] = tls_cipher_suites[i].code;
        hello.compression_methods_len = sizeof ( hello.compression_methods );
        hello.extensions_len = htons ( sizeof ( hello.extensions ) );
        hello.extensions.server_name_type = htons ( TLS_SERVER_NAME );
@@ -1252,7 +1285,7 @@ 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 digest_algorithm *digest = cipherspec->digest;
+       struct digest_algorithm *digest = cipherspec->suite->digest;
        uint8_t digest_ctx[digest->ctxsize];
 
        hmac_init ( digest, digest_ctx, cipherspec->mac_secret,
@@ -1278,7 +1311,7 @@ static void tls_hmac ( struct tls_session *tls __unused,
 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;
+       size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize;
        void *plaintext;
        void *content;
        void *mac;
@@ -1313,8 +1346,8 @@ static void * __malloc tls_assemble_stream ( struct tls_session *tls,
 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 mac_len = tls->tx_cipherspec.digest->digestsize;
+       size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize;
+       size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize;
        size_t iv_len;
        size_t padding_len;
        void *plaintext;
@@ -1362,11 +1395,12 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
        struct tls_header plaintext_tlshdr;
        struct tls_header *tlshdr;
        struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
+       struct cipher_algorithm *cipher = cipherspec->suite->cipher;
        void *plaintext = NULL;
        size_t plaintext_len;
        struct io_buffer *ciphertext = NULL;
        size_t ciphertext_len;
-       size_t mac_len = cipherspec->digest->digestsize;
+       size_t mac_len = cipherspec->suite->digest->digestsize;
        uint8_t mac[mac_len];
        int rc;
 
@@ -1380,7 +1414,7 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
                   data, len, mac );
 
        /* Allocate and assemble plaintext struct */
-       if ( is_stream_cipher ( cipherspec->cipher ) ) {
+       if ( is_stream_cipher ( cipher ) ) {
                plaintext = tls_assemble_stream ( tls, data, len, mac,
                                                  &plaintext_len );
        } else {
@@ -1413,10 +1447,9 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
        tlshdr->version = htons ( tls->version );
        tlshdr->length = htons ( plaintext_len );
        memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx,
-                cipherspec->cipher->ctxsize );
-       cipher_encrypt ( cipherspec->cipher, cipherspec->cipher_next_ctx,
-                        plaintext, iob_put ( ciphertext, plaintext_len ),
-                        plaintext_len );
+                cipher->ctxsize );
+       cipher_encrypt ( cipher, cipherspec->cipher_next_ctx, plaintext,
+                        iob_put ( ciphertext, plaintext_len ), plaintext_len );
 
        /* Free plaintext as soon as possible to conserve memory */
        free ( plaintext );
@@ -1433,8 +1466,7 @@ static int tls_send_plaintext ( struct tls_session *tls, unsigned int type,
        /* 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 );
+                tls->tx_cipherspec.cipher_next_ctx, cipher->ctxsize );
 
  done:
        free ( plaintext );
@@ -1462,7 +1494,7 @@ static int tls_split_stream ( struct tls_session *tls,
        size_t mac_len;
 
        /* Decompose stream-ciphered data */
-       mac_len = tls->rx_cipherspec.digest->digestsize;
+       mac_len = tls->rx_cipherspec.suite->digest->digestsize;
        if ( plaintext_len < mac_len ) {
                DBGC ( tls, "TLS %p received underlength record\n", tls );
                DBGC_HD ( tls, plaintext, plaintext_len );
@@ -1514,10 +1546,10 @@ static int tls_split_block ( struct tls_session *tls,
 
        /* TLSv1.1 and later use an explicit IV */
        iv_len = ( ( tls->version >= TLS_VERSION_TLS_1_1 ) ?
-                  tls->rx_cipherspec.cipher->blocksize : 0 );
+                  tls->rx_cipherspec.suite->cipher->blocksize : 0 );
 
        /* Decompose block-ciphered data */
-       mac_len = tls->rx_cipherspec.digest->digestsize;
+       mac_len = tls->rx_cipherspec.suite->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 );
@@ -1559,12 +1591,13 @@ 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;
+       struct cipher_algorithm *cipher = cipherspec->suite->cipher;
        size_t record_len = ntohs ( tlshdr->length );
        void *plaintext = NULL;
        void *data;
        size_t len;
        void *mac;
-       size_t mac_len = cipherspec->digest->digestsize;
+       size_t mac_len = cipherspec->suite->digest->digestsize;
        uint8_t verify_mac[mac_len];
        int rc;
 
@@ -1578,11 +1611,11 @@ static int tls_new_ciphertext ( struct tls_session *tls,
        }
 
        /* Decrypt the record */
-       cipher_decrypt ( cipherspec->cipher, cipherspec->cipher_ctx,
+       cipher_decrypt ( cipher, cipherspec->cipher_ctx,
                         ciphertext, plaintext, record_len );
 
        /* Split record into content and MAC */
-       if ( is_stream_cipher ( cipherspec->cipher ) ) {
+       if ( is_stream_cipher ( cipher ) ) {
                if ( ( rc = tls_split_stream ( tls, plaintext, record_len,
                                               &data, &len, &mac ) ) != 0 )
                        goto done;