tls: teach it to send AES256-encrypted data
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 18 Jan 2017 19:37:24 +0000 (20:37 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 18 Jan 2017 19:37:24 +0000 (20:37 +0100)
>> CLIENT_HELLO
wrote 50 bytes
insize:0 tail:0
got block len:74
got HANDSHAKE
<< SERVER_HELLO
insize:79 tail:0
got block len:2397
got HANDSHAKE
<< CERTIFICATE
key bytes:271, first:0x00
server_rsa_pub_key.size:256
insize:2402 tail:0
got block len:4
got HANDSHAKE
<< SERVER_HELLO_DONE
>> CLIENT_KEY_EXCHANGE
wrote 267 bytes
master secret:c51df5b1e3b3f57373cdd8ea28e8ce562059636cf9f585d0b89c7f4bacec97e674d7b91f93e7b500cb64637f240c3b78
client_write_MAC_key:3b0b7e2bab241b629c37eb3a3824f09b39fe71a00876b0c8026dda16ef0d2f82
client_write_key:d36e801470ed2f0a8fc886ac25df57ffbe4265d06e3192122c4ef4df1e32fab2
>> CHANGE_CIPHER_SPEC
from secret: c51df5b1e3b3f57373cdd8ea28e8ce562059636cf9f585d0b89c7f4bacec97e674d7b91f93e7b500cb64637f240c3b78
from labelSeed: 636c69656e742066696e6973686564b22e0e6008b8ee218cc02e4a93e4a42b570535f9b57662e262d43b379d125b69
=> digest: a45bfee8ed6507a2a9920d0c
>> FINISHED
before crypt: 5 hdr + 16 data + 32 hash bytes
writing 5 + 16 IV + 64 encrypted bytes, padding_length:0x0f
wrote 85 bytes
insize:9 tail:0
got block len:1
<< CHANGE_CIPHER_SPEC
insize:6 tail:0
got block len:80
< hdr_type:22 ver:3.3 len:80 type:21 len24:9541723 |1591985b...a3da|

The last line is the server's FINISHED response, encrypted.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/tls.c

index 4986c991d618b3ac2e9b105c91dfae7b9234be8a..2b1e36110f8033a6316362be8f0800f4bb02772f 100644 (file)
 //#define CIPHER_ID TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 // SSL_ALERT_HANDSHAKE_FAILURE
 //#define CIPHER_ID TLS_RSA_WITH_AES_256_GCM_SHA384 // ok, no SERVER_KEY_EXCHANGE
 //#define CIPHER_ID TLS_RSA_WITH_AES_128_GCM_SHA256 // ok, no SERVER_KEY_EXCHANGE *** select this?
-#define CIPHER_ID TLS_RSA_WITH_NULL_SHA256 // for testing (does everything except encrypting)
 //#define CIPHER_ID TLS_DH_anon_WITH_AES_256_CBC_SHA // SSL_ALERT_HANDSHAKE_FAILURE
 //^^^^^^^^^^^^^^^^^^^^^^^ (tested b/c this one doesn't req server certs... no luck)
-//test TLS_RSA_WITH_AES_128_CBC_SHA, in tls 1.2 it's mandated to be always supported
+//test TLS_RSA_WITH_AES_128_CBC_SHA, in TLS 1.2 it's mandated to be always supported
+
+// works against "openssl s_server -cipher NULL"
+// and against wolfssl-3.9.10-stable/examples/server/server.c:
+//#define CIPHER_ID TLS_RSA_WITH_NULL_SHA256 // for testing (does everything except encrypting)
+// "works", meaning
+// "can send encrypted FINISHED to  wolfssl-3.9.10-stable/examples/server/server.c",
+// don't yet read its encrypted answers:
+#define CIPHER_ID TLS_RSA_WITH_AES_256_CBC_SHA256 // ok, no SERVER_KEY_EXCHANGE
 
 enum {
        SHA256_INSIZE = 64,
        SHA256_OUTSIZE = 32,
+
+       AES_BLOCKSIZE = 16,
+       AES128_KEYSIZE = 16,
+       AES256_KEYSIZE = 32,
 };
 
 struct record_hdr {
@@ -172,6 +183,9 @@ typedef struct tls_state {
        uint8_t encrypt_on_write;
        uint8_t decrypt_on_read;
        uint8_t client_write_MAC_key[SHA256_OUTSIZE];
+       uint8_t server_write_MAC_key[SHA256_OUTSIZE];
+       uint8_t client_write_key[AES256_KEYSIZE];
+       uint8_t server_write_key[AES256_KEYSIZE];
 // RFC 5246
 // sequence number
 // Each connection state contains a sequence number, which is
@@ -208,7 +222,10 @@ typedef struct tls_state {
        // Since our buffer also contains 5-byte headers, make it a bit bigger:
        int insize;
        int tail;
-       uint8_t inbuf[18*1024];
+//needed?
+       uint64_t align____;
+       uint8_t inbuf[20*1024];
+       uint8_t outbuf[20*1024];
 } tls_state_t;
 
 
@@ -512,13 +529,114 @@ static void xwrite_and_hash(tls_state_t *tls, /*const*/ void *buf, unsigned size
                        NULL);
        tls->write_seq64_be = SWAP_BE64(1 + SWAP_BE64(tls->write_seq64_be));
 
-       xhdr->len16_lo += SHA256_OUTSIZE;
-       xwrite(tls->fd, buf, size);
-       xhdr->len16_lo -= SHA256_OUTSIZE;
-       dbg("wrote %u bytes\n", size);
+       if (CIPHER_ID == TLS_RSA_WITH_NULL_SHA256) {
+               /* No encryption, only signing */
+               xhdr->len16_lo += SHA256_OUTSIZE;
+//FIXME: overflow into len16_hi?
+               xwrite(tls->fd, buf, size);
+               xhdr->len16_lo -= SHA256_OUTSIZE;
+               dbg("wrote %u bytes\n", size);
+
+               xwrite(tls->fd, mac_hash, sizeof(mac_hash));
+               dbg("wrote %u bytes of hash\n", (int)sizeof(mac_hash));
+               return;
+       }
+
+       // RFC 5246
+       // 6.2.3.2.  CBC Block Cipher
+       // For block ciphers (such as 3DES or AES), the encryption and MAC
+       // functions convert TLSCompressed.fragment structures to and from block
+       // TLSCiphertext.fragment structures.
+       //    struct {
+       //        opaque IV[SecurityParameters.record_iv_length];
+       //        block-ciphered struct {
+       //            opaque content[TLSCompressed.length];
+       //            opaque MAC[SecurityParameters.mac_length];
+       //            uint8 padding[GenericBlockCipher.padding_length];
+       //            uint8 padding_length;
+       //        };
+       //    } GenericBlockCipher;
+       //...
+       // IV
+       //    The Initialization Vector (IV) SHOULD be chosen at random, and
+       //    MUST be unpredictable.  Note that in versions of TLS prior to 1.1,
+       //    there was no IV field (...).  For block ciphers, the IV length is
+       //    of length SecurityParameters.record_iv_length, which is equal to the
+       //    SecurityParameters.block_size.
+       // padding
+       //    Padding that is added to force the length of the plaintext to be
+       //    an integral multiple of the block cipher's block length.
+       // padding_length
+       //    The padding length MUST be such that the total size of the
+       //    GenericBlockCipher structure is a multiple of the cipher's block
+       //    length.  Legal values range from zero to 255, inclusive.
+       //...
+       // Appendix C.  Cipher Suite Definitions
+       //...
+       //                         Key      IV   Block
+       // Cipher        Type    Material  Size  Size
+       // ------------  ------  --------  ----  -----
+       // NULL          Stream      0       0    N/A
+       // RC4_128       Stream     16       0    N/A
+       // 3DES_EDE_CBC  Block      24       8      8
+       // AES_128_CBC   Block      16      16     16
+       // AES_256_CBC   Block      32      16     16
+    {
+       psCipherContext_t ctx;
+       uint8_t *p;
+       uint8_t padding_length;
+
+       /* Build IV+content+MAC+padding in outbuf */
+       tls_get_random(tls->outbuf, AES_BLOCKSIZE); /* IV */
+       p = tls->outbuf + AES_BLOCKSIZE;
+       size -= sizeof(*xhdr);
+       dbg("before crypt: 5 hdr + %u data + %u hash bytes\n", size, sizeof(mac_hash));
+       p = mempcpy(p, buf + sizeof(*xhdr), size);  /* content */
+       p = mempcpy(p, mac_hash, sizeof(mac_hash)); /* MAC */
+       size += sizeof(mac_hash);
+       // RFC is talking nonsense:
+        //    Padding that is added to force the length of the plaintext to be
+        //    an integral multiple of the block cipher's block length.
+       // WRONG. _padding+padding_length_, not just _padding_,
+       // pads the data.
+       // IOW: padding_length is the last byte of padding[] array,
+       // contrary to what RFC depicts.
+       //
+       // What actually happens is that there is always padding.
+       // If you need one byte to reach BLOCKSIZE, this byte is 0x00.
+       // If you need two bytes, they are both 0x01.
+       // If you need three, they are 0x02,0x02,0x02. And so on.
+       // If you need no bytes to reach BLOCKSIZE, you have to pad a full
+       // BLOCKSIZE with bytes of value (BLOCKSIZE-1).
+       // It's ok to have more than minimum padding, but we do minimum.
+       padding_length = (~size) & (AES_BLOCKSIZE - 1);
+       do {
+               *p++ = padding_length;              /* padding */
+               size++;
+       } while ((size & (AES_BLOCKSIZE - 1)) != 0);
+
+       /* Encrypt content+MAC+padding in place */
+       psAesInit(&ctx, tls->outbuf, /* IV */
+                       tls->client_write_key, sizeof(tls->client_write_key)
+       );
+       psAesEncrypt(&ctx,
+                       tls->outbuf + AES_BLOCKSIZE, /* plaintext */
+                       tls->outbuf + AES_BLOCKSIZE, /* ciphertext */
+                       size
+       );
 
-       xwrite(tls->fd, mac_hash, sizeof(mac_hash));
-       dbg("wrote %u bytes of hash\n", (int)sizeof(mac_hash));
+       /* Write out */
+       dbg("writing 5 + %u IV + %u encrypted bytes, padding_length:0x%02x\n",
+                       AES_BLOCKSIZE, size, padding_length);
+       size += AES_BLOCKSIZE;     /* + IV */
+       xhdr->len16_hi = size >> 8;
+       xhdr->len16_lo = size & 0xff;
+       xwrite(tls->fd, xhdr, sizeof(*xhdr));
+       xwrite(tls->fd, tls->outbuf, size);
+       dbg("wrote %u bytes\n", sizeof(*xhdr) + size);
+//restore xhdr->len16_hi = ;
+//restore xhdr->len16_lo = ;
+    }
 }
 
 static int xread_tls_block(tls_state_t *tls)
@@ -1048,14 +1166,30 @@ static void send_client_key_exchange(tls_state_t *tls)
        //    server_write_key[SecurityParameters.enc_key_length]
        //    client_write_IV[SecurityParameters.fixed_iv_length]
        //    server_write_IV[SecurityParameters.fixed_iv_length]
+
+
+        //                         Key      IV   Block
+        // Cipher        Type    Material  Size  Size
+        // ------------  ------  --------  ----  -----
+        // NULL          Stream      0       0    N/A
+        // RC4_128       Stream     16       0    N/A
+        // 3DES_EDE_CBC  Block      24       8      8
+        // AES_128_CBC   Block      16      16     16
+        // AES_256_CBC   Block      32      16     16
+
        {
                uint8_t tmp64[64];
-               /* make server_rand32 + client_rand32 */
+
+               /* make "server_rand32 + client_rand32" */
                memcpy(&tmp64[0] , &tls->client_and_server_rand32[32], 32);
                memcpy(&tmp64[32], &tls->client_and_server_rand32[0] , 32);
 
                prf_hmac_sha256(
-                       tls->client_write_MAC_key, sizeof(tls->client_write_MAC_key),
+                       tls->client_write_MAC_key, 2 * (SHA256_OUTSIZE + AES256_KEYSIZE),
+                       // also fills:
+                       // server_write_MAC_key[SHA256_OUTSIZE]
+                       // client_write_key[AES256_KEYSIZE]
+                       // server_write_key[AES256_KEYSIZE]
                        tls->master_secret, sizeof(tls->master_secret),
                        "key expansion",
                        tmp64, 64
@@ -1063,6 +1197,9 @@ static void send_client_key_exchange(tls_state_t *tls)
                dump_hex("client_write_MAC_key:%s\n",
                        tls->client_write_MAC_key, sizeof(tls->client_write_MAC_key)
                );
+               dump_hex("client_write_key:%s\n",
+                       tls->client_write_key, sizeof(tls->client_write_key)
+               );
        }
 }