summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRadu Alexe <radu.alexe@nxp.com>2017-06-09 11:49:17 (GMT)
committerXie Xiaobo <xiaobo.xie@nxp.com>2017-09-25 07:25:36 (GMT)
commit40c0ca3f08f9f6c6e78bd8fecae403e28f26d08b (patch)
treef45b041de93cfeab99fa81b175f5359acf875544
parent7966143269747f5096e1ba47b04716c4f87ca8cb (diff)
downloadlinux-40c0ca3f08f9f6c6e78bd8fecae403e28f26d08b.tar.xz
crypto: caam/qi2 - add support for TLS 1.0 record
TLS 1.0 descriptors run on SEC 4.x or higher. For now, only tls10(hmac(sha1),cbc(aes)) algorithm is registered by the driver. Known limitations: - when src == dst - there should be no element in the src scatterlist array that contains both associated data and message data. - when src != dst - associated data is not copied from source into destination. - for decryption when src != dst the size of the destination should be large enough so that the buffer may contain the decrypted authenc and padded data. Signed-off-by: Radu Alexe <radu.alexe@nxp.com>
-rw-r--r--drivers/crypto/caam/caamalg_qi2.c483
-rw-r--r--drivers/crypto/caam/caamalg_qi2.h22
2 files changed, 505 insertions, 0 deletions
diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c
index b38a37d..c204317 100644
--- a/drivers/crypto/caam/caamalg_qi2.c
+++ b/drivers/crypto/caam/caamalg_qi2.c
@@ -750,6 +750,342 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
return edesc;
}
+static struct tls_edesc *tls_edesc_alloc(struct aead_request *req,
+ bool encrypt)
+{
+ struct crypto_aead *tls = crypto_aead_reqtfm(req);
+ unsigned int blocksize = crypto_aead_blocksize(tls);
+ unsigned int padsize, authsize;
+ struct caam_request *req_ctx = aead_request_ctx(req);
+ struct dpaa2_fl_entry *in_fle = &req_ctx->fd_flt[1];
+ struct dpaa2_fl_entry *out_fle = &req_ctx->fd_flt[0];
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ struct caam_aead_alg *alg = container_of(crypto_aead_alg(tls),
+ typeof(*alg), aead);
+ struct device *dev = ctx->dev;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ int src_nents, mapped_src_nents, dst_nents = 0, mapped_dst_nents = 0;
+ struct tls_edesc *edesc;
+ dma_addr_t qm_sg_dma, iv_dma = 0;
+ int ivsize = 0;
+ int qm_sg_index, qm_sg_ents = 0, qm_sg_bytes;
+ int in_len, out_len;
+ struct dpaa2_sg_entry *sg_table;
+ enum optype op_type = encrypt ? ENCRYPT : DECRYPT;
+ struct scatterlist *dst;
+
+ if (encrypt) {
+ padsize = blocksize - ((req->cryptlen + ctx->authsize) %
+ blocksize);
+ authsize = ctx->authsize + padsize;
+ } else {
+ authsize = ctx->authsize;
+ }
+
+ /* allocate space for base edesc and link tables */
+ edesc = qi_cache_alloc(GFP_DMA | flags);
+ if (unlikely(!edesc)) {
+ dev_err(dev, "could not allocate extended descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (likely(req->src == req->dst)) {
+ src_nents = sg_nents_for_len(req->src, req->assoclen +
+ req->cryptlen +
+ (encrypt ? authsize : 0));
+ if (unlikely(src_nents < 0)) {
+ dev_err(dev, "Insufficient bytes (%d) in src S/G\n",
+ req->assoclen + req->cryptlen +
+ (encrypt ? authsize : 0));
+ qi_cache_free(edesc);
+ return ERR_PTR(src_nents);
+ }
+
+ mapped_src_nents = dma_map_sg(dev, req->src, src_nents,
+ DMA_BIDIRECTIONAL);
+ if (unlikely(!mapped_src_nents)) {
+ dev_err(dev, "unable to map source\n");
+ qi_cache_free(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+ dst = req->dst;
+ } else {
+ src_nents = sg_nents_for_len(req->src, req->assoclen +
+ req->cryptlen);
+ if (unlikely(src_nents < 0)) {
+ dev_err(dev, "Insufficient bytes (%d) in src S/G\n",
+ req->assoclen + req->cryptlen);
+ qi_cache_free(edesc);
+ return ERR_PTR(src_nents);
+ }
+
+ dst = scatterwalk_ffwd(edesc->tmp, req->dst, req->assoclen);
+ dst_nents = sg_nents_for_len(dst, req->cryptlen +
+ (encrypt ? authsize : 0));
+ if (unlikely(dst_nents < 0)) {
+ dev_err(dev, "Insufficient bytes (%d) in dst S/G\n",
+ req->cryptlen +
+ (encrypt ? authsize : 0));
+ qi_cache_free(edesc);
+ return ERR_PTR(dst_nents);
+ }
+
+ if (src_nents) {
+ mapped_src_nents = dma_map_sg(dev, req->src,
+ src_nents, DMA_TO_DEVICE);
+ if (unlikely(!mapped_src_nents)) {
+ dev_err(dev, "unable to map source\n");
+ qi_cache_free(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+ } else {
+ mapped_src_nents = 0;
+ }
+
+ mapped_dst_nents = dma_map_sg(dev, dst, dst_nents,
+ DMA_FROM_DEVICE);
+ if (unlikely(!mapped_dst_nents)) {
+ dev_err(dev, "unable to map destination\n");
+ dma_unmap_sg(dev, req->src, src_nents, DMA_TO_DEVICE);
+ qi_cache_free(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ ivsize = crypto_aead_ivsize(tls);
+ iv_dma = dma_map_single(dev, req->iv, ivsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, iv_dma)) {
+ dev_err(dev, "unable to map IV\n");
+ caam_unmap(dev, req->src, dst, src_nents, dst_nents, 0, 0,
+ op_type, 0, 0);
+ qi_cache_free(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * Create S/G table: IV, src, dst.
+ * Input is not contiguous.
+ */
+ qm_sg_ents = 1 + mapped_src_nents +
+ (mapped_dst_nents > 1 ? mapped_dst_nents : 0);
+ sg_table = &edesc->sgt[0];
+ qm_sg_bytes = qm_sg_ents * sizeof(*sg_table);
+
+ edesc->src_nents = src_nents;
+ edesc->dst_nents = dst_nents;
+ edesc->dst = dst;
+ edesc->iv_dma = iv_dma;
+
+ dma_to_qm_sg_one(sg_table, iv_dma, ivsize, 0);
+ qm_sg_index = 1;
+
+ sg_to_qm_sg_last(req->src, mapped_src_nents, sg_table + qm_sg_index, 0);
+ qm_sg_index += mapped_src_nents;
+
+ if (mapped_dst_nents > 1)
+ sg_to_qm_sg_last(dst, mapped_dst_nents, sg_table +
+ qm_sg_index, 0);
+
+ qm_sg_dma = dma_map_single(dev, sg_table, qm_sg_bytes, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, qm_sg_dma)) {
+ dev_err(dev, "unable to map S/G table\n");
+ caam_unmap(dev, req->src, dst, src_nents, dst_nents, iv_dma,
+ ivsize, op_type, 0, 0);
+ qi_cache_free(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ edesc->qm_sg_dma = qm_sg_dma;
+ edesc->qm_sg_bytes = qm_sg_bytes;
+
+ out_len = req->cryptlen + (encrypt ? authsize : 0);
+ in_len = ivsize + req->assoclen + req->cryptlen;
+
+ memset(&req_ctx->fd_flt, 0, sizeof(req_ctx->fd_flt));
+ dpaa2_fl_set_final(in_fle, true);
+ dpaa2_fl_set_format(in_fle, dpaa2_fl_sg);
+ dpaa2_fl_set_addr(in_fle, qm_sg_dma);
+ dpaa2_fl_set_len(in_fle, in_len);
+
+ if (req->dst == req->src) {
+ dpaa2_fl_set_format(out_fle, dpaa2_fl_sg);
+ dpaa2_fl_set_addr(out_fle, qm_sg_dma +
+ (sg_nents_for_len(req->src, req->assoclen) +
+ 1) * sizeof(*sg_table));
+ } else if (mapped_dst_nents == 1) {
+ dpaa2_fl_set_format(out_fle, dpaa2_fl_single);
+ dpaa2_fl_set_addr(out_fle, sg_dma_address(dst));
+ } else {
+ dpaa2_fl_set_format(out_fle, dpaa2_fl_sg);
+ dpaa2_fl_set_addr(out_fle, qm_sg_dma + qm_sg_index *
+ sizeof(*sg_table));
+ }
+
+ dpaa2_fl_set_len(out_fle, out_len);
+
+ return edesc;
+}
+
+static int tls_set_sh_desc(struct crypto_aead *tls)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ unsigned int ivsize = crypto_aead_ivsize(tls);
+ unsigned int blocksize = crypto_aead_blocksize(tls);
+ struct device *dev = ctx->dev;
+ struct caam_flc *flc;
+ dma_addr_t *flc_dma;
+ u32 *desc;
+ unsigned int assoclen = 13; /* always 13 bytes for TLS */
+ unsigned int data_len[2];
+ u32 inl_mask;
+
+ if (!ctx->cdata.keylen || !ctx->authsize)
+ return 0;
+
+ /*
+ * TLS 1.0 encrypt shared descriptor
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ data_len[0] = ctx->adata.keylen_pad;
+ data_len[1] = ctx->cdata.keylen;
+
+ if (desc_inline_query(DESC_TLS10_ENC_LEN, DESC_JOB_IO_LEN, data_len,
+ &inl_mask, ARRAY_SIZE(data_len)) < 0)
+ return -EINVAL;
+
+ if (inl_mask & 1)
+ ctx->adata.key_virt = ctx->key;
+ else
+ ctx->adata.key_dma = ctx->key_dma;
+
+ if (inl_mask & 2)
+ ctx->cdata.key_virt = ctx->key + ctx->adata.keylen_pad;
+ else
+ ctx->cdata.key_dma = ctx->key_dma + ctx->adata.keylen_pad;
+
+ ctx->adata.key_inline = !!(inl_mask & 1);
+ ctx->cdata.key_inline = !!(inl_mask & 2);
+
+ flc = &ctx->flc[ENCRYPT];
+ flc_dma = &ctx->flc_dma[ENCRYPT];
+ desc = flc->sh_desc;
+
+ cnstr_shdsc_tls_encap(desc, &ctx->cdata, &ctx->adata,
+ assoclen, ivsize, ctx->authsize, blocksize);
+
+ flc->flc[1] = desc_len(desc);
+ *flc_dma = dma_map_single(dev, flc, sizeof(flc->flc) +
+ desc_bytes(desc), DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, *flc_dma)) {
+ dev_err(dev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * TLS 1.0 decrypt shared descriptor
+ * Keys do not fit inline, regardless of algorithms used
+ */
+ ctx->adata.key_dma = ctx->key_dma;
+ ctx->cdata.key_dma = ctx->key_dma + ctx->adata.keylen_pad;
+
+ flc = &ctx->flc[DECRYPT];
+ flc_dma = &ctx->flc_dma[DECRYPT];
+ desc = flc->sh_desc;
+
+ cnstr_shdsc_tls_decap(desc, &ctx->cdata, &ctx->adata, assoclen, ivsize,
+ ctx->authsize, blocksize);
+
+ flc->flc[1] = desc_len(desc); /* SDL */
+ *flc_dma = dma_map_single(dev, flc, sizeof(flc->flc) +
+ desc_bytes(desc), DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, *flc_dma)) {
+ dev_err(dev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int tls_setkey(struct crypto_aead *tls, const u8 *key,
+ unsigned int keylen)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ struct device *dev = ctx->dev;
+ struct crypto_authenc_keys keys;
+ int ret;
+
+ if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
+ goto badkey;
+
+#ifdef DEBUG
+ dev_err(dev, "keylen %d enckeylen %d authkeylen %d\n",
+ keys.authkeylen + keys.enckeylen, keys.enckeylen,
+ keys.authkeylen);
+ print_hex_dump(KERN_ERR, "key in @" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+ ctx->adata.keylen = split_key_len(ctx->adata.algtype &
+ OP_ALG_ALGSEL_MASK);
+ ctx->adata.keylen_pad = split_key_pad_len(ctx->adata.algtype &
+ OP_ALG_ALGSEL_MASK);
+
+#ifdef DEBUG
+ dev_err(dev, "split keylen %d split keylen padded %d\n",
+ ctx->adata.keylen, ctx->adata.keylen_pad);
+ print_hex_dump(KERN_ERR, "ctx.key@" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, keys.authkey,
+ keys.authkeylen + keys.enckeylen, 1);
+#endif
+
+ if (ctx->adata.keylen_pad + keys.enckeylen > CAAM_MAX_KEY_SIZE)
+ goto badkey;
+
+ ret = gen_split_aead_key(ctx, keys.authkey, keys.authkeylen);
+ if (ret)
+ goto badkey;
+
+ /* postpend encryption key to auth split key */
+ memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen);
+
+ ctx->key_dma = dma_map_single(dev, ctx->key, ctx->adata.keylen_pad +
+ keys.enckeylen, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, ctx->key_dma)) {
+ dev_err(dev, "unable to map key i/o memory\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ctx.key@" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
+ ctx->adata.keylen_pad + keys.enckeylen, 1);
+#endif
+
+ ctx->cdata.keylen = keys.enckeylen;
+
+ ret = tls_set_sh_desc(tls);
+ if (ret)
+ dma_unmap_single(dev, ctx->key_dma, ctx->adata.keylen_pad +
+ keys.enckeylen, DMA_TO_DEVICE);
+
+ return ret;
+badkey:
+ crypto_aead_set_flags(tls, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+}
+
+static int tls_setauthsize(struct crypto_aead *tls, unsigned int authsize)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+
+ ctx->authsize = authsize;
+ tls_set_sh_desc(tls);
+
+ return 0;
+}
+
static int gcm_set_sh_desc(struct crypto_aead *aead)
{
struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -1545,6 +1881,18 @@ static void aead_unmap(struct device *dev, struct aead_edesc *edesc,
dma_unmap_single(dev, edesc->assoclen_dma, 4, DMA_TO_DEVICE);
}
+static void tls_unmap(struct device *dev, struct tls_edesc *edesc,
+ struct aead_request *req)
+{
+ struct crypto_aead *tls = crypto_aead_reqtfm(req);
+ int ivsize = crypto_aead_ivsize(tls);
+ struct caam_request *caam_req = aead_request_ctx(req);
+
+ caam_unmap(dev, req->src, edesc->dst, edesc->src_nents,
+ edesc->dst_nents, edesc->iv_dma, ivsize, caam_req->op_type,
+ edesc->qm_sg_dma, edesc->qm_sg_bytes);
+}
+
static void ablkcipher_unmap(struct device *dev,
struct ablkcipher_edesc *edesc,
struct ablkcipher_request *req)
@@ -1673,6 +2021,121 @@ static int aead_decrypt(struct aead_request *req)
return ret;
}
+static void tls_encrypt_done(void *cbk_ctx, u32 status)
+{
+ struct crypto_async_request *areq = cbk_ctx;
+ struct aead_request *req = container_of(areq, struct aead_request,
+ base);
+ struct caam_request *req_ctx = to_caam_req(areq);
+ struct tls_edesc *edesc = req_ctx->edesc;
+ struct crypto_aead *tls = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ int ecode = 0;
+
+#ifdef DEBUG
+ dev_err(ctx->dev, "%s %d: err 0x%x\n", __func__, __LINE__, status);
+#endif
+
+ if (unlikely(status)) {
+ caam_qi2_strstatus(ctx->dev, status);
+ ecode = -EIO;
+ }
+
+ tls_unmap(ctx->dev, edesc, req);
+ qi_cache_free(edesc);
+ aead_request_complete(req, ecode);
+}
+
+static void tls_decrypt_done(void *cbk_ctx, u32 status)
+{
+ struct crypto_async_request *areq = cbk_ctx;
+ struct aead_request *req = container_of(areq, struct aead_request,
+ base);
+ struct caam_request *req_ctx = to_caam_req(areq);
+ struct tls_edesc *edesc = req_ctx->edesc;
+ struct crypto_aead *tls = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ int ecode = 0;
+
+#ifdef DEBUG
+ dev_err(ctx->dev, "%s %d: err 0x%x\n", __func__, __LINE__, status);
+#endif
+
+ if (unlikely(status)) {
+ caam_qi2_strstatus(ctx->dev, status);
+ /*
+ * verify hw auth check passed else return -EBADMSG
+ */
+ if ((status & JRSTA_CCBERR_ERRID_MASK) ==
+ JRSTA_CCBERR_ERRID_ICVCHK)
+ ecode = -EBADMSG;
+ else
+ ecode = -EIO;
+ }
+
+ tls_unmap(ctx->dev, edesc, req);
+ qi_cache_free(edesc);
+ aead_request_complete(req, ecode);
+}
+
+static int tls_encrypt(struct aead_request *req)
+{
+ struct tls_edesc *edesc;
+ struct crypto_aead *tls = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ struct caam_request *caam_req = aead_request_ctx(req);
+ int ret;
+
+ /* allocate extended descriptor */
+ edesc = tls_edesc_alloc(req, true);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ caam_req->flc = &ctx->flc[ENCRYPT];
+ caam_req->flc_dma = ctx->flc_dma[ENCRYPT];
+ caam_req->op_type = ENCRYPT;
+ caam_req->cbk = tls_encrypt_done;
+ caam_req->ctx = &req->base;
+ caam_req->edesc = edesc;
+ ret = dpaa2_caam_enqueue(ctx->dev, caam_req);
+ if (ret != -EINPROGRESS &&
+ !(ret == -EBUSY && req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
+ tls_unmap(ctx->dev, edesc, req);
+ qi_cache_free(edesc);
+ }
+
+ return ret;
+}
+
+static int tls_decrypt(struct aead_request *req)
+{
+ struct tls_edesc *edesc;
+ struct crypto_aead *tls = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(tls);
+ struct caam_request *caam_req = aead_request_ctx(req);
+ int ret;
+
+ /* allocate extended descriptor */
+ edesc = tls_edesc_alloc(req, false);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ caam_req->flc = &ctx->flc[DECRYPT];
+ caam_req->flc_dma = ctx->flc_dma[DECRYPT];
+ caam_req->op_type = DECRYPT;
+ caam_req->cbk = tls_decrypt_done;
+ caam_req->ctx = &req->base;
+ caam_req->edesc = edesc;
+ ret = dpaa2_caam_enqueue(ctx->dev, caam_req);
+ if (ret != -EINPROGRESS &&
+ !(ret == -EBUSY && req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
+ tls_unmap(ctx->dev, edesc, req);
+ qi_cache_free(edesc);
+ }
+
+ return ret;
+}
+
static int ipsec_gcm_encrypt(struct aead_request *req)
{
if (req->assoclen < 8)
@@ -3159,6 +3622,26 @@ static struct caam_aead_alg driver_aeads[] = {
.geniv = true,
},
},
+ {
+ .aead = {
+ .base = {
+ .cra_name = "tls10(hmac(sha1),cbc(aes))",
+ .cra_driver_name = "tls10-hmac-sha1-cbc-aes-caam-qi2",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ },
+ .setkey = tls_setkey,
+ .setauthsize = tls_setauthsize,
+ .encrypt = tls_encrypt,
+ .decrypt = tls_decrypt,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+ .class2_alg_type = OP_ALG_ALGSEL_SHA1 |
+ OP_ALG_AAI_HMAC_PRECOMP,
+ },
+ },
};
static struct caam_crypto_alg *caam_alg_alloc(struct caam_alg_template
diff --git a/drivers/crypto/caam/caamalg_qi2.h b/drivers/crypto/caam/caamalg_qi2.h
index f4c9290c..0eaad0e 100644
--- a/drivers/crypto/caam/caamalg_qi2.h
+++ b/drivers/crypto/caam/caamalg_qi2.h
@@ -161,6 +161,28 @@ struct aead_edesc {
};
/*
+ * tls_edesc - s/w-extended tls descriptor
+ * @src_nents: number of segments in input scatterlist
+ * @dst_nents: number of segments in output scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
+ * @qm_sg_bytes: length of dma mapped h/w link table
+ * @qm_sg_dma: bus physical mapped address of h/w link table
+ * @tmp: array of scatterlists used by 'scatterwalk_ffwd'
+ * @dst: pointer to output scatterlist, usefull for unmapping
+ * @sgt: the h/w link table
+ */
+struct tls_edesc {
+ int src_nents;
+ int dst_nents;
+ dma_addr_t iv_dma;
+ int qm_sg_bytes;
+ dma_addr_t qm_sg_dma;
+ struct scatterlist tmp[2];
+ struct scatterlist *dst;
+ struct dpaa2_sg_entry sgt[0];
+};
+
+/*
* ablkcipher_edesc - s/w-extended ablkcipher descriptor
* @src_nents: number of segments in input scatterlist
* @dst_nents: number of segments in output scatterlist