| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 - 2021 |
| * |
| * Richard van Schagen <vschagen@icloud.com> |
| * Christian Marangi <ansuelsmth@gmail.com |
| */ |
| |
| #include <crypto/aes.h> |
| #include <crypto/ctr.h> |
| #include <crypto/hmac.h> |
| #include <crypto/sha1.h> |
| #include <crypto/sha2.h> |
| #include <linux/kernel.h> |
| #include <linux/delay.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/scatterlist.h> |
| |
| #include "eip93-cipher.h" |
| #include "eip93-hash.h" |
| #include "eip93-common.h" |
| #include "eip93-main.h" |
| #include "eip93-regs.h" |
| |
| int eip93_parse_ctrl_stat_err(struct eip93_device *eip93, int err) |
| { |
| u32 ext_err; |
| |
| if (!err) |
| return 0; |
| |
| switch (err & ~EIP93_PE_CTRL_PE_EXT_ERR_CODE) { |
| case EIP93_PE_CTRL_PE_AUTH_ERR: |
| case EIP93_PE_CTRL_PE_PAD_ERR: |
| return -EBADMSG; |
| /* let software handle anti-replay errors */ |
| case EIP93_PE_CTRL_PE_SEQNUM_ERR: |
| return 0; |
| case EIP93_PE_CTRL_PE_EXT_ERR: |
| break; |
| default: |
| dev_err(eip93->dev, "Unhandled error 0x%08x\n", err); |
| return -EINVAL; |
| } |
| |
| /* Parse additional ext errors */ |
| ext_err = FIELD_GET(EIP93_PE_CTRL_PE_EXT_ERR_CODE, err); |
| switch (ext_err) { |
| case EIP93_PE_CTRL_PE_EXT_ERR_BUS: |
| case EIP93_PE_CTRL_PE_EXT_ERR_PROCESSING: |
| return -EIO; |
| case EIP93_PE_CTRL_PE_EXT_ERR_DESC_OWNER: |
| return -EACCES; |
| case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_CRYPTO_OP: |
| case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_CRYPTO_ALGO: |
| case EIP93_PE_CTRL_PE_EXT_ERR_SPI: |
| return -EINVAL; |
| case EIP93_PE_CTRL_PE_EXT_ERR_ZERO_LENGTH: |
| case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_PK_LENGTH: |
| case EIP93_PE_CTRL_PE_EXT_ERR_BLOCK_SIZE_ERR: |
| return -EBADMSG; |
| default: |
| dev_err(eip93->dev, "Unhandled ext error 0x%08x\n", ext_err); |
| return -EINVAL; |
| } |
| } |
| |
| static void *eip93_ring_next_wptr(struct eip93_device *eip93, |
| struct eip93_desc_ring *ring) |
| { |
| void *ptr = ring->write; |
| |
| if ((ring->write == ring->read - ring->offset) || |
| (ring->read == ring->base && ring->write == ring->base_end)) |
| return ERR_PTR(-ENOMEM); |
| |
| if (ring->write == ring->base_end) |
| ring->write = ring->base; |
| else |
| ring->write += ring->offset; |
| |
| return ptr; |
| } |
| |
| static void *eip93_ring_next_rptr(struct eip93_device *eip93, |
| struct eip93_desc_ring *ring) |
| { |
| void *ptr = ring->read; |
| |
| if (ring->write == ring->read) |
| return ERR_PTR(-ENOENT); |
| |
| if (ring->read == ring->base_end) |
| ring->read = ring->base; |
| else |
| ring->read += ring->offset; |
| |
| return ptr; |
| } |
| |
| int eip93_put_descriptor(struct eip93_device *eip93, |
| struct eip93_descriptor *desc) |
| { |
| struct eip93_descriptor *cdesc; |
| struct eip93_descriptor *rdesc; |
| |
| rdesc = eip93_ring_next_wptr(eip93, &eip93->ring->rdr); |
| if (IS_ERR(rdesc)) |
| return -ENOENT; |
| |
| cdesc = eip93_ring_next_wptr(eip93, &eip93->ring->cdr); |
| if (IS_ERR(cdesc)) |
| return -ENOENT; |
| |
| memset(rdesc, 0, sizeof(struct eip93_descriptor)); |
| |
| memcpy(cdesc, desc, sizeof(struct eip93_descriptor)); |
| |
| return 0; |
| } |
| |
| void *eip93_get_descriptor(struct eip93_device *eip93) |
| { |
| struct eip93_descriptor *cdesc; |
| void *ptr; |
| |
| cdesc = eip93_ring_next_rptr(eip93, &eip93->ring->cdr); |
| if (IS_ERR(cdesc)) |
| return ERR_PTR(-ENOENT); |
| |
| memset(cdesc, 0, sizeof(struct eip93_descriptor)); |
| |
| ptr = eip93_ring_next_rptr(eip93, &eip93->ring->rdr); |
| if (IS_ERR(ptr)) |
| return ERR_PTR(-ENOENT); |
| |
| return ptr; |
| } |
| |
| static void eip93_free_sg_copy(const int len, struct scatterlist **sg) |
| { |
| if (!*sg || !len) |
| return; |
| |
| free_pages((unsigned long)sg_virt(*sg), get_order(len)); |
| kfree(*sg); |
| *sg = NULL; |
| } |
| |
| static int eip93_make_sg_copy(struct scatterlist *src, struct scatterlist **dst, |
| const u32 len, const bool copy) |
| { |
| void *pages; |
| |
| *dst = kmalloc(sizeof(**dst), GFP_KERNEL); |
| if (!*dst) |
| return -ENOMEM; |
| |
| pages = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, |
| get_order(len)); |
| if (!pages) { |
| kfree(*dst); |
| *dst = NULL; |
| return -ENOMEM; |
| } |
| |
| sg_init_table(*dst, 1); |
| sg_set_buf(*dst, pages, len); |
| |
| /* copy only as requested */ |
| if (copy) |
| sg_copy_to_buffer(src, sg_nents(src), pages, len); |
| |
| return 0; |
| } |
| |
| static bool eip93_is_sg_aligned(struct scatterlist *sg, u32 len, |
| const int blksize) |
| { |
| int nents; |
| |
| for (nents = 0; sg; sg = sg_next(sg), ++nents) { |
| if (!IS_ALIGNED(sg->offset, 4)) |
| return false; |
| |
| if (len <= sg->length) { |
| if (!IS_ALIGNED(len, blksize)) |
| return false; |
| |
| return true; |
| } |
| |
| if (!IS_ALIGNED(sg->length, blksize)) |
| return false; |
| |
| len -= sg->length; |
| } |
| return false; |
| } |
| |
| int check_valid_request(struct eip93_cipher_reqctx *rctx) |
| { |
| struct scatterlist *src = rctx->sg_src; |
| struct scatterlist *dst = rctx->sg_dst; |
| u32 textsize = rctx->textsize; |
| u32 authsize = rctx->authsize; |
| u32 blksize = rctx->blksize; |
| u32 totlen_src = rctx->assoclen + rctx->textsize; |
| u32 totlen_dst = rctx->assoclen + rctx->textsize; |
| u32 copy_len; |
| bool src_align, dst_align; |
| int src_nents, dst_nents; |
| int err = -EINVAL; |
| |
| if (!IS_CTR(rctx->flags)) { |
| if (!IS_ALIGNED(textsize, blksize)) |
| return err; |
| } |
| |
| if (authsize) { |
| if (IS_ENCRYPT(rctx->flags)) |
| totlen_dst += authsize; |
| else |
| totlen_src += authsize; |
| } |
| |
| src_nents = sg_nents_for_len(src, totlen_src); |
| if (src_nents < 0) |
| return src_nents; |
| |
| dst_nents = sg_nents_for_len(dst, totlen_dst); |
| if (dst_nents < 0) |
| return dst_nents; |
| |
| if (src == dst) { |
| src_nents = max(src_nents, dst_nents); |
| dst_nents = src_nents; |
| if (unlikely((totlen_src || totlen_dst) && !src_nents)) |
| return err; |
| |
| } else { |
| if (unlikely(totlen_src && !src_nents)) |
| return err; |
| |
| if (unlikely(totlen_dst && !dst_nents)) |
| return err; |
| } |
| |
| if (authsize) { |
| if (dst_nents == 1 && src_nents == 1) { |
| src_align = eip93_is_sg_aligned(src, totlen_src, blksize); |
| if (src == dst) |
| dst_align = src_align; |
| else |
| dst_align = eip93_is_sg_aligned(dst, totlen_dst, blksize); |
| } else { |
| src_align = false; |
| dst_align = false; |
| } |
| } else { |
| src_align = eip93_is_sg_aligned(src, totlen_src, blksize); |
| if (src == dst) |
| dst_align = src_align; |
| else |
| dst_align = eip93_is_sg_aligned(dst, totlen_dst, blksize); |
| } |
| |
| copy_len = max(totlen_src, totlen_dst); |
| if (!src_align) { |
| err = eip93_make_sg_copy(src, &rctx->sg_src, copy_len, true); |
| if (err) |
| return err; |
| } |
| |
| if (!dst_align) { |
| err = eip93_make_sg_copy(dst, &rctx->sg_dst, copy_len, false); |
| if (err) |
| return err; |
| } |
| |
| src_nents = sg_nents_for_len(rctx->sg_src, totlen_src); |
| if (src_nents < 0) |
| return src_nents; |
| |
| dst_nents = sg_nents_for_len(rctx->sg_dst, totlen_dst); |
| if (dst_nents < 0) |
| return dst_nents; |
| |
| rctx->src_nents = src_nents; |
| rctx->dst_nents = dst_nents; |
| |
| return 0; |
| } |
| |
| /* |
| * Set sa_record function: |
| * Even sa_record is set to "0", keep " = 0" for readability. |
| */ |
| void eip93_set_sa_record(struct sa_record *sa_record, const unsigned int keylen, |
| const u32 flags) |
| { |
| /* Reset cmd word */ |
| sa_record->sa_cmd0_word = 0; |
| sa_record->sa_cmd1_word = 0; |
| |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_IV_FROM_STATE; |
| if (!IS_ECB(flags)) |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_SAVE_IV; |
| |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_OP_BASIC; |
| |
| switch ((flags & EIP93_ALG_MASK)) { |
| case EIP93_ALG_AES: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_AES; |
| sa_record->sa_cmd1_word |= FIELD_PREP(EIP93_SA_CMD_AES_KEY_LENGTH, |
| keylen >> 3); |
| break; |
| case EIP93_ALG_3DES: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_3DES; |
| break; |
| case EIP93_ALG_DES: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_DES; |
| break; |
| default: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_NULL; |
| } |
| |
| switch ((flags & EIP93_HASH_MASK)) { |
| case EIP93_HASH_SHA256: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA256; |
| break; |
| case EIP93_HASH_SHA224: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA224; |
| break; |
| case EIP93_HASH_SHA1: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA1; |
| break; |
| case EIP93_HASH_MD5: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_MD5; |
| break; |
| default: |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_NULL; |
| } |
| |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_PAD_ZERO; |
| |
| switch ((flags & EIP93_MODE_MASK)) { |
| case EIP93_MODE_CBC: |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_CBC; |
| break; |
| case EIP93_MODE_CTR: |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_CTR; |
| break; |
| case EIP93_MODE_ECB: |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_ECB; |
| break; |
| } |
| |
| sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIGEST_3WORD; |
| if (IS_HASH(flags)) { |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_PAD; |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_DIGEST; |
| } |
| |
| if (IS_HMAC(flags)) { |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_HMAC; |
| sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_HEADER; |
| } |
| |
| sa_record->sa_spi = 0x0; |
| sa_record->sa_seqmum_mask[0] = 0xFFFFFFFF; |
| sa_record->sa_seqmum_mask[1] = 0x0; |
| } |
| |
| /* |
| * Poor mans Scatter/gather function: |
| * Create a Descriptor for every segment to avoid copying buffers. |
| * For performance better to wait for hardware to perform multiple DMA |
| */ |
| static int eip93_scatter_combine(struct eip93_device *eip93, |
| struct eip93_cipher_reqctx *rctx, |
| u32 datalen, u32 split, int offsetin) |
| { |
| struct eip93_descriptor *cdesc = rctx->cdesc; |
| struct scatterlist *sgsrc = rctx->sg_src; |
| struct scatterlist *sgdst = rctx->sg_dst; |
| unsigned int remainin = sg_dma_len(sgsrc); |
| unsigned int remainout = sg_dma_len(sgdst); |
| dma_addr_t saddr = sg_dma_address(sgsrc); |
| dma_addr_t daddr = sg_dma_address(sgdst); |
| dma_addr_t state_addr; |
| u32 src_addr, dst_addr, len, n; |
| bool nextin = false; |
| bool nextout = false; |
| int offsetout = 0; |
| int err; |
| |
| if (IS_ECB(rctx->flags)) |
| rctx->sa_state_base = 0; |
| |
| if (split < datalen) { |
| state_addr = rctx->sa_state_ctr_base; |
| n = split; |
| } else { |
| state_addr = rctx->sa_state_base; |
| n = datalen; |
| } |
| |
| do { |
| if (nextin) { |
| sgsrc = sg_next(sgsrc); |
| remainin = sg_dma_len(sgsrc); |
| if (remainin == 0) |
| continue; |
| |
| saddr = sg_dma_address(sgsrc); |
| offsetin = 0; |
| nextin = false; |
| } |
| |
| if (nextout) { |
| sgdst = sg_next(sgdst); |
| remainout = sg_dma_len(sgdst); |
| if (remainout == 0) |
| continue; |
| |
| daddr = sg_dma_address(sgdst); |
| offsetout = 0; |
| nextout = false; |
| } |
| src_addr = saddr + offsetin; |
| dst_addr = daddr + offsetout; |
| |
| if (remainin == remainout) { |
| len = remainin; |
| if (len > n) { |
| len = n; |
| remainin -= n; |
| remainout -= n; |
| offsetin += n; |
| offsetout += n; |
| } else { |
| nextin = true; |
| nextout = true; |
| } |
| } else if (remainin < remainout) { |
| len = remainin; |
| if (len > n) { |
| len = n; |
| remainin -= n; |
| remainout -= n; |
| offsetin += n; |
| offsetout += n; |
| } else { |
| offsetout += len; |
| remainout -= len; |
| nextin = true; |
| } |
| } else { |
| len = remainout; |
| if (len > n) { |
| len = n; |
| remainin -= n; |
| remainout -= n; |
| offsetin += n; |
| offsetout += n; |
| } else { |
| offsetin += len; |
| remainin -= len; |
| nextout = true; |
| } |
| } |
| n -= len; |
| |
| cdesc->src_addr = src_addr; |
| cdesc->dst_addr = dst_addr; |
| cdesc->state_addr = state_addr; |
| cdesc->pe_length_word = FIELD_PREP(EIP93_PE_LENGTH_HOST_PE_READY, |
| EIP93_PE_LENGTH_HOST_READY); |
| cdesc->pe_length_word |= FIELD_PREP(EIP93_PE_LENGTH_LENGTH, len); |
| |
| if (n == 0) { |
| n = datalen - split; |
| split = datalen; |
| state_addr = rctx->sa_state_base; |
| } |
| |
| if (n == 0) |
| cdesc->user_id |= FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, |
| EIP93_DESC_LAST); |
| |
| /* |
| * Loop - Delay - No need to rollback |
| * Maybe refine by slowing down at EIP93_RING_BUSY |
| */ |
| again: |
| scoped_guard(spinlock_irqsave, &eip93->ring->write_lock) |
| err = eip93_put_descriptor(eip93, cdesc); |
| if (err) { |
| usleep_range(EIP93_RING_BUSY_DELAY, |
| EIP93_RING_BUSY_DELAY * 2); |
| goto again; |
| } |
| /* Writing new descriptor count starts DMA action */ |
| writel(1, eip93->base + EIP93_REG_PE_CD_COUNT); |
| } while (n); |
| |
| return -EINPROGRESS; |
| } |
| |
| int eip93_send_req(struct crypto_async_request *async, |
| const u8 *reqiv, struct eip93_cipher_reqctx *rctx) |
| { |
| struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); |
| struct eip93_device *eip93 = ctx->eip93; |
| struct scatterlist *src = rctx->sg_src; |
| struct scatterlist *dst = rctx->sg_dst; |
| struct sa_state *sa_state; |
| struct eip93_descriptor cdesc; |
| u32 flags = rctx->flags; |
| int offsetin = 0, err; |
| u32 datalen = rctx->assoclen + rctx->textsize; |
| u32 split = datalen; |
| u32 start, end, ctr, blocks; |
| u32 iv[AES_BLOCK_SIZE / sizeof(u32)]; |
| int crypto_async_idr; |
| |
| rctx->sa_state_ctr = NULL; |
| rctx->sa_state = NULL; |
| |
| if (IS_ECB(flags)) |
| goto skip_iv; |
| |
| memcpy(iv, reqiv, rctx->ivsize); |
| |
| rctx->sa_state = kzalloc(sizeof(*rctx->sa_state), GFP_KERNEL); |
| if (!rctx->sa_state) |
| return -ENOMEM; |
| |
| sa_state = rctx->sa_state; |
| |
| memcpy(sa_state->state_iv, iv, rctx->ivsize); |
| if (IS_RFC3686(flags)) { |
| sa_state->state_iv[0] = ctx->sa_nonce; |
| sa_state->state_iv[1] = iv[0]; |
| sa_state->state_iv[2] = iv[1]; |
| sa_state->state_iv[3] = (u32 __force)cpu_to_be32(0x1); |
| } else if (!IS_HMAC(flags) && IS_CTR(flags)) { |
| /* Compute data length. */ |
| blocks = DIV_ROUND_UP(rctx->textsize, AES_BLOCK_SIZE); |
| ctr = be32_to_cpu((__be32 __force)iv[3]); |
| /* Check 32bit counter overflow. */ |
| start = ctr; |
| end = start + blocks - 1; |
| if (end < start) { |
| split = AES_BLOCK_SIZE * -start; |
| /* |
| * Increment the counter manually to cope with |
| * the hardware counter overflow. |
| */ |
| iv[3] = 0xffffffff; |
| crypto_inc((u8 *)iv, AES_BLOCK_SIZE); |
| |
| rctx->sa_state_ctr = kzalloc(sizeof(*rctx->sa_state_ctr), |
| GFP_KERNEL); |
| if (!rctx->sa_state_ctr) { |
| err = -ENOMEM; |
| goto free_sa_state; |
| } |
| |
| memcpy(rctx->sa_state_ctr->state_iv, reqiv, rctx->ivsize); |
| memcpy(sa_state->state_iv, iv, rctx->ivsize); |
| |
| rctx->sa_state_ctr_base = dma_map_single(eip93->dev, rctx->sa_state_ctr, |
| sizeof(*rctx->sa_state_ctr), |
| DMA_TO_DEVICE); |
| err = dma_mapping_error(eip93->dev, rctx->sa_state_ctr_base); |
| if (err) |
| goto free_sa_state_ctr; |
| } |
| } |
| |
| rctx->sa_state_base = dma_map_single(eip93->dev, rctx->sa_state, |
| sizeof(*rctx->sa_state), DMA_TO_DEVICE); |
| err = dma_mapping_error(eip93->dev, rctx->sa_state_base); |
| if (err) |
| goto free_sa_state_ctr_dma; |
| |
| skip_iv: |
| |
| cdesc.pe_ctrl_stat_word = FIELD_PREP(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, |
| EIP93_PE_CTRL_HOST_READY); |
| cdesc.sa_addr = rctx->sa_record_base; |
| cdesc.arc4_addr = 0; |
| |
| scoped_guard(spinlock_bh, &eip93->ring->idr_lock) |
| crypto_async_idr = idr_alloc(&eip93->ring->crypto_async_idr, async, 0, |
| EIP93_RING_NUM - 1, GFP_ATOMIC); |
| |
| cdesc.user_id = FIELD_PREP(EIP93_PE_USER_ID_CRYPTO_IDR, (u16)crypto_async_idr) | |
| FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, rctx->desc_flags); |
| |
| rctx->cdesc = &cdesc; |
| |
| /* map DMA_BIDIRECTIONAL to invalidate cache on destination |
| * implies __dma_cache_wback_inv |
| */ |
| if (!dma_map_sg(eip93->dev, dst, rctx->dst_nents, DMA_BIDIRECTIONAL)) { |
| err = -ENOMEM; |
| goto free_sa_state_ctr_dma; |
| } |
| |
| if (src != dst && |
| !dma_map_sg(eip93->dev, src, rctx->src_nents, DMA_TO_DEVICE)) { |
| err = -ENOMEM; |
| goto free_sg_dma; |
| } |
| |
| return eip93_scatter_combine(eip93, rctx, datalen, split, offsetin); |
| |
| free_sg_dma: |
| dma_unmap_sg(eip93->dev, dst, rctx->dst_nents, DMA_BIDIRECTIONAL); |
| free_sa_state_ctr_dma: |
| if (rctx->sa_state_ctr) |
| dma_unmap_single(eip93->dev, rctx->sa_state_ctr_base, |
| sizeof(*rctx->sa_state_ctr), |
| DMA_TO_DEVICE); |
| free_sa_state_ctr: |
| kfree(rctx->sa_state_ctr); |
| if (rctx->sa_state) |
| dma_unmap_single(eip93->dev, rctx->sa_state_base, |
| sizeof(*rctx->sa_state), |
| DMA_TO_DEVICE); |
| free_sa_state: |
| kfree(rctx->sa_state); |
| |
| return err; |
| } |
| |
| void eip93_unmap_dma(struct eip93_device *eip93, struct eip93_cipher_reqctx *rctx, |
| struct scatterlist *reqsrc, struct scatterlist *reqdst) |
| { |
| u32 len = rctx->assoclen + rctx->textsize; |
| u32 authsize = rctx->authsize; |
| u32 flags = rctx->flags; |
| u32 *otag; |
| int i; |
| |
| if (rctx->sg_src == rctx->sg_dst) { |
| dma_unmap_sg(eip93->dev, rctx->sg_dst, rctx->dst_nents, |
| DMA_BIDIRECTIONAL); |
| goto process_tag; |
| } |
| |
| dma_unmap_sg(eip93->dev, rctx->sg_src, rctx->src_nents, |
| DMA_TO_DEVICE); |
| |
| if (rctx->sg_src != reqsrc) |
| eip93_free_sg_copy(len + rctx->authsize, &rctx->sg_src); |
| |
| dma_unmap_sg(eip93->dev, rctx->sg_dst, rctx->dst_nents, |
| DMA_BIDIRECTIONAL); |
| |
| /* SHA tags need conversion from net-to-host */ |
| process_tag: |
| if (IS_DECRYPT(flags)) |
| authsize = 0; |
| |
| if (authsize) { |
| if (!IS_HASH_MD5(flags)) { |
| otag = sg_virt(rctx->sg_dst) + len; |
| for (i = 0; i < (authsize / 4); i++) |
| otag[i] = be32_to_cpu((__be32 __force)otag[i]); |
| } |
| } |
| |
| if (rctx->sg_dst != reqdst) { |
| sg_copy_from_buffer(reqdst, sg_nents(reqdst), |
| sg_virt(rctx->sg_dst), len + authsize); |
| eip93_free_sg_copy(len + rctx->authsize, &rctx->sg_dst); |
| } |
| } |
| |
| void eip93_handle_result(struct eip93_device *eip93, struct eip93_cipher_reqctx *rctx, |
| u8 *reqiv) |
| { |
| if (rctx->sa_state_ctr) |
| dma_unmap_single(eip93->dev, rctx->sa_state_ctr_base, |
| sizeof(*rctx->sa_state_ctr), |
| DMA_FROM_DEVICE); |
| |
| if (rctx->sa_state) |
| dma_unmap_single(eip93->dev, rctx->sa_state_base, |
| sizeof(*rctx->sa_state), |
| DMA_FROM_DEVICE); |
| |
| if (!IS_ECB(rctx->flags)) |
| memcpy(reqiv, rctx->sa_state->state_iv, rctx->ivsize); |
| |
| kfree(rctx->sa_state_ctr); |
| kfree(rctx->sa_state); |
| } |
| |
| int eip93_hmac_setkey(u32 ctx_flags, const u8 *key, unsigned int keylen, |
| unsigned int hashlen, u8 *dest_ipad, u8 *dest_opad, |
| bool skip_ipad) |
| { |
| u8 ipad[SHA256_BLOCK_SIZE], opad[SHA256_BLOCK_SIZE]; |
| struct crypto_ahash *ahash_tfm; |
| struct eip93_hash_reqctx *rctx; |
| struct ahash_request *req; |
| DECLARE_CRYPTO_WAIT(wait); |
| struct scatterlist sg[1]; |
| const char *alg_name; |
| int i, ret; |
| |
| switch (ctx_flags & EIP93_HASH_MASK) { |
| case EIP93_HASH_SHA256: |
| alg_name = "sha256-eip93"; |
| break; |
| case EIP93_HASH_SHA224: |
| alg_name = "sha224-eip93"; |
| break; |
| case EIP93_HASH_SHA1: |
| alg_name = "sha1-eip93"; |
| break; |
| case EIP93_HASH_MD5: |
| alg_name = "md5-eip93"; |
| break; |
| default: /* Impossible */ |
| return -EINVAL; |
| } |
| |
| ahash_tfm = crypto_alloc_ahash(alg_name, 0, CRYPTO_ALG_ASYNC); |
| if (IS_ERR(ahash_tfm)) |
| return PTR_ERR(ahash_tfm); |
| |
| req = ahash_request_alloc(ahash_tfm, GFP_ATOMIC); |
| if (!req) { |
| ret = -ENOMEM; |
| goto err_ahash; |
| } |
| |
| rctx = ahash_request_ctx_dma(req); |
| crypto_init_wait(&wait); |
| ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, |
| crypto_req_done, &wait); |
| |
| /* Hash the key if > SHA256_BLOCK_SIZE */ |
| if (keylen > SHA256_BLOCK_SIZE) { |
| sg_init_one(&sg[0], key, keylen); |
| |
| ahash_request_set_crypt(req, sg, ipad, keylen); |
| ret = crypto_wait_req(crypto_ahash_digest(req), &wait); |
| if (ret) |
| goto err_req; |
| |
| keylen = hashlen; |
| } else { |
| memcpy(ipad, key, keylen); |
| } |
| |
| /* Copy to opad */ |
| memset(ipad + keylen, 0, SHA256_BLOCK_SIZE - keylen); |
| memcpy(opad, ipad, SHA256_BLOCK_SIZE); |
| |
| /* Pad with HMAC constants */ |
| for (i = 0; i < SHA256_BLOCK_SIZE; i++) { |
| ipad[i] ^= HMAC_IPAD_VALUE; |
| opad[i] ^= HMAC_OPAD_VALUE; |
| } |
| |
| if (skip_ipad) { |
| memcpy(dest_ipad, ipad, SHA256_BLOCK_SIZE); |
| } else { |
| /* Hash ipad */ |
| sg_init_one(&sg[0], ipad, SHA256_BLOCK_SIZE); |
| ahash_request_set_crypt(req, sg, dest_ipad, SHA256_BLOCK_SIZE); |
| ret = crypto_ahash_init(req); |
| if (ret) |
| goto err_req; |
| |
| /* Disable HASH_FINALIZE for ipad hash */ |
| rctx->partial_hash = true; |
| |
| ret = crypto_wait_req(crypto_ahash_finup(req), &wait); |
| if (ret) |
| goto err_req; |
| } |
| |
| /* Hash opad */ |
| sg_init_one(&sg[0], opad, SHA256_BLOCK_SIZE); |
| ahash_request_set_crypt(req, sg, dest_opad, SHA256_BLOCK_SIZE); |
| ret = crypto_ahash_init(req); |
| if (ret) |
| goto err_req; |
| |
| /* Disable HASH_FINALIZE for opad hash */ |
| rctx->partial_hash = true; |
| |
| ret = crypto_wait_req(crypto_ahash_finup(req), &wait); |
| if (ret) |
| goto err_req; |
| |
| if (!IS_HASH_MD5(ctx_flags)) { |
| for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++) { |
| u32 *ipad_hash = (u32 *)dest_ipad; |
| u32 *opad_hash = (u32 *)dest_opad; |
| |
| if (!skip_ipad) |
| ipad_hash[i] = (u32 __force)cpu_to_be32(ipad_hash[i]); |
| opad_hash[i] = (u32 __force)cpu_to_be32(opad_hash[i]); |
| } |
| } |
| |
| err_req: |
| ahash_request_free(req); |
| err_ahash: |
| crypto_free_ahash(ahash_tfm); |
| |
| return ret; |
| } |