tls: add support for TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 cipher
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 23 Nov 2018 16:21:38 +0000 (17:21 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 23 Nov 2018 16:48:07 +0000 (17:48 +0100)
function                                             old     new   delta
xwrite_encrypted                                     209     605    +396
GHASH                                                  -     395    +395
aes_encrypt_1                                          -     382    +382
GMULT                                                  -     192    +192
tls_xread_record                                     489     659    +170
aes_encrypt_one_block                                  -      65     +65
aesgcm_setkey                                          -      58     +58
FlattenSzInBits                                        -      52     +52
tls_handshake                                       1890    1941     +51
xwrite_and_update_handshake_hash                      46      81     +35
xorbuf                                                 -      24     +24
aes_setkey                                             -      16     +16
psRsaEncryptPub                                      413     421      +8
stty_main                                           1221    1227      +6
ssl_client_main                                      138     143      +5
next_token                                           841     845      +4
spawn_ssl_client                                     218     219      +1
volume_id_probe_hfs_hfsplus                          564     563      -1
read_package_field                                   232     230      -2
i2cdetect_main                                       674     672      -2
fail_hunk                                            139     136      -3
parse_expr                                           891     883      -8
curve25519                                           802     793      -9
aes_cbc_decrypt                                      971     958     -13
xwrite_handshake_record                               43       -     -43
aes_cbc_encrypt                                      644     172    -472
------------------------------------------------------------------------------
(add/remove: 9/1 grow/shrink: 9/8 up/down: 1860/-553)        Total: 1307 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
include/libbb.h
networking/tls.c
networking/tls.h
networking/tls_aes.c
networking/tls_aes.h
networking/tls_aesgcm.c [new file with mode: 0644]
networking/tls_aesgcm.h [new file with mode: 0644]
networking/tls_fe.c
networking/tls_fe.h
networking/tls_rsa.c
networking/tls_rsa.h

index aa9e9d019305f961a9c84785f7330869e6ee4d71..b041ce04720ad80a38910fe71da5fc91de6492f6 100644 (file)
@@ -736,10 +736,17 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC;
 // + inet_common.c has additional IPv4-only stuff
 
 
+struct tls_aes {
+       uint32_t key[60];
+       unsigned rounds;
+};
 #define TLS_MAX_MAC_SIZE 32
 #define TLS_MAX_KEY_SIZE 32
+#define TLS_MAX_IV_SIZE   4
 struct tls_handshake_data; /* opaque */
 typedef struct tls_state {
+       unsigned flags;
+
        int ofd;
        int ifd;
 
@@ -748,6 +755,7 @@ typedef struct tls_state {
        uint8_t  encrypt_on_write;
        unsigned MAC_size;
        unsigned key_size;
+       unsigned IV_size;
 
        uint8_t *outbuf;
        int     outbuf_size;
@@ -769,12 +777,21 @@ typedef struct tls_state {
        /*uint64_t read_seq64_be;*/
        uint64_t write_seq64_be;
 
+       /*uint8_t *server_write_MAC_key;*/
        uint8_t *client_write_key;
        uint8_t *server_write_key;
+       uint8_t *client_write_IV;
+       uint8_t *server_write_IV;
        uint8_t client_write_MAC_key[TLS_MAX_MAC_SIZE];
        uint8_t server_write_MAC_k__[TLS_MAX_MAC_SIZE];
        uint8_t client_write_k__[TLS_MAX_KEY_SIZE];
        uint8_t server_write_k__[TLS_MAX_KEY_SIZE];
+       uint8_t client_write_I_[TLS_MAX_IV_SIZE];
+       uint8_t server_write_I_[TLS_MAX_IV_SIZE];
+
+       struct tls_aes aes_encrypt;
+       struct tls_aes aes_decrypt;
+       uint8_t H[16]; //used by AES_GCM
 } tls_state_t;
 
 static inline tls_state_t *new_tls_state(void)
index fba66f6f0d2270279b5573cbb84f20f2a22e2b8e..38a965ad684244e86c219bb970eabe0fa1eefac8 100644 (file)
 //kbuild:lib-$(CONFIG_TLS) += tls_pstm_mul_comba.o
 //kbuild:lib-$(CONFIG_TLS) += tls_pstm_sqr_comba.o
 //kbuild:lib-$(CONFIG_TLS) += tls_aes.o
+//kbuild:lib-$(CONFIG_TLS) += tls_aesgcm.o
 //kbuild:lib-$(CONFIG_TLS) += tls_rsa.o
 //kbuild:lib-$(CONFIG_TLS) += tls_fe.o
-////kbuild:lib-$(CONFIG_TLS) += tls_aes_gcm.o
 
 #include "tls.h"
 
-//Tested against kernel.org:
 //TLS 1.2
 #define TLS_MAJ 3
 #define TLS_MIN 3
+
+//Tested against kernel.org:
 //#define CIPHER_ID TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA // ok, recvs SERVER_KEY_EXCHANGE *** matrixssl uses this on my box
 //#define CIPHER_ID TLS_RSA_WITH_AES_256_CBC_SHA256 // ok, no SERVER_KEY_EXCHANGE
 //#define CIPHER_ID TLS_DH_anon_WITH_AES_256_CBC_SHA // SSL_ALERT_HANDSHAKE_FAILURE
@@ -36,7 +37,7 @@
 //#define CIPHER_ID TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
 //#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_AES_128_GCM_SHA256 // ok, no SERVER_KEY_EXCHANGE
 
 // works against "openssl s_server -cipher NULL"
 // and against wolfssl-3.9.10-stable/examples/server/server.c:
 // bug #11456: host is.gd accepts only ECDHE-ECDSA-foo (the simplest which works: ECDHE-ECDSA-AES128-SHA 0xC009)
 #define CIPHER_ID3  TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
 
+// ftp.openbsd.org only supports ECDHE-RSA-AESnnn-GCM-SHAnnn or ECDHE-RSA-CHACHA20-POLY1305
+#define CIPHER_ID4  TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+
+#define NUM_CIPHERS 4
+
 
 #define TLS_DEBUG      0
 #define TLS_DEBUG_HASH 0
@@ -207,7 +213,6 @@ enum {
        SHA1_OUTSIZE   = 20,
        SHA256_OUTSIZE = 32,
 
-       AES_BLOCKSIZE  = 16,
        AES128_KEYSIZE = 16,
        AES256_KEYSIZE = 32,
 
@@ -216,7 +221,7 @@ enum {
        RECHDR_LEN = 5,
 
        /* 8 = 3+5. 3 extra bytes result in record data being 32-bit aligned */
-       OUTBUF_PFX = 8 + AES_BLOCKSIZE, /* header + IV */
+       OUTBUF_PFX = 8 + AES_BLOCK_SIZE, /* header + IV */
        OUTBUF_SFX = TLS_MAX_MAC_SIZE + TLS_MAX_CRYPTBLOCK_SIZE, /* MAC + padding */
 
        // RFC 5246
@@ -263,8 +268,11 @@ struct record_hdr {
 };
 
 enum {
-       KEY_ALG_RSA,
-       KEY_ALG_ECDSA,
+       NEED_EC_KEY            = 1 << 0,
+       GOT_CERT_RSA_KEY_ALG   = 1 << 1,
+        GOT_CERT_ECDSA_KEY_ALG = 1 << 2,
+       GOT_EC_KEY             = 1 << 3,
+       ENCRYPTION_AESGCM      = 1 << 4,
 };
 struct tls_handshake_data {
        /* In bbox, md5/sha1/sha256 ctx's are the same structure */
@@ -273,14 +281,14 @@ struct tls_handshake_data {
        uint8_t client_and_server_rand32[2 * 32];
        uint8_t master_secret[48];
 
-       smallint key_alg;
 //TODO: store just the DER key here, parse/use/delete it when sending client key
 //this way it will stay key type agnostic here.
        psRsaKey_t server_rsa_pub_key;
        uint8_t ecc_pub_key32[32];
 
-       unsigned saved_client_hello_size;
-       uint8_t saved_client_hello[1];
+/* HANDSHAKE HASH: */
+       //unsigned saved_client_hello_size;
+       //uint8_t saved_client_hello[1];
 };
 
 
@@ -609,7 +617,7 @@ static void *tls_get_zeroed_outbuf(tls_state_t *tls, int len)
        return record;
 }
 
-static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type)
+static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type)
 {
        uint8_t *buf = tls->outbuf + OUTBUF_PFX;
        struct record_hdr *xhdr;
@@ -619,7 +627,7 @@ static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type)
        if (CIPHER_ID1 != TLS_RSA_WITH_NULL_SHA256 /* if "no encryption" can't be selected */
         || tls->cipher_id != TLS_RSA_WITH_NULL_SHA256 /* or if it wasn't selected */
        ) {
-               xhdr = (void*)(buf - RECHDR_LEN - AES_BLOCKSIZE); /* place for IV */
+               xhdr = (void*)(buf - RECHDR_LEN - AES_BLOCK_SIZE); /* place for IV */
        }
 
        xhdr->type = type;
@@ -722,7 +730,7 @@ static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type)
        // AES_128_CBC   Block      16      16     16
        // AES_256_CBC   Block      32      16     16
 
-       tls_get_random(buf - AES_BLOCKSIZE, AES_BLOCKSIZE); /* IV */
+       tls_get_random(buf - AES_BLOCK_SIZE, AES_BLOCK_SIZE); /* IV */
        dbg("before crypt: 5 hdr + %u data + %u hash bytes\n",
                        size - tls->MAC_size, tls->MAC_size);
 
@@ -742,23 +750,24 @@ static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type)
        // 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);
+       padding_length = (~size) & (AES_BLOCK_SIZE - 1);
        do {
                buf[size++] = padding_length; /* padding */
-       } while ((size & (AES_BLOCKSIZE - 1)) != 0);
+       } while ((size & (AES_BLOCK_SIZE - 1)) != 0);
 
        /* Encrypt content+MAC+padding in place */
+//optimize key setup
        aes_cbc_encrypt(
                tls->client_write_key, tls->key_size, /* selects 128/256 */
-               buf - AES_BLOCKSIZE, /* IV */
+               buf - AES_BLOCK_SIZE, /* IV */
                buf, size, /* plaintext */
                buf /* ciphertext */
        );
 
        /* Write out */
        dbg("writing 5 + %u IV + %u encrypted bytes, padding_length:0x%02x\n",
-                       AES_BLOCKSIZE, size, padding_length);
-       size += AES_BLOCKSIZE;     /* + IV */
+                       AES_BLOCK_SIZE, size, padding_length);
+       size += AES_BLOCK_SIZE;     /* + IV */
        xhdr->len16_hi = size >> 8;
        xhdr->len16_lo = size & 0xff;
        dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size);
@@ -766,23 +775,109 @@ static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type)
        dbg("wrote %u bytes\n", (int)RECHDR_LEN + size);
 }
 
+/* Example how GCM encryption combines nonce, aad, input and generates
+ * "header | exp_nonce | encrypted output | tag":
+ * nonce:0d 6a 26 31 00 00 00 00 00 00 00 01 (implicit 4 bytes (derived from master secret), then explicit 8 bytes)
+ * aad:  00 00 00 00 00 00 00 01 17 03 03 00 1c
+ * in:   47 45 54 20 2f 69 6e 64 65 78 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 30 0d 0a 0d 0a "GET /index.html HTTP/1.0\r\n\r\n" (0x1c bytes)
+ * out:  f7 8a b2 8f 78 0e f6 d5 76 17 2e b5 6d 46 59 56 8b 46 9f 0b d9 2c 35 28 13 66 19 be
+ * tag:  c2 86 ce 4a 50 4a d0 aa 50 b3 76 5c 49 2a 3f 33
+ * sent: 17 03 03 00 34|00 00 00 00 00 00 00 01|f7 8a b2 8f 78 0e f6 d5 76 17 2e b5 6d 46 59 56 8b 46 9f 0b d9 2c 35 28 13 66 19 be|c2 86 ce 4a 50 4a d0 aa 50 b3 76 5c 49 2a 3f 33
+ * .............................................^^ buf points here
+ */
+static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned type)
+{
+//go for [16]
+       uint8_t aad[13];
+       uint8_t nonce[12 + 4]; /* +4 creates space for AES block counter */
+       uint8_t scratch[AES_BLOCK_SIZE]; //[16]
+       uint8_t authtag[AES_BLOCK_SIZE]; //[16]
+       uint8_t *buf;
+       struct record_hdr *xhdr;
+       unsigned remaining;
+       unsigned cnt;
+
+       buf = tls->outbuf + OUTBUF_PFX; /* see above for the byte it points to */
+       dump_hex("xwrite_encrypted_aesgcm plaintext:%s\n", buf, size);
+
+       xhdr = (void*)(buf - 8 - RECHDR_LEN);
+       xhdr->type = type; /* do it here so that "type" param no longer used */
+
+       aad[8] = type;
+       aad[9] = TLS_MAJ;
+       aad[10] = TLS_MIN;
+       aad[11] = size >> 8;
+       aad[12] = size & 0xff;
+
+       memcpy(nonce,     tls->client_write_IV, 4);
+       memcpy(nonce + 4, &tls->write_seq64_be, 8);
+       memcpy(aad,       &tls->write_seq64_be, 8);
+       memcpy(buf - 8,   &tls->write_seq64_be, 8);
+//optimize
+       /* seq64 is not used later in this func, can increment here */
+       tls->write_seq64_be = SWAP_BE64(1 + SWAP_BE64(tls->write_seq64_be));
+
+#define COUNTER(v) (*(uint32_t*)(v + 12))
+
+       cnt = 1;
+       remaining = size;
+       while (remaining != 0) {
+               unsigned n;
+
+               cnt++;
+               COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
+               aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch);
+               n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining;
+               xorbuf(buf, scratch, n);
+               buf += n;
+               remaining -= n;
+       }
+
+//optimize fixed sizes
+       aesgcm_GHASH(tls->H, aad, sizeof(aad), tls->outbuf + OUTBUF_PFX, size, authtag, sizeof(authtag));
+       COUNTER(nonce) = htonl(1);
+       aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch);
+       xorbuf(authtag, scratch, sizeof(authtag));
+
+       memcpy(buf, authtag, sizeof(authtag));
+#undef COUNTER
+
+       /* Write out */
+       xhdr = (void*)(tls->outbuf + OUTBUF_PFX - 8 - RECHDR_LEN);
+       size += 8 + sizeof(authtag);
+       /*xhdr->type = type; - already is */
+       xhdr->proto_maj = TLS_MAJ;
+       xhdr->proto_min = TLS_MIN;
+       xhdr->len16_hi = size >> 8;
+       xhdr->len16_lo = size & 0xff;
+       size += RECHDR_LEN;
+       dump_raw_out(">> %s\n", xhdr, size);
+       xwrite(tls->ofd, xhdr, size);
+       dbg("wrote %u bytes\n", size);
+}
+
+static void xwrite_encrypted(tls_state_t *tls, unsigned size, unsigned type)
+{
+       if (!(tls->flags & ENCRYPTION_AESGCM)) {
+               xwrite_encrypted_and_hmac_signed(tls, size, type);
+               return;
+       }
+       xwrite_encrypted_aesgcm(tls, size, type);
+}
+
 static void xwrite_handshake_record(tls_state_t *tls, unsigned size)
 {
-       //if (!tls->encrypt_on_write) {
-               uint8_t *buf = tls->outbuf + OUTBUF_PFX;
-               struct record_hdr *xhdr = (void*)(buf - RECHDR_LEN);
+       uint8_t *buf = tls->outbuf + OUTBUF_PFX;
+       struct record_hdr *xhdr = (void*)(buf - RECHDR_LEN);
 
-               xhdr->type = RECORD_TYPE_HANDSHAKE;
-               xhdr->proto_maj = TLS_MAJ;
-               xhdr->proto_min = TLS_MIN;
-               xhdr->len16_hi = size >> 8;
-               xhdr->len16_lo = size & 0xff;
-               dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size);
-               xwrite(tls->ofd, xhdr, RECHDR_LEN + size);
-               dbg("wrote %u bytes\n", (int)RECHDR_LEN + size);
-       //      return;
-       //}
-       //xwrite_encrypted(tls, size, RECORD_TYPE_HANDSHAKE);
+       xhdr->type = RECORD_TYPE_HANDSHAKE;
+       xhdr->proto_maj = TLS_MAJ;
+       xhdr->proto_min = TLS_MIN;
+       xhdr->len16_hi = size >> 8;
+       xhdr->len16_lo = size & 0xff;
+       dump_raw_out(">> %s\n", xhdr, RECHDR_LEN + size);
+       xwrite(tls->ofd, xhdr, RECHDR_LEN + size);
+       dbg("wrote %u bytes\n", (int)RECHDR_LEN + size);
 }
 
 static void xwrite_and_update_handshake_hash(tls_state_t *tls, unsigned size)
@@ -826,6 +921,52 @@ static const char *alert_text(int code)
        return itoa(code);
 }
 
+static void tls_aesgcm_decrypt(tls_state_t *tls, uint8_t *buf, int size)
+{
+//go for [16]
+       //uint8_t aad[13];
+       uint8_t nonce[12 + 4]; /* +4 creates space for AES block counter */
+       uint8_t scratch[AES_BLOCK_SIZE]; //[16]
+       //uint8_t authtag[AES_BLOCK_SIZE]; //[16]
+       unsigned remaining;
+       unsigned cnt;
+
+       //aad[8] = type;
+       //aad[9] = TLS_MAJ;
+       //aad[10] = TLS_MIN;
+       //aad[11] = size >> 8;
+       //aad[12] = size & 0xff;
+
+       memcpy(nonce,     tls->server_write_IV, 4);
+       memcpy(nonce + 4, buf, 8);
+       buf += 8;
+
+#define COUNTER(v) (*(uint32_t*)(v + 12))
+
+       cnt = 1;
+       remaining = size;
+       while (remaining != 0) {
+               unsigned n;
+
+               cnt++;
+               COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
+               aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch);
+               n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining;
+               xorbuf(buf, scratch, n);
+               buf += n;
+               remaining -= n;
+       }
+
+////optimize fixed sizes
+       //aesgcm_GHASH(tls->H, aad, sizeof(aad), tls->outbuf + OUTBUF_PFX, size, authtag, sizeof(authtag));
+       //COUNTER(nonce) = htonl(1);
+       //aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch);
+       //xorbuf(authtag, scratch, sizeof(authtag));
+
+       //memcmp(buf, authtag, sizeof(authtag)) || DIE("HASH DOES NOT MATCH!");
+#undef COUNTER
+}
+
 static int tls_xread_record(tls_state_t *tls, const char *expected)
 {
        struct record_hdr *xhdr;
@@ -896,34 +1037,44 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
        sz = target - RECHDR_LEN;
 
        /* Needs to be decrypted? */
-       if (tls->min_encrypted_len_on_read > tls->MAC_size) {
-               uint8_t *p = tls->inbuf + RECHDR_LEN;
-               int padding_len;
-
-               if (sz & (AES_BLOCKSIZE-1)
-                || sz < (int)tls->min_encrypted_len_on_read
-               ) {
-                       bb_error_msg_and_die("bad encrypted len:%u < %u",
-                               sz, tls->min_encrypted_len_on_read);
+       if (tls->min_encrypted_len_on_read != 0) {
+               if (sz < (int)tls->min_encrypted_len_on_read)
+                       bb_error_msg_and_die("bad encrypted len:%u", sz);
+
+               if (tls->flags & ENCRYPTION_AESGCM) {
+                       /* AESGCM */
+                       uint8_t *p = tls->inbuf + RECHDR_LEN;
+
+                       sz -= 8 + AES_BLOCK_SIZE; /* we will overwrite nonce, drop hash */
+                       tls_aesgcm_decrypt(tls, p, sz);
+                       memmove(p, p + 8, sz);
+                       dbg("encrypted size:%u\n", sz);
+               } else
+               if (tls->min_encrypted_len_on_read > tls->MAC_size) {
+                       /* AES+SHA */
+                       uint8_t *p = tls->inbuf + RECHDR_LEN;
+                       int padding_len;
+
+                       if (sz & (AES_BLOCK_SIZE-1))
+                               bb_error_msg_and_die("bad encrypted len:%u", sz);
+
+                       /* Decrypt content+MAC+padding, moving it over IV in the process */
+                       sz -= AES_BLOCK_SIZE; /* we will overwrite IV now */
+                       aes_cbc_decrypt(
+                               tls->server_write_key, tls->key_size, /* selects 128/256 */
+                               p, /* IV */
+                               p + AES_BLOCK_SIZE, sz, /* ciphertext */
+                               p /* plaintext */
+                       );
+                       padding_len = p[sz - 1];
+                       dbg("encrypted size:%u type:0x%02x padding_length:0x%02x\n", sz, p[0], padding_len);
+                       padding_len++;
+                       sz -= tls->MAC_size + padding_len; /* drop MAC and padding */
+               } else {
+                       /* if nonzero, then it's TLS_RSA_WITH_NULL_SHA256: drop MAC */
+                       /* else: no encryption yet on input, subtract zero = NOP */
+                       sz -= tls->min_encrypted_len_on_read;
                }
-               /* Decrypt content+MAC+padding, moving it over IV in the process */
-               sz -= AES_BLOCKSIZE; /* we will overwrite IV now */
-               aes_cbc_decrypt(
-                       tls->server_write_key, tls->key_size, /* selects 128/256 */
-                       p, /* IV */
-                       p + AES_BLOCKSIZE, sz, /* ciphertext */
-                       p /* plaintext */
-               );
-               padding_len = p[sz - 1];
-               dbg("encrypted size:%u type:0x%02x padding_length:0x%02x\n", sz, p[0], padding_len);
-               padding_len++;
-               sz -= tls->MAC_size + padding_len; /* drop MAC and padding */
-               //if (sz < 0)
-               //      bb_error_msg_and_die("bad padding size:%u", padding_len);
-       } else {
-               /* if nonzero, then it's TLS_RSA_WITH_NULL_SHA256: drop MAC */
-               /* else: no encryption yet on input, subtract zero = NOP */
-               sz -= tls->min_encrypted_len_on_read;
        }
        if (sz < 0)
                bb_error_msg_and_die("encrypted data too short");
@@ -964,7 +1115,8 @@ static int tls_xread_record(tls_state_t *tls, const char *expected)
         * in our FINISHED record must include data of incoming packets too!
         */
        if (tls->inbuf[0] == RECORD_TYPE_HANDSHAKE
-        && tls->MAC_size != 0 /* do we know which hash to use? (server_hello() does not!) */
+/* HANDSHAKE HASH: */
+       // && do_we_know_which_hash_to_use /* server_hello() might not know it in the future! */
        ) {
                hash_handshake(tls, "<< hash:%s", tls->inbuf + RECHDR_LEN, sz);
        }
@@ -1198,16 +1350,16 @@ static void find_key_in_der_cert(tls_state_t *tls, uint8_t *der, int len)
                };
                if (memcmp(der, OID_RSA_KEY_ALG, sizeof(OID_RSA_KEY_ALG)) == 0) {
                        dbg("RSA key\n");
-                       tls->hsd->key_alg = KEY_ALG_RSA;
+                       tls->flags |= GOT_CERT_RSA_KEY_ALG;
                } else
                if (memcmp(der, OID_ECDSA_KEY_ALG, sizeof(OID_ECDSA_KEY_ALG)) == 0) {
                        dbg("ECDSA key\n");
-                       tls->hsd->key_alg = KEY_ALG_ECDSA;
+                       tls->flags |= GOT_CERT_ECDSA_KEY_ALG;
                } else
-                       bb_error_msg_and_die("not RSA or ECDSA key");
+                       bb_error_msg_and_die("not RSA or ECDSA cert");
        }
 
-       if (tls->hsd->key_alg == KEY_ALG_RSA) {
+       if (tls->flags & GOT_CERT_RSA_KEY_ALG) {
                /* parse RSA key: */
        //based on getAsnRsaPubKey(), pkcs1ParsePrivBin() is also of note
                /* skip subjectPublicKeyInfo.algorithm */
@@ -1301,7 +1453,7 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
                uint8_t session_id_len;
                /* uint8_t session_id[]; */
                uint8_t cipherid_len16_hi, cipherid_len16_lo;
-               uint8_t cipherid[2 * (2 + !!CIPHER_ID2 + !!CIPHER_ID3)]; /* actually variable */
+               uint8_t cipherid[2 * (1 + NUM_CIPHERS)]; /* actually variable */
                uint8_t comprtypes_len;
                uint8_t comprtypes[1]; /* actually variable */
                /* Extensions (SNI shown):
@@ -1364,6 +1516,10 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
        if ((CIPHER_ID3 >> 8) != 0) record->cipherid[6] = CIPHER_ID3 >> 8;
        /*************************/ record->cipherid[7] = CIPHER_ID3 & 0xff;
 #endif
+#if CIPHER_ID4
+       if ((CIPHER_ID4 >> 8) != 0) record->cipherid[6] = CIPHER_ID4 >> 8;
+       /*************************/ record->cipherid[7] = CIPHER_ID4 & 0xff;
+#endif
 
        record->comprtypes_len = 1;
        /* record->comprtypes[0] = 0; */
@@ -1385,15 +1541,23 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
        }
        memcpy(ptr, supported_groups, sizeof(supported_groups));
 
-       dbg(">> CLIENT_HELLO\n");
-       /* Can hash it only when we know which MAC hash to use */
-       /*xwrite_and_update_handshake_hash(tls, len); - WRONG! */
-       xwrite_handshake_record(tls, len);
-
-       tls->hsd = xzalloc(sizeof(*tls->hsd) + len);
+       tls->hsd = xzalloc(sizeof(*tls->hsd));
+       /* HANDSHAKE HASH: ^^^ + len if need to save saved_client_hello */
+       memcpy(tls->hsd->client_and_server_rand32, record->rand32, sizeof(record->rand32));
+/* HANDSHAKE HASH:
        tls->hsd->saved_client_hello_size = len;
        memcpy(tls->hsd->saved_client_hello, record, len);
-       memcpy(tls->hsd->client_and_server_rand32, record->rand32, sizeof(record->rand32));
+ */
+       dbg(">> CLIENT_HELLO\n");
+       /* Can hash immediately only if we know which MAC hash to use.
+        * So far we do know: it's sha256:
+        */
+       sha256_begin(&tls->hsd->handshake_hash_ctx);
+       xwrite_and_update_handshake_hash(tls, len);
+       /* if this would become infeasible: save tls->hsd->saved_client_hello,
+        * use "xwrite_handshake_record(tls, len)" here,
+        * and hash saved_client_hello later.
+        */
 }
 
 static void get_server_hello(tls_state_t *tls)
@@ -1463,18 +1627,28 @@ static void get_server_hello(tls_state_t *tls)
        if (cipher == TLS_RSA_WITH_AES_128_CBC_SHA
         || cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
        ) {
+               if (cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
+                       tls->flags |= NEED_EC_KEY;
                tls->key_size = AES128_KEYSIZE;
                tls->MAC_size = SHA1_OUTSIZE;
        }
-       else { /* TLS_RSA_WITH_AES_256_CBC_SHA256 */
+       else
+       if (cipher == TLS_RSA_WITH_AES_256_CBC_SHA256) {
                tls->key_size = AES256_KEYSIZE;
                tls->MAC_size = SHA256_OUTSIZE;
        }
+       else { /* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */
+               tls->flags |= NEED_EC_KEY | ENCRYPTION_AESGCM;
+               tls->key_size = AES128_KEYSIZE;
+               /* tls->MAC_size = 0; */
+               tls->IV_size = 4;
+       }
        /* Handshake hash eventually destined to FINISHED record
         * is sha256 regardless of cipher
         * (at least for all ciphers defined by RFC5246).
         * It's not sha1 for AES_128_CBC_SHA - only MAC is sha1, not this hash.
         */
+/* HANDSHAKE HASH:
        sha256_begin(&tls->hsd->handshake_hash_ctx);
        hash_handshake(tls, ">> client hello hash:%s",
                tls->hsd->saved_client_hello, tls->hsd->saved_client_hello_size
@@ -1482,6 +1656,7 @@ static void get_server_hello(tls_state_t *tls)
        hash_handshake(tls, "<< server hello hash:%s",
                tls->inbuf + RECHDR_LEN, len
        );
+ */
 }
 
 static void get_server_cert(tls_state_t *tls)
@@ -1548,7 +1723,7 @@ static void process_server_key(tls_state_t *tls, int len)
 //          64523d6216cb94c43c9b20e377d8c52c55be6703fd6730a155930c705eaf3af6 //32bytes
 //same about this item ^^^^^
 
-//seen from www.openbsd.org
+//seen from ftp.openbsd.org
 //(which only accepts ECDHE-RSA-AESnnn-GCM-SHAnnn and ECDHE-RSA-CHACHA20-POLY1305 ciphers):
 //  0c 000228 //SERVER_KEY_EXCHANGE, len
 //    03 //curve_type: named curve
@@ -1572,6 +1747,7 @@ static void process_server_key(tls_state_t *tls, int len)
                bb_error_msg_and_die("elliptic curve is not x25519");
 
        memcpy(tls->hsd->ecc_pub_key32, keybuf + 4, 32);
+       tls->flags |= GOT_EC_KEY;
        dbg("got eccPubKey\n");
 }
 
@@ -1612,7 +1788,11 @@ static void send_client_key_exchange(tls_state_t *tls)
        int premaster_size;
        int len;
 
-       if (tls->hsd->key_alg == KEY_ALG_RSA) {
+       if (!(tls->flags & NEED_EC_KEY)) {
+               /* RSA */
+               if (!(tls->flags & GOT_CERT_RSA_KEY_ALG))
+                       bb_error_msg("server cert is not RSA");
+
                tls_get_random(rsa_premaster, sizeof(rsa_premaster));
                if (TLS_DEBUG_FIXED_SECRETS)
                        memset(rsa_premaster, 0x44, sizeof(rsa_premaster));
@@ -1636,10 +1816,13 @@ static void send_client_key_exchange(tls_state_t *tls)
                premaster = rsa_premaster;
                premaster_size = sizeof(rsa_premaster);
        } else {
-               /* KEY_ALG_ECDSA */
+               /* ECDHE */
                static const uint8_t basepoint9[CURVE25519_KEYSIZE] = {9};
                uint8_t privkey[CURVE25519_KEYSIZE]; //[32]
 
+               if (!(tls->flags & GOT_EC_KEY))
+                       bb_error_msg("server did not provide EC key");
+
                /* Generate random private key, see RFC 7748 */
                tls_get_random(privkey, sizeof(privkey));
                privkey[0] &= 0xf8;
@@ -1727,23 +1910,32 @@ static void send_client_key_exchange(tls_state_t *tls)
                memcpy(&tmp64[32], &tls->hsd->client_and_server_rand32[0] , 32);
 
                prf_hmac_sha256(/*tls,*/
-                       tls->client_write_MAC_key, 2 * (tls->MAC_size + tls->key_size),
+                       tls->client_write_MAC_key, 2 * (tls->MAC_size + tls->key_size + tls->IV_size),
                        // also fills:
                        // server_write_MAC_key[]
                        // client_write_key[]
                        // server_write_key[]
+                       // client_write_IV[]
+                       // server_write_IV[]
                        tls->hsd->master_secret, sizeof(tls->hsd->master_secret),
                        "key expansion",
                        tmp64, 64
                );
                tls->client_write_key = tls->client_write_MAC_key + (2 * tls->MAC_size);
                tls->server_write_key = tls->client_write_key + tls->key_size;
+               tls->client_write_IV = tls->server_write_key + tls->key_size;
+               tls->server_write_IV = tls->client_write_IV + tls->IV_size;
                dump_hex("client_write_MAC_key:%s\n",
                        tls->client_write_MAC_key, tls->MAC_size
                );
                dump_hex("client_write_key:%s\n",
                        tls->client_write_key, tls->key_size
                );
+               dump_hex("client_write_IV:%s\n",
+                       tls->client_write_IV, tls->IV_size
+               );
+               aesgcm_setkey(tls->H, &tls->aes_encrypt, tls->client_write_key, tls->key_size);
+               aes_setkey(&tls->aes_decrypt, tls->server_write_key, tls->key_size);
        }
 }
 
@@ -1876,7 +2068,7 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
                // client.
                dbg("<< SERVER_KEY_EXCHANGE len:%u\n", len);
                dump_raw_in("<< %s\n", tls->inbuf, RECHDR_LEN + len);
-               if (tls->hsd->key_alg == KEY_ALG_ECDSA)
+               if (tls->flags & NEED_EC_KEY)
                        process_server_key(tls, len);
 
                // read next handshake block
@@ -1922,18 +2114,22 @@ void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni)
        if (len != 1 || memcmp(tls->inbuf, rec_CHANGE_CIPHER_SPEC, 6) != 0)
                bad_record_die(tls, "switch to encrypted traffic", len);
        dbg("<< CHANGE_CIPHER_SPEC\n");
+
        if (CIPHER_ID1 == TLS_RSA_WITH_NULL_SHA256
         && tls->cipher_id == TLS_RSA_WITH_NULL_SHA256
        ) {
                tls->min_encrypted_len_on_read = tls->MAC_size;
-       } else {
-               unsigned mac_blocks = (unsigned)(tls->MAC_size + AES_BLOCKSIZE-1) / AES_BLOCKSIZE;
+       } else
+       if (!(tls->flags & ENCRYPTION_AESGCM)) {
+               unsigned mac_blocks = (unsigned)(tls->MAC_size + AES_BLOCK_SIZE-1) / AES_BLOCK_SIZE;
                /* all incoming packets now should be encrypted and have
                 * at least IV + (MAC padded to blocksize):
                 */
-               tls->min_encrypted_len_on_read = AES_BLOCKSIZE + (mac_blocks * AES_BLOCKSIZE);
-               dbg("min_encrypted_len_on_read: %u", tls->min_encrypted_len_on_read);
+               tls->min_encrypted_len_on_read = AES_BLOCK_SIZE + (mac_blocks * AES_BLOCK_SIZE);
+       } else {
+               tls->min_encrypted_len_on_read = 8 + AES_BLOCK_SIZE;
        }
+       dbg("min_encrypted_len_on_read: %u\n", tls->min_encrypted_len_on_read);
 
        /* Get (encrypted) FINISHED from the server */
        len = tls_xread_record(tls, "'server finished'");
index 66d25eff5a12b335de626e65e5aa46503212371c..1d379c1935bae6830cefed25e28a540009feefcf 100644 (file)
@@ -78,6 +78,8 @@ typedef  int16_t  int16;
 #define PUBKEY_TYPE     0x01
 #define PRIVKEY_TYPE    0x02
 
+#define AES_BLOCK_SIZE  16
+
 void tls_get_random(void *buf, unsigned len);
 
 #define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS)
@@ -96,5 +98,6 @@ void tls_get_random(void *buf, unsigned len);
 #include "tls_pstm.h"
 #include "tls_symmetric.h"
 #include "tls_aes.h"
+#include "tls_aesgcm.h"
 #include "tls_rsa.h"
 #include "tls_fe.h"
index c137442e91c800191d4510cbe1b6f92e12a00bac..4d2b689754ecf8ed1b4db8722689804492b985b9 100644 (file)
@@ -340,8 +340,12 @@ static void aes_encrypt_1(unsigned astate[16], unsigned rounds, const uint32_t *
        AddRoundKey(astate, RoundKey);
 }
 
-#if 0 // UNUSED
-static void aes_encrypt_one_block(unsigned rounds, const uint32_t *RoundKey, const void *data, void *dst)
+void FAST_FUNC aes_setkey(struct tls_aes *aes, const void *key, unsigned key_len)
+{
+       aes->rounds = KeyExpansion(aes->key, key, key_len);
+}
+
+void FAST_FUNC aes_encrypt_one_block(struct tls_aes *aes, const void *data, void *dst)
 {
        unsigned astate[16];
        unsigned i;
@@ -351,13 +355,12 @@ static void aes_encrypt_one_block(unsigned rounds, const uint32_t *RoundKey, con
 
        for (i = 0; i < 16; i++)
                astate[i] = pt[i];
-       aes_encrypt_1(astate, rounds, RoundKey);
+       aes_encrypt_1(astate, aes->rounds, aes->key);
        for (i = 0; i < 16; i++)
                ct[i] = astate[i];
 }
-#endif
 
-void aes_cbc_encrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst)
+void FAST_FUNC aes_cbc_encrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst)
 {
        uint32_t RoundKey[60];
        uint8_t iv2[16];
@@ -420,7 +423,7 @@ static void aes_decrypt_one_block(unsigned rounds, const uint32_t *RoundKey, con
 }
 #endif
 
-void aes_cbc_decrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst)
+void FAST_FUNC aes_cbc_decrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst)
 {
        uint32_t RoundKey[60];
        uint8_t iv2[16];
index c6791866aef7c56f8542d1ca9f28225d27f805bf..fc388179334faff8c7d4abc8c403cd931e38b965 100644 (file)
@@ -6,5 +6,9 @@
  * Selected few declarations for AES.
  */
 
-void aes_cbc_encrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst);
-void aes_cbc_decrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst);
+void aes_setkey(struct tls_aes *aes, const void *key, unsigned key_len) FAST_FUNC;
+
+void aes_encrypt_one_block(struct tls_aes *aes, const void *data, void *dst) FAST_FUNC;
+
+void aes_cbc_encrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst) FAST_FUNC;
+void aes_cbc_decrypt(const void *key, int klen, void *iv, const void *data, size_t len, void *dst) FAST_FUNC;
diff --git a/networking/tls_aesgcm.c b/networking/tls_aesgcm.c
new file mode 100644 (file)
index 0000000..584cee9
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "tls.h"
+
+typedef uint8_t byte;
+typedef uint32_t word32;
+#define XMEMSET memset
+#define XMEMCPY memcpy
+
+#define TLS_MAJ 3
+#define TLS_MIN 3
+#define RECHDR_LEN 5
+#define OUTBUF_PFX (8 + AES_BLOCK_SIZE)
+
+void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count)
+{
+    word32 i;
+    byte*       b = (byte*)buf;
+    const byte* m = (const byte*)mask;
+    for (i = 0; i < count; i++)
+        b[i] ^= m[i];
+}
+
+/* wolfssl-3.15.3/wolfcrypt/src/aes.c */
+
+static void FlattenSzInBits(byte* buf, word32 sz)
+{
+    /* Multiply the sz by 8 */
+    word32 szHi = (sz >> (8*sizeof(sz) - 3));
+    sz <<= 3;
+
+    /* copy over the words of the sz into the destination buffer */
+    buf[0] = (szHi >> 24) & 0xff;
+    buf[1] = (szHi >> 16) & 0xff;
+    buf[2] = (szHi >>  8) & 0xff;
+    buf[3] = szHi & 0xff;
+    buf[4] = (sz >> 24) & 0xff;
+    buf[5] = (sz >> 16) & 0xff;
+    buf[6] = (sz >>  8) & 0xff;
+    buf[7] = sz & 0xff;
+}
+
+static void RIGHTSHIFTX(byte* x)
+{
+    int i;
+    int carryOut = 0;
+    int carryIn = 0;
+    int borrow = x[15] & 0x01;
+
+    for (i = 0; i < AES_BLOCK_SIZE; i++) {
+        carryOut = x[i] & 0x01;
+        x[i] = (x[i] >> 1) | (carryIn ? 0x80 : 0);
+        carryIn = carryOut;
+    }
+    if (borrow) x[0] ^= 0xE1;
+}
+
+static void GMULT(byte* X, byte* Y)
+{
+    byte Z[AES_BLOCK_SIZE];
+    byte V[AES_BLOCK_SIZE];
+    int i, j;
+
+    XMEMSET(Z, 0, AES_BLOCK_SIZE);
+    XMEMCPY(V, X, AES_BLOCK_SIZE);
+    for (i = 0; i < AES_BLOCK_SIZE; i++)
+    {
+        byte y = Y[i];
+        for (j = 0; j < 8; j++)
+        {
+            if (y & 0x80) {
+                xorbuf(Z, V, AES_BLOCK_SIZE);
+            }
+
+            RIGHTSHIFTX(V);
+            y = y << 1;
+        }
+    }
+    XMEMCPY(X, Z, AES_BLOCK_SIZE);
+}
+
+void FAST_FUNC aesgcm_GHASH(byte* h, const byte* a, unsigned aSz, const byte* c,
+    unsigned cSz, byte* s, unsigned sSz)
+{
+    byte x[AES_BLOCK_SIZE];
+    byte scratch[AES_BLOCK_SIZE];
+    word32 blocks, partial;
+    //was: byte* h = aes->H;
+
+    XMEMSET(x, 0, AES_BLOCK_SIZE);
+
+    /* Hash in A, the Additional Authentication Data */
+    if (aSz != 0 && a != NULL) {
+        blocks = aSz / AES_BLOCK_SIZE;
+        partial = aSz % AES_BLOCK_SIZE;
+        while (blocks--) {
+            xorbuf(x, a, AES_BLOCK_SIZE);
+            GMULT(x, h);
+            a += AES_BLOCK_SIZE;
+        }
+        if (partial != 0) {
+            XMEMSET(scratch, 0, AES_BLOCK_SIZE);
+            XMEMCPY(scratch, a, partial);
+            xorbuf(x, scratch, AES_BLOCK_SIZE);
+            GMULT(x, h);
+        }
+    }
+
+    /* Hash in C, the Ciphertext */
+    if (cSz != 0 && c != NULL) {
+        blocks = cSz / AES_BLOCK_SIZE;
+        partial = cSz % AES_BLOCK_SIZE;
+        while (blocks--) {
+            xorbuf(x, c, AES_BLOCK_SIZE);
+            GMULT(x, h);
+            c += AES_BLOCK_SIZE;
+        }
+        if (partial != 0) {
+            XMEMSET(scratch, 0, AES_BLOCK_SIZE);
+            XMEMCPY(scratch, c, partial);
+            xorbuf(x, scratch, AES_BLOCK_SIZE);
+            GMULT(x, h);
+        }
+    }
+
+    /* Hash in the lengths of A and C in bits */
+    FlattenSzInBits(&scratch[0], aSz);
+    FlattenSzInBits(&scratch[8], cSz);
+    xorbuf(x, scratch, AES_BLOCK_SIZE);
+    GMULT(x, h);
+
+    /* Copy the result into s. */
+    XMEMCPY(s, x, sSz);
+}
+
+void FAST_FUNC aesgcm_setkey(uint8_t H[16], struct tls_aes *aes, const byte* key, unsigned len)
+{
+    byte iv[AES_BLOCK_SIZE];
+
+    aes_setkey(aes, key, len);
+
+    memset(iv, 0, AES_BLOCK_SIZE);
+    aes_encrypt_one_block(aes, iv, H);
+}
diff --git a/networking/tls_aesgcm.h b/networking/tls_aesgcm.h
new file mode 100644 (file)
index 0000000..d4cde01
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2018 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+void xorbuf(void* buf, const void* mask, unsigned count) FAST_FUNC;
+
+void aesgcm_GHASH(uint8_t* h,
+       const uint8_t* a, unsigned aSz,
+       const uint8_t* c, unsigned cSz,
+       uint8_t* s, unsigned sSz
+) FAST_FUNC;
+
+void aesgcm_setkey(uint8_t H[16], struct tls_aes *aes, const uint8_t* key, unsigned len) FAST_FUNC;
index dcb41468af75326deb829005bb82a4d542248857..f235082f536f861dd89d76238d57e10b63dc453a 100644 (file)
@@ -554,7 +554,7 @@ static void xc_double(byte *x3, byte *z3,
        fe_mul_c(z3, x1sq, 4);
 }
 
-void curve25519(byte *result, const byte *e, const byte *q)
+void FAST_FUNC curve25519(byte *result, const byte *e, const byte *q)
 {
        int i;
 
index bd2f2b3dac443365627c551ca923b7b9c6b8829c..fe8cff2288b60f984037f1e5d67ac11f42c0b619 100644 (file)
@@ -4,4 +4,4 @@
  * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #define CURVE25519_KEYSIZE 32
-void curve25519(uint8_t *result, const uint8_t *e, const uint8_t *q);
+void curve25519(uint8_t *result, const uint8_t *e, const uint8_t *q) FAST_FUNC;
index 60c54248ed21e33ef9dc80b99af6fbb5cf0a5119..631397e4dddf3a0f5e21da5f36b4bd7f557367ec 100644 (file)
@@ -179,7 +179,7 @@ done:
        return res;
 }
 
-int32 psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key,
+int32 FAST_FUNC psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key,
                                                unsigned char *in, uint32 inlen,
                                                unsigned char *out, uint32 outlen, void *data)
 {
index c464ed5528f4335f25939664da8a0a834fba754a..f42923ff5b9120c645469f7a853e878c1debcf71 100644 (file)
@@ -17,4 +17,4 @@ typedef struct {
         psRsaEncryptPub(      key, in, inlen, out, outlen)
 int32 psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key,
                                                 unsigned char *in, uint32 inlen,
-                                                unsigned char *out, uint32 outlen, void *data);
+                                                unsigned char *out, uint32 outlen, void *data) FAST_FUNC;