12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Intel Keem Bay OCS ECC Crypto Driver.
- *
- * Copyright (C) 2019-2021 Intel Corporation
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/clk.h>
- #include <linux/completion.h>
- #include <linux/crypto.h>
- #include <linux/delay.h>
- #include <linux/fips.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/iopoll.h>
- #include <linux/irq.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/scatterlist.h>
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <crypto/ecc_curve.h>
- #include <crypto/ecdh.h>
- #include <crypto/engine.h>
- #include <crypto/kpp.h>
- #include <crypto/rng.h>
- #include <crypto/internal/ecc.h>
- #include <crypto/internal/kpp.h>
- #define DRV_NAME "keembay-ocs-ecc"
- #define KMB_OCS_ECC_PRIORITY 350
- #define HW_OFFS_OCS_ECC_COMMAND 0x00000000
- #define HW_OFFS_OCS_ECC_STATUS 0x00000004
- #define HW_OFFS_OCS_ECC_DATA_IN 0x00000080
- #define HW_OFFS_OCS_ECC_CX_DATA_OUT 0x00000100
- #define HW_OFFS_OCS_ECC_CY_DATA_OUT 0x00000180
- #define HW_OFFS_OCS_ECC_ISR 0x00000400
- #define HW_OFFS_OCS_ECC_IER 0x00000404
- #define HW_OCS_ECC_ISR_INT_STATUS_DONE BIT(0)
- #define HW_OCS_ECC_COMMAND_INS_BP BIT(0)
- #define HW_OCS_ECC_COMMAND_START_VAL BIT(0)
- #define OCS_ECC_OP_SIZE_384 BIT(8)
- #define OCS_ECC_OP_SIZE_256 0
- /* ECC Instruction : for ECC_COMMAND */
- #define OCS_ECC_INST_WRITE_AX (0x1 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_WRITE_AY (0x2 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_WRITE_BX_D (0x3 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_WRITE_BY_L (0x4 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_WRITE_P (0x5 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_WRITE_A (0x6 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_CALC_D_IDX_A (0x8 << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_CALC_A_POW_B_MODP (0xB << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_CALC_A_MUL_B_MODP (0xC << HW_OCS_ECC_COMMAND_INS_BP)
- #define OCS_ECC_INST_CALC_A_ADD_B_MODP (0xD << HW_OCS_ECC_COMMAND_INS_BP)
- #define ECC_ENABLE_INTR 1
- #define POLL_USEC 100
- #define TIMEOUT_USEC 10000
- #define KMB_ECC_VLI_MAX_DIGITS ECC_CURVE_NIST_P384_DIGITS
- #define KMB_ECC_VLI_MAX_BYTES (KMB_ECC_VLI_MAX_DIGITS \
- << ECC_DIGITS_TO_BYTES_SHIFT)
- #define POW_CUBE 3
- /**
- * struct ocs_ecc_dev - ECC device context
- * @list: List of device contexts
- * @dev: OCS ECC device
- * @base_reg: IO base address of OCS ECC
- * @engine: Crypto engine for the device
- * @irq_done: IRQ done completion.
- * @irq: IRQ number
- */
- struct ocs_ecc_dev {
- struct list_head list;
- struct device *dev;
- void __iomem *base_reg;
- struct crypto_engine *engine;
- struct completion irq_done;
- int irq;
- };
- /**
- * struct ocs_ecc_ctx - Transformation context.
- * @engine_ctx: Crypto engine ctx.
- * @ecc_dev: The ECC driver associated with this context.
- * @curve: The elliptic curve used by this transformation.
- * @private_key: The private key.
- */
- struct ocs_ecc_ctx {
- struct crypto_engine_ctx engine_ctx;
- struct ocs_ecc_dev *ecc_dev;
- const struct ecc_curve *curve;
- u64 private_key[KMB_ECC_VLI_MAX_DIGITS];
- };
- /* Driver data. */
- struct ocs_ecc_drv {
- struct list_head dev_list;
- spinlock_t lock; /* Protects dev_list. */
- };
- /* Global variable holding the list of OCS ECC devices (only one expected). */
- static struct ocs_ecc_drv ocs_ecc = {
- .dev_list = LIST_HEAD_INIT(ocs_ecc.dev_list),
- .lock = __SPIN_LOCK_UNLOCKED(ocs_ecc.lock),
- };
- /* Get OCS ECC tfm context from kpp_request. */
- static inline struct ocs_ecc_ctx *kmb_ocs_ecc_tctx(struct kpp_request *req)
- {
- return kpp_tfm_ctx(crypto_kpp_reqtfm(req));
- }
- /* Converts number of digits to number of bytes. */
- static inline unsigned int digits_to_bytes(unsigned int n)
- {
- return n << ECC_DIGITS_TO_BYTES_SHIFT;
- }
- /*
- * Wait for ECC idle i.e when an operation (other than write operations)
- * is done.
- */
- static inline int ocs_ecc_wait_idle(struct ocs_ecc_dev *dev)
- {
- u32 value;
- return readl_poll_timeout((dev->base_reg + HW_OFFS_OCS_ECC_STATUS),
- value,
- !(value & HW_OCS_ECC_ISR_INT_STATUS_DONE),
- POLL_USEC, TIMEOUT_USEC);
- }
- static void ocs_ecc_cmd_start(struct ocs_ecc_dev *ecc_dev, u32 op_size)
- {
- iowrite32(op_size | HW_OCS_ECC_COMMAND_START_VAL,
- ecc_dev->base_reg + HW_OFFS_OCS_ECC_COMMAND);
- }
- /* Direct write of u32 buffer to ECC engine with associated instruction. */
- static void ocs_ecc_write_cmd_and_data(struct ocs_ecc_dev *dev,
- u32 op_size,
- u32 inst,
- const void *data_in,
- size_t data_size)
- {
- iowrite32(op_size | inst, dev->base_reg + HW_OFFS_OCS_ECC_COMMAND);
- /* MMIO Write src uint32 to dst. */
- memcpy_toio(dev->base_reg + HW_OFFS_OCS_ECC_DATA_IN, data_in,
- data_size);
- }
- /* Start OCS ECC operation and wait for its completion. */
- static int ocs_ecc_trigger_op(struct ocs_ecc_dev *ecc_dev, u32 op_size,
- u32 inst)
- {
- reinit_completion(&ecc_dev->irq_done);
- iowrite32(ECC_ENABLE_INTR, ecc_dev->base_reg + HW_OFFS_OCS_ECC_IER);
- iowrite32(op_size | inst, ecc_dev->base_reg + HW_OFFS_OCS_ECC_COMMAND);
- return wait_for_completion_interruptible(&ecc_dev->irq_done);
- }
- /**
- * ocs_ecc_read_cx_out() - Read the CX data output buffer.
- * @dev: The OCS ECC device to read from.
- * @cx_out: The buffer where to store the CX value. Must be at least
- * @byte_count byte long.
- * @byte_count: The amount of data to read.
- */
- static inline void ocs_ecc_read_cx_out(struct ocs_ecc_dev *dev, void *cx_out,
- size_t byte_count)
- {
- memcpy_fromio(cx_out, dev->base_reg + HW_OFFS_OCS_ECC_CX_DATA_OUT,
- byte_count);
- }
- /**
- * ocs_ecc_read_cy_out() - Read the CX data output buffer.
- * @dev: The OCS ECC device to read from.
- * @cy_out: The buffer where to store the CY value. Must be at least
- * @byte_count byte long.
- * @byte_count: The amount of data to read.
- */
- static inline void ocs_ecc_read_cy_out(struct ocs_ecc_dev *dev, void *cy_out,
- size_t byte_count)
- {
- memcpy_fromio(cy_out, dev->base_reg + HW_OFFS_OCS_ECC_CY_DATA_OUT,
- byte_count);
- }
- static struct ocs_ecc_dev *kmb_ocs_ecc_find_dev(struct ocs_ecc_ctx *tctx)
- {
- if (tctx->ecc_dev)
- return tctx->ecc_dev;
- spin_lock(&ocs_ecc.lock);
- /* Only a single OCS device available. */
- tctx->ecc_dev = list_first_entry(&ocs_ecc.dev_list, struct ocs_ecc_dev,
- list);
- spin_unlock(&ocs_ecc.lock);
- return tctx->ecc_dev;
- }
- /* Do point multiplication using OCS ECC HW. */
- static int kmb_ecc_point_mult(struct ocs_ecc_dev *ecc_dev,
- struct ecc_point *result,
- const struct ecc_point *point,
- u64 *scalar,
- const struct ecc_curve *curve)
- {
- u8 sca[KMB_ECC_VLI_MAX_BYTES]; /* Use the maximum data size. */
- u32 op_size = (curve->g.ndigits > ECC_CURVE_NIST_P256_DIGITS) ?
- OCS_ECC_OP_SIZE_384 : OCS_ECC_OP_SIZE_256;
- size_t nbytes = digits_to_bytes(curve->g.ndigits);
- int rc = 0;
- /* Generate random nbytes for Simple and Differential SCA protection. */
- rc = crypto_get_default_rng();
- if (rc)
- return rc;
- rc = crypto_rng_get_bytes(crypto_default_rng, sca, nbytes);
- crypto_put_default_rng();
- if (rc)
- return rc;
- /* Wait engine to be idle before starting new operation. */
- rc = ocs_ecc_wait_idle(ecc_dev);
- if (rc)
- return rc;
- /* Send ecc_start pulse as well as indicating operation size. */
- ocs_ecc_cmd_start(ecc_dev, op_size);
- /* Write ax param; Base point (Gx). */
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AX,
- point->x, nbytes);
- /* Write ay param; Base point (Gy). */
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AY,
- point->y, nbytes);
- /*
- * Write the private key into DATA_IN reg.
- *
- * Since DATA_IN register is used to write different values during the
- * computation private Key value is overwritten with
- * side-channel-resistance value.
- */
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_BX_D,
- scalar, nbytes);
- /* Write operand by/l. */
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_BY_L,
- sca, nbytes);
- memzero_explicit(sca, sizeof(sca));
- /* Write p = curve prime(GF modulus). */
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_P,
- curve->p, nbytes);
- /* Write a = curve coefficient. */
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_A,
- curve->a, nbytes);
- /* Make hardware perform the multiplication. */
- rc = ocs_ecc_trigger_op(ecc_dev, op_size, OCS_ECC_INST_CALC_D_IDX_A);
- if (rc)
- return rc;
- /* Read result. */
- ocs_ecc_read_cx_out(ecc_dev, result->x, nbytes);
- ocs_ecc_read_cy_out(ecc_dev, result->y, nbytes);
- return 0;
- }
- /**
- * kmb_ecc_do_scalar_op() - Perform Scalar operation using OCS ECC HW.
- * @ecc_dev: The OCS ECC device to use.
- * @scalar_out: Where to store the output scalar.
- * @scalar_a: Input scalar operand 'a'.
- * @scalar_b: Input scalar operand 'b'
- * @curve: The curve on which the operation is performed.
- * @ndigits: The size of the operands (in digits).
- * @inst: The operation to perform (as an OCS ECC instruction).
- *
- * Return: 0 on success, negative error code otherwise.
- */
- static int kmb_ecc_do_scalar_op(struct ocs_ecc_dev *ecc_dev, u64 *scalar_out,
- const u64 *scalar_a, const u64 *scalar_b,
- const struct ecc_curve *curve,
- unsigned int ndigits, const u32 inst)
- {
- u32 op_size = (ndigits > ECC_CURVE_NIST_P256_DIGITS) ?
- OCS_ECC_OP_SIZE_384 : OCS_ECC_OP_SIZE_256;
- size_t nbytes = digits_to_bytes(ndigits);
- int rc;
- /* Wait engine to be idle before starting new operation. */
- rc = ocs_ecc_wait_idle(ecc_dev);
- if (rc)
- return rc;
- /* Send ecc_start pulse as well as indicating operation size. */
- ocs_ecc_cmd_start(ecc_dev, op_size);
- /* Write ax param (Base point (Gx).*/
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AX,
- scalar_a, nbytes);
- /* Write ay param Base point (Gy).*/
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_AY,
- scalar_b, nbytes);
- /* Write p = curve prime(GF modulus).*/
- ocs_ecc_write_cmd_and_data(ecc_dev, op_size, OCS_ECC_INST_WRITE_P,
- curve->p, nbytes);
- /* Give instruction A.B or A+B to ECC engine. */
- rc = ocs_ecc_trigger_op(ecc_dev, op_size, inst);
- if (rc)
- return rc;
- ocs_ecc_read_cx_out(ecc_dev, scalar_out, nbytes);
- if (vli_is_zero(scalar_out, ndigits))
- return -EINVAL;
- return 0;
- }
- /* SP800-56A section 5.6.2.3.4 partial verification: ephemeral keys only */
- static int kmb_ocs_ecc_is_pubkey_valid_partial(struct ocs_ecc_dev *ecc_dev,
- const struct ecc_curve *curve,
- struct ecc_point *pk)
- {
- u64 xxx[KMB_ECC_VLI_MAX_DIGITS] = { 0 };
- u64 yy[KMB_ECC_VLI_MAX_DIGITS] = { 0 };
- u64 w[KMB_ECC_VLI_MAX_DIGITS] = { 0 };
- int rc;
- if (WARN_ON(pk->ndigits != curve->g.ndigits))
- return -EINVAL;
- /* Check 1: Verify key is not the zero point. */
- if (ecc_point_is_zero(pk))
- return -EINVAL;
- /* Check 2: Verify key is in the range [0, p-1]. */
- if (vli_cmp(curve->p, pk->x, pk->ndigits) != 1)
- return -EINVAL;
- if (vli_cmp(curve->p, pk->y, pk->ndigits) != 1)
- return -EINVAL;
- /* Check 3: Verify that y^2 == (x^3 + a·x + b) mod p */
- /* y^2 */
- /* Compute y^2 -> store in yy */
- rc = kmb_ecc_do_scalar_op(ecc_dev, yy, pk->y, pk->y, curve, pk->ndigits,
- OCS_ECC_INST_CALC_A_MUL_B_MODP);
- if (rc)
- goto exit;
- /* x^3 */
- /* Assigning w = 3, used for calculating x^3. */
- w[0] = POW_CUBE;
- /* Load the next stage.*/
- rc = kmb_ecc_do_scalar_op(ecc_dev, xxx, pk->x, w, curve, pk->ndigits,
- OCS_ECC_INST_CALC_A_POW_B_MODP);
- if (rc)
- goto exit;
- /* Do a*x -> store in w. */
- rc = kmb_ecc_do_scalar_op(ecc_dev, w, curve->a, pk->x, curve,
- pk->ndigits,
- OCS_ECC_INST_CALC_A_MUL_B_MODP);
- if (rc)
- goto exit;
- /* Do ax + b == w + b; store in w. */
- rc = kmb_ecc_do_scalar_op(ecc_dev, w, w, curve->b, curve,
- pk->ndigits,
- OCS_ECC_INST_CALC_A_ADD_B_MODP);
- if (rc)
- goto exit;
- /* x^3 + ax + b == x^3 + w -> store in w. */
- rc = kmb_ecc_do_scalar_op(ecc_dev, w, xxx, w, curve, pk->ndigits,
- OCS_ECC_INST_CALC_A_ADD_B_MODP);
- if (rc)
- goto exit;
- /* Compare y^2 == x^3 + a·x + b. */
- rc = vli_cmp(yy, w, pk->ndigits);
- if (rc)
- rc = -EINVAL;
- exit:
- memzero_explicit(xxx, sizeof(xxx));
- memzero_explicit(yy, sizeof(yy));
- memzero_explicit(w, sizeof(w));
- return rc;
- }
- /* SP800-56A section 5.6.2.3.3 full verification */
- static int kmb_ocs_ecc_is_pubkey_valid_full(struct ocs_ecc_dev *ecc_dev,
- const struct ecc_curve *curve,
- struct ecc_point *pk)
- {
- struct ecc_point *nQ;
- int rc;
- /* Checks 1 through 3 */
- rc = kmb_ocs_ecc_is_pubkey_valid_partial(ecc_dev, curve, pk);
- if (rc)
- return rc;
- /* Check 4: Verify that nQ is the zero point. */
- nQ = ecc_alloc_point(pk->ndigits);
- if (!nQ)
- return -ENOMEM;
- rc = kmb_ecc_point_mult(ecc_dev, nQ, pk, curve->n, curve);
- if (rc)
- goto exit;
- if (!ecc_point_is_zero(nQ))
- rc = -EINVAL;
- exit:
- ecc_free_point(nQ);
- return rc;
- }
- static int kmb_ecc_is_key_valid(const struct ecc_curve *curve,
- const u64 *private_key, size_t private_key_len)
- {
- size_t ndigits = curve->g.ndigits;
- u64 one[KMB_ECC_VLI_MAX_DIGITS] = {1};
- u64 res[KMB_ECC_VLI_MAX_DIGITS];
- if (private_key_len != digits_to_bytes(ndigits))
- return -EINVAL;
- if (!private_key)
- return -EINVAL;
- /* Make sure the private key is in the range [2, n-3]. */
- if (vli_cmp(one, private_key, ndigits) != -1)
- return -EINVAL;
- vli_sub(res, curve->n, one, ndigits);
- vli_sub(res, res, one, ndigits);
- if (vli_cmp(res, private_key, ndigits) != 1)
- return -EINVAL;
- return 0;
- }
- /*
- * ECC private keys are generated using the method of extra random bits,
- * equivalent to that described in FIPS 186-4, Appendix B.4.1.
- *
- * d = (c mod(n–1)) + 1 where c is a string of random bits, 64 bits longer
- * than requested
- * 0 <= c mod(n-1) <= n-2 and implies that
- * 1 <= d <= n-1
- *
- * This method generates a private key uniformly distributed in the range
- * [1, n-1].
- */
- static int kmb_ecc_gen_privkey(const struct ecc_curve *curve, u64 *privkey)
- {
- size_t nbytes = digits_to_bytes(curve->g.ndigits);
- u64 priv[KMB_ECC_VLI_MAX_DIGITS];
- size_t nbits;
- int rc;
- nbits = vli_num_bits(curve->n, curve->g.ndigits);
- /* Check that N is included in Table 1 of FIPS 186-4, section 6.1.1 */
- if (nbits < 160 || curve->g.ndigits > ARRAY_SIZE(priv))
- return -EINVAL;
- /*
- * FIPS 186-4 recommends that the private key should be obtained from a
- * RBG with a security strength equal to or greater than the security
- * strength associated with N.
- *
- * The maximum security strength identified by NIST SP800-57pt1r4 for
- * ECC is 256 (N >= 512).
- *
- * This condition is met by the default RNG because it selects a favored
- * DRBG with a security strength of 256.
- */
- if (crypto_get_default_rng())
- return -EFAULT;
- rc = crypto_rng_get_bytes(crypto_default_rng, (u8 *)priv, nbytes);
- crypto_put_default_rng();
- if (rc)
- goto cleanup;
- rc = kmb_ecc_is_key_valid(curve, priv, nbytes);
- if (rc)
- goto cleanup;
- ecc_swap_digits(priv, privkey, curve->g.ndigits);
- cleanup:
- memzero_explicit(&priv, sizeof(priv));
- return rc;
- }
- static int kmb_ocs_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
- unsigned int len)
- {
- struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
- struct ecdh params;
- int rc = 0;
- rc = crypto_ecdh_decode_key(buf, len, ¶ms);
- if (rc)
- goto cleanup;
- /* Ensure key size is not bigger then expected. */
- if (params.key_size > digits_to_bytes(tctx->curve->g.ndigits)) {
- rc = -EINVAL;
- goto cleanup;
- }
- /* Auto-generate private key is not provided. */
- if (!params.key || !params.key_size) {
- rc = kmb_ecc_gen_privkey(tctx->curve, tctx->private_key);
- goto cleanup;
- }
- rc = kmb_ecc_is_key_valid(tctx->curve, (const u64 *)params.key,
- params.key_size);
- if (rc)
- goto cleanup;
- ecc_swap_digits((const u64 *)params.key, tctx->private_key,
- tctx->curve->g.ndigits);
- cleanup:
- memzero_explicit(¶ms, sizeof(params));
- if (rc)
- tctx->curve = NULL;
- return rc;
- }
- /* Compute shared secret. */
- static int kmb_ecc_do_shared_secret(struct ocs_ecc_ctx *tctx,
- struct kpp_request *req)
- {
- struct ocs_ecc_dev *ecc_dev = tctx->ecc_dev;
- const struct ecc_curve *curve = tctx->curve;
- u64 shared_secret[KMB_ECC_VLI_MAX_DIGITS];
- u64 pubk_buf[KMB_ECC_VLI_MAX_DIGITS * 2];
- size_t copied, nbytes, pubk_len;
- struct ecc_point *pk, *result;
- int rc;
- nbytes = digits_to_bytes(curve->g.ndigits);
- /* Public key is a point, thus it has two coordinates */
- pubk_len = 2 * nbytes;
- /* Copy public key from SG list to pubk_buf. */
- copied = sg_copy_to_buffer(req->src,
- sg_nents_for_len(req->src, pubk_len),
- pubk_buf, pubk_len);
- if (copied != pubk_len)
- return -EINVAL;
- /* Allocate and initialize public key point. */
- pk = ecc_alloc_point(curve->g.ndigits);
- if (!pk)
- return -ENOMEM;
- ecc_swap_digits(pubk_buf, pk->x, curve->g.ndigits);
- ecc_swap_digits(&pubk_buf[curve->g.ndigits], pk->y, curve->g.ndigits);
- /*
- * Check the public key for following
- * Check 1: Verify key is not the zero point.
- * Check 2: Verify key is in the range [1, p-1].
- * Check 3: Verify that y^2 == (x^3 + a·x + b) mod p
- */
- rc = kmb_ocs_ecc_is_pubkey_valid_partial(ecc_dev, curve, pk);
- if (rc)
- goto exit_free_pk;
- /* Allocate point for storing computed shared secret. */
- result = ecc_alloc_point(pk->ndigits);
- if (!result) {
- rc = -ENOMEM;
- goto exit_free_pk;
- }
- /* Calculate the shared secret.*/
- rc = kmb_ecc_point_mult(ecc_dev, result, pk, tctx->private_key, curve);
- if (rc)
- goto exit_free_result;
- if (ecc_point_is_zero(result)) {
- rc = -EFAULT;
- goto exit_free_result;
- }
- /* Copy shared secret from point to buffer. */
- ecc_swap_digits(result->x, shared_secret, result->ndigits);
- /* Request might ask for less bytes than what we have. */
- nbytes = min_t(size_t, nbytes, req->dst_len);
- copied = sg_copy_from_buffer(req->dst,
- sg_nents_for_len(req->dst, nbytes),
- shared_secret, nbytes);
- if (copied != nbytes)
- rc = -EINVAL;
- memzero_explicit(shared_secret, sizeof(shared_secret));
- exit_free_result:
- ecc_free_point(result);
- exit_free_pk:
- ecc_free_point(pk);
- return rc;
- }
- /* Compute public key. */
- static int kmb_ecc_do_public_key(struct ocs_ecc_ctx *tctx,
- struct kpp_request *req)
- {
- const struct ecc_curve *curve = tctx->curve;
- u64 pubk_buf[KMB_ECC_VLI_MAX_DIGITS * 2];
- struct ecc_point *pk;
- size_t pubk_len;
- size_t copied;
- int rc;
- /* Public key is a point, so it has double the digits. */
- pubk_len = 2 * digits_to_bytes(curve->g.ndigits);
- pk = ecc_alloc_point(curve->g.ndigits);
- if (!pk)
- return -ENOMEM;
- /* Public Key(pk) = priv * G. */
- rc = kmb_ecc_point_mult(tctx->ecc_dev, pk, &curve->g, tctx->private_key,
- curve);
- if (rc)
- goto exit;
- /* SP800-56A rev 3 5.6.2.1.3 key check */
- if (kmb_ocs_ecc_is_pubkey_valid_full(tctx->ecc_dev, curve, pk)) {
- rc = -EAGAIN;
- goto exit;
- }
- /* Copy public key from point to buffer. */
- ecc_swap_digits(pk->x, pubk_buf, pk->ndigits);
- ecc_swap_digits(pk->y, &pubk_buf[pk->ndigits], pk->ndigits);
- /* Copy public key to req->dst. */
- copied = sg_copy_from_buffer(req->dst,
- sg_nents_for_len(req->dst, pubk_len),
- pubk_buf, pubk_len);
- if (copied != pubk_len)
- rc = -EINVAL;
- exit:
- ecc_free_point(pk);
- return rc;
- }
- static int kmb_ocs_ecc_do_one_request(struct crypto_engine *engine,
- void *areq)
- {
- struct kpp_request *req = container_of(areq, struct kpp_request, base);
- struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req);
- struct ocs_ecc_dev *ecc_dev = tctx->ecc_dev;
- int rc;
- if (req->src)
- rc = kmb_ecc_do_shared_secret(tctx, req);
- else
- rc = kmb_ecc_do_public_key(tctx, req);
- crypto_finalize_kpp_request(ecc_dev->engine, req, rc);
- return 0;
- }
- static int kmb_ocs_ecdh_generate_public_key(struct kpp_request *req)
- {
- struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req);
- const struct ecc_curve *curve = tctx->curve;
- /* Ensure kmb_ocs_ecdh_set_secret() has been successfully called. */
- if (!tctx->curve)
- return -EINVAL;
- /* Ensure dst is present. */
- if (!req->dst)
- return -EINVAL;
- /* Check the request dst is big enough to hold the public key. */
- if (req->dst_len < (2 * digits_to_bytes(curve->g.ndigits)))
- return -EINVAL;
- /* 'src' is not supposed to be present when generate pubk is called. */
- if (req->src)
- return -EINVAL;
- return crypto_transfer_kpp_request_to_engine(tctx->ecc_dev->engine,
- req);
- }
- static int kmb_ocs_ecdh_compute_shared_secret(struct kpp_request *req)
- {
- struct ocs_ecc_ctx *tctx = kmb_ocs_ecc_tctx(req);
- const struct ecc_curve *curve = tctx->curve;
- /* Ensure kmb_ocs_ecdh_set_secret() has been successfully called. */
- if (!tctx->curve)
- return -EINVAL;
- /* Ensure dst is present. */
- if (!req->dst)
- return -EINVAL;
- /* Ensure src is present. */
- if (!req->src)
- return -EINVAL;
- /*
- * req->src is expected to the (other-side) public key, so its length
- * must be 2 * coordinate size (in bytes).
- */
- if (req->src_len != 2 * digits_to_bytes(curve->g.ndigits))
- return -EINVAL;
- return crypto_transfer_kpp_request_to_engine(tctx->ecc_dev->engine,
- req);
- }
- static int kmb_ecc_tctx_init(struct ocs_ecc_ctx *tctx, unsigned int curve_id)
- {
- memset(tctx, 0, sizeof(*tctx));
- tctx->ecc_dev = kmb_ocs_ecc_find_dev(tctx);
- if (IS_ERR(tctx->ecc_dev)) {
- pr_err("Failed to find the device : %ld\n",
- PTR_ERR(tctx->ecc_dev));
- return PTR_ERR(tctx->ecc_dev);
- }
- tctx->curve = ecc_get_curve(curve_id);
- if (!tctx->curve)
- return -EOPNOTSUPP;
- tctx->engine_ctx.op.prepare_request = NULL;
- tctx->engine_ctx.op.do_one_request = kmb_ocs_ecc_do_one_request;
- tctx->engine_ctx.op.unprepare_request = NULL;
- return 0;
- }
- static int kmb_ocs_ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)
- {
- struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
- return kmb_ecc_tctx_init(tctx, ECC_CURVE_NIST_P256);
- }
- static int kmb_ocs_ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)
- {
- struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
- return kmb_ecc_tctx_init(tctx, ECC_CURVE_NIST_P384);
- }
- static void kmb_ocs_ecdh_exit_tfm(struct crypto_kpp *tfm)
- {
- struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
- memzero_explicit(tctx->private_key, sizeof(*tctx->private_key));
- }
- static unsigned int kmb_ocs_ecdh_max_size(struct crypto_kpp *tfm)
- {
- struct ocs_ecc_ctx *tctx = kpp_tfm_ctx(tfm);
- /* Public key is made of two coordinates, so double the digits. */
- return digits_to_bytes(tctx->curve->g.ndigits) * 2;
- }
- static struct kpp_alg ocs_ecdh_p256 = {
- .set_secret = kmb_ocs_ecdh_set_secret,
- .generate_public_key = kmb_ocs_ecdh_generate_public_key,
- .compute_shared_secret = kmb_ocs_ecdh_compute_shared_secret,
- .init = kmb_ocs_ecdh_nist_p256_init_tfm,
- .exit = kmb_ocs_ecdh_exit_tfm,
- .max_size = kmb_ocs_ecdh_max_size,
- .base = {
- .cra_name = "ecdh-nist-p256",
- .cra_driver_name = "ecdh-nist-p256-keembay-ocs",
- .cra_priority = KMB_OCS_ECC_PRIORITY,
- .cra_module = THIS_MODULE,
- .cra_ctxsize = sizeof(struct ocs_ecc_ctx),
- },
- };
- static struct kpp_alg ocs_ecdh_p384 = {
- .set_secret = kmb_ocs_ecdh_set_secret,
- .generate_public_key = kmb_ocs_ecdh_generate_public_key,
- .compute_shared_secret = kmb_ocs_ecdh_compute_shared_secret,
- .init = kmb_ocs_ecdh_nist_p384_init_tfm,
- .exit = kmb_ocs_ecdh_exit_tfm,
- .max_size = kmb_ocs_ecdh_max_size,
- .base = {
- .cra_name = "ecdh-nist-p384",
- .cra_driver_name = "ecdh-nist-p384-keembay-ocs",
- .cra_priority = KMB_OCS_ECC_PRIORITY,
- .cra_module = THIS_MODULE,
- .cra_ctxsize = sizeof(struct ocs_ecc_ctx),
- },
- };
- static irqreturn_t ocs_ecc_irq_handler(int irq, void *dev_id)
- {
- struct ocs_ecc_dev *ecc_dev = dev_id;
- u32 status;
- /*
- * Read the status register and write it back to clear the
- * DONE_INT_STATUS bit.
- */
- status = ioread32(ecc_dev->base_reg + HW_OFFS_OCS_ECC_ISR);
- iowrite32(status, ecc_dev->base_reg + HW_OFFS_OCS_ECC_ISR);
- if (!(status & HW_OCS_ECC_ISR_INT_STATUS_DONE))
- return IRQ_NONE;
- complete(&ecc_dev->irq_done);
- return IRQ_HANDLED;
- }
- static int kmb_ocs_ecc_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct ocs_ecc_dev *ecc_dev;
- int rc;
- ecc_dev = devm_kzalloc(dev, sizeof(*ecc_dev), GFP_KERNEL);
- if (!ecc_dev)
- return -ENOMEM;
- ecc_dev->dev = dev;
- platform_set_drvdata(pdev, ecc_dev);
- INIT_LIST_HEAD(&ecc_dev->list);
- init_completion(&ecc_dev->irq_done);
- /* Get base register address. */
- ecc_dev->base_reg = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(ecc_dev->base_reg)) {
- dev_err(dev, "Failed to get base address\n");
- rc = PTR_ERR(ecc_dev->base_reg);
- goto list_del;
- }
- /* Get and request IRQ */
- ecc_dev->irq = platform_get_irq(pdev, 0);
- if (ecc_dev->irq < 0) {
- rc = ecc_dev->irq;
- goto list_del;
- }
- rc = devm_request_threaded_irq(dev, ecc_dev->irq, ocs_ecc_irq_handler,
- NULL, 0, "keembay-ocs-ecc", ecc_dev);
- if (rc < 0) {
- dev_err(dev, "Could not request IRQ\n");
- goto list_del;
- }
- /* Add device to the list of OCS ECC devices. */
- spin_lock(&ocs_ecc.lock);
- list_add_tail(&ecc_dev->list, &ocs_ecc.dev_list);
- spin_unlock(&ocs_ecc.lock);
- /* Initialize crypto engine. */
- ecc_dev->engine = crypto_engine_alloc_init(dev, 1);
- if (!ecc_dev->engine) {
- dev_err(dev, "Could not allocate crypto engine\n");
- rc = -ENOMEM;
- goto list_del;
- }
- rc = crypto_engine_start(ecc_dev->engine);
- if (rc) {
- dev_err(dev, "Could not start crypto engine\n");
- goto cleanup;
- }
- /* Register the KPP algo. */
- rc = crypto_register_kpp(&ocs_ecdh_p256);
- if (rc) {
- dev_err(dev,
- "Could not register OCS algorithms with Crypto API\n");
- goto cleanup;
- }
- rc = crypto_register_kpp(&ocs_ecdh_p384);
- if (rc) {
- dev_err(dev,
- "Could not register OCS algorithms with Crypto API\n");
- goto ocs_ecdh_p384_error;
- }
- return 0;
- ocs_ecdh_p384_error:
- crypto_unregister_kpp(&ocs_ecdh_p256);
- cleanup:
- crypto_engine_exit(ecc_dev->engine);
- list_del:
- spin_lock(&ocs_ecc.lock);
- list_del(&ecc_dev->list);
- spin_unlock(&ocs_ecc.lock);
- return rc;
- }
- static int kmb_ocs_ecc_remove(struct platform_device *pdev)
- {
- struct ocs_ecc_dev *ecc_dev;
- ecc_dev = platform_get_drvdata(pdev);
- crypto_unregister_kpp(&ocs_ecdh_p384);
- crypto_unregister_kpp(&ocs_ecdh_p256);
- spin_lock(&ocs_ecc.lock);
- list_del(&ecc_dev->list);
- spin_unlock(&ocs_ecc.lock);
- crypto_engine_exit(ecc_dev->engine);
- return 0;
- }
- /* Device tree driver match. */
- static const struct of_device_id kmb_ocs_ecc_of_match[] = {
- {
- .compatible = "intel,keembay-ocs-ecc",
- },
- {}
- };
- /* The OCS driver is a platform device. */
- static struct platform_driver kmb_ocs_ecc_driver = {
- .probe = kmb_ocs_ecc_probe,
- .remove = kmb_ocs_ecc_remove,
- .driver = {
- .name = DRV_NAME,
- .of_match_table = kmb_ocs_ecc_of_match,
- },
- };
- module_platform_driver(kmb_ocs_ecc_driver);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Intel Keem Bay OCS ECC Driver");
- MODULE_ALIAS_CRYPTO("ecdh-nist-p256");
- MODULE_ALIAS_CRYPTO("ecdh-nist-p384");
- MODULE_ALIAS_CRYPTO("ecdh-nist-p256-keembay-ocs");
- MODULE_ALIAS_CRYPTO("ecdh-nist-p384-keembay-ocs");
|