blob: 8a5468a22123c4802b5aaf7021e6066b27164699 [file] [log] [blame]
/*
* Copyright 2012 <James.Bottomley@HansenPartnership.com>
*
* see COPYING file
*/
#include <stdint.h>
#define _XOPEN_SOURCE
#include <efi.h>
#ifdef CONFIG_arm
/* FIXME:
* arm efi leaves a visibilit pragma pushed that won't work for
* non efi programs, so eliminate it */
#pragma GCC visibility pop
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <wchar.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <guid.h>
#include <variables.h>
#include <version.h>
static void
usage(const char *progname)
{
printf("Usage: %s [-g <guid>][-t <timestamp>][-s <hash>] <crt file> <efi sig list file>\n", progname);
}
static void
help(const char * progname)
{
usage(progname);
printf("Take an input X509 certificate (in PEM format) and convert it to an EFI\n"
"signature hash list file containing only that single certificate\n\n"
"Options:\n"
"\t-g <guid> Use <guid> as the owner of the signature. If this is not\n"
"\t supplied, an all zero guid will be used\n"
"\t-s <hash> Use SHA<hash> hash algorithm (256, 384, 512)\n"
"\t-t <timestamp> Time of Revocation for hash signature\n"
" Set to 0 if not specified meaning revoke\n"
" for all time.\n"
);
}
int
main(int argc, char *argv[])
{
char *certfile, *efifile;
const char *progname = argv[0];
EFI_GUID owner = { 0 };
int sha = 256;
EFI_TIME timestamp;
char *timestampstr = NULL;
memset(&timestamp, 0, sizeof(timestamp));
while (argc > 1) {
if (strcmp("--version", argv[1]) == 0) {
version(progname);
exit(0);
} else if (strcmp("--help", argv[1]) == 0) {
help(progname);
exit(0);
} else if (strcmp("-g", argv[1]) == 0) {
str_to_guid(argv[2], &owner);
argv += 2;
argc -= 2;
} else if (strcmp("-s", argv[1]) == 0) {
sha = atoi(argv[2]);
argv += 2;
argc -= 2;
} else if (strcmp("-t", argv[1]) == 0) {
timestampstr = argv[2];
argv += 2;
argc -= 2;
} else {
break;
}
}
if (argc != 3) {
usage(progname);
exit(1);
}
if (sha != 256 && sha != 384 && sha != 512) {
fprintf(stderr, "Supported algorithms are sha256, sha384 or sha512\n");
exit(1);
}
if (timestampstr) {
struct tm tms;
strptime(timestampstr, "%Y-%m-%d %H:%M:%S", &tms);
/* timestamp.Year is from 0 not 1900 as tm year is */
tms.tm_year += 1900;
timestamp.Year = tms.tm_year;
timestamp.Month = tms.tm_mon + 1;
timestamp.Day = tms.tm_mday;
timestamp.Hour = tms.tm_hour;
timestamp.Minute = tms.tm_min;
timestamp.Second = tms.tm_sec;
}
certfile = argv[1];
efifile = argv[2];
printf("TimeOfRevocation is %d-%d-%d %02d:%02d:%02d\n", timestamp.Year,
timestamp.Month, timestamp.Day, timestamp.Hour, timestamp.Minute,
timestamp.Second);
ERR_load_crypto_strings();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
/* here we may get highly unlikely failures or we'll get a
* complaint about FIPS signatures (usually becuase the FIPS
* module isn't present). In either case ignore the errors
* (malloc will cause other failures out lower down */
ERR_clear_error();
BIO *cert_bio = BIO_new_file(certfile, "r");
X509 *cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL);
unsigned char *cert_buf = NULL;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
int cert_len = i2d_X509_CINF(cert->cert_info, &cert_buf);
#else
int cert_len = i2d_re_X509_tbs(cert, &cert_buf);
#endif
ERR_print_errors_fp(stdout);
int len, digest_len, time_offset;
EFI_GUID guid;
const EVP_MD *md;
if (sha == 256) {
len = sizeof(EFI_CERT_X509_SHA256);
digest_len = sizeof(EFI_SHA256_HASH);
guid = EFI_CERT_X509_SHA256_GUID;
md = EVP_get_digestbyname("SHA256");
time_offset = OFFSET_OF(EFI_CERT_X509_SHA256, TimeOfRevocation);
} else if (sha == 384) {
len = sizeof(EFI_CERT_X509_SHA384);
digest_len = sizeof(EFI_SHA384_HASH);
guid = EFI_CERT_X509_SHA384_GUID;
md = EVP_get_digestbyname("SHA384");
time_offset = OFFSET_OF(EFI_CERT_X509_SHA384, TimeOfRevocation);
} else if (sha == 512) {
len = sizeof(EFI_CERT_X509_SHA512);
digest_len = sizeof(EFI_SHA512_HASH);
guid = EFI_CERT_X509_SHA512_GUID;
md = EVP_get_digestbyname("SHA512");
time_offset = OFFSET_OF(EFI_CERT_X509_SHA512, TimeOfRevocation);
} else {
fprintf(stderr, "assertion failure sha%d\n", sha);
exit(1);
}
len += sizeof(EFI_SIGNATURE_LIST) + OFFSET_OF(EFI_SIGNATURE_DATA, SignatureData);
unsigned char *buf = malloc(len);
EFI_SIGNATURE_LIST *SigList = (EFI_SIGNATURE_LIST *)buf;
SigList->SignatureListSize = len;
SigList->SignatureSize = (UINT32)(len - sizeof(EFI_SIGNATURE_LIST));
SigList->SignatureHeaderSize = 0;
SigList->SignatureType = guid;
EFI_SIGNATURE_DATA *SigData = (void *)buf + sizeof(EFI_SIGNATURE_LIST);
SigData->SignatureOwner = owner;
/* point buf at hash buffer */
unsigned char *digest = (void *)SigData + OFFSET_OF(EFI_SIGNATURE_DATA, SignatureData);
EFI_TIME *TimeOfRevocation = (void *)digest + time_offset;
*TimeOfRevocation = timestamp;
EVP_MD_CTX *ctx;
unsigned int md_len;
ctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(ctx, md, NULL);
EVP_DigestUpdate(ctx, cert_buf, cert_len);
EVP_DigestFinal_ex(ctx, digest, &md_len);
EVP_MD_CTX_destroy(ctx);
if (digest_len != md_len) {
fprintf(stderr, "Digest assertion failure sha%d %d != %d\n",
sha, digest_len, md_len);
exit(1);
}
FILE *f = fopen(efifile, "w");
if (!f) {
fprintf(stderr, "failed to open efi file %s: ", efifile);
perror("");
exit(1);
}
if (fwrite(buf, 1, len, f) != len) {
perror("Did not write enough bytes to efi file");
exit(1);
}
return 0;
}