|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/pem.h>
|
|
|
|
#include <openssl/x509.h>
|
|
|
|
#include <openssl/x509v3.h>
|
|
|
|
|
|
|
|
#include <image.h>
|
|
|
|
#include <macros.h>
|
|
|
|
#include <sign.h>
|
|
|
|
#include <x509.h>
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OPTION_HELP = 'h',
|
|
|
|
OPTION_IMAGE = 'i',
|
|
|
|
OPTION_DIGEST = 'd',
|
|
|
|
OPTION_KEY = 'k',
|
|
|
|
OPTION_CERT = 'c',
|
|
|
|
};
|
|
|
|
|
|
|
|
struct args {
|
|
|
|
const char *image, *digest, *key, *cert;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int parse_args(struct args *args, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct option options[] = {
|
|
|
|
{ "help", no_argument, NULL, OPTION_HELP },
|
|
|
|
{ "image", required_argument, 0, OPTION_IMAGE },
|
|
|
|
{ "digest", required_argument, 0, OPTION_DIGEST },
|
|
|
|
{ "key", required_argument, 0, OPTION_KEY },
|
|
|
|
{ "cert", required_argument, 0, OPTION_CERT },
|
|
|
|
{ NULL, 0, 0, 0 },
|
|
|
|
};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
while ((ret = getopt_long(argc, (char * const *)argv, "hi:d:k:", options,
|
|
|
|
NULL)) >= 0) {
|
|
|
|
switch (ret) {
|
|
|
|
case OPTION_HELP: return -1;
|
|
|
|
case OPTION_IMAGE: args->image = optarg; break;
|
|
|
|
case OPTION_DIGEST: args->digest = optarg; break;
|
|
|
|
case OPTION_KEY: args->key = optarg; break;
|
|
|
|
case OPTION_CERT: args->cert = optarg; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static EVP_PKEY *open_priv_key(const char *path)
|
|
|
|
{
|
|
|
|
EVP_PKEY *key;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
if (!(fp = fopen(path, "r")))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
PEM_read_PrivateKey(fp, &key, NULL, NULL);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sign(const char *image, const char *name, const char *digest_name,
|
|
|
|
EVP_PKEY *key)
|
|
|
|
{
|
|
|
|
char data[512];
|
|
|
|
struct rots_hdr hdr;
|
|
|
|
struct rots_sig_hdr sig_hdr;
|
|
|
|
const EVP_MD *digest;
|
|
|
|
FILE *fp;
|
|
|
|
unsigned char *sig;
|
|
|
|
EVP_MD_CTX *ctx = NULL;
|
|
|
|
size_t nbytes, size, sig_len = 0;
|
|
|
|
|
|
|
|
if (!image || !digest_name || !key)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(fp = fopen(image, "r+b")))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (rots_read_hdr(fp, &hdr) < 0)
|
|
|
|
goto err_close_image;
|
|
|
|
|
|
|
|
size = hdr.size + ftell(fp);
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
|
|
|
|
if (!(ctx = EVP_MD_CTX_create()))
|
|
|
|
goto err_close_image;
|
|
|
|
|
|
|
|
if (!(digest = EVP_get_digestbyname(digest_name)))
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
if (!(EVP_DigestSignInit(ctx, NULL, digest, NULL, key)))
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
nbytes = fread(data, sizeof *data, min(size, sizeof data), fp);
|
|
|
|
|
|
|
|
if (nbytes == 0)
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
if (!(EVP_DigestSignUpdate(ctx, data, nbytes)))
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
size -= nbytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
sig_hdr.timestamp = (uint64_t)time(NULL);
|
|
|
|
|
|
|
|
if (!(EVP_DigestSignUpdate(ctx, &sig_hdr.timestamp,
|
|
|
|
sizeof sig_hdr.timestamp)))
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
if (!(EVP_DigestSignFinal(ctx, NULL, &sig_len)))
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
if (!(sig = malloc(sizeof(char) * sig_len)))
|
|
|
|
goto err_destroy_ctx;
|
|
|
|
|
|
|
|
if (!(EVP_DigestSignFinal(ctx, sig, &sig_len)))
|
|
|
|
goto err_free_sig;
|
|
|
|
|
|
|
|
if (fseek(fp, 0, SEEK_END) < 0)
|
|
|
|
goto err_free_sig;
|
|
|
|
|
|
|
|
sig_hdr.name = name;
|
|
|
|
sig_hdr.digest = digest_name;
|
|
|
|
sig_hdr.size = sig_len;
|
|
|
|
|
|
|
|
if (rots_write_sig_hdr(fp, &sig_hdr) < 0)
|
|
|
|
goto err_free_sig;
|
|
|
|
|
|
|
|
if (fwrite(sig, sizeof *sig, sig_len, fp) < sig_len)
|
|
|
|
goto err_free_sig;
|
|
|
|
|
|
|
|
free(sig);
|
|
|
|
EVP_MD_CTX_destroy(ctx);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_free_sig:
|
|
|
|
free(sig);
|
|
|
|
err_destroy_ctx:
|
|
|
|
EVP_MD_CTX_destroy(ctx);
|
|
|
|
err_close_image:
|
|
|
|
fclose(fp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_sign(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
struct args args;
|
|
|
|
EVP_PKEY *key;
|
|
|
|
X509 *cert;
|
|
|
|
char *cn;
|
|
|
|
|
|
|
|
if (parse_args(&args, argc, argv) < 0) {
|
|
|
|
fprintf(stderr, "invalid\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
|
|
|
|
if (!(key = open_priv_key(args.key))) {
|
|
|
|
fprintf(stderr, "error: unable to read the private key.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cert = X509_open_cert(args.cert))) {
|
|
|
|
fprintf(stderr, "error: unable to read the X509 certificate.\n");
|
|
|
|
OPENSSL_free(key);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cn = X509_get_common_name(cert))) {
|
|
|
|
fprintf(stderr, "error: unable to get the common name.\n");
|
|
|
|
OPENSSL_free(cert);
|
|
|
|
OPENSSL_free(key);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sign(args.image, cn, args.digest, key) < 0) {
|
|
|
|
fprintf(stderr, "error: unable to sign the payload.\n");
|
|
|
|
OPENSSL_free(cn);
|
|
|
|
OPENSSL_free(cert);
|
|
|
|
OPENSSL_free(key);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
OPENSSL_free(cn);
|
|
|
|
OPENSSL_free(cert);
|
|
|
|
OPENSSL_free(key);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|