219 lines
3.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* bignum support for Rockchip crypto
*
* Copyright (c) 2020 Rockchip Electronics Co., Ltd.
*
* Author: Lin Jinhan <troy.lin@rock-chips.com>
*
*/
#include <linux/slab.h>
#include "rk_crypto_bignum.h"
#define DEFAULT_ENDIAN RK_BG_LITTILE_ENDIAN
#define BYTES2WORDS(bytes) (round_up((bytes), sizeof(u32)) / sizeof(u32))
#define WORDS2BYTES(words) ((words) * sizeof(u32))
#define RK_WORD_SIZE 32
static void rk_reverse_memcpy(void *dst, const void *src, u32 size)
{
char *_dst = (char *)dst, *_src = (char *)src;
u32 i;
if (!dst || !src || !size)
return;
for (i = 0; i < size; ++i)
_dst[size - i - 1] = _src[i];
}
struct rk_bignum *rk_bn_alloc(u32 max_size)
{
struct rk_bignum *bn;
bn = kzalloc(sizeof(*bn), GFP_KERNEL);
if (!bn)
return NULL;
bn->data = kzalloc(round_up(max_size, sizeof(u32)), GFP_KERNEL);
if (!bn->data) {
kfree(bn);
return NULL;
}
bn->n_words = BYTES2WORDS(max_size);
return bn;
}
void rk_bn_free(struct rk_bignum *bn)
{
if (!bn)
return;
if (bn->data) {
memset(bn->data, 0x00, WORDS2BYTES(bn->n_words));
kfree(bn->data);
}
kfree(bn);
}
int rk_bn_set_data(struct rk_bignum *bn, const u8 *data, u32 size, enum bignum_endian endian)
{
if (!bn || !data)
return -EINVAL;
if (BYTES2WORDS(size) > bn->n_words)
return -EINVAL;
if (endian == DEFAULT_ENDIAN)
memcpy(bn->data, data, size);
else
rk_reverse_memcpy(bn->data, data, size);
return 0;
}
int rk_bn_get_data(const struct rk_bignum *bn, u8 *data, u32 size, enum bignum_endian endian)
{
if (!bn || !data)
return -EINVAL;
if (size < WORDS2BYTES(bn->n_words))
return -EINVAL;
memset(data, 0x00, size);
if (endian == DEFAULT_ENDIAN)
memcpy(data + size - WORDS2BYTES(bn->n_words), bn->data, bn->n_words);
else
rk_reverse_memcpy(data + size - WORDS2BYTES(bn->n_words),
bn->data, WORDS2BYTES(bn->n_words));
return 0;
}
u32 rk_bn_get_size(const struct rk_bignum *bn)
{
if (!bn)
return 0;
return WORDS2BYTES(bn->n_words);
}
/*
* @brief Returns the index of the highest 1 in |bn|.
* @param bn: the point of input data bignum.
* @return The index starts at 0 for the least significant bit.
* If src == zero, it will return -1
*/
int rk_bn_highest_bit(const struct rk_bignum *bn)
{
u32 w;
u32 b;
if (!bn || !bn->data || !bn->n_words)
return -1;
w = bn->data[bn->n_words - 1];
for (b = 0; b < RK_WORD_SIZE; b++) {
w >>= 1;
if (w == 0)
break;
}
return (int)(bn->n_words - 1) * RK_WORD_SIZE + b;
}
void rk_ecc_free_point(struct rk_ecp_point *point)
{
if (!point)
return;
rk_bn_free(point->x);
rk_bn_free(point->y);
kfree(point);
}
struct rk_ecp_point *rk_ecc_alloc_point_zero(u32 max_size)
{
struct rk_ecp_point *point = NULL;
point = kzalloc(sizeof(*point), GFP_KERNEL);
if (!point)
return NULL;
point->x = rk_bn_alloc(max_size);
if (!point->x)
goto error;
point->y = rk_bn_alloc(max_size);
if (!point->y)
goto error;
return point;
error:
rk_ecc_free_point(point);
return NULL;
}
struct rk_ecp_point *rk_ecc_alloc_point(const uint8_t *x, uint32_t x_len,
const uint8_t *y, uint32_t y_len,
enum bignum_endian endian, u32 max_size)
{
struct rk_ecp_point *point = NULL;
point = kzalloc(sizeof(*point), GFP_KERNEL);
if (!point)
return NULL;
point->x = rk_bn_alloc(max_size);
if (!point->x)
goto error;
if (rk_bn_set_data(point->x, x, x_len, endian) != 0)
goto error;
point->y = rk_bn_alloc(max_size);
if (!point->y)
goto error;
if (rk_bn_set_data(point->y, y, y_len, endian) != 0)
goto error;
return point;
error:
rk_ecc_free_point(point);
return NULL;
}
bool rk_ecp_point_is_zero(struct rk_ecp_point *point)
{
uint32_t i;
bool ret = true;
if (!point || !point->x || !point->y)
return false;
for (i = 0; i < point->x->n_words; i++) {
if (point->x->data[i] != 0)
ret = false;
}
for (i = 0; i < point->y->n_words; i++) {
if (point->y->data[i] != 0)
ret = false;
}
return ret;
}