]> Git Repo - u-boot.git/blob - lib/ecdsa/ecdsa-libcrypto.c
Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-sh
[u-boot.git] / lib / ecdsa / ecdsa-libcrypto.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * ECDSA image signing implementation using libcrypto backend
4  *
5  * The signature is a binary representation of the (R, S) points, padded to the
6  * key size. The signature will be (2 * key_size_bits) / 8 bytes.
7  *
8  * Deviations from behavior of RSA equivalent:
9  *  - Verification uses private key. This is not technically required, but a
10  *    limitation on how clumsy the openssl API is to use.
11  *  - Handling of keys and key paths:
12  *    - The '-K' key directory option must contain path to the key file,
13  *      instead of the key directory.
14  *    - No assumptions are made about the file extension of the key
15  *    - The 'key-name-hint' property is only used for naming devicetree nodes,
16  *      but is not used for looking up keys on the filesystem.
17  *
18  * Copyright (c) 2020,2021, Alexandru Gagniuc <[email protected]>
19  */
20
21 #define OPENSSL_API_COMPAT 0x10101000L
22
23 #include <u-boot/ecdsa.h>
24 #include <u-boot/fdt-libcrypto.h>
25 #include <openssl/ssl.h>
26 #include <openssl/ec.h>
27 #include <openssl/bn.h>
28
29 /* Image signing context for openssl-libcrypto */
30 struct signer {
31         EVP_PKEY *evp_key;      /* Pointer to EVP_PKEY object */
32         EC_KEY *ecdsa_key;      /* Pointer to EC_KEY object */
33         void *hash;             /* Pointer to hash used for verification */
34         void *signature;        /* Pointer to output signature. Do not free()!*/
35 };
36
37 static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
38 {
39         memset(ctx, 0, sizeof(*ctx));
40
41         if (!OPENSSL_init_ssl(0, NULL)) {
42                 fprintf(stderr, "Failure to init SSL library\n");
43                 return -1;
44         }
45
46         ctx->hash = malloc(info->checksum->checksum_len);
47         ctx->signature = malloc(info->crypto->key_len * 2);
48
49         if (!ctx->hash || !ctx->signature)
50                 return -ENOMEM;
51
52         return 0;
53 }
54
55 static void free_ctx(struct signer *ctx)
56 {
57         if (ctx->ecdsa_key)
58                 EC_KEY_free(ctx->ecdsa_key);
59
60         if (ctx->evp_key)
61                 EVP_PKEY_free(ctx->evp_key);
62
63         if (ctx->hash)
64                 free(ctx->hash);
65 }
66
67 /*
68  * Convert an ECDSA signature to raw format
69  *
70  * openssl DER-encodes 'binary' signatures. We want the signature in a raw
71  * (R, S) point pair. So we have to dance a bit.
72  */
73 static void ecdsa_sig_encode_raw(void *buf, const ECDSA_SIG *sig, size_t order)
74 {
75         int point_bytes = order;
76         const BIGNUM *r, *s;
77         uintptr_t s_buf;
78
79         ECDSA_SIG_get0(sig, &r, &s);
80         s_buf = (uintptr_t)buf + point_bytes;
81         BN_bn2binpad(r, buf, point_bytes);
82         BN_bn2binpad(s, (void *)s_buf, point_bytes);
83 }
84
85 /* Get a signature from a raw encoding */
86 static ECDSA_SIG *ecdsa_sig_from_raw(void *buf, size_t order)
87 {
88         int point_bytes = order;
89         uintptr_t s_buf;
90         ECDSA_SIG *sig;
91         BIGNUM *r, *s;
92
93         sig = ECDSA_SIG_new();
94         if (!sig)
95                 return NULL;
96
97         s_buf = (uintptr_t)buf + point_bytes;
98         r = BN_bin2bn(buf, point_bytes, NULL);
99         s = BN_bin2bn((void *)s_buf, point_bytes, NULL);
100         ECDSA_SIG_set0(sig, r, s);
101
102         return sig;
103 }
104
105 /* ECDSA key size in bytes */
106 static size_t ecdsa_key_size_bytes(const EC_KEY *key)
107 {
108         const EC_GROUP *group;
109
110         group = EC_KEY_get0_group(key);
111         return (EC_GROUP_order_bits(group) + 7) / 8;
112 }
113
114 static int default_password(char *buf, int size, int rwflag, void *u)
115 {
116         strncpy(buf, (char *)u, size);
117         buf[size - 1] = '\0';
118         return strlen(buf);
119 }
120
121 static int read_key(struct signer *ctx, const char *key_name)
122 {
123         FILE *f = fopen(key_name, "r");
124         const char *key_pass;
125
126         if (!f) {
127                 fprintf(stderr, "Can not get key file '%s'\n", key_name);
128                 return -ENOENT;
129         }
130
131         key_pass = getenv("MKIMAGE_SIGN_PASSWORD");
132         if (key_pass) {
133                 ctx->evp_key = PEM_read_PrivateKey(f, NULL, default_password, (void *)key_pass);
134
135         } else {
136                 ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
137         }
138         fclose(f);
139         if (!ctx->evp_key) {
140                 fprintf(stderr, "Can not read key from '%s'\n", key_name);
141                 return -EIO;
142         }
143
144         if (EVP_PKEY_id(ctx->evp_key) != EVP_PKEY_EC) {
145                 fprintf(stderr, "'%s' is not an ECDSA key\n", key_name);
146                 return -EINVAL;
147         }
148
149         ctx->ecdsa_key = EVP_PKEY_get1_EC_KEY(ctx->evp_key);
150         if (!ctx->ecdsa_key)
151                 fprintf(stderr, "Can not extract ECDSA key\n");
152
153         return (ctx->ecdsa_key) ? 0 : -EINVAL;
154 }
155
156 /* Prepare a 'signer' context that's ready to sign and verify. */
157 static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
158 {
159         int key_len_bytes, ret;
160         char kname[1024];
161
162         memset(ctx, 0, sizeof(*ctx));
163
164         if (info->keyfile) {
165                 snprintf(kname,  sizeof(kname), "%s", info->keyfile);
166         } else if (info->keydir && info->keyname) {
167                 snprintf(kname, sizeof(kname), "%s/%s.pem", info->keydir,
168                          info->keyname);
169         } else {
170                 fprintf(stderr, "keyfile, keyname, or key-name-hint missing\n");
171                 return -EINVAL;
172         }
173
174         ret = alloc_ctx(ctx, info);
175         if (ret)
176                 return ret;
177
178         ret = read_key(ctx, kname);
179         if (ret)
180                 return ret;
181
182         key_len_bytes = ecdsa_key_size_bytes(ctx->ecdsa_key);
183         if (key_len_bytes != info->crypto->key_len) {
184                 fprintf(stderr, "Expected a %u-bit key, got %u-bit key\n",
185                         info->crypto->key_len * 8, key_len_bytes * 8);
186                 return -EINVAL;
187         }
188
189         return 0;
190 }
191
192 static int do_sign(struct signer *ctx, struct image_sign_info *info,
193                    const struct image_region region[], int region_count)
194 {
195         const struct checksum_algo *algo = info->checksum;
196         ECDSA_SIG *sig;
197
198         algo->calculate(algo->name, region, region_count, ctx->hash);
199         sig = ECDSA_do_sign(ctx->hash, algo->checksum_len, ctx->ecdsa_key);
200
201         ecdsa_sig_encode_raw(ctx->signature, sig, info->crypto->key_len);
202
203         return 0;
204 }
205
206 static int ecdsa_check_signature(struct signer *ctx, struct image_sign_info *info)
207 {
208         ECDSA_SIG *sig;
209         int okay;
210
211         sig = ecdsa_sig_from_raw(ctx->signature, info->crypto->key_len);
212         if (!sig)
213                 return -ENOMEM;
214
215         okay = ECDSA_do_verify(ctx->hash, info->checksum->checksum_len,
216                                sig, ctx->ecdsa_key);
217         if (!okay)
218                 fprintf(stderr, "WARNING: Signature is fake news!\n");
219
220         ECDSA_SIG_free(sig);
221         return !okay;
222 }
223
224 static int do_verify(struct signer *ctx, struct image_sign_info *info,
225                      const struct image_region region[], int region_count,
226                      uint8_t *raw_sig, uint sig_len)
227 {
228         const struct checksum_algo *algo = info->checksum;
229
230         if (sig_len != info->crypto->key_len * 2) {
231                 fprintf(stderr, "Signature has wrong length\n");
232                 return -EINVAL;
233         }
234
235         memcpy(ctx->signature, raw_sig, sig_len);
236         algo->calculate(algo->name, region, region_count, ctx->hash);
237
238         return ecdsa_check_signature(ctx, info);
239 }
240
241 int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
242                int region_count, uint8_t **sigp, uint *sig_len)
243 {
244         struct signer ctx;
245         int ret;
246
247         ret = prepare_ctx(&ctx, info);
248         if (ret >= 0) {
249                 do_sign(&ctx, info, region, region_count);
250                 *sigp = ctx.signature;
251                 *sig_len = info->crypto->key_len * 2;
252
253                 ret = ecdsa_check_signature(&ctx, info);
254         }
255
256         free_ctx(&ctx);
257         return ret;
258 }
259
260 int ecdsa_verify(struct image_sign_info *info,
261                  const struct image_region region[], int region_count,
262                  uint8_t *sig, uint sig_len)
263 {
264         struct signer ctx;
265         int ret;
266
267         ret = prepare_ctx(&ctx, info);
268         if (ret >= 0)
269                 ret = do_verify(&ctx, info, region, region_count, sig, sig_len);
270
271         free_ctx(&ctx);
272         return ret;
273 }
274
275 static int do_add(struct signer *ctx, void *fdt, const char *key_node_name,
276                   struct image_sign_info *info)
277 {
278         int signature_node, key_node, ret, key_bits;
279         const char *curve_name;
280         const EC_GROUP *group;
281         const EC_POINT *point;
282         BIGNUM *x, *y;
283
284         signature_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
285         if (signature_node == -FDT_ERR_NOTFOUND) {
286                 signature_node = fdt_add_subnode(fdt, 0, FIT_SIG_NODENAME);
287                 if (signature_node < 0) {
288                         if (signature_node != -FDT_ERR_NOSPACE) {
289                                 fprintf(stderr, "Couldn't create signature node: %s\n",
290                                         fdt_strerror(signature_node));
291                         }
292                         return signature_node;
293                 }
294         } else if (signature_node < 0) {
295                 fprintf(stderr, "Cannot select keys signature_node: %s\n",
296                         fdt_strerror(signature_node));
297                 return signature_node;
298         }
299
300         /* Either create or overwrite the named key node */
301         key_node = fdt_subnode_offset(fdt, signature_node, key_node_name);
302         if (key_node == -FDT_ERR_NOTFOUND) {
303                 key_node = fdt_add_subnode(fdt, signature_node, key_node_name);
304                 if (key_node < 0) {
305                         if (key_node != -FDT_ERR_NOSPACE) {
306                                 fprintf(stderr, "Could not create key subnode: %s\n",
307                                         fdt_strerror(key_node));
308                         }
309                         return key_node;
310                 }
311         } else if (key_node < 0) {
312                 fprintf(stderr, "Cannot select keys key_node: %s\n",
313                         fdt_strerror(key_node));
314                 return key_node;
315         }
316
317         group = EC_KEY_get0_group(ctx->ecdsa_key);
318         key_bits = EC_GROUP_order_bits(group);
319         curve_name = OBJ_nid2sn(EC_GROUP_get_curve_name(group));
320         /* Let 'x' and 'y' memory leak by not BN_free()'ing them. */
321         x = BN_new();
322         y = BN_new();
323         point = EC_KEY_get0_public_key(ctx->ecdsa_key);
324         EC_POINT_get_affine_coordinates(group, point, x, y, NULL);
325
326         ret = fdt_setprop_string(fdt, key_node, FIT_KEY_HINT,
327                                  info->keyname);
328         if (ret < 0)
329                 return ret;
330
331         ret = fdt_setprop_string(fdt, key_node, "ecdsa,curve", curve_name);
332         if (ret < 0)
333                 return ret;
334
335         ret = fdt_add_bignum(fdt, key_node, "ecdsa,x-point", x, key_bits);
336         if (ret < 0)
337                 return ret;
338
339         ret = fdt_add_bignum(fdt, key_node, "ecdsa,y-point", y, key_bits);
340         if (ret < 0)
341                 return ret;
342
343         ret = fdt_setprop_string(fdt, key_node, FIT_ALGO_PROP,
344                                  info->name);
345         if (ret < 0)
346                 return ret;
347
348         ret = fdt_setprop_string(fdt, key_node, FIT_KEY_REQUIRED,
349                                  info->require_keys);
350         if (ret < 0)
351                 return ret;
352
353         return key_node;
354 }
355
356 int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt)
357 {
358         const char *fdt_key_name;
359         struct signer ctx;
360         int ret;
361
362         fdt_key_name = info->keyname ? info->keyname : "default-key";
363         ret = prepare_ctx(&ctx, info);
364         if (ret >= 0) {
365                 ret = do_add(&ctx, fdt, fdt_key_name, info);
366                 if (ret < 0)
367                         ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
368         }
369
370         free_ctx(&ctx);
371         return ret;
372 }
This page took 0.048928 seconds and 4 git commands to generate.