brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · 590ea98 Raw
416 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Microchip / Atmel ECC (I2C) driver.4 *5 * Copyright (c) 2017, Microchip Technology Inc.6 * Author: Tudor Ambarus7 */8 9#include <linux/delay.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/errno.h>13#include <linux/i2c.h>14#include <linux/init.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/of.h>18#include <linux/scatterlist.h>19#include <linux/slab.h>20#include <linux/workqueue.h>21#include <crypto/internal/kpp.h>22#include <crypto/ecdh.h>23#include <crypto/kpp.h>24#include "atmel-i2c.h"25 26static struct atmel_ecc_driver_data driver_data;27 28/**29 * struct atmel_ecdh_ctx - transformation context30 * @client     : pointer to i2c client device31 * @fallback   : used for unsupported curves or when user wants to use its own32 *               private key.33 * @public_key : generated when calling set_secret(). It's the responsibility34 *               of the user to not call set_secret() while35 *               generate_public_key() or compute_shared_secret() are in flight.36 * @curve_id   : elliptic curve id37 * @do_fallback: true when the device doesn't support the curve or when the user38 *               wants to use its own private key.39 */40struct atmel_ecdh_ctx {41	struct i2c_client *client;42	struct crypto_kpp *fallback;43	const u8 *public_key;44	unsigned int curve_id;45	bool do_fallback;46};47 48static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq,49			    int status)50{51	struct kpp_request *req = areq;52	struct atmel_i2c_cmd *cmd = &work_data->cmd;53	size_t copied, n_sz;54 55	if (status)56		goto free_work_data;57 58	/* might want less than we've got */59	n_sz = min_t(size_t, ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len);60 61	/* copy the shared secret */62	copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, n_sz),63				     &cmd->data[RSP_DATA_IDX], n_sz);64	if (copied != n_sz)65		status = -EINVAL;66 67	/* fall through */68free_work_data:69	kfree_sensitive(work_data);70	kpp_request_complete(req, status);71}72 73/*74 * A random private key is generated and stored in the device. The device75 * returns the pair public key.76 */77static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,78				 unsigned int len)79{80	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);81	struct atmel_i2c_cmd *cmd;82	void *public_key;83	struct ecdh params;84	int ret = -ENOMEM;85 86	/* free the old public key, if any */87	kfree(ctx->public_key);88	/* make sure you don't free the old public key twice */89	ctx->public_key = NULL;90 91	if (crypto_ecdh_decode_key(buf, len, &params) < 0) {92		dev_err(&ctx->client->dev, "crypto_ecdh_decode_key failed\n");93		return -EINVAL;94	}95 96	if (params.key_size) {97		/* fallback to ecdh software implementation */98		ctx->do_fallback = true;99		return crypto_kpp_set_secret(ctx->fallback, buf, len);100	}101 102	cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);103	if (!cmd)104		return -ENOMEM;105 106	/*107	 * The device only supports NIST P256 ECC keys. The public key size will108	 * always be the same. Use a macro for the key size to avoid unnecessary109	 * computations.110	 */111	public_key = kmalloc(ATMEL_ECC_PUBKEY_SIZE, GFP_KERNEL);112	if (!public_key)113		goto free_cmd;114 115	ctx->do_fallback = false;116 117	atmel_i2c_init_genkey_cmd(cmd, DATA_SLOT_2);118 119	ret = atmel_i2c_send_receive(ctx->client, cmd);120	if (ret)121		goto free_public_key;122 123	/* save the public key */124	memcpy(public_key, &cmd->data[RSP_DATA_IDX], ATMEL_ECC_PUBKEY_SIZE);125	ctx->public_key = public_key;126 127	kfree(cmd);128	return 0;129 130free_public_key:131	kfree(public_key);132free_cmd:133	kfree(cmd);134	return ret;135}136 137static int atmel_ecdh_generate_public_key(struct kpp_request *req)138{139	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);140	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);141	size_t copied, nbytes;142	int ret = 0;143 144	if (ctx->do_fallback) {145		kpp_request_set_tfm(req, ctx->fallback);146		return crypto_kpp_generate_public_key(req);147	}148 149	if (!ctx->public_key)150		return -EINVAL;151 152	/* might want less than we've got */153	nbytes = min_t(size_t, ATMEL_ECC_PUBKEY_SIZE, req->dst_len);154 155	/* public key was saved at private key generation */156	copied = sg_copy_from_buffer(req->dst,157				     sg_nents_for_len(req->dst, nbytes),158				     ctx->public_key, nbytes);159	if (copied != nbytes)160		ret = -EINVAL;161 162	return ret;163}164 165static int atmel_ecdh_compute_shared_secret(struct kpp_request *req)166{167	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);168	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);169	struct atmel_i2c_work_data *work_data;170	gfp_t gfp;171	int ret;172 173	if (ctx->do_fallback) {174		kpp_request_set_tfm(req, ctx->fallback);175		return crypto_kpp_compute_shared_secret(req);176	}177 178	/* must have exactly two points to be on the curve */179	if (req->src_len != ATMEL_ECC_PUBKEY_SIZE)180		return -EINVAL;181 182	gfp = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ? GFP_KERNEL :183							     GFP_ATOMIC;184 185	work_data = kmalloc(sizeof(*work_data), gfp);186	if (!work_data)187		return -ENOMEM;188 189	work_data->ctx = ctx;190	work_data->client = ctx->client;191 192	ret = atmel_i2c_init_ecdh_cmd(&work_data->cmd, req->src);193	if (ret)194		goto free_work_data;195 196	atmel_i2c_enqueue(work_data, atmel_ecdh_done, req);197 198	return -EINPROGRESS;199 200free_work_data:201	kfree(work_data);202	return ret;203}204 205static struct i2c_client *atmel_ecc_i2c_client_alloc(void)206{207	struct atmel_i2c_client_priv *i2c_priv, *min_i2c_priv = NULL;208	struct i2c_client *client = ERR_PTR(-ENODEV);209	int min_tfm_cnt = INT_MAX;210	int tfm_cnt;211 212	spin_lock(&driver_data.i2c_list_lock);213 214	if (list_empty(&driver_data.i2c_client_list)) {215		spin_unlock(&driver_data.i2c_list_lock);216		return ERR_PTR(-ENODEV);217	}218 219	list_for_each_entry(i2c_priv, &driver_data.i2c_client_list,220			    i2c_client_list_node) {221		tfm_cnt = atomic_read(&i2c_priv->tfm_count);222		if (tfm_cnt < min_tfm_cnt) {223			min_tfm_cnt = tfm_cnt;224			min_i2c_priv = i2c_priv;225		}226		if (!min_tfm_cnt)227			break;228	}229 230	if (min_i2c_priv) {231		atomic_inc(&min_i2c_priv->tfm_count);232		client = min_i2c_priv->client;233	}234 235	spin_unlock(&driver_data.i2c_list_lock);236 237	return client;238}239 240static void atmel_ecc_i2c_client_free(struct i2c_client *client)241{242	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);243 244	atomic_dec(&i2c_priv->tfm_count);245}246 247static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)248{249	const char *alg = kpp_alg_name(tfm);250	struct crypto_kpp *fallback;251	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);252 253	ctx->curve_id = ECC_CURVE_NIST_P256;254	ctx->client = atmel_ecc_i2c_client_alloc();255	if (IS_ERR(ctx->client)) {256		pr_err("tfm - i2c_client binding failed\n");257		return PTR_ERR(ctx->client);258	}259 260	fallback = crypto_alloc_kpp(alg, 0, CRYPTO_ALG_NEED_FALLBACK);261	if (IS_ERR(fallback)) {262		dev_err(&ctx->client->dev, "Failed to allocate transformation for '%s': %ld\n",263			alg, PTR_ERR(fallback));264		return PTR_ERR(fallback);265	}266 267	crypto_kpp_set_flags(fallback, crypto_kpp_get_flags(tfm));268	ctx->fallback = fallback;269 270	return 0;271}272 273static void atmel_ecdh_exit_tfm(struct crypto_kpp *tfm)274{275	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);276 277	kfree(ctx->public_key);278	crypto_free_kpp(ctx->fallback);279	atmel_ecc_i2c_client_free(ctx->client);280}281 282static unsigned int atmel_ecdh_max_size(struct crypto_kpp *tfm)283{284	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);285 286	if (ctx->fallback)287		return crypto_kpp_maxsize(ctx->fallback);288 289	/*290	 * The device only supports NIST P256 ECC keys. The public key size will291	 * always be the same. Use a macro for the key size to avoid unnecessary292	 * computations.293	 */294	return ATMEL_ECC_PUBKEY_SIZE;295}296 297static struct kpp_alg atmel_ecdh_nist_p256 = {298	.set_secret = atmel_ecdh_set_secret,299	.generate_public_key = atmel_ecdh_generate_public_key,300	.compute_shared_secret = atmel_ecdh_compute_shared_secret,301	.init = atmel_ecdh_init_tfm,302	.exit = atmel_ecdh_exit_tfm,303	.max_size = atmel_ecdh_max_size,304	.base = {305		.cra_flags = CRYPTO_ALG_NEED_FALLBACK,306		.cra_name = "ecdh-nist-p256",307		.cra_driver_name = "atmel-ecdh",308		.cra_priority = ATMEL_ECC_PRIORITY,309		.cra_module = THIS_MODULE,310		.cra_ctxsize = sizeof(struct atmel_ecdh_ctx),311	},312};313 314static int atmel_ecc_probe(struct i2c_client *client)315{316	struct atmel_i2c_client_priv *i2c_priv;317	int ret;318 319	ret = atmel_i2c_probe(client);320	if (ret)321		return ret;322 323	i2c_priv = i2c_get_clientdata(client);324 325	spin_lock(&driver_data.i2c_list_lock);326	list_add_tail(&i2c_priv->i2c_client_list_node,327		      &driver_data.i2c_client_list);328	spin_unlock(&driver_data.i2c_list_lock);329 330	ret = crypto_register_kpp(&atmel_ecdh_nist_p256);331	if (ret) {332		spin_lock(&driver_data.i2c_list_lock);333		list_del(&i2c_priv->i2c_client_list_node);334		spin_unlock(&driver_data.i2c_list_lock);335 336		dev_err(&client->dev, "%s alg registration failed\n",337			atmel_ecdh_nist_p256.base.cra_driver_name);338	} else {339		dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");340	}341 342	return ret;343}344 345static void atmel_ecc_remove(struct i2c_client *client)346{347	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);348 349	/* Return EBUSY if i2c client already allocated. */350	if (atomic_read(&i2c_priv->tfm_count)) {351		/*352		 * After we return here, the memory backing the device is freed.353		 * That happens no matter what the return value of this function354		 * is because in the Linux device model there is no error355		 * handling for unbinding a driver.356		 * If there is still some action pending, it probably involves357		 * accessing the freed memory.358		 */359		dev_emerg(&client->dev, "Device is busy, expect memory corruption.\n");360		return;361	}362 363	crypto_unregister_kpp(&atmel_ecdh_nist_p256);364 365	spin_lock(&driver_data.i2c_list_lock);366	list_del(&i2c_priv->i2c_client_list_node);367	spin_unlock(&driver_data.i2c_list_lock);368}369 370#ifdef CONFIG_OF371static const struct of_device_id atmel_ecc_dt_ids[] = {372	{373		.compatible = "atmel,atecc508a",374	}, {375		/* sentinel */376	}377};378MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids);379#endif380 381static const struct i2c_device_id atmel_ecc_id[] = {382	{ "atecc508a", 0 },383	{ }384};385MODULE_DEVICE_TABLE(i2c, atmel_ecc_id);386 387static struct i2c_driver atmel_ecc_driver = {388	.driver = {389		.name	= "atmel-ecc",390		.of_match_table = of_match_ptr(atmel_ecc_dt_ids),391	},392	.probe		= atmel_ecc_probe,393	.remove		= atmel_ecc_remove,394	.id_table	= atmel_ecc_id,395};396 397static int __init atmel_ecc_init(void)398{399	spin_lock_init(&driver_data.i2c_list_lock);400	INIT_LIST_HEAD(&driver_data.i2c_client_list);401	return i2c_add_driver(&atmel_ecc_driver);402}403 404static void __exit atmel_ecc_exit(void)405{406	atmel_i2c_flush_queue();407	i2c_del_driver(&atmel_ecc_driver);408}409 410module_init(atmel_ecc_init);411module_exit(atmel_ecc_exit);412 413MODULE_AUTHOR("Tudor Ambarus");414MODULE_DESCRIPTION("Microchip / Atmel ECC (I2C) driver");415MODULE_LICENSE("GPL v2");416