xref: /openbmc/linux/net/bluetooth/ecdh_helper.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
158771c1cSSalvatore Benedetto /*
258771c1cSSalvatore Benedetto  * ECDH helper functions - KPP wrappings
358771c1cSSalvatore Benedetto  *
458771c1cSSalvatore Benedetto  * Copyright (C) 2017 Intel Corporation
558771c1cSSalvatore Benedetto  *
658771c1cSSalvatore Benedetto  * This program is free software; you can redistribute it and/or modify
758771c1cSSalvatore Benedetto  * it under the terms of the GNU General Public License version 2 as
858771c1cSSalvatore Benedetto  * published by the Free Software Foundation;
958771c1cSSalvatore Benedetto  *
1058771c1cSSalvatore Benedetto  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1158771c1cSSalvatore Benedetto  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1258771c1cSSalvatore Benedetto  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
1358771c1cSSalvatore Benedetto  * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
1458771c1cSSalvatore Benedetto  * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
1558771c1cSSalvatore Benedetto  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1658771c1cSSalvatore Benedetto  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1758771c1cSSalvatore Benedetto  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1858771c1cSSalvatore Benedetto  *
1958771c1cSSalvatore Benedetto  * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
2058771c1cSSalvatore Benedetto  * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
2158771c1cSSalvatore Benedetto  * SOFTWARE IS DISCLAIMED.
2258771c1cSSalvatore Benedetto  */
2358771c1cSSalvatore Benedetto #include "ecdh_helper.h"
2458771c1cSSalvatore Benedetto 
2558771c1cSSalvatore Benedetto #include <linux/scatterlist.h>
2658771c1cSSalvatore Benedetto #include <crypto/ecdh.h>
2758771c1cSSalvatore Benedetto 
swap_digits(u64 * in,u64 * out,unsigned int ndigits)2858771c1cSSalvatore Benedetto static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits)
2958771c1cSSalvatore Benedetto {
3058771c1cSSalvatore Benedetto 	int i;
3158771c1cSSalvatore Benedetto 
3258771c1cSSalvatore Benedetto 	for (i = 0; i < ndigits; i++)
3358771c1cSSalvatore Benedetto 		out[i] = __swab64(in[ndigits - 1 - i]);
3458771c1cSSalvatore Benedetto }
3558771c1cSSalvatore Benedetto 
36c0153b0bSTudor Ambarus /* compute_ecdh_secret() - function assumes that the private key was
37c0153b0bSTudor Ambarus  *                         already set.
38c0153b0bSTudor Ambarus  * @tfm:          KPP tfm handle allocated with crypto_alloc_kpp().
39c0153b0bSTudor Ambarus  * @public_key:   pair's ecc public key.
40c0153b0bSTudor Ambarus  * secret:        memory where the ecdh computed shared secret will be saved.
41c0153b0bSTudor Ambarus  *
42c0153b0bSTudor Ambarus  * Return: zero on success; error code in case of error.
43c0153b0bSTudor Ambarus  */
compute_ecdh_secret(struct crypto_kpp * tfm,const u8 public_key[64],u8 secret[32])44a2976416STudor Ambarus int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 public_key[64],
45c0153b0bSTudor Ambarus 			u8 secret[32])
4658771c1cSSalvatore Benedetto {
47*fe93d841SHerbert Xu 	DECLARE_CRYPTO_WAIT(result);
4858771c1cSSalvatore Benedetto 	struct kpp_request *req;
49c0153b0bSTudor Ambarus 	u8 *tmp;
5058771c1cSSalvatore Benedetto 	struct scatterlist src, dst;
51a2976416STudor Ambarus 	int err;
5258771c1cSSalvatore Benedetto 
53763d9a30SSalvatore Benedetto 	tmp = kmalloc(64, GFP_KERNEL);
54763d9a30SSalvatore Benedetto 	if (!tmp)
55a2976416STudor Ambarus 		return -ENOMEM;
56763d9a30SSalvatore Benedetto 
5758771c1cSSalvatore Benedetto 	req = kpp_request_alloc(tfm, GFP_KERNEL);
58a2976416STudor Ambarus 	if (!req) {
59a2976416STudor Ambarus 		err = -ENOMEM;
6047eb2ac8STudor Ambarus 		goto free_tmp;
61a2976416STudor Ambarus 	}
6258771c1cSSalvatore Benedetto 
6358771c1cSSalvatore Benedetto 	swap_digits((u64 *)public_key, (u64 *)tmp, 4); /* x */
6458771c1cSSalvatore Benedetto 	swap_digits((u64 *)&public_key[32], (u64 *)&tmp[32], 4); /* y */
6558771c1cSSalvatore Benedetto 
6658771c1cSSalvatore Benedetto 	sg_init_one(&src, tmp, 64);
6758771c1cSSalvatore Benedetto 	sg_init_one(&dst, secret, 32);
6858771c1cSSalvatore Benedetto 	kpp_request_set_input(req, &src, 64);
6958771c1cSSalvatore Benedetto 	kpp_request_set_output(req, &dst, 32);
7058771c1cSSalvatore Benedetto 	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
71*fe93d841SHerbert Xu 				 crypto_req_done, &result);
7258771c1cSSalvatore Benedetto 	err = crypto_kpp_compute_shared_secret(req);
73*fe93d841SHerbert Xu 	err = crypto_wait_req(err, &result);
7458771c1cSSalvatore Benedetto 	if (err < 0) {
7558771c1cSSalvatore Benedetto 		pr_err("alg: ecdh: compute shared secret failed. err %d\n",
7658771c1cSSalvatore Benedetto 		       err);
7758771c1cSSalvatore Benedetto 		goto free_all;
7858771c1cSSalvatore Benedetto 	}
7958771c1cSSalvatore Benedetto 
8058771c1cSSalvatore Benedetto 	swap_digits((u64 *)secret, (u64 *)tmp, 4);
8158771c1cSSalvatore Benedetto 	memcpy(secret, tmp, 32);
8258771c1cSSalvatore Benedetto 
8358771c1cSSalvatore Benedetto free_all:
8458771c1cSSalvatore Benedetto 	kpp_request_free(req);
85763d9a30SSalvatore Benedetto free_tmp:
86453431a5SWaiman Long 	kfree_sensitive(tmp);
87a2976416STudor Ambarus 	return err;
8858771c1cSSalvatore Benedetto }
8958771c1cSSalvatore Benedetto 
90c0153b0bSTudor Ambarus /* set_ecdh_privkey() - set or generate ecc private key.
91c0153b0bSTudor Ambarus  *
92c0153b0bSTudor Ambarus  * Function generates an ecc private key in the crypto subsystem when receiving
93c0153b0bSTudor Ambarus  * a NULL private key or sets the received key when not NULL.
94c0153b0bSTudor Ambarus  *
95c0153b0bSTudor Ambarus  * @tfm:           KPP tfm handle allocated with crypto_alloc_kpp().
96c0153b0bSTudor Ambarus  * @private_key:   user's ecc private key. When not NULL, the key is expected
97c0153b0bSTudor Ambarus  *                 in little endian format.
98c0153b0bSTudor Ambarus  *
99c0153b0bSTudor Ambarus  * Return: zero on success; error code in case of error.
100c0153b0bSTudor Ambarus  */
set_ecdh_privkey(struct crypto_kpp * tfm,const u8 private_key[32])101c0153b0bSTudor Ambarus int set_ecdh_privkey(struct crypto_kpp *tfm, const u8 private_key[32])
10258771c1cSSalvatore Benedetto {
103c0153b0bSTudor Ambarus 	u8 *buf, *tmp = NULL;
10458771c1cSSalvatore Benedetto 	unsigned int buf_len;
105a2976416STudor Ambarus 	int err;
106c0153b0bSTudor Ambarus 	struct ecdh p = {0};
107c0153b0bSTudor Ambarus 
108c0153b0bSTudor Ambarus 	if (private_key) {
109c0153b0bSTudor Ambarus 		tmp = kmalloc(32, GFP_KERNEL);
110c0153b0bSTudor Ambarus 		if (!tmp)
111c0153b0bSTudor Ambarus 			return -ENOMEM;
112c0153b0bSTudor Ambarus 		swap_digits((u64 *)private_key, (u64 *)tmp, 4);
113c0153b0bSTudor Ambarus 		p.key = tmp;
114c0153b0bSTudor Ambarus 		p.key_size = 32;
115c0153b0bSTudor Ambarus 	}
116c0153b0bSTudor Ambarus 
117c0153b0bSTudor Ambarus 	buf_len = crypto_ecdh_key_len(&p);
118c0153b0bSTudor Ambarus 	buf = kmalloc(buf_len, GFP_KERNEL);
119c0153b0bSTudor Ambarus 	if (!buf) {
120c0153b0bSTudor Ambarus 		err = -ENOMEM;
121c0153b0bSTudor Ambarus 		goto free_tmp;
122c0153b0bSTudor Ambarus 	}
123c0153b0bSTudor Ambarus 
124c0153b0bSTudor Ambarus 	err = crypto_ecdh_encode_key(buf, buf_len, &p);
125c0153b0bSTudor Ambarus 	if (err)
126c0153b0bSTudor Ambarus 		goto free_all;
127c0153b0bSTudor Ambarus 
128c0153b0bSTudor Ambarus 	err = crypto_kpp_set_secret(tfm, buf, buf_len);
129c0153b0bSTudor Ambarus 	/* fall through */
130c0153b0bSTudor Ambarus free_all:
131453431a5SWaiman Long 	kfree_sensitive(buf);
132c0153b0bSTudor Ambarus free_tmp:
133453431a5SWaiman Long 	kfree_sensitive(tmp);
134c0153b0bSTudor Ambarus 	return err;
135c0153b0bSTudor Ambarus }
136c0153b0bSTudor Ambarus 
137c0153b0bSTudor Ambarus /* generate_ecdh_public_key() - function assumes that the private key was
138c0153b0bSTudor Ambarus  *                              already set.
139c0153b0bSTudor Ambarus  *
140c0153b0bSTudor Ambarus  * @tfm:          KPP tfm handle allocated with crypto_alloc_kpp().
141c0153b0bSTudor Ambarus  * @public_key:   memory where the computed ecc public key will be saved.
142c0153b0bSTudor Ambarus  *
143c0153b0bSTudor Ambarus  * Return: zero on success; error code in case of error.
144c0153b0bSTudor Ambarus  */
generate_ecdh_public_key(struct crypto_kpp * tfm,u8 public_key[64])145c0153b0bSTudor Ambarus int generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64])
146c0153b0bSTudor Ambarus {
147*fe93d841SHerbert Xu 	DECLARE_CRYPTO_WAIT(result);
148c0153b0bSTudor Ambarus 	struct kpp_request *req;
149c0153b0bSTudor Ambarus 	u8 *tmp;
150c0153b0bSTudor Ambarus 	struct scatterlist dst;
151c0153b0bSTudor Ambarus 	int err;
15258771c1cSSalvatore Benedetto 
153763d9a30SSalvatore Benedetto 	tmp = kmalloc(64, GFP_KERNEL);
154763d9a30SSalvatore Benedetto 	if (!tmp)
155a2976416STudor Ambarus 		return -ENOMEM;
156763d9a30SSalvatore Benedetto 
15758771c1cSSalvatore Benedetto 	req = kpp_request_alloc(tfm, GFP_KERNEL);
158a2976416STudor Ambarus 	if (!req) {
159a2976416STudor Ambarus 		err = -ENOMEM;
16047eb2ac8STudor Ambarus 		goto free_tmp;
161a2976416STudor Ambarus 	}
16258771c1cSSalvatore Benedetto 
16358771c1cSSalvatore Benedetto 	sg_init_one(&dst, tmp, 64);
164f9583153SMarcel Holtmann 	kpp_request_set_input(req, NULL, 0);
16558771c1cSSalvatore Benedetto 	kpp_request_set_output(req, &dst, 64);
16658771c1cSSalvatore Benedetto 	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
167*fe93d841SHerbert Xu 				 crypto_req_done, &result);
16858771c1cSSalvatore Benedetto 
16958771c1cSSalvatore Benedetto 	err = crypto_kpp_generate_public_key(req);
170*fe93d841SHerbert Xu 	err = crypto_wait_req(err, &result);
17158771c1cSSalvatore Benedetto 	if (err < 0)
17258771c1cSSalvatore Benedetto 		goto free_all;
17358771c1cSSalvatore Benedetto 
174c0153b0bSTudor Ambarus 	/* The public key is handed back in little endian as expected by
175c0153b0bSTudor Ambarus 	 * the Security Manager Protocol.
17658771c1cSSalvatore Benedetto 	 */
17758771c1cSSalvatore Benedetto 	swap_digits((u64 *)tmp, (u64 *)public_key, 4); /* x */
17858771c1cSSalvatore Benedetto 	swap_digits((u64 *)&tmp[32], (u64 *)&public_key[32], 4); /* y */
17958771c1cSSalvatore Benedetto 
18058771c1cSSalvatore Benedetto free_all:
18158771c1cSSalvatore Benedetto 	kpp_request_free(req);
182763d9a30SSalvatore Benedetto free_tmp:
183763d9a30SSalvatore Benedetto 	kfree(tmp);
184a2976416STudor Ambarus 	return err;
18558771c1cSSalvatore Benedetto }
186c0153b0bSTudor Ambarus 
187c0153b0bSTudor Ambarus /* generate_ecdh_keys() - generate ecc key pair.
188c0153b0bSTudor Ambarus  *
189c0153b0bSTudor Ambarus  * @tfm:          KPP tfm handle allocated with crypto_alloc_kpp().
190c0153b0bSTudor Ambarus  * @public_key:   memory where the computed ecc public key will be saved.
191c0153b0bSTudor Ambarus  *
192c0153b0bSTudor Ambarus  * Return: zero on success; error code in case of error.
193c0153b0bSTudor Ambarus  */
generate_ecdh_keys(struct crypto_kpp * tfm,u8 public_key[64])194c0153b0bSTudor Ambarus int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64])
195c0153b0bSTudor Ambarus {
196c0153b0bSTudor Ambarus 	int err;
197c0153b0bSTudor Ambarus 
198c0153b0bSTudor Ambarus 	err = set_ecdh_privkey(tfm, NULL);
199c0153b0bSTudor Ambarus 	if (err)
200c0153b0bSTudor Ambarus 		return err;
201c0153b0bSTudor Ambarus 
202c0153b0bSTudor Ambarus 	return generate_ecdh_public_key(tfm, public_key);
203c0153b0bSTudor Ambarus }
204