/* Global variable holding the list of OCS ECC devices (only one expected). */ staticstruct 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. */ staticinlinestruct 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. */ staticinlineunsignedint digits_to_bytes(unsignedint n)
{ return n << ECC_DIGITS_TO_BYTES_SHIFT;
}
/* * Wait for ECC idle i.e when an operation (other than write operations) * is done.
*/ staticinlineint ocs_ecc_wait_idle(struct ocs_ecc_dev *dev)
{
u32 value;
/** * 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.
*/ staticinlinevoid 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.
*/ staticinlinevoid 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);
}
staticstruct 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. */ staticint kmb_ecc_point_mult(struct ocs_ecc_dev *ecc_dev, struct ecc_point *result, conststruct ecc_point *point,
u64 *scalar, conststruct 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);
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) gotoexit;
/* 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) gotoexit;
/* 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) gotoexit;
/* 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) gotoexit;
/* 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) gotoexit;
/* Compare y^2 == x^3 + a·x + b. */
rc = vli_cmp(yy, w, pk->ndigits); if (rc)
rc = -EINVAL;
/* * 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].
*/ staticint kmb_ecc_gen_privkey(conststruct 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;
/* 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;
/* * 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);
/* 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);
/* 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;
/* * 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;
/* 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;
}
/* 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.