xref: /linux/net/bluetooth/ecdh_helper.c (revision 168ed65483a1777c2570f4c0a4a64e20a823cf25)
1 /*
2  * ECDH helper functions - KPP wrappings
3  *
4  * Copyright (C) 2017 Intel Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation;
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
11  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
14  * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
20  * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
21  * SOFTWARE IS DISCLAIMED.
22  */
23 #include "ecdh_helper.h"
24 
25 #include <linux/scatterlist.h>
26 #include <crypto/ecdh.h>
27 
28 struct ecdh_completion {
29 	struct completion completion;
30 	int err;
31 };
32 
33 static void ecdh_complete(struct crypto_async_request *req, int err)
34 {
35 	struct ecdh_completion *res = req->data;
36 
37 	if (err == -EINPROGRESS)
38 		return;
39 
40 	res->err = err;
41 	complete(&res->completion);
42 }
43 
44 static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits)
45 {
46 	int i;
47 
48 	for (i = 0; i < ndigits; i++)
49 		out[i] = __swab64(in[ndigits - 1 - i]);
50 }
51 
52 int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 public_key[64],
53 			const u8 private_key[32], u8 secret[32])
54 {
55 	struct kpp_request *req;
56 	struct ecdh p;
57 	struct ecdh_completion result;
58 	struct scatterlist src, dst;
59 	u8 *tmp, *buf;
60 	unsigned int buf_len;
61 	int err;
62 
63 	tmp = kmalloc(64, GFP_KERNEL);
64 	if (!tmp)
65 		return -ENOMEM;
66 
67 	req = kpp_request_alloc(tfm, GFP_KERNEL);
68 	if (!req) {
69 		err = -ENOMEM;
70 		goto free_tmp;
71 	}
72 
73 	init_completion(&result.completion);
74 
75 	/* Security Manager Protocol holds digits in litte-endian order
76 	 * while ECC API expect big-endian data
77 	 */
78 	swap_digits((u64 *)private_key, (u64 *)tmp, 4);
79 	p.key = (char *)tmp;
80 	p.key_size = 32;
81 	/* Set curve_id */
82 	p.curve_id = ECC_CURVE_NIST_P256;
83 	buf_len = crypto_ecdh_key_len(&p);
84 	buf = kmalloc(buf_len, GFP_KERNEL);
85 	if (!buf) {
86 		err = -ENOMEM;
87 		goto free_req;
88 	}
89 
90 	crypto_ecdh_encode_key(buf, buf_len, &p);
91 
92 	/* Set A private Key */
93 	err = crypto_kpp_set_secret(tfm, (void *)buf, buf_len);
94 	if (err)
95 		goto free_all;
96 
97 	swap_digits((u64 *)public_key, (u64 *)tmp, 4); /* x */
98 	swap_digits((u64 *)&public_key[32], (u64 *)&tmp[32], 4); /* y */
99 
100 	sg_init_one(&src, tmp, 64);
101 	sg_init_one(&dst, secret, 32);
102 	kpp_request_set_input(req, &src, 64);
103 	kpp_request_set_output(req, &dst, 32);
104 	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
105 				 ecdh_complete, &result);
106 	err = crypto_kpp_compute_shared_secret(req);
107 	if (err == -EINPROGRESS) {
108 		wait_for_completion(&result.completion);
109 		err = result.err;
110 	}
111 	if (err < 0) {
112 		pr_err("alg: ecdh: compute shared secret failed. err %d\n",
113 		       err);
114 		goto free_all;
115 	}
116 
117 	swap_digits((u64 *)secret, (u64 *)tmp, 4);
118 	memcpy(secret, tmp, 32);
119 
120 free_all:
121 	kzfree(buf);
122 free_req:
123 	kpp_request_free(req);
124 free_tmp:
125 	kzfree(tmp);
126 	return err;
127 }
128 
129 int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64],
130 		       u8 private_key[32])
131 {
132 	struct kpp_request *req;
133 	struct ecdh p;
134 	struct ecdh_completion result;
135 	struct scatterlist dst;
136 	u8 *tmp, *buf;
137 	unsigned int buf_len;
138 	int err;
139 	const unsigned short max_tries = 16;
140 	unsigned short tries = 0;
141 
142 	tmp = kmalloc(64, GFP_KERNEL);
143 	if (!tmp)
144 		return -ENOMEM;
145 
146 	req = kpp_request_alloc(tfm, GFP_KERNEL);
147 	if (!req) {
148 		err = -ENOMEM;
149 		goto free_tmp;
150 	}
151 
152 	init_completion(&result.completion);
153 
154 	/* Set curve_id */
155 	p.curve_id = ECC_CURVE_NIST_P256;
156 	p.key_size = 32;
157 	buf_len = crypto_ecdh_key_len(&p);
158 	buf = kmalloc(buf_len, GFP_KERNEL);
159 	if (!buf)
160 		goto free_req;
161 
162 	do {
163 		if (tries++ >= max_tries)
164 			goto free_all;
165 
166 		/* Set private Key */
167 		p.key = (char *)private_key;
168 		crypto_ecdh_encode_key(buf, buf_len, &p);
169 		err = crypto_kpp_set_secret(tfm, buf, buf_len);
170 		if (err)
171 			goto free_all;
172 
173 		sg_init_one(&dst, tmp, 64);
174 		kpp_request_set_input(req, NULL, 0);
175 		kpp_request_set_output(req, &dst, 64);
176 		kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
177 					 ecdh_complete, &result);
178 
179 		err = crypto_kpp_generate_public_key(req);
180 
181 		if (err == -EINPROGRESS) {
182 			wait_for_completion(&result.completion);
183 			err = result.err;
184 		}
185 
186 		/* Private key is not valid. Regenerate */
187 		if (err == -EINVAL)
188 			continue;
189 
190 		if (err < 0)
191 			goto free_all;
192 		else
193 			break;
194 
195 	} while (true);
196 
197 	/* Keys are handed back in little endian as expected by Security
198 	 * Manager Protocol
199 	 */
200 	swap_digits((u64 *)tmp, (u64 *)public_key, 4); /* x */
201 	swap_digits((u64 *)&tmp[32], (u64 *)&public_key[32], 4); /* y */
202 	swap_digits((u64 *)private_key, (u64 *)tmp, 4);
203 	memcpy(private_key, tmp, 32);
204 
205 free_all:
206 	kzfree(buf);
207 free_req:
208 	kpp_request_free(req);
209 free_tmp:
210 	kfree(tmp);
211 	return err;
212 }
213