162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel Keem Bay OCS ECC Crypto Driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019-2021 Intel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <crypto/ecc_curve.h> 1162306a36Sopenharmony_ci#include <crypto/ecdh.h> 1262306a36Sopenharmony_ci#include <crypto/engine.h> 1362306a36Sopenharmony_ci#include <crypto/internal/ecc.h> 1462306a36Sopenharmony_ci#include <crypto/internal/kpp.h> 1562306a36Sopenharmony_ci#include <crypto/kpp.h> 1662306a36Sopenharmony_ci#include <crypto/rng.h> 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/completion.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/fips.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/iopoll.h> 2462306a36Sopenharmony_ci#include <linux/irq.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/of.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci#include <linux/scatterlist.h> 3062306a36Sopenharmony_ci#include <linux/string.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DRV_NAME "keembay-ocs-ecc" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define KMB_OCS_ECC_PRIORITY 350 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_COMMAND 0x00000000 3762306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_STATUS 0x00000004 3862306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_DATA_IN 0x00000080 3962306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_CX_DATA_OUT 0x00000100 4062306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_CY_DATA_OUT 0x00000180 4162306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_ISR 0x00000400 4262306a36Sopenharmony_ci#define HW_OFFS_OCS_ECC_IER 0x00000404 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define HW_OCS_ECC_ISR_INT_STATUS_DONE BIT(0) 4562306a36Sopenharmony_ci#define HW_OCS_ECC_COMMAND_INS_BP BIT(0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define HW_OCS_ECC_COMMAND_START_VAL BIT(0) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define OCS_ECC_OP_SIZE_384 BIT(8) 5062306a36Sopenharmony_ci#define OCS_ECC_OP_SIZE_256 0 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* ECC Instruction : for ECC_COMMAND */ 5362306a36Sopenharmony_ci#define OCS_ECC_INST_WRITE_AX (0x1 << HW_OCS_ECC_COMMAND_INS_BP) 5462306a36Sopenharmony_ci#define OCS_ECC_INST_WRITE_AY (0x2 << HW_OCS_ECC_COMMAND_INS_BP) 5562306a36Sopenharmony_ci#define OCS_ECC_INST_WRITE_BX_D (0x3 << HW_OCS_ECC_COMMAND_INS_BP) 5662306a36Sopenharmony_ci#define OCS_ECC_INST_WRITE_BY_L (0x4 << HW_OCS_ECC_COMMAND_INS_BP) 5762306a36Sopenharmony_ci#define OCS_ECC_INST_WRITE_P (0x5 << HW_OCS_ECC_COMMAND_INS_BP) 5862306a36Sopenharmony_ci#define OCS_ECC_INST_WRITE_A (0x6 << HW_OCS_ECC_COMMAND_INS_BP) 5962306a36Sopenharmony_ci#define OCS_ECC_INST_CALC_D_IDX_A (0x8 << HW_OCS_ECC_COMMAND_INS_BP) 6062306a36Sopenharmony_ci#define OCS_ECC_INST_CALC_A_POW_B_MODP (0xB << HW_OCS_ECC_COMMAND_INS_BP) 6162306a36Sopenharmony_ci#define OCS_ECC_INST_CALC_A_MUL_B_MODP (0xC << HW_OCS_ECC_COMMAND_INS_BP) 6262306a36Sopenharmony_ci#define OCS_ECC_INST_CALC_A_ADD_B_MODP (0xD << HW_OCS_ECC_COMMAND_INS_BP) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define ECC_ENABLE_INTR 1 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define POLL_USEC 100 6762306a36Sopenharmony_ci#define TIMEOUT_USEC 10000 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define KMB_ECC_VLI_MAX_DIGITS ECC_CURVE_NIST_P384_DIGITS 7062306a36Sopenharmony_ci#define KMB_ECC_VLI_MAX_BYTES (KMB_ECC_VLI_MAX_DIGITS \ 7162306a36Sopenharmony_ci << ECC_DIGITS_TO_BYTES_SHIFT) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define POW_CUBE 3 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * struct ocs_ecc_dev - ECC device context 7762306a36Sopenharmony_ci * @list: List of device contexts 7862306a36Sopenharmony_ci * @dev: OCS ECC device 7962306a36Sopenharmony_ci * @base_reg: IO base address of OCS ECC 8062306a36Sopenharmony_ci * @engine: Crypto engine for the device 8162306a36Sopenharmony_ci * @irq_done: IRQ done completion. 8262306a36Sopenharmony_ci * @irq: IRQ number 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistruct ocs_ecc_dev { 8562306a36Sopenharmony_ci struct list_head list; 8662306a36Sopenharmony_ci struct device *dev; 8762306a36Sopenharmony_ci void __iomem *base_reg; 8862306a36Sopenharmony_ci struct crypto_engine *engine; 8962306a36Sopenharmony_ci struct completion irq_done; 9062306a36Sopenharmony_ci int irq; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/** 9462306a36Sopenharmony_ci * struct ocs_ecc_ctx - Transformation context. 9562306a36Sopenharmony_ci * @ecc_dev: The ECC driver associated with this context. 9662306a36Sopenharmony_ci * @curve: The elliptic curve used by this transformation. 9762306a36Sopenharmony_ci * @private_key: The private key. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistruct ocs_ecc_ctx { 10062306a36Sopenharmony_ci struct ocs_ecc_dev *ecc_dev; 10162306a36Sopenharmony_ci const struct ecc_curve *curve; 10262306a36Sopenharmony_ci u64 private_key[KMB_ECC_VLI_MAX_DIGITS]; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Driver data. */ 10662306a36Sopenharmony_cistruct ocs_ecc_drv { 10762306a36Sopenharmony_ci struct list_head dev_list; 10862306a36Sopenharmony_ci spinlock_t lock; /* Protects dev_list. */ 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Global variable holding the list of OCS ECC devices (only one expected). */ 11262306a36Sopenharmony_cistatic struct ocs_ecc_drv ocs_ecc = { 11362306a36Sopenharmony_ci .dev_list = LIST_HEAD_INIT(ocs_ecc.dev_list), 11462306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(ocs_ecc.lock), 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* Get OCS ECC tfm context from kpp_request. */ 11862306a36Sopenharmony_cistatic inline struct ocs_ecc_ctx *kmb_ocs_ecc_tctx(struct kpp_request *req) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return kpp_tfm_ctx(crypto_kpp_reqtfm(req)); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Converts number of digits to number of bytes. */ 12462306a36Sopenharmony_cistatic inline unsigned int digits_to_bytes(unsigned int n) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci return n << ECC_DIGITS_TO_BYTES_SHIFT; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* 13062306a36Sopenharmony_ci * Wait for ECC idle i.e when an operation (other than write operations) 13162306a36Sopenharmony_ci * is done. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic inline int ocs_ecc_wait_idle(struct ocs_ecc_dev *dev) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci u32 value; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return readl_poll_timeout((dev->base_reg + HW_OFFS_OCS_ECC_STATUS), 13862306a36Sopenharmony_ci value, 13962306a36Sopenharmony_ci !(value & HW_OCS_ECC_ISR_INT_STATUS_DONE), 14062306a36Sopenharmony_ci POLL_USEC, TIMEOUT_USEC); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void ocs_ecc_cmd_start(struct ocs_ecc_dev *ecc_dev, u32 op_size) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci iowrite32(op_size | HW_OCS_ECC_COMMAND_START_VAL, 14662306a36Sopenharmony_ci ecc_dev->base_reg + HW_OFFS_OCS_ECC_COMMAND); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Direct write of u32 buffer to ECC engine with associated instruction. */ 15062306a36Sopenharmony_cistatic void ocs_ecc_write_cmd_and_data(struct ocs_ecc_dev *dev, 15162306a36Sopenharmony_ci u32 op_size, 15262306a36Sopenharmony_ci u32 inst, 15362306a36Sopenharmony_ci const void *data_in, 15462306a36Sopenharmony_ci size_t data_size) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci iowrite32(op_size | inst, dev->base_reg + HW_OFFS_OCS_ECC_COMMAND); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* MMIO Write src uint32 to dst. */ 15962306a36Sopenharmony_ci memcpy_toio(dev->base_reg + HW_OFFS_OCS_ECC_DATA_IN, data_in, 16062306a36Sopenharmony_ci data_size); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* Start OCS ECC operation and wait for its completion. */ 16462306a36Sopenharmony_cistatic int ocs_ecc_trigger_op(struct ocs_ecc_dev *ecc_dev, u32 op_size, 16562306a36Sopenharmony_ci u32 inst) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci reinit_completion(&ecc_dev->irq_done); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci iowrite32(ECC_ENABLE_INTR, ecc_dev->base_reg + HW_OFFS_OCS_ECC_IER); 17062306a36Sopenharmony_ci iowrite32(op_size | inst, ecc_dev->base_reg + HW_OFFS_OCS_ECC_COMMAND); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return wait_for_completion_interruptible(&ecc_dev->irq_done); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/** 17662306a36Sopenharmony_ci * ocs_ecc_read_cx_out() - Read the CX data output buffer. 17762306a36Sopenharmony_ci * @dev: The OCS ECC device to read from. 17862306a36Sopenharmony_ci * @cx_out: The buffer where to store the CX value. Must be at least 17962306a36Sopenharmony_ci * @byte_count byte long. 18062306a36Sopenharmony_ci * @byte_count: The amount of data to read. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_cistatic inline void ocs_ecc_read_cx_out(struct ocs_ecc_dev *dev, void *cx_out, 18362306a36Sopenharmony_ci size_t byte_count) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci memcpy_fromio(cx_out, dev->base_reg + HW_OFFS_OCS_ECC_CX_DATA_OUT, 18662306a36Sopenharmony_ci byte_count); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * ocs_ecc_read_cy_out() - Read the CX data output buffer. 19162306a36Sopenharmony_ci * @dev: The OCS ECC device to read from. 19262306a36Sopenharmony_ci * @cy_out: The buffer where to store the CY value. Must be at least 19362306a36Sopenharmony_ci * @byte_count byte long. 19462306a36Sopenharmony_ci * @byte_count: The amount of data to read. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic inline void ocs_ecc_read_cy_out(struct ocs_ecc_dev *dev, void *cy_out, 19762306a36Sopenharmony_ci size_t byte_count) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci memcpy_fromio(cy_out, dev->base_reg + HW_OFFS_OCS_ECC_CY_DATA_OUT, 20062306a36Sopenharmony_ci byte_count); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct ocs_ecc_dev *kmb_ocs_ecc_find_dev(struct ocs_ecc_ctx *tctx) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci if (tctx->ecc_dev) 20662306a36Sopenharmony_ci return tctx->ecc_dev; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci spin_lock(&ocs_ecc.lock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Only a single OCS device available. */ 21162306a36Sopenharmony_ci tctx->ecc_dev = list_first_entry(&ocs_ecc.dev_list, struct ocs_ecc_dev, 21262306a36Sopenharmony_ci list); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci spin_unlock(&ocs_ecc.lock); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return tctx->ecc_dev; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* Do point multiplication using OCS ECC HW. */ 22062306a36Sopenharmony_cistatic int kmb_ecc_point_mult(struct ocs_ecc_dev *ecc_dev, 22162306a36Sopenharmony_ci struct ecc_point *result, 22262306a36Sopenharmony_ci const struct ecc_point *point, 22362306a36Sopenharmony_ci u64 *scalar, 22462306a36Sopenharmony_ci const struct ecc_curve *curve) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci u8 sca[KMB_ECC_VLI_MAX_BYTES]; /* Use the maximum data size. */ 22762306a36Sopenharmony_ci u32 op_size = (curve->g.ndigits > ECC_CURVE_NIST_P256_DIGITS) ? 22862306a36Sopenharmony_ci OCS_ECC_OP_SIZE_384 : OCS_ECC_OP_SIZE_256; 22962306a36Sopenharmony_ci size_t nbytes = digits_to_bytes(curve->g.ndigits); 23062306a36Sopenharmony_ci int rc = 0; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Generate random nbytes for Simple and Differential SCA protection. */ 23362306a36Sopenharmony_ci rc = crypto_get_default_rng(); 23462306a36Sopenharmony_ci if (rc) 23562306a36Sopenharmony_ci return rc; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci rc = crypto_rng_get_bytes(crypto_default_rng, sca, nbytes); 23862306a36Sopenharmony_ci crypto_put_default_rng(); 23962306a36Sopenharmony_ci if (rc) 24062306a36Sopenharmony_ci return rc; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Wait engine to be idle before starting new operation. */ 24362306a36Sopenharmony_ci rc = ocs_ecc_wait_idle(ecc_dev); 24462306a36Sopenharmony_ci if (rc) 24562306a36Sopenharmony_ci return rc; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Send ecc_start pulse as well as indicating operation size. */ 24862306a36Sopenharmony_ci ocs_ecc_cmd_start(ecc_dev, op_size); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Write ax param; Base point (Gx). */ 25162306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AX, 25262306a36Sopenharmony_ci point->x, nbytes); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Write ay param; Base point (Gy). */ 25562306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AY, 25662306a36Sopenharmony_ci point->y, nbytes); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Write the private key into DATA_IN reg. 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * Since DATA_IN register is used to write different values during the 26262306a36Sopenharmony_ci * computation private Key value is overwritten with 26362306a36Sopenharmony_ci * side-channel-resistance value. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_BX_D, 26662306a36Sopenharmony_ci scalar, nbytes); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Write operand by/l. */ 26962306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_BY_L, 27062306a36Sopenharmony_ci sca, nbytes); 27162306a36Sopenharmony_ci memzero_explicit(sca, sizeof(sca)); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Write p = curve prime(GF modulus). */ 27462306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_P, 27562306a36Sopenharmony_ci curve->p, nbytes); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Write a = curve coefficient. */ 27862306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_A, 27962306a36Sopenharmony_ci curve->a, nbytes); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Make hardware perform the multiplication. */ 28262306a36Sopenharmony_ci rc = ocs_ecc_trigger_op(ecc_dev, op_size, OCS_ECC_INST_CALC_D_IDX_A); 28362306a36Sopenharmony_ci if (rc) 28462306a36Sopenharmony_ci return rc; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Read result. */ 28762306a36Sopenharmony_ci ocs_ecc_read_cx_out(ecc_dev, result->x, nbytes); 28862306a36Sopenharmony_ci ocs_ecc_read_cy_out(ecc_dev, result->y, nbytes); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/** 29462306a36Sopenharmony_ci * kmb_ecc_do_scalar_op() - Perform Scalar operation using OCS ECC HW. 29562306a36Sopenharmony_ci * @ecc_dev: The OCS ECC device to use. 29662306a36Sopenharmony_ci * @scalar_out: Where to store the output scalar. 29762306a36Sopenharmony_ci * @scalar_a: Input scalar operand 'a'. 29862306a36Sopenharmony_ci * @scalar_b: Input scalar operand 'b' 29962306a36Sopenharmony_ci * @curve: The curve on which the operation is performed. 30062306a36Sopenharmony_ci * @ndigits: The size of the operands (in digits). 30162306a36Sopenharmony_ci * @inst: The operation to perform (as an OCS ECC instruction). 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic int kmb_ecc_do_scalar_op(struct ocs_ecc_dev *ecc_dev, u64 *scalar_out, 30662306a36Sopenharmony_ci const u64 *scalar_a, const u64 *scalar_b, 30762306a36Sopenharmony_ci const struct ecc_curve *curve, 30862306a36Sopenharmony_ci unsigned int ndigits, const u32 inst) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci u32 op_size = (ndigits > ECC_CURVE_NIST_P256_DIGITS) ? 31162306a36Sopenharmony_ci OCS_ECC_OP_SIZE_384 : OCS_ECC_OP_SIZE_256; 31262306a36Sopenharmony_ci size_t nbytes = digits_to_bytes(ndigits); 31362306a36Sopenharmony_ci int rc; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* Wait engine to be idle before starting new operation. */ 31662306a36Sopenharmony_ci rc = ocs_ecc_wait_idle(ecc_dev); 31762306a36Sopenharmony_ci if (rc) 31862306a36Sopenharmony_ci return rc; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Send ecc_start pulse as well as indicating operation size. */ 32162306a36Sopenharmony_ci ocs_ecc_cmd_start(ecc_dev, op_size); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Write ax param (Base point (Gx).*/ 32462306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AX, 32562306a36Sopenharmony_ci scalar_a, nbytes); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Write ay param Base point (Gy).*/ 32862306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AY, 32962306a36Sopenharmony_ci scalar_b, nbytes); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Write p = curve prime(GF modulus).*/ 33262306a36Sopenharmony_ci ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_P, 33362306a36Sopenharmony_ci curve->p, nbytes); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Give instruction A.B or A+B to ECC engine. */ 33662306a36Sopenharmony_ci rc = ocs_ecc_trigger_op(ecc_dev, op_size, inst); 33762306a36Sopenharmony_ci if (rc) 33862306a36Sopenharmony_ci return rc; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ocs_ecc_read_cx_out(ecc_dev, scalar_out, nbytes); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (vli_is_zero(scalar_out, ndigits)) 34362306a36Sopenharmony_ci return -EINVAL; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* SP800-56A section 5.6.2.3.4 partial verification: ephemeral keys only */ 34962306a36Sopenharmony_cistatic int kmb_ocs_ecc_is_pubkey_valid_partial(struct ocs_ecc_dev *ecc_dev, 35062306a36Sopenharmony_ci const struct ecc_curve *curve, 35162306a36Sopenharmony_ci struct ecc_point *pk) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u64 xxx[KMB_ECC_VLI_MAX_DIGITS] = { 0 }; 35462306a36Sopenharmony_ci u64 yy[KMB_ECC_VLI_MAX_DIGITS] = { 0 }; 35562306a36Sopenharmony_ci u64 w[KMB_ECC_VLI_MAX_DIGITS] = { 0 }; 35662306a36Sopenharmony_ci int rc; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (WARN_ON(pk->ndigits != curve->g.ndigits)) 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Check 1: Verify key is not the zero point. */ 36262306a36Sopenharmony_ci if (ecc_point_is_zero(pk)) 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Check 2: Verify key is in the range [0, p-1]. */ 36662306a36Sopenharmony_ci if (vli_cmp(curve->p, pk->x, pk->ndigits) != 1) 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (vli_cmp(curve->p, pk->y, pk->ndigits) != 1) 37062306a36Sopenharmony_ci return -EINVAL; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Check 3: Verify that y^2 == (x^3 + a·x + b) mod p */ 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* y^2 */ 37562306a36Sopenharmony_ci /* Compute y^2 -> store in yy */ 37662306a36Sopenharmony_ci rc = kmb_ecc_do_scalar_op(ecc_dev, yy, pk->y, pk->y, curve, pk->ndigits, 37762306a36Sopenharmony_ci OCS_ECC_INST_CALC_A_MUL_B_MODP); 37862306a36Sopenharmony_ci if (rc) 37962306a36Sopenharmony_ci goto exit; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* x^3 */ 38262306a36Sopenharmony_ci /* Assigning w = 3, used for calculating x^3. */ 38362306a36Sopenharmony_ci w[0] = POW_CUBE; 38462306a36Sopenharmony_ci /* Load the next stage.*/ 38562306a36Sopenharmony_ci rc = kmb_ecc_do_scalar_op(ecc_dev, xxx, pk->x, w, curve, pk->ndigits, 38662306a36Sopenharmony_ci OCS_ECC_INST_CALC_A_POW_B_MODP); 38762306a36Sopenharmony_ci if (rc) 38862306a36Sopenharmony_ci goto exit; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Do a*x -> store in w. */ 39162306a36Sopenharmony_ci rc = kmb_ecc_do_scalar_op(ecc_dev, w, curve->a, pk->x, curve, 39262306a36Sopenharmony_ci pk->ndigits, 39362306a36Sopenharmony_ci OCS_ECC_INST_CALC_A_MUL_B_MODP); 39462306a36Sopenharmony_ci if (rc) 39562306a36Sopenharmony_ci goto exit; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Do ax + b == w + b; store in w. */ 39862306a36Sopenharmony_ci rc = kmb_ecc_do_scalar_op(ecc_dev, w, w, curve->b, curve, 39962306a36Sopenharmony_ci pk->ndigits, 40062306a36Sopenharmony_ci OCS_ECC_INST_CALC_A_ADD_B_MODP); 40162306a36Sopenharmony_ci if (rc) 40262306a36Sopenharmony_ci goto exit; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* x^3 + ax + b == x^3 + w -> store in w. */ 40562306a36Sopenharmony_ci rc = kmb_ecc_do_scalar_op(ecc_dev, w, xxx, w, curve, pk->ndigits, 40662306a36Sopenharmony_ci OCS_ECC_INST_CALC_A_ADD_B_MODP); 40762306a36Sopenharmony_ci if (rc) 40862306a36Sopenharmony_ci goto exit; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Compare y^2 == x^3 + a·x + b. */ 41162306a36Sopenharmony_ci rc = vli_cmp(yy, w, pk->ndigits); 41262306a36Sopenharmony_ci if (rc) 41362306a36Sopenharmony_ci rc = -EINVAL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciexit: 41662306a36Sopenharmony_ci memzero_explicit(xxx, sizeof(xxx)); 41762306a36Sopenharmony_ci memzero_explicit(yy, sizeof(yy)); 41862306a36Sopenharmony_ci memzero_explicit(w, sizeof(w)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return rc; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* SP800-56A section 5.6.2.3.3 full verification */ 42462306a36Sopenharmony_cistatic int kmb_ocs_ecc_is_pubkey_valid_full(struct ocs_ecc_dev *ecc_dev, 42562306a36Sopenharmony_ci const struct ecc_curve *curve, 42662306a36Sopenharmony_ci struct ecc_point *pk) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct ecc_point *nQ; 42962306a36Sopenharmony_ci int rc; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Checks 1 through 3 */ 43262306a36Sopenharmony_ci rc = kmb_ocs_ecc_is_pubkey_valid_partial(ecc_dev, curve, pk); 43362306a36Sopenharmony_ci if (rc) 43462306a36Sopenharmony_ci return rc; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Check 4: Verify that nQ is the zero point. */ 43762306a36Sopenharmony_ci nQ = ecc_alloc_point(pk->ndigits); 43862306a36Sopenharmony_ci if (!nQ) 43962306a36Sopenharmony_ci return -ENOMEM; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci rc = kmb_ecc_point_mult(ecc_dev, nQ, pk, curve->n, curve); 44262306a36Sopenharmony_ci if (rc) 44362306a36Sopenharmony_ci goto exit; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!ecc_point_is_zero(nQ)) 44662306a36Sopenharmony_ci rc = -EINVAL; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ciexit: 44962306a36Sopenharmony_ci ecc_free_point(nQ); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return rc; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int kmb_ecc_is_key_valid(const struct ecc_curve *curve, 45562306a36Sopenharmony_ci const u64 *private_key, size_t private_key_len) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci size_t ndigits = curve->g.ndigits; 45862306a36Sopenharmony_ci u64 one[KMB_ECC_VLI_MAX_DIGITS] = {1}; 45962306a36Sopenharmony_ci u64 res[KMB_ECC_VLI_MAX_DIGITS]; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (private_key_len != digits_to_bytes(ndigits)) 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (!private_key) 46562306a36Sopenharmony_ci return -EINVAL; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Make sure the private key is in the range [2, n-3]. */ 46862306a36Sopenharmony_ci if (vli_cmp(one, private_key, ndigits) != -1) 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci vli_sub(res, curve->n, one, ndigits); 47262306a36Sopenharmony_ci vli_sub(res, res, one, ndigits); 47362306a36Sopenharmony_ci if (vli_cmp(res, private_key, ndigits) != 1) 47462306a36Sopenharmony_ci return -EINVAL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* 48062306a36Sopenharmony_ci * ECC private keys are generated using the method of extra random bits, 48162306a36Sopenharmony_ci * equivalent to that described in FIPS 186-4, Appendix B.4.1. 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * d = (c mod(n–1)) + 1 where c is a string of random bits, 64 bits longer 48462306a36Sopenharmony_ci * than requested 48562306a36Sopenharmony_ci * 0 <= c mod(n-1) <= n-2 and implies that 48662306a36Sopenharmony_ci * 1 <= d <= n-1 48762306a36Sopenharmony_ci * 48862306a36Sopenharmony_ci * This method generates a private key uniformly distributed in the range 48962306a36Sopenharmony_ci * [1, n-1]. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic int kmb_ecc_gen_privkey(const struct ecc_curve *curve, u64 *privkey) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci size_t nbytes = digits_to_bytes(curve->g.ndigits); 49462306a36Sopenharmony_ci u64 priv[KMB_ECC_VLI_MAX_DIGITS]; 49562306a36Sopenharmony_ci size_t nbits; 49662306a36Sopenharmony_ci int rc; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci nbits = vli_num_bits(curve->n, curve->g.ndigits); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Check that N is included in Table 1 of FIPS 186-4, section 6.1.1 */ 50162306a36Sopenharmony_ci if (nbits < 160 || curve->g.ndigits > ARRAY_SIZE(priv)) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * FIPS 186-4 recommends that the private key should be obtained from a 50662306a36Sopenharmony_ci * RBG with a security strength equal to or greater than the security 50762306a36Sopenharmony_ci * strength associated with N. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * The maximum security strength identified by NIST SP800-57pt1r4 for 51062306a36Sopenharmony_ci * ECC is 256 (N >= 512). 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * This condition is met by the default RNG because it selects a favored 51362306a36Sopenharmony_ci * DRBG with a security strength of 256. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci if (crypto_get_default_rng()) 51662306a36Sopenharmony_ci return -EFAULT; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci rc = crypto_rng_get_bytes(crypto_default_rng, (u8 *)priv, nbytes); 51962306a36Sopenharmony_ci crypto_put_default_rng(); 52062306a36Sopenharmony_ci if (rc) 52162306a36Sopenharmony_ci goto cleanup; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci rc = kmb_ecc_is_key_valid(curve, priv, nbytes); 52462306a36Sopenharmony_ci if (rc) 52562306a36Sopenharmony_ci goto cleanup; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ecc_swap_digits(priv, privkey, curve->g.ndigits); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cicleanup: 53062306a36Sopenharmony_ci memzero_explicit(&priv, sizeof(priv)); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return rc; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int kmb_ocs_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf, 53662306a36Sopenharmony_ci unsigned int len) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm); 53962306a36Sopenharmony_ci struct ecdh params; 54062306a36Sopenharmony_ci int rc = 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci rc = crypto_ecdh_decode_key(buf, len, ¶ms); 54362306a36Sopenharmony_ci if (rc) 54462306a36Sopenharmony_ci goto cleanup; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Ensure key size is not bigger then expected. */ 54762306a36Sopenharmony_ci if (params.key_size > digits_to_bytes(tctx->curve->g.ndigits)) { 54862306a36Sopenharmony_ci rc = -EINVAL; 54962306a36Sopenharmony_ci goto cleanup; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Auto-generate private key is not provided. */ 55362306a36Sopenharmony_ci if (!params.key || !params.key_size) { 55462306a36Sopenharmony_ci rc = kmb_ecc_gen_privkey(tctx->curve, tctx->private_key); 55562306a36Sopenharmony_ci goto cleanup; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci rc = kmb_ecc_is_key_valid(tctx->curve, (const u64 *)params.key, 55962306a36Sopenharmony_ci params.key_size); 56062306a36Sopenharmony_ci if (rc) 56162306a36Sopenharmony_ci goto cleanup; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ecc_swap_digits((const u64 *)params.key, tctx->private_key, 56462306a36Sopenharmony_ci tctx->curve->g.ndigits); 56562306a36Sopenharmony_cicleanup: 56662306a36Sopenharmony_ci memzero_explicit(¶ms, sizeof(params)); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (rc) 56962306a36Sopenharmony_ci tctx->curve = NULL; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return rc; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/* Compute shared secret. */ 57562306a36Sopenharmony_cistatic int kmb_ecc_do_shared_secret(struct ocs_ecc_ctx *tctx, 57662306a36Sopenharmony_ci struct kpp_request *req) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct ocs_ecc_dev *ecc_dev = tctx->ecc_dev; 57962306a36Sopenharmony_ci const struct ecc_curve *curve = tctx->curve; 58062306a36Sopenharmony_ci u64 shared_secret[KMB_ECC_VLI_MAX_DIGITS]; 58162306a36Sopenharmony_ci u64 pubk_buf[KMB_ECC_VLI_MAX_DIGITS * 2]; 58262306a36Sopenharmony_ci size_t copied, nbytes, pubk_len; 58362306a36Sopenharmony_ci struct ecc_point *pk, *result; 58462306a36Sopenharmony_ci int rc; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci nbytes = digits_to_bytes(curve->g.ndigits); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Public key is a point, thus it has two coordinates */ 58962306a36Sopenharmony_ci pubk_len = 2 * nbytes; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Copy public key from SG list to pubk_buf. */ 59262306a36Sopenharmony_ci copied = sg_copy_to_buffer(req->src, 59362306a36Sopenharmony_ci sg_nents_for_len(req->src, pubk_len), 59462306a36Sopenharmony_ci pubk_buf, pubk_len); 59562306a36Sopenharmony_ci if (copied != pubk_len) 59662306a36Sopenharmony_ci return -EINVAL; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Allocate and initialize public key point. */ 59962306a36Sopenharmony_ci pk = ecc_alloc_point(curve->g.ndigits); 60062306a36Sopenharmony_ci if (!pk) 60162306a36Sopenharmony_ci return -ENOMEM; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ecc_swap_digits(pubk_buf, pk->x, curve->g.ndigits); 60462306a36Sopenharmony_ci ecc_swap_digits(&pubk_buf[curve->g.ndigits], pk->y, curve->g.ndigits); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Check the public key for following 60862306a36Sopenharmony_ci * Check 1: Verify key is not the zero point. 60962306a36Sopenharmony_ci * Check 2: Verify key is in the range [1, p-1]. 61062306a36Sopenharmony_ci * Check 3: Verify that y^2 == (x^3 + a·x + b) mod p 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci rc = kmb_ocs_ecc_is_pubkey_valid_partial(ecc_dev, curve, pk); 61362306a36Sopenharmony_ci if (rc) 61462306a36Sopenharmony_ci goto exit_free_pk; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Allocate point for storing computed shared secret. */ 61762306a36Sopenharmony_ci result = ecc_alloc_point(pk->ndigits); 61862306a36Sopenharmony_ci if (!result) { 61962306a36Sopenharmony_ci rc = -ENOMEM; 62062306a36Sopenharmony_ci goto exit_free_pk; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Calculate the shared secret.*/ 62462306a36Sopenharmony_ci rc = kmb_ecc_point_mult(ecc_dev, result, pk, tctx->private_key, curve); 62562306a36Sopenharmony_ci if (rc) 62662306a36Sopenharmony_ci goto exit_free_result; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (ecc_point_is_zero(result)) { 62962306a36Sopenharmony_ci rc = -EFAULT; 63062306a36Sopenharmony_ci goto exit_free_result; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Copy shared secret from point to buffer. */ 63462306a36Sopenharmony_ci ecc_swap_digits(result->x, shared_secret, result->ndigits); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Request might ask for less bytes than what we have. */ 63762306a36Sopenharmony_ci nbytes = min_t(size_t, nbytes, req->dst_len); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci copied = sg_copy_from_buffer(req->dst, 64062306a36Sopenharmony_ci sg_nents_for_len(req->dst, nbytes), 64162306a36Sopenharmony_ci shared_secret, nbytes); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (copied != nbytes) 64462306a36Sopenharmony_ci rc = -EINVAL; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci memzero_explicit(shared_secret, sizeof(shared_secret)); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciexit_free_result: 64962306a36Sopenharmony_ci ecc_free_point(result); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciexit_free_pk: 65262306a36Sopenharmony_ci ecc_free_point(pk); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return rc; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/* Compute public key. */ 65862306a36Sopenharmony_cistatic int kmb_ecc_do_public_key(struct ocs_ecc_ctx *tctx, 65962306a36Sopenharmony_ci struct kpp_request *req) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci const struct ecc_curve *curve = tctx->curve; 66262306a36Sopenharmony_ci u64 pubk_buf[KMB_ECC_VLI_MAX_DIGITS * 2]; 66362306a36Sopenharmony_ci struct ecc_point *pk; 66462306a36Sopenharmony_ci size_t pubk_len; 66562306a36Sopenharmony_ci size_t copied; 66662306a36Sopenharmony_ci int rc; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Public key is a point, so it has double the digits. */ 66962306a36Sopenharmony_ci pubk_len = 2 * digits_to_bytes(curve->g.ndigits); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci pk = ecc_alloc_point(curve->g.ndigits); 67262306a36Sopenharmony_ci if (!pk) 67362306a36Sopenharmony_ci return -ENOMEM; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Public Key(pk) = priv * G. */ 67662306a36Sopenharmony_ci rc = kmb_ecc_point_mult(tctx->ecc_dev, pk, &curve->g, tctx->private_key, 67762306a36Sopenharmony_ci curve); 67862306a36Sopenharmony_ci if (rc) 67962306a36Sopenharmony_ci goto exit; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* SP800-56A rev 3 5.6.2.1.3 key check */ 68262306a36Sopenharmony_ci if (kmb_ocs_ecc_is_pubkey_valid_full(tctx->ecc_dev, curve, pk)) { 68362306a36Sopenharmony_ci rc = -EAGAIN; 68462306a36Sopenharmony_ci goto exit; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Copy public key from point to buffer. */ 68862306a36Sopenharmony_ci ecc_swap_digits(pk->x, pubk_buf, pk->ndigits); 68962306a36Sopenharmony_ci ecc_swap_digits(pk->y, &pubk_buf[pk->ndigits], pk->ndigits); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* Copy public key to req->dst. */ 69262306a36Sopenharmony_ci copied = sg_copy_from_buffer(req->dst, 69362306a36Sopenharmony_ci sg_nents_for_len(req->dst, pubk_len), 69462306a36Sopenharmony_ci pubk_buf, pubk_len); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (copied != pubk_len) 69762306a36Sopenharmony_ci rc = -EINVAL; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ciexit: 70062306a36Sopenharmony_ci ecc_free_point(pk); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci return rc; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int kmb_ocs_ecc_do_one_request(struct crypto_engine *engine, 70662306a36Sopenharmony_ci void *areq) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct kpp_request *req = container_of(areq, struct kpp_request, base); 70962306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req); 71062306a36Sopenharmony_ci struct ocs_ecc_dev *ecc_dev = tctx->ecc_dev; 71162306a36Sopenharmony_ci int rc; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (req->src) 71462306a36Sopenharmony_ci rc = kmb_ecc_do_shared_secret(tctx, req); 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci rc = kmb_ecc_do_public_key(tctx, req); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci crypto_finalize_kpp_request(ecc_dev->engine, req, rc); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int kmb_ocs_ecdh_generate_public_key(struct kpp_request *req) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req); 72662306a36Sopenharmony_ci const struct ecc_curve *curve = tctx->curve; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Ensure kmb_ocs_ecdh_set_secret() has been successfully called. */ 72962306a36Sopenharmony_ci if (!tctx->curve) 73062306a36Sopenharmony_ci return -EINVAL; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Ensure dst is present. */ 73362306a36Sopenharmony_ci if (!req->dst) 73462306a36Sopenharmony_ci return -EINVAL; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* Check the request dst is big enough to hold the public key. */ 73762306a36Sopenharmony_ci if (req->dst_len < (2 * digits_to_bytes(curve->g.ndigits))) 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* 'src' is not supposed to be present when generate pubk is called. */ 74162306a36Sopenharmony_ci if (req->src) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return crypto_transfer_kpp_request_to_engine(tctx->ecc_dev->engine, 74562306a36Sopenharmony_ci req); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int kmb_ocs_ecdh_compute_shared_secret(struct kpp_request *req) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req); 75162306a36Sopenharmony_ci const struct ecc_curve *curve = tctx->curve; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Ensure kmb_ocs_ecdh_set_secret() has been successfully called. */ 75462306a36Sopenharmony_ci if (!tctx->curve) 75562306a36Sopenharmony_ci return -EINVAL; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* Ensure dst is present. */ 75862306a36Sopenharmony_ci if (!req->dst) 75962306a36Sopenharmony_ci return -EINVAL; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* Ensure src is present. */ 76262306a36Sopenharmony_ci if (!req->src) 76362306a36Sopenharmony_ci return -EINVAL; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* 76662306a36Sopenharmony_ci * req->src is expected to the (other-side) public key, so its length 76762306a36Sopenharmony_ci * must be 2 * coordinate size (in bytes). 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci if (req->src_len != 2 * digits_to_bytes(curve->g.ndigits)) 77062306a36Sopenharmony_ci return -EINVAL; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci return crypto_transfer_kpp_request_to_engine(tctx->ecc_dev->engine, 77362306a36Sopenharmony_ci req); 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int kmb_ecc_tctx_init(struct ocs_ecc_ctx *tctx, unsigned int curve_id) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci memset(tctx, 0, sizeof(*tctx)); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci tctx->ecc_dev = kmb_ocs_ecc_find_dev(tctx); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (IS_ERR(tctx->ecc_dev)) { 78362306a36Sopenharmony_ci pr_err("Failed to find the device : %ld\n", 78462306a36Sopenharmony_ci PTR_ERR(tctx->ecc_dev)); 78562306a36Sopenharmony_ci return PTR_ERR(tctx->ecc_dev); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci tctx->curve = ecc_get_curve(curve_id); 78962306a36Sopenharmony_ci if (!tctx->curve) 79062306a36Sopenharmony_ci return -EOPNOTSUPP; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int kmb_ocs_ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return kmb_ecc_tctx_init(tctx, ECC_CURVE_NIST_P256); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int kmb_ocs_ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return kmb_ecc_tctx_init(tctx, ECC_CURVE_NIST_P384); 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic void kmb_ocs_ecdh_exit_tfm(struct crypto_kpp *tfm) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci memzero_explicit(tctx->private_key, sizeof(*tctx->private_key)); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic unsigned int kmb_ocs_ecdh_max_size(struct crypto_kpp *tfm) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Public key is made of two coordinates, so double the digits. */ 82162306a36Sopenharmony_ci return digits_to_bytes(tctx->curve->g.ndigits) * 2; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic struct kpp_engine_alg ocs_ecdh_p256 = { 82562306a36Sopenharmony_ci .base.set_secret = kmb_ocs_ecdh_set_secret, 82662306a36Sopenharmony_ci .base.generate_public_key = kmb_ocs_ecdh_generate_public_key, 82762306a36Sopenharmony_ci .base.compute_shared_secret = kmb_ocs_ecdh_compute_shared_secret, 82862306a36Sopenharmony_ci .base.init = kmb_ocs_ecdh_nist_p256_init_tfm, 82962306a36Sopenharmony_ci .base.exit = kmb_ocs_ecdh_exit_tfm, 83062306a36Sopenharmony_ci .base.max_size = kmb_ocs_ecdh_max_size, 83162306a36Sopenharmony_ci .base.base = { 83262306a36Sopenharmony_ci .cra_name = "ecdh-nist-p256", 83362306a36Sopenharmony_ci .cra_driver_name = "ecdh-nist-p256-keembay-ocs", 83462306a36Sopenharmony_ci .cra_priority = KMB_OCS_ECC_PRIORITY, 83562306a36Sopenharmony_ci .cra_module = THIS_MODULE, 83662306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct ocs_ecc_ctx), 83762306a36Sopenharmony_ci }, 83862306a36Sopenharmony_ci .op.do_one_request = kmb_ocs_ecc_do_one_request, 83962306a36Sopenharmony_ci}; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic struct kpp_engine_alg ocs_ecdh_p384 = { 84262306a36Sopenharmony_ci .base.set_secret = kmb_ocs_ecdh_set_secret, 84362306a36Sopenharmony_ci .base.generate_public_key = kmb_ocs_ecdh_generate_public_key, 84462306a36Sopenharmony_ci .base.compute_shared_secret = kmb_ocs_ecdh_compute_shared_secret, 84562306a36Sopenharmony_ci .base.init = kmb_ocs_ecdh_nist_p384_init_tfm, 84662306a36Sopenharmony_ci .base.exit = kmb_ocs_ecdh_exit_tfm, 84762306a36Sopenharmony_ci .base.max_size = kmb_ocs_ecdh_max_size, 84862306a36Sopenharmony_ci .base.base = { 84962306a36Sopenharmony_ci .cra_name = "ecdh-nist-p384", 85062306a36Sopenharmony_ci .cra_driver_name = "ecdh-nist-p384-keembay-ocs", 85162306a36Sopenharmony_ci .cra_priority = KMB_OCS_ECC_PRIORITY, 85262306a36Sopenharmony_ci .cra_module = THIS_MODULE, 85362306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct ocs_ecc_ctx), 85462306a36Sopenharmony_ci }, 85562306a36Sopenharmony_ci .op.do_one_request = kmb_ocs_ecc_do_one_request, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic irqreturn_t ocs_ecc_irq_handler(int irq, void *dev_id) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct ocs_ecc_dev *ecc_dev = dev_id; 86162306a36Sopenharmony_ci u32 status; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* 86462306a36Sopenharmony_ci * Read the status register and write it back to clear the 86562306a36Sopenharmony_ci * DONE_INT_STATUS bit. 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_ci status = ioread32(ecc_dev->base_reg + HW_OFFS_OCS_ECC_ISR); 86862306a36Sopenharmony_ci iowrite32(status, ecc_dev->base_reg + HW_OFFS_OCS_ECC_ISR); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (!(status & HW_OCS_ECC_ISR_INT_STATUS_DONE)) 87162306a36Sopenharmony_ci return IRQ_NONE; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci complete(&ecc_dev->irq_done); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return IRQ_HANDLED; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int kmb_ocs_ecc_probe(struct platform_device *pdev) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 88162306a36Sopenharmony_ci struct ocs_ecc_dev *ecc_dev; 88262306a36Sopenharmony_ci int rc; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci ecc_dev = devm_kzalloc(dev, sizeof(*ecc_dev), GFP_KERNEL); 88562306a36Sopenharmony_ci if (!ecc_dev) 88662306a36Sopenharmony_ci return -ENOMEM; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ecc_dev->dev = dev; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci platform_set_drvdata(pdev, ecc_dev); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci INIT_LIST_HEAD(&ecc_dev->list); 89362306a36Sopenharmony_ci init_completion(&ecc_dev->irq_done); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Get base register address. */ 89662306a36Sopenharmony_ci ecc_dev->base_reg = devm_platform_ioremap_resource(pdev, 0); 89762306a36Sopenharmony_ci if (IS_ERR(ecc_dev->base_reg)) { 89862306a36Sopenharmony_ci dev_err(dev, "Failed to get base address\n"); 89962306a36Sopenharmony_ci rc = PTR_ERR(ecc_dev->base_reg); 90062306a36Sopenharmony_ci goto list_del; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* Get and request IRQ */ 90462306a36Sopenharmony_ci ecc_dev->irq = platform_get_irq(pdev, 0); 90562306a36Sopenharmony_ci if (ecc_dev->irq < 0) { 90662306a36Sopenharmony_ci rc = ecc_dev->irq; 90762306a36Sopenharmony_ci goto list_del; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci rc = devm_request_threaded_irq(dev, ecc_dev->irq, ocs_ecc_irq_handler, 91162306a36Sopenharmony_ci NULL, 0, "keembay-ocs-ecc", ecc_dev); 91262306a36Sopenharmony_ci if (rc < 0) { 91362306a36Sopenharmony_ci dev_err(dev, "Could not request IRQ\n"); 91462306a36Sopenharmony_ci goto list_del; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* Add device to the list of OCS ECC devices. */ 91862306a36Sopenharmony_ci spin_lock(&ocs_ecc.lock); 91962306a36Sopenharmony_ci list_add_tail(&ecc_dev->list, &ocs_ecc.dev_list); 92062306a36Sopenharmony_ci spin_unlock(&ocs_ecc.lock); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* Initialize crypto engine. */ 92362306a36Sopenharmony_ci ecc_dev->engine = crypto_engine_alloc_init(dev, 1); 92462306a36Sopenharmony_ci if (!ecc_dev->engine) { 92562306a36Sopenharmony_ci dev_err(dev, "Could not allocate crypto engine\n"); 92662306a36Sopenharmony_ci rc = -ENOMEM; 92762306a36Sopenharmony_ci goto list_del; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci rc = crypto_engine_start(ecc_dev->engine); 93162306a36Sopenharmony_ci if (rc) { 93262306a36Sopenharmony_ci dev_err(dev, "Could not start crypto engine\n"); 93362306a36Sopenharmony_ci goto cleanup; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* Register the KPP algo. */ 93762306a36Sopenharmony_ci rc = crypto_engine_register_kpp(&ocs_ecdh_p256); 93862306a36Sopenharmony_ci if (rc) { 93962306a36Sopenharmony_ci dev_err(dev, 94062306a36Sopenharmony_ci "Could not register OCS algorithms with Crypto API\n"); 94162306a36Sopenharmony_ci goto cleanup; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci rc = crypto_engine_register_kpp(&ocs_ecdh_p384); 94562306a36Sopenharmony_ci if (rc) { 94662306a36Sopenharmony_ci dev_err(dev, 94762306a36Sopenharmony_ci "Could not register OCS algorithms with Crypto API\n"); 94862306a36Sopenharmony_ci goto ocs_ecdh_p384_error; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return 0; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ciocs_ecdh_p384_error: 95462306a36Sopenharmony_ci crypto_engine_unregister_kpp(&ocs_ecdh_p256); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cicleanup: 95762306a36Sopenharmony_ci crypto_engine_exit(ecc_dev->engine); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cilist_del: 96062306a36Sopenharmony_ci spin_lock(&ocs_ecc.lock); 96162306a36Sopenharmony_ci list_del(&ecc_dev->list); 96262306a36Sopenharmony_ci spin_unlock(&ocs_ecc.lock); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return rc; 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int kmb_ocs_ecc_remove(struct platform_device *pdev) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci struct ocs_ecc_dev *ecc_dev; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci ecc_dev = platform_get_drvdata(pdev); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci crypto_engine_unregister_kpp(&ocs_ecdh_p384); 97462306a36Sopenharmony_ci crypto_engine_unregister_kpp(&ocs_ecdh_p256); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci spin_lock(&ocs_ecc.lock); 97762306a36Sopenharmony_ci list_del(&ecc_dev->list); 97862306a36Sopenharmony_ci spin_unlock(&ocs_ecc.lock); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci crypto_engine_exit(ecc_dev->engine); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci return 0; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci/* Device tree driver match. */ 98662306a36Sopenharmony_cistatic const struct of_device_id kmb_ocs_ecc_of_match[] = { 98762306a36Sopenharmony_ci { 98862306a36Sopenharmony_ci .compatible = "intel,keembay-ocs-ecc", 98962306a36Sopenharmony_ci }, 99062306a36Sopenharmony_ci {} 99162306a36Sopenharmony_ci}; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci/* The OCS driver is a platform device. */ 99462306a36Sopenharmony_cistatic struct platform_driver kmb_ocs_ecc_driver = { 99562306a36Sopenharmony_ci .probe = kmb_ocs_ecc_probe, 99662306a36Sopenharmony_ci .remove = kmb_ocs_ecc_remove, 99762306a36Sopenharmony_ci .driver = { 99862306a36Sopenharmony_ci .name = DRV_NAME, 99962306a36Sopenharmony_ci .of_match_table = kmb_ocs_ecc_of_match, 100062306a36Sopenharmony_ci }, 100162306a36Sopenharmony_ci}; 100262306a36Sopenharmony_cimodule_platform_driver(kmb_ocs_ecc_driver); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 100562306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Keem Bay OCS ECC Driver"); 100662306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecdh-nist-p256"); 100762306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecdh-nist-p384"); 100862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecdh-nist-p256-keembay-ocs"); 100962306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecdh-nist-p384-keembay-ocs"); 1010