From 6b9a65077f314d5ede4a0a917e6cfde93c5377a9 Mon Sep 17 00:00:00 2001 From: Cristian Stoica Date: Thu, 31 Oct 2013 20:05:12 +0200 Subject: crypto: add support for TLS 1.0 record encryption This patch adds kernel support for encryption/decryption of TLS 1.0 records using block ciphers. Implementation is similar to authenc in the sense that the base algorithms (AES, SHA1) are combined in a template to produce TLS encapsulation frames. The composite algorithm will be called "tls10(hmac(),cbc())". The cipher and hmac keys are wrapped in the same format used by authenc.c Change-Id: If2211062f1e8805ee1fe9e6684e7c0902bf44467 Signed-off-by: Cristian Stoica Reviewed-on: http://git.am.freescale.net:8181/6211 Reviewed-by: Mircea Pop Reviewed-by: Thomas Trefny Tested-by: Review Code-CDREVIEW Reviewed-by: Jose Rivera (cherry picked from commit e2fe61d3fe94949f9fc5766f7b27a1d19c9d4d6e) Conflicts: crypto/tcrypt.c Signed-off-by: Horia Geanta Change-Id: I6ecb63c7cb8c64aef984e71e439dab6000666b29 Reviewed-on: http://git.am.freescale.net:8181/10373 Reviewed-by: Cristian Stoica Reviewed-by: Mircea Pop Reviewed-by: Alexandru Porosanu Tested-by: Review Code-CDREVIEW Reviewed-by: Jose Rivera diff --git a/crypto/Kconfig b/crypto/Kconfig index 7bcb70d..6c37bdb 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -211,6 +211,24 @@ config CRYPTO_SEQIV This IV generator generates an IV based on a sequence number by xoring it with a salt. This algorithm is mainly useful for CTR +config CRYPTO_TLS + tristate "TLS support" + select CRYPTO_AEAD + select CRYPTO_BLKCIPHER + select CRYPTO_MANAGER + select CRYPTO_HASH + help + Support for TLS 1.0 record encryption and decryption + + This module adds support for encryption/decryption of TLS 1.0 frames + using blockcipher algorithms. The name of the resulting algorithm is + "tls10(hmac(),cbc())". By default, the generic base + algorithms are used (e.g. aes-generic, sha1-generic), but hardware + accelerated versions will be used automatically if available. + + User-space applications (OpenSSL, GnuTLS) can offload TLS 1.0 + operations through AF_ALG or cryptodev interfaces + comment "Block modes" config CRYPTO_CBC diff --git a/crypto/Makefile b/crypto/Makefile index ab6ce921..0d55bcc 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o obj-$(CONFIG_CRYPTO_CRC32) += crc32.o obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif_common.o crct10dif_generic.o obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o +obj-$(CONFIG_CRYPTO_TLS) += tls.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o obj-$(CONFIG_CRYPTO_LZ4) += lz4.o obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index 001f07c..cbbfc7a 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -1246,6 +1246,10 @@ static int do_test(int m) ret += tcrypt_test("authenc(hmac(sha1),cbc(aes))"); break; + case 180: + ret += tcrypt_test("tls10(hmac(sha1),cbc(aes))"); + break; + case 200: test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0, speed_template_16_24_32); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 7795550..6221c54 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -108,6 +108,13 @@ struct cprng_test_suite { unsigned int count; }; +struct tls_test_suite { + struct { + struct tls_testvec *vecs; + unsigned int count; + } enc, dec; +}; + struct alg_test_desc { const char *alg; int (*test)(const struct alg_test_desc *desc, const char *driver, @@ -121,6 +128,7 @@ struct alg_test_desc { struct pcomp_test_suite pcomp; struct hash_test_suite hash; struct cprng_test_suite cprng; + struct tls_test_suite tls; } suite; }; @@ -803,6 +811,218 @@ static int test_aead(struct crypto_aead *tfm, int enc, return 0; } +static int __test_tls(struct crypto_aead *tfm, int enc, + struct tls_testvec *template, unsigned int tcount, + const bool diff_dst) +{ + const char *algo = crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)); + unsigned int i, rlen; + int ret = -ENOMEM; + char *q; + char *key; + struct aead_request *req; + struct scatterlist *sg; + struct scatterlist *asg; + struct scatterlist *sgout; + const char *e, *d; + struct tcrypt_result result; + void *input; + void *output; + void *assoc; + char iv[MAX_IVLEN]; + char *xbuf[XBUFSIZE]; + char *xoutbuf[XBUFSIZE]; + char *axbuf[XBUFSIZE]; + + if (testmgr_alloc_buf(xbuf)) + goto out_noxbuf; + if (testmgr_alloc_buf(axbuf)) + goto out_noaxbuf; + + if (diff_dst && testmgr_alloc_buf(xoutbuf)) + goto out_nooutbuf; + + sg = kmalloc(sizeof(*sg) * 8 * (diff_dst ? 3 : 2), GFP_KERNEL); + if (!sg) + goto out_nosg; + asg = &sg[8]; + sgout = &asg[8]; + + if (diff_dst) + d = "-ddst"; + else + d = ""; + + if (enc == ENCRYPT) + e = "encryption"; + else + e = "decryption"; + + init_completion(&result.completion); + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("alg: tls%s: Failed to allocate request for %s\n", + d, algo); + goto out; + } + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + tcrypt_complete, &result); + + for (i = 0; i < tcount; i++) { + input = xbuf[0]; + assoc = axbuf[0]; + + ret = -EINVAL; + if (WARN_ON(template[i].ilen > PAGE_SIZE || + template[i].alen > PAGE_SIZE)) + goto out; + + memcpy(input, template[i].input, template[i].ilen); + memcpy(assoc, template[i].assoc, template[i].alen); + if (template[i].iv) + memcpy(iv, template[i].iv, MAX_IVLEN); + else + memset(iv, 0, MAX_IVLEN); + + crypto_aead_clear_flags(tfm, ~0); + key = template[i].key; + + ret = crypto_aead_setkey(tfm, key, template[i].klen); + if (!ret == template[i].fail) { + pr_err("alg: tls%s: setkey failed on test %d for %s: flags=%x\n", + d, i, algo, crypto_aead_get_flags(tfm)); + goto out; + } + if (ret) + continue; + + output = diff_dst ? xoutbuf[0] : input; + + /* Allocate enough space in the input and output scatterlists. + * They must accomodate the result for in-place encryption and + * different-place decryption + */ + rlen = max(template[i].ilen, template[i].rlen); + if (diff_dst) { + output = xoutbuf[0]; + sg_init_one(&sg[0], input, template[i].ilen); + sg_init_one(&sgout[0], output, rlen); + } else { + output = input; + sg_init_one(&sg[0], input, rlen); + } + + sg_init_one(&asg[0], assoc, template[i].alen); + + aead_request_set_assoc(req, asg, template[i].alen); + aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg, + template[i].ilen, iv); + + ret = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req); + + switch (ret) { + case 0: + if (template[i].novrfy) { + /* verification was supposed to fail */ + pr_err("alg: tls%s: %s failed on test %d for %s: ret was 0, expected -EBADMSG\n", + d, e, i, algo); + /* so really, we got a bad message */ + ret = -EBADMSG; + goto out; + } + break; + case -EINPROGRESS: + case -EBUSY: + /* The result is not ready yet. We wait for it and then + * check if the request succeeded or not + */ + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !result.err) { + reinit_completion(&result.completion); + break; + } else { + ret = result.err; + } + /* fall through */ + case -EBADMSG: + /* verification failure was expected */ + if (template[i].novrfy) + continue; + /* fall through */ + default: + pr_err("alg: tls%s: %s failed on test %d for %s: ret=%d\n", + d, e, i, algo, -ret); + goto out; + } + + q = output; + if (memcmp(q, template[i].result, template[i].rlen)) { + pr_err("alg: tls%s: Test %d failed on %s for %s\n", + d, i, e, algo); + hexdump(q, template[i].rlen); + pr_err("should be:\n"); + hexdump(template[i].result, template[i].rlen); + ret = -EINVAL; + goto out; + } + } + +out: + aead_request_free(req); + kfree(sg); +out_nosg: + if (diff_dst) + testmgr_free_buf(xoutbuf); +out_nooutbuf: + testmgr_free_buf(axbuf); +out_noaxbuf: + testmgr_free_buf(xbuf); +out_noxbuf: + return ret; +} + +static int test_tls(struct crypto_aead *tfm, int enc, + struct tls_testvec *template, unsigned int tcount) +{ + int ret; + /* test 'dst == src' case */ + ret = __test_tls(tfm, enc, template, tcount, false); + if (ret) + return ret; + /* test 'dst != src' case */ + return __test_tls(tfm, enc, template, tcount, true); +} + +static int alg_test_tls(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) +{ + struct crypto_aead *tfm; + int err = 0; + + tfm = crypto_alloc_aead(driver, type, mask); + if (IS_ERR(tfm)) { + pr_err("alg: aead: Failed to load transform for %s: %ld\n", + driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + if (desc->suite.tls.enc.vecs) { + err = test_tls(tfm, ENCRYPT, desc->suite.tls.enc.vecs, + desc->suite.tls.enc.count); + if (err) + goto out; + } + if (!err && desc->suite.tls.dec.vecs) + err = test_tls(tfm, DECRYPT, desc->suite.tls.dec.vecs, + desc->suite.tls.dec.count); + +out: + crypto_free_aead(tfm); + return err; +} + static int test_cipher(struct crypto_cipher *tfm, int enc, struct cipher_testvec *template, unsigned int tcount) { @@ -3000,6 +3220,21 @@ static const struct alg_test_desc alg_test_descs[] = { } } }, { + .alg = "tls10(hmac(sha1),cbc(aes))", + .test = alg_test_tls, + .suite = { + .tls = { + .enc = { + .vecs = tls_enc_tv_template, + .count = TLS_ENC_TEST_VECTORS + }, + .dec = { + .vecs = tls_dec_tv_template, + .count = TLS_DEC_TEST_VECTORS + } + } + } + }, { .alg = "vmac(aes)", .test = alg_test_hash, .suite = { diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 7d44aa3..72b4e80 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -92,8 +92,110 @@ struct cprng_testvec { unsigned short loops; }; +struct tls_testvec { + char *key; /* wrapped keys for encryption and authentication */ + char *iv; /* initialization vector */ + char *input; /* input data */ + char *assoc; /* associated data: seq num, type, version, input len */ + char *result; /* result data */ + unsigned char fail; /* the test failure is expected */ + unsigned char novrfy; /* dec verification failure expected */ + unsigned char klen; /* key length */ + unsigned short ilen; /* input data length */ + unsigned short alen; /* associated data length */ + unsigned short rlen; /* result length */ +}; + static char zeroed_string[48]; + +/* + * TLS1.0 synthetic test vectors + */ +#define TLS_ENC_TEST_VECTORS 2 +#define TLS_DEC_TEST_VECTORS 1 + +static struct tls_testvec tls_enc_tv_template[] = { + { +#ifdef __LITTLE_ENDIAN + .key = "\x08\x00" /* rta length */ + "\x01\x00" /* rta type */ +#else + .key = "\x00\x08" /* rta length */ + "\x00\x01" /* rta type */ +#endif + "\x00\x00\x00\x10" /* enc key length */ + "authenticationkey20b" + "enckeyis16_bytes", + .klen = 8 + 20 + 16, + .iv = "iv0123456789abcd", + .input = "Single block msg", + .ilen = 16, + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x00\x03\x01\x00\x10", + .alen = 13, + .result = "\xd5\xac\xb\xd2\xac\xad\x3f\xb1" + "\x59\x79\x1e\x91\x5f\x52\x14\x9c" + "\xc0\x75\xd8\x4c\x97\x0f\x07\x73" + "\xdc\x89\x47\x49\x49\xcb\x30\x6b" + "\x1b\x45\x23\xa1\xd0\x51\xcf\x02" + "\x2e\xa8\x5d\xa0\xfe\xca\x82\x61", + .rlen = 16 + 20 + 12, + }, { +#ifdef __LITTLE_ENDIAN + .key = "\x08\x00" /* rta length */ + "\x01\x00" /* rta type */ +#else + .key = "\x00\x08" /* rta length */ + "\x00\x01" /* rta type */ +#endif + "\x00\x00\x00\x10" /* enc key length */ + "authenticationkey20b" + "enckeyis16_bytes", + .klen = 8 + 20 + 16, + .iv = "iv0123456789abcd", + .input = "", + .ilen = 0, + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x00\x03\x01\x00\x10", + .alen = 13, + .result = "\xa3\xdc\xf9\xac\x52\x70\x08\xcf" + "\x8d\x47\xbb\xe0\x64\xc0\x89\x2c" + "\xfb\x5d\x2d\x43\x35\x16\xeb\x4c" + "\x55\xb7\xcf\xaa\x9a\xe0\x02\x71", + .rlen = 20 + 12, + } +}; + +static struct tls_testvec tls_dec_tv_template[] = { + { +#ifdef __LITTLE_ENDIAN + .key = "\x08\x00" /* rta length */ + "\x01\x00" /* rta type */ +#else + .key = "\x00\x08" /* rta length */ + "\x00\x01" /* rta type */ +#endif + "\x00\x00\x00\x10" /* enc key length */ + "authenticationkey20b" + "enckeyis16_bytes", + .klen = 8 + 20 + 16, + .iv = "iv0123456789abcd", + .input = "\xd5\xac\xb\xd2\xac\xad\x3f\xb1" + "\x59\x79\x1e\x91\x5f\x52\x14\x9c" + "\xc0\x75\xd8\x4c\x97\x0f\x07\x73" + "\xdc\x89\x47\x49\x49\xcb\x30\x6b" + "\x1b\x45\x23\xa1\xd0\x51\xcf\x02" + "\x2e\xa8\x5d\xa0\xfe\xca\x82\x61", + .ilen = 16 + 20 + 12, + .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x00\x03\x01\x00\x30", + .alen = 13, + .result = "Single block msg", + .rlen = 16, + } +}; + /* * MD4 test vectors from RFC1320 */ diff --git a/crypto/tls.c b/crypto/tls.c new file mode 100644 index 0000000..18cc536 --- /dev/null +++ b/crypto/tls.c @@ -0,0 +1,567 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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 (at your option) + * any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tls_instance_ctx { + struct crypto_ahash_spawn auth; + struct crypto_skcipher_spawn enc; +}; + +struct crypto_tls_ctx { + unsigned int reqoff; + struct crypto_ahash *auth; + struct crypto_ablkcipher *enc; +}; + +struct tls_request_ctx { + /* + * cryptlen holds the payload length in the case of encryption or + * payload_len + icv_len + padding_len in case of decryption + */ + unsigned int cryptlen; + /* working space for partial results */ + struct scatterlist icv[2]; + struct scatterlist cipher[2]; + char tail[]; +}; + +struct async_op { + struct completion completion; + int err; +}; + +static void tls_async_op_done(struct crypto_async_request *req, int err) +{ + struct async_op *areq = req->data; + + if (err == -EINPROGRESS) + return; + + areq->err = err; + complete(&areq->completion); +} + +static void tls_request_done(struct crypto_async_request *req, int err) +{ + /* Mark the completion of the TLS request */ + struct aead_request *areq = req->data; + + if (err == -EINPROGRESS) + return; + + aead_request_complete(areq, err); +} + +static int crypto_tls_setkey(struct crypto_aead *tls, const u8 *key, + unsigned int keylen) +{ + unsigned int authkeylen; + unsigned int enckeylen; + struct crypto_tls_ctx *ctx = crypto_aead_ctx(tls); + struct crypto_ahash *auth = ctx->auth; + struct crypto_ablkcipher *enc = ctx->enc; + struct rtattr *rta = (void *)key; + struct crypto_authenc_key_param *param; + int err = -EINVAL; + + if (!RTA_OK(rta, keylen)) + goto badkey; + if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM) + goto badkey; + if (RTA_PAYLOAD(rta) < sizeof(*param)) + goto badkey; + + param = RTA_DATA(rta); + enckeylen = be32_to_cpu(param->enckeylen); + + key += RTA_ALIGN(rta->rta_len); + keylen -= RTA_ALIGN(rta->rta_len); + + if (keylen < enckeylen) + goto badkey; + + authkeylen = keylen - enckeylen; + + crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK); + crypto_ahash_set_flags(auth, crypto_aead_get_flags(tls) & + CRYPTO_TFM_REQ_MASK); + err = crypto_ahash_setkey(auth, key, authkeylen); + crypto_aead_set_flags(tls, crypto_ahash_get_flags(auth) & + CRYPTO_TFM_RES_MASK); + + if (err) + goto out; + + crypto_ablkcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK); + crypto_ablkcipher_set_flags(enc, crypto_aead_get_flags(tls) & + CRYPTO_TFM_REQ_MASK); + err = crypto_ablkcipher_setkey(enc, key + authkeylen, enckeylen); + crypto_aead_set_flags(tls, crypto_ablkcipher_get_flags(enc) & + CRYPTO_TFM_RES_MASK); +out: + return err; + +badkey: + crypto_aead_set_flags(tls, CRYPTO_TFM_RES_BAD_KEY_LEN); + goto out; +} + +/** + * crypto_tls_genicv - Calculate hmac digest for a TLS record + * @hash: (output) buffer to save the digest into + * @src: (input) scatterlist with the payload data + * @srclen: (input) size of the payload data + * @req: (input) aead request (with pointers to associated data) + **/ +static int crypto_tls_genicv(u8 *hash, struct scatterlist *src, + unsigned int srclen, struct aead_request *req) +{ + struct crypto_aead *tls = crypto_aead_reqtfm(req); + struct crypto_tls_ctx *ctx = crypto_aead_ctx(tls); + struct tls_request_ctx *treq_ctx = aead_request_ctx(req); + struct scatterlist *assoc = req->assoc; + struct scatterlist *icv = treq_ctx->icv; + struct async_op ahash_op; + struct ahash_request *ahreq = (void *)(treq_ctx->tail + ctx->reqoff); + unsigned int flags = CRYPTO_TFM_REQ_MAY_SLEEP; + int err = -EBADMSG; + + /* + * Bail out as we have only two maneuvering scatterlists in icv. Check + * also if the request assoc len matches the scatterlist len + */ + if (!req->assoclen || !sg_is_last(assoc) || + req->assoclen != assoc->length) + return err; + + /* + * Prepend associated data to the source scatterlist. If the source is + * empty, use directly the associated data scatterlist + */ + if (srclen) { + sg_init_table(icv, 2); + sg_set_page(icv, sg_page(assoc), assoc->length, assoc->offset); + scatterwalk_crypto_chain(icv, src, 0, 2); + } else { + icv = assoc; + } + srclen += assoc->length; + + init_completion(&ahash_op.completion); + + /* the hash transform to be executed comes from the original request */ + ahash_request_set_tfm(ahreq, ctx->auth); + /* prepare the hash request with input data and result pointer */ + ahash_request_set_crypt(ahreq, icv, hash, srclen); + /* set the notifier for when the async hash function returns */ + ahash_request_set_callback(ahreq, aead_request_flags(req) & flags, + tls_async_op_done, &ahash_op); + + /* Calculate the digest on the given data. The result is put in hash */ + err = crypto_ahash_digest(ahreq); + if (err == -EINPROGRESS) { + err = wait_for_completion_interruptible(&ahash_op.completion); + if (!err) + err = ahash_op.err; + } + + return err; +} + +/** + * crypto_tls_gen_padicv - Calculate and pad hmac digest for a TLS record + * @hash: (output) buffer to save the digest and padding into + * @phashlen: (output) the size of digest + padding + * @cryptlen: (output) the size of the total frame to be encrypted + * @req: (input) aead request + **/ +static int crypto_tls_gen_padicv(u8 *hash, unsigned int *phashlen, + unsigned int *cryptlen, struct aead_request *req) +{ + struct crypto_aead *tls = crypto_aead_reqtfm(req); + unsigned int hash_size = crypto_aead_authsize(tls); + unsigned int block_size = crypto_aead_blocksize(tls); + unsigned int srclen = req->cryptlen + hash_size; + unsigned int padlen; + int err; + + err = crypto_tls_genicv(hash, req->src, req->cryptlen, req); + if (err) + goto out; + + /* add padding after digest */ + padlen = block_size - (srclen % block_size); + memset(hash + hash_size, padlen - 1, padlen); + + /* save the frame length to be encrypted */ + *cryptlen = srclen + padlen; + *phashlen = hash_size + padlen; +out: + return err; +} + +static int crypto_tls_encrypt(struct aead_request *req) +{ + struct crypto_aead *tls = crypto_aead_reqtfm(req); + struct crypto_tls_ctx *ctx = crypto_aead_ctx(tls); + struct tls_request_ctx *treq_ctx = aead_request_ctx(req); + + unsigned int cryptlen, phashlen; + struct scatterlist *cipher = treq_ctx->cipher; + struct scatterlist *icv = treq_ctx->icv; + int err; + /* + * The hash and the cipher are applied at different times and their + * requests can use the same memory space without interference + */ + struct ablkcipher_request *abreq = (void *)(treq_ctx->tail + + ctx->reqoff); + /* + * The hash result is saved at the beginning of the tls request and is + * aligned as required by the hash transform. Enough space was + * allocated in crypto_tls_init_tfm to accomodate the difference. The + * requests themselves start later at treq_ctx->tail + ctx->reqoff so + * the result is not overwritten by the second (cipher) request + */ + u8 *hash = treq_ctx->tail; + hash = (u8 *)ALIGN((unsigned long)hash + + crypto_ahash_alignmask(ctx->auth), + crypto_ahash_alignmask(ctx->auth) + 1); + + /* + * STEP 1: create ICV and add necessary padding + */ + err = crypto_tls_gen_padicv(hash, &phashlen, &cryptlen, req); + if (err) + return err; + + /* we can reuse treq_cfr->icv because crypto_tls_gen_padicv finished */ + sg_init_one(icv, hash, phashlen); + + /* + * STEP 2: encrypt the frame and return the result + * Chain the payload with the hash. If the payload is empty, use + * directly the hash scatterlist + */ + if (req->cryptlen) { + sg_init_table(cipher, 2); + sg_set_page(cipher, sg_page(req->src), req->cryptlen, + req->src->offset); + scatterwalk_crypto_chain(cipher, icv, 0, 2); + } else { + cipher = icv; + } + /* prepare the cipher request */ + ablkcipher_request_set_tfm(abreq, ctx->enc); + ablkcipher_request_set_crypt(abreq, cipher, req->dst, cryptlen, + req->iv); + /* mark the completion of the whole encryption request */ + ablkcipher_request_set_callback(abreq, aead_request_flags(req), + tls_request_done, req); + /* Apply the cipher transform. The result will be in req->dst */ + err = crypto_ablkcipher_encrypt(abreq); + + return err; +} + +static int crypto_tls_decrypt(struct aead_request *req) +{ + struct crypto_aead *tls = crypto_aead_reqtfm(req); + struct crypto_tls_ctx *ctx = crypto_aead_ctx(tls); + struct tls_request_ctx *treq_ctx = aead_request_ctx(req); + struct scatterlist *assoc = req->assoc; + unsigned int cryptlen = req->cryptlen; + unsigned int hash_size = crypto_aead_authsize(tls); + unsigned int block_size = crypto_aead_blocksize(tls); + struct ablkcipher_request *abreq = (void *)(treq_ctx->tail + + ctx->reqoff); + u8 padding[255]; /* padding can be 0-255 bytes */ + u8 pad_size; + u16 *len_field; + u8 *ihash, *hash = treq_ctx->tail; + + int paderr = 0; + int err = -EINVAL; + int i; + struct async_op ciph_op; + + /* + * Rule out bad packets. The input packet length must be at least one + * byte more than the hash_size + */ + if (cryptlen <= hash_size || cryptlen % block_size) + goto out; + + /* + * Step 1 - Decrypt the source + */ + init_completion(&ciph_op.completion); + + ablkcipher_request_set_tfm(abreq, ctx->enc); + ablkcipher_request_set_callback(abreq, aead_request_flags(req), + tls_async_op_done, &ciph_op); + ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen, + req->iv); + err = crypto_ablkcipher_decrypt(abreq); + if (err == -EINPROGRESS) { + err = wait_for_completion_interruptible(&ciph_op.completion); + if (!err) + err = ciph_op.err; + } + if (err) + goto out; + + /* + * Step 2 - Verify padding + * Retrieve the last byte of the payload; this is the padding size + */ + cryptlen -= 1; + scatterwalk_map_and_copy(&pad_size, req->dst, cryptlen, 1, 0); + + /* RFC recommendation for invalid padding size */ + if (cryptlen < pad_size + hash_size) { + pad_size = 0; + paderr = -EBADMSG; + } + cryptlen -= pad_size; + scatterwalk_map_and_copy(padding, req->dst, cryptlen, pad_size, 0); + + /* Padding content must be equal with pad_size. We verify it all */ + for (i = 0; i < pad_size; i++) + if (padding[i] != pad_size) + paderr = -EBADMSG; + + /* + * Step 3 - Verify hash + * Align the digest result as required by the hash transform. Enough + * space was allocated in crypto_tls_init_tfm + */ + hash = (u8 *)ALIGN((unsigned long)hash + + crypto_ahash_alignmask(ctx->auth), + crypto_ahash_alignmask(ctx->auth) + 1); + /* + * Two bytes at the end of the associated data make the length field. + * It must be updated with the length of the cleartext message before + * the hash is calculated. + */ + len_field = sg_virt(assoc) + assoc->length - 2; + cryptlen -= hash_size; + *len_field = htons(cryptlen); + + /* This is the hash from the decrypted packet. Save it for later */ + ihash = hash + hash_size; + scatterwalk_map_and_copy(ihash, req->dst, cryptlen, hash_size, 0); + + /* Now compute and compare our ICV with the one from the packet */ + err = crypto_tls_genicv(hash, req->dst, cryptlen, req); + if (!err) + err = memcmp(hash, ihash, hash_size) ? -EBADMSG : 0; + + /* return the first found error */ + if (paderr) + err = paderr; + +out: + aead_request_complete(req, err); + return err; +} + +static int crypto_tls_init_tfm(struct crypto_tfm *tfm) +{ + struct crypto_instance *inst = crypto_tfm_alg_instance(tfm); + struct tls_instance_ctx *ictx = crypto_instance_ctx(inst); + struct crypto_tls_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_ahash *auth; + struct crypto_ablkcipher *enc; + int err; + + auth = crypto_spawn_ahash(&ictx->auth); + if (IS_ERR(auth)) + return PTR_ERR(auth); + + enc = crypto_spawn_skcipher(&ictx->enc); + err = PTR_ERR(enc); + if (IS_ERR(enc)) + goto err_free_ahash; + + ctx->auth = auth; + ctx->enc = enc; + /* + * Allow enough space for two digests. The two digests will be compared + * during the decryption phase. One will come from the decrypted packet + * and the other will be calculated. For encryption, one digest is + * padded (up to a cipher blocksize) and chained with the payload + */ + ctx->reqoff = ALIGN(crypto_ahash_digestsize(auth) + + crypto_ahash_alignmask(auth), + crypto_ahash_alignmask(auth) + 1) + + max(crypto_ahash_digestsize(auth), + crypto_ablkcipher_blocksize(enc)); + + tfm->crt_aead.reqsize = sizeof(struct tls_request_ctx) + ctx->reqoff + + max_t(unsigned int, + crypto_ahash_reqsize(auth) + + sizeof(struct ahash_request), + crypto_ablkcipher_reqsize(enc) + + sizeof(struct ablkcipher_request)); + + return 0; + +err_free_ahash: + crypto_free_ahash(auth); + return err; +} + +static void crypto_tls_exit_tfm(struct crypto_tfm *tfm) +{ + struct crypto_tls_ctx *ctx = crypto_tfm_ctx(tfm); + + crypto_free_ahash(ctx->auth); + crypto_free_ablkcipher(ctx->enc); +} + +static struct crypto_instance *crypto_tls_alloc(struct rtattr **tb) +{ + struct crypto_attr_type *algt; + struct crypto_instance *inst; + struct hash_alg_common *auth; + struct crypto_alg *auth_base; + struct crypto_alg *enc; + struct tls_instance_ctx *ctx; + const char *enc_name; + int err; + + algt = crypto_get_attr_type(tb); + err = PTR_ERR(algt); + if (IS_ERR(algt)) + return ERR_PTR(err); + + if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask) + return ERR_PTR(-EINVAL); + + auth = ahash_attr_alg(tb[1], CRYPTO_ALG_TYPE_HASH, + CRYPTO_ALG_TYPE_AHASH_MASK); + if (IS_ERR(auth)) + return ERR_CAST(auth); + + auth_base = &auth->base; + + enc_name = crypto_attr_alg_name(tb[2]); + err = PTR_ERR(enc_name); + if (IS_ERR(enc_name)) + goto out_put_auth; + + inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL); + err = -ENOMEM; + if (!inst) + goto out_put_auth; + + ctx = crypto_instance_ctx(inst); + + err = crypto_init_ahash_spawn(&ctx->auth, auth, inst); + if (err) + goto err_free_inst; + + crypto_set_skcipher_spawn(&ctx->enc, inst); + err = crypto_grab_skcipher(&ctx->enc, enc_name, 0, + crypto_requires_sync(algt->type, + algt->mask)); + if (err) + goto err_drop_auth; + + enc = crypto_skcipher_spawn_alg(&ctx->enc); + + err = -ENAMETOOLONG; + if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, + "tls10(%s,%s)", auth_base->cra_name, enc->cra_name) >= + CRYPTO_MAX_ALG_NAME) + goto err_drop_enc; + + if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, + "tls10(%s,%s)", auth_base->cra_driver_name, + enc->cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_drop_enc; + + inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD; + inst->alg.cra_flags |= enc->cra_flags & CRYPTO_ALG_ASYNC; + /* priority calculation is taken from authenc.c */ + inst->alg.cra_priority = enc->cra_priority * 10 + + auth_base->cra_priority; + inst->alg.cra_blocksize = enc->cra_blocksize; + inst->alg.cra_alignmask = auth_base->cra_alignmask | enc->cra_alignmask; + inst->alg.cra_type = &crypto_aead_type; + + inst->alg.cra_aead.ivsize = enc->cra_ablkcipher.ivsize; + inst->alg.cra_aead.maxauthsize = auth->digestsize; + + inst->alg.cra_ctxsize = sizeof(struct crypto_tls_ctx); + + inst->alg.cra_init = crypto_tls_init_tfm; + inst->alg.cra_exit = crypto_tls_exit_tfm; + + inst->alg.cra_aead.setkey = crypto_tls_setkey; + inst->alg.cra_aead.encrypt = crypto_tls_encrypt; + inst->alg.cra_aead.decrypt = crypto_tls_decrypt; + +out: + crypto_mod_put(auth_base); + return inst; + +err_drop_enc: + crypto_drop_skcipher(&ctx->enc); +err_drop_auth: + crypto_drop_ahash(&ctx->auth); +err_free_inst: + kfree(inst); +out_put_auth: + inst = ERR_PTR(err); + goto out; +} + +static void crypto_tls_free(struct crypto_instance *inst) +{ + struct tls_instance_ctx *ctx = crypto_instance_ctx(inst); + + crypto_drop_skcipher(&ctx->enc); + crypto_drop_ahash(&ctx->auth); + kfree(inst); +} + +static struct crypto_template crypto_tls_tmpl = { + .name = "tls10", + .alloc = crypto_tls_alloc, + .free = crypto_tls_free, + .module = THIS_MODULE, +}; + +static int __init crypto_tls_module_init(void) +{ + return crypto_register_template(&crypto_tls_tmpl); +} + +static void __exit crypto_tls_module_exit(void) +{ + crypto_unregister_template(&crypto_tls_tmpl); +} + +module_init(crypto_tls_module_init); +module_exit(crypto_tls_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TLS 1.0 record encryption"); -- cgit v0.10.2