2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
43 #include <openssl/conf.h>
44 #include <openssl/evp.h>
45 #include <openssl/err.h>
46 #include <openssl/pem.h>
47 #include <openssl/pkcs7.h>
56 fprintf(stderr, "usage: uefisign -c cert -k key -o outfile [-v] file\n"
57 " uefisign -V [-c cert] [-v] file\n");
62 checked_strdup(const char *s)
73 checked_fopen(const char *path, const char *mode)
79 fp = fopen(path, mode);
86 send_chunk(const void *buf, size_t len, int pipefd)
90 ret = write(pipefd, &len, sizeof(len));
91 if (ret != sizeof(len))
93 ret = write(pipefd, buf, len);
94 if (ret != (ssize_t)len)
99 receive_chunk(void **bufp, size_t *lenp, int pipefd)
105 ret = read(pipefd, &len, sizeof(len));
106 if (ret != sizeof(len))
109 buf = calloc(1, len);
113 ret = read(pipefd, buf, len);
114 if (ret != (ssize_t)len)
122 bin2hex(const char *bin, size_t bin_len)
124 unsigned char *hex, *tmp, ch;
128 hex_len = bin_len * 2 + 1; /* +1 for '\0'. */
129 hex = malloc(hex_len);
134 for (i = 0; i < bin_len; i++) {
136 tmp += sprintf(tmp, "%02x", ch);
143 * We need to replace a standard chunk of PKCS7 signature with one mandated
144 * by Authenticode. Problem is, replacing it just like that and then calling
145 * PKCS7_final() would make OpenSSL segfault somewhere in PKCS7_dataFinal().
146 * So, instead, we call PKCS7_dataInit(), then put our Authenticode-specific
147 * data into BIO it returned, then call PKCS7_dataFinal() - which now somehow
148 * does not panic - and _then_ we replace it in the signature. This technique
149 * was used in sbsigntool by Jeremy Kerr, and might have originated in
153 magic(PKCS7 *pkcs7, const char *digest, size_t digest_len)
159 unsigned char *buf, *tmp;
160 char *digest_hex, *magic_conf, *str;
163 digest_hex = bin2hex(digest, digest_len);
166 * Construct the SpcIndirectDataContent chunk.
168 nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL);
170 asprintf(&magic_conf, magic_fmt, digest_hex);
171 if (magic_conf == NULL)
174 bio = BIO_new_mem_buf((void *)magic_conf, -1);
176 ERR_print_errors_fp(stderr);
177 errx(1, "BIO_new_mem_buf(3) failed");
180 cnf = NCONF_new(NULL);
182 ERR_print_errors_fp(stderr);
183 errx(1, "NCONF_new(3) failed");
186 ok = NCONF_load_bio(cnf, bio, NULL);
188 ERR_print_errors_fp(stderr);
189 errx(1, "NCONF_load_bio(3) failed");
192 str = NCONF_get_string(cnf, "default", "asn1");
194 ERR_print_errors_fp(stderr);
195 errx(1, "NCONF_get_string(3) failed");
198 t = ASN1_generate_nconf(str, cnf);
200 ERR_print_errors_fp(stderr);
201 errx(1, "ASN1_generate_nconf(3) failed");
205 * We now have our proprietary piece of ASN.1. Let's do
206 * the actual signing.
208 len = i2d_ASN1_TYPE(t, NULL);
209 tmp = buf = calloc(1, len);
212 i2d_ASN1_TYPE(t, &tmp);
215 * We now have contents of 't' stuffed into memory buffer 'buf'.
220 t_bio = PKCS7_dataInit(pkcs7, NULL);
222 ERR_print_errors_fp(stderr);
223 errx(1, "PKCS7_dataInit(3) failed");
226 BIO_write(t_bio, buf + 2, len - 2);
228 ok = PKCS7_dataFinal(pkcs7, t_bio);
230 ERR_print_errors_fp(stderr);
231 errx(1, "PKCS7_dataFinal(3) failed");
235 s = ASN1_STRING_new();
236 ASN1_STRING_set(s, buf, len);
237 ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s);
239 PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t);
243 sign(X509 *cert, EVP_PKEY *key, int pipefd)
248 PKCS7_SIGNER_INFO *info;
249 void *digest, *signature;
250 size_t digest_len, signature_len;
253 assert(cert != NULL);
256 receive_chunk(&digest, &digest_len, pipefd);
258 bio = BIO_new_mem_buf(digest, digest_len);
260 ERR_print_errors_fp(stderr);
261 errx(1, "BIO_new_mem_buf(3) failed");
264 pkcs7 = PKCS7_sign(NULL, NULL, NULL, bio, PKCS7_BINARY | PKCS7_PARTIAL);
266 ERR_print_errors_fp(stderr);
267 errx(1, "PKCS7_sign(3) failed");
270 md = EVP_get_digestbyname(DIGEST);
272 ERR_print_errors_fp(stderr);
273 errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST);
276 info = PKCS7_sign_add_signer(pkcs7, cert, key, md, 0);
278 ERR_print_errors_fp(stderr);
279 errx(1, "PKCS7_sign_add_signer(3) failed");
283 * XXX: All the signed binaries seem to have this, but where is it
284 * described in the spec?
286 PKCS7_add_signed_attribute(info, NID_pkcs9_contentType,
287 V_ASN1_OBJECT, OBJ_txt2obj("1.3.6.1.4.1.311.2.1.4", 1));
289 magic(pkcs7, digest, digest_len);
292 out = BIO_new(BIO_s_file());
293 BIO_set_fp(out, stdout, BIO_NOCLOSE);
294 PKCS7_print_ctx(out, pkcs7, 0, NULL);
296 i2d_PKCS7_bio(out, pkcs7);
299 out = BIO_new(BIO_s_mem());
301 ERR_print_errors_fp(stderr);
302 errx(1, "BIO_new(3) failed");
305 ok = i2d_PKCS7_bio(out, pkcs7);
307 ERR_print_errors_fp(stderr);
308 errx(1, "i2d_PKCS7_bio(3) failed");
311 signature_len = BIO_get_mem_data(out, &signature);
312 if (signature_len <= 0) {
313 ERR_print_errors_fp(stderr);
314 errx(1, "BIO_get_mem_data(3) failed");
317 (void)BIO_set_close(out, BIO_NOCLOSE);
320 send_chunk(signature, signature_len, pipefd);
324 wait_for_child(pid_t pid)
328 pid = waitpid(pid, &status, 0);
332 return (WEXITSTATUS(status));
336 main(int argc, char **argv)
339 bool Vflag = false, vflag = false;
340 const char *certpath = NULL, *keypath = NULL, *outpath = NULL, *inpath = NULL;
341 FILE *certfp = NULL, *keyfp = NULL;
343 EVP_PKEY *key = NULL;
347 while ((ch = getopt(argc, argv, "Vc:k:o:v")) != -1) {
353 if (certpath == NULL)
354 certpath = checked_strdup(optarg);
356 err(1, "-c can only be specified once");
360 keypath = checked_strdup(optarg);
362 err(1, "-k can only be specified once");
366 outpath = checked_strdup(optarg);
368 err(1, "-o can only be specified once");
384 if (certpath != NULL)
385 errx(1, "-V and -c are mutually exclusive");
387 errx(1, "-V and -k are mutually exclusive");
389 errx(1, "-V and -o are mutually exclusive");
391 if (certpath == NULL)
392 errx(1, "-c option is mandatory");
394 errx(1, "-k option is mandatory");
396 errx(1, "-o option is mandatory");
401 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG |
402 OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
403 OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
405 error = pipe(pipefds);
415 exit(child(inpath, outpath, pipefds[1], Vflag, vflag));
421 certfp = checked_fopen(certpath, "r");
422 cert = PEM_read_X509(certfp, NULL, NULL, NULL);
424 ERR_print_errors_fp(stderr);
425 errx(1, "failed to load certificate from %s", certpath);
428 keyfp = checked_fopen(keypath, "r");
429 key = PEM_read_PrivateKey(keyfp, NULL, NULL, NULL);
431 ERR_print_errors_fp(stderr);
432 errx(1, "failed to load private key from %s", keypath);
435 sign(cert, key, pipefds[0]);
438 exit(wait_for_child(pid));