commit
5494d66eeb
@ -0,0 +1,45 @@ |
||||
BUILD ?= build
|
||||
|
||||
all: $(BUILD)/rots-util |
||||
|
||||
CFLAGS += -Iinclude
|
||||
CFLAGS += -Wall -Wundef -Wextra -Wshadow -Wimplicit-function-declaration
|
||||
CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
LIBS = -lcrypto -lssl
|
||||
CC = gcc
|
||||
LD = gcc
|
||||
|
||||
obj-y += source/file.o
|
||||
obj-y += source/image.o
|
||||
obj-y += source/main.o
|
||||
obj-y += source/pack.o
|
||||
obj-y += source/sign.o
|
||||
obj-y += source/unpack.o
|
||||
obj-y += source/verify.o
|
||||
obj-y += source/x509.o
|
||||
|
||||
obj = $(addprefix $(BUILD)/, $(obj-y))
|
||||
|
||||
# Include the dependencies.
|
||||
-include $(obj:.o=.d) |
||||
|
||||
# Set up the toolchain.
|
||||
.SECONDARY: |
||||
|
||||
clean: |
||||
@echo "CLEAN"
|
||||
@rm -rf $(BUILD)
|
||||
|
||||
# Rule to compile C source code.
|
||||
$(BUILD)/%.o: %.c |
||||
@echo "CC $<"
|
||||
@mkdir -p $(dir $@)
|
||||
@$(CC) -c $< -o $@ $(CFLAGS) -MT $@ -MMD -MP -MF $(@:.o=.d)
|
||||
|
||||
$(BUILD)/rots-util: $(obj) $(LDSCRIPT) |
||||
@echo "LD $@"
|
||||
@mkdir -p $(dir $@)
|
||||
@$(LD) -o $@ $(CFLAGS) $(LDFLAGS) $(obj) $(LIBS)
|
||||
|
||||
.PHONY: clean |
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
int get_file_size(size_t *size, const char *path); |
@ -0,0 +1,29 @@ |
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
|
||||
#define ROTS_MAGIC "ROTS-IMG" |
||||
|
||||
struct rots_hdr { |
||||
uint64_t size; |
||||
}; |
||||
|
||||
struct rots_sig_hdr { |
||||
char *name; |
||||
char *digest; |
||||
uint32_t size; |
||||
}; |
||||
|
||||
size_t read_u8(FILE *fp, uint8_t *val); |
||||
size_t read_u32(FILE *fp, uint32_t *val); |
||||
size_t read_u64(FILE *fp, uint64_t *val); |
||||
size_t write_u8(FILE *fp, uint8_t val); |
||||
size_t write_u32(FILE *fp, uint32_t val); |
||||
size_t write_u64(FILE *fp, uint64_t val); |
||||
|
||||
int rots_read_hdr(FILE *fp, struct rots_hdr *hdr); |
||||
int rots_write_hdr(FILE *fp, struct rots_hdr *hdr); |
||||
int rots_read_sig_hdr(FILE *fp, struct rots_sig_hdr *sig_hdr); |
||||
int rots_write_sig_hdr(FILE *fp, struct rots_sig_hdr *sig_hdr); |
||||
void rots_cleanup_sig_hdr(struct rots_sig_hdr *sig_hdr); |
@ -0,0 +1,4 @@ |
||||
#pragma once |
||||
|
||||
#define min(x, y) (((x) < (y)) ? (x) : (y)) |
||||
#define max(x, y) (((x) > (y)) ? (x) : (y)) |
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
int do_pack(int argc, char *argv[]); |
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
int do_sign(int argc, char *argv[]); |
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
int do_unpack(int argc, char *argv[]); |
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
int do_verify(int argc, char *argv[]); |
@ -0,0 +1,9 @@ |
||||
#pragma once |
||||
|
||||
#include <openssl/evp.h> |
||||
#include <openssl/pem.h> |
||||
#include <openssl/x509.h> |
||||
#include <openssl/x509v3.h> |
||||
|
||||
X509 *X509_open_cert(const char *path); |
||||
char *X509_get_common_name(X509 *cert); |
@ -0,0 +1,21 @@ |
||||
#include <unistd.h> |
||||
|
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
|
||||
#include <file.h> |
||||
|
||||
int get_file_size(size_t *size, const char *path) |
||||
{ |
||||
struct stat st; |
||||
|
||||
if (!size || !path) |
||||
return -1; |
||||
|
||||
if (stat(path, &st) < 0) |
||||
return -1; |
||||
|
||||
*size = st.st_size; |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,205 @@ |
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <image.h> |
||||
|
||||
size_t read_u8(FILE *fp, uint8_t *val) |
||||
{ |
||||
int byte; |
||||
size_t shift = 8, nbytes; |
||||
|
||||
*val = 0; |
||||
|
||||
for (nbytes = 0; nbytes < sizeof *val; ++nbytes) { |
||||
if ((byte = fgetc(fp)) == EOF) |
||||
return nbytes; |
||||
|
||||
shift -= 8; |
||||
*val |= (byte & 0xff) << shift; |
||||
} |
||||
|
||||
return nbytes; |
||||
} |
||||
|
||||
size_t read_u32(FILE *fp, uint32_t *val) |
||||
{ |
||||
int byte; |
||||
size_t shift = 32, nbytes; |
||||
|
||||
*val = 0; |
||||
|
||||
for (nbytes = 0; nbytes < sizeof *val; ++nbytes) { |
||||
if ((byte = fgetc(fp)) == EOF) |
||||
return nbytes; |
||||
|
||||
shift -= 8; |
||||
*val |= (byte & 0xff) << shift; |
||||
} |
||||
|
||||
return nbytes; |
||||
} |
||||
|
||||
size_t read_u64(FILE *fp, uint64_t *val) |
||||
{ |
||||
int byte; |
||||
size_t shift = 64, nbytes; |
||||
|
||||
*val = 0; |
||||
|
||||
for (nbytes = 0; nbytes < sizeof *val; ++nbytes) { |
||||
if ((byte = fgetc(fp)) == EOF) |
||||
return nbytes; |
||||
|
||||
shift -= 8; |
||||
*val |= (byte & 0xff) << shift; |
||||
} |
||||
|
||||
return nbytes; |
||||
} |
||||
|
||||
size_t write_u8(FILE *fp, uint8_t val) |
||||
{ |
||||
size_t shift = 8, nbytes; |
||||
|
||||
for (nbytes = 0; nbytes < sizeof val; ++nbytes) { |
||||
shift -= 8; |
||||
|
||||
if (fputc((val >> shift) & 0xff, fp) == EOF) |
||||
return nbytes; |
||||
} |
||||
|
||||
return nbytes; |
||||
} |
||||
|
||||
size_t write_u32(FILE *fp, uint32_t val) |
||||
{ |
||||
size_t shift = 32, nbytes; |
||||
|
||||
for (nbytes = 0; nbytes < sizeof val; ++nbytes) { |
||||
shift -= 8; |
||||
|
||||
if (fputc((val >> shift) & 0xff, fp) == EOF) |
||||
return nbytes; |
||||
} |
||||
|
||||
return nbytes; |
||||
} |
||||
|
||||
size_t write_u64(FILE *fp, uint64_t val) |
||||
{ |
||||
size_t shift = 64, nbytes; |
||||
|
||||
for (nbytes = 0; nbytes < sizeof val; ++nbytes) { |
||||
shift -= 8; |
||||
|
||||
if (fputc((val >> shift) & 0xff, fp) == EOF) |
||||
return nbytes; |
||||
} |
||||
|
||||
return nbytes; |
||||
} |
||||
|
||||
int rots_read_hdr(FILE *fp, struct rots_hdr *hdr) |
||||
{ |
||||
char magic[8]; |
||||
|
||||
if (fread(magic, sizeof *magic, sizeof magic, fp) < sizeof magic) |
||||
return -1; |
||||
|
||||
if (memcmp(magic, ROTS_MAGIC, 8) != 0) |
||||
return -1; |
||||
|
||||
if (read_u64(fp, &hdr->size) < sizeof hdr->size) |
||||
return -1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int rots_write_hdr(FILE *fp, struct rots_hdr *hdr) |
||||
{ |
||||
if (fwrite(ROTS_MAGIC, sizeof(char), 8, fp) < 8) |
||||
return -1; |
||||
|
||||
if (write_u64(fp, hdr->size) < sizeof hdr->size) |
||||
return -1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int rots_read_sig_hdr(FILE *fp, struct rots_sig_hdr *sig_hdr) |
||||
{ |
||||
uint8_t len; |
||||
|
||||
if (read_u8(fp, &len) < 1) |
||||
return -1; |
||||
|
||||
if (!(sig_hdr->name = calloc(len + 1, sizeof *sig_hdr->name))) |
||||
return -1; |
||||
|
||||
if (fread(sig_hdr->name, sizeof *sig_hdr->name, len, fp) < len) |
||||
goto err_free_name; |
||||
|
||||
if (read_u8(fp, &len) < 1) |
||||
goto err_free_name; |
||||
|
||||
if (!(sig_hdr->digest = calloc(len + 1, sizeof *sig_hdr->digest))) |
||||
goto err_free_name; |
||||
|
||||
if (fread(sig_hdr->digest, sizeof *sig_hdr->digest, len, fp) < len) |
||||
goto err_free_digest; |
||||
|
||||
if (read_u32(fp, &sig_hdr->size) < sizeof sig_hdr->size) |
||||
goto err_free_digest; |
||||
|
||||
return 0; |
||||
|
||||
err_free_digest: |
||||
free(sig_hdr->digest); |
||||
err_free_name: |
||||
free(sig_hdr->name); |
||||
return -1; |
||||
} |
||||
|
||||
int rots_write_sig_hdr(FILE *fp, struct rots_sig_hdr *sig_hdr) |
||||
{ |
||||
size_t len; |
||||
|
||||
len = strlen(sig_hdr->name); |
||||
|
||||
if (write_u8(fp, len) < 1) |
||||
return -1; |
||||
|
||||
if (fwrite(sig_hdr->name, sizeof *sig_hdr->name, len, fp) < len) |
||||
return -1; |
||||
|
||||
len = strlen(sig_hdr->digest); |
||||
|
||||
if (write_u8(fp, len) < 1) |
||||
return -1; |
||||
|
||||
if (fwrite(sig_hdr->digest, sizeof *sig_hdr->digest, len, fp) < len) |
||||
return -1; |
||||
|
||||
if (write_u32(fp, sig_hdr->size) < sizeof sig_hdr->size) |
||||
return -1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void rots_cleanup_sig_hdr(struct rots_sig_hdr *sig_hdr) |
||||
{ |
||||
if (!sig_hdr) |
||||
return; |
||||
|
||||
if (sig_hdr->name) { |
||||
free(sig_hdr->name); |
||||
sig_hdr->name = NULL; |
||||
} |
||||
|
||||
if (sig_hdr->digest) { |
||||
free(sig_hdr->digest); |
||||
sig_hdr->digest = NULL; |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <pack.h> |
||||
#include <sign.h> |
||||
#include <unpack.h> |
||||
#include <verify.h> |
||||
|
||||
struct entry_point { |
||||
const char *cmd; |
||||
int (* main)(int argc, char *argv[]); |
||||
}; |
||||
|
||||
struct entry_point entries[] = { |
||||
{ "pack", do_pack }, |
||||
{ "sign", do_sign }, |
||||
{ "unpack", do_unpack }, |
||||
{ "verify", do_verify }, |
||||
{ NULL, NULL }, |
||||
}; |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
struct entry_point *entry; |
||||
|
||||
if (argc < 2) { |
||||
fprintf(stderr, "usage: %s <command>\n", argv[0]); |
||||
return -1; |
||||
} |
||||
|
||||
for (entry = entries; entry->cmd; ++entry) { |
||||
if (strcmp(entry->cmd, argv[1]) == 0) |
||||
return entry->main(argc, argv); |
||||
} |
||||
|
||||
fprintf(stderr, "usage: %s <command>\n", argv[0]); |
||||
return -1; |
||||
} |
@ -0,0 +1,104 @@ |
||||
#include <stdio.h> |
||||
|
||||
#include <getopt.h> |
||||
|
||||
#include <file.h> |
||||
#include <image.h> |
||||
#include <pack.h> |
||||
|
||||
enum { |
||||
OPTION_HELP = 'h', |
||||
OPTION_OUTPUT = 'o', |
||||
}; |
||||
|
||||
struct args { |
||||
const char *input, *output; |
||||
}; |
||||
|
||||
static int parse_args(struct args *args, int argc, char *argv[]) |
||||
{ |
||||
struct option options[] = { |
||||
{ "help", no_argument, NULL, OPTION_HELP }, |
||||
{ "output", required_argument, 0, OPTION_OUTPUT }, |
||||
{ NULL, 0, 0, 0 }, |
||||
}; |
||||
int ret; |
||||
|
||||
while ((ret = getopt_long(argc, (char * const *)argv, "ho:", options, |
||||
NULL)) >= 0) { |
||||
switch (ret) { |
||||
case OPTION_HELP: return -1; |
||||
case OPTION_OUTPUT: args->output = optarg; break; |
||||
default: break; |
||||
} |
||||
} |
||||
|
||||
if (optind >= argc) |
||||
return -1; |
||||
|
||||
++optind; |
||||
|
||||
if (optind >= argc) |
||||
return -1; |
||||
|
||||
args->input = argv[optind]; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int do_pack(int argc, char *argv[]) |
||||
{ |
||||
char data[4096]; |
||||
struct rots_hdr hdr; |
||||
struct args args; |
||||
FILE *input, *output; |
||||
size_t nbytes, size; |
||||
|
||||
if (parse_args(&args, argc, argv) < 0) { |
||||
fprintf(stderr, "invalid\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (!(input = fopen(args.input, "rb"))) { |
||||
fprintf(stderr, "unable to open '%s' for reading.\n", args.input); |
||||
return -1; |
||||
} |
||||
|
||||
if (!(output = fopen(args.output, "wb"))) { |
||||
fprintf(stderr, "unable to open '%s' for writing.\n", args.output); |
||||
goto err_close_input; |
||||
} |
||||
|
||||
get_file_size(&size, args.input); |
||||
hdr.size = size; |
||||
|
||||
if (rots_write_hdr(output, &hdr) < 0) |
||||
goto err_close_output; |
||||
|
||||
while (size) { |
||||
nbytes = fread(data, sizeof *data, sizeof data, input); |
||||
|
||||
if (nbytes == 0) { |
||||
fprintf(stderr, "unable to read the next chunk\n"); |
||||
goto err_close_output; |
||||
} |
||||
|
||||
if (fwrite(data, sizeof *data, nbytes, output) < nbytes) { |
||||
fprintf(stderr, "unable to write the current chunk\n"); |
||||
goto err_close_output; |
||||
} |
||||
|
||||
size -= nbytes; |
||||
} |
||||
|
||||
fclose(output); |
||||
fclose(input); |
||||
|
||||
return 0; |
||||
|
||||
err_close_output: |
||||
fclose(output); |
||||
err_close_input: |
||||
fclose(input); |
||||
return -1; |
||||
} |
@ -0,0 +1,196 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.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; |
||||
|
||||
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) |
||||
return -1; |
||||
|
||||
if (!(EVP_DigestSignUpdate(ctx, data, nbytes))) |
||||
goto err_destroy_ctx; |
||||
|
||||
size -= nbytes; |
||||
} |
||||
|
||||
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; |
||||
} |
@ -0,0 +1,100 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <getopt.h> |
||||
|
||||
#include <image.h> |
||||
#include <macros.h> |
||||
#include <unpack.h> |
||||
|
||||
enum { |
||||
OPTION_HELP = 'h', |
||||
OPTION_IMAGE = 'i', |
||||
OPTION_OUTPUT = 'o', |
||||
}; |
||||
|
||||
struct args { |
||||
const char *image, *output; |
||||
}; |
||||
|
||||
static int parse_args(struct args *args, int argc, char *argv[]) |
||||
{ |
||||
struct option options[] = { |
||||
{ "help", no_argument, NULL, OPTION_HELP }, |
||||
{ "image", required_argument, NULL, OPTION_IMAGE }, |
||||
{ "output", required_argument, NULL, OPTION_OUTPUT }, |
||||
{ NULL, 0, 0, 0 }, |
||||
}; |
||||
int ret; |
||||
|
||||
while ((ret = getopt_long(argc, (char * const *)argv, "hi:o:", options, |
||||
NULL)) >= 0) { |
||||
switch (ret) { |
||||
case OPTION_HELP: return -1; |
||||
case OPTION_IMAGE: args->image = optarg; break; |
||||
case OPTION_OUTPUT: args->output = optarg; break; |
||||
default: break; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int do_unpack(int argc, char *argv[]) |
||||
{ |
||||
char data[512]; |
||||
struct rots_hdr hdr; |
||||
FILE *in, *out; |
||||
struct args args; |
||||
size_t nbytes, size; |
||||
|
||||
if (parse_args(&args, argc, argv) < 0) { |
||||
fprintf(stderr, "invalid\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (!(in = fopen(args.image, "rb"))) { |
||||
fprintf(stderr, "error: file '%s' not found.\n", args.image); |
||||
return -1; |
||||
} |
||||
|
||||
if (!(out = fopen(args.output, "wb"))) { |
||||
fprintf(stderr, "error: cannot open '%s' for writing.\n", args.output); |
||||
goto err_close_in; |
||||
} |
||||
|
||||
if (rots_read_hdr(in, &hdr) < 0) { |
||||
fprintf(stderr, "error: file '%s' is not a ROTS-image.\n", args.image); |
||||
goto err_close_out; |
||||
} |
||||
|
||||
size = hdr.size; |
||||
|
||||
while (size) { |
||||
nbytes = fread(data, sizeof *data, min(size, sizeof data), in); |
||||
|
||||
if (nbytes == 0) { |
||||
fprintf(stderr, "error: cannot read the next chunk.\n"); |
||||
goto err_close_out; |
||||
} |
||||
|
||||
if (fwrite(data, sizeof *data, nbytes, out) < nbytes) { |
||||
fprintf(stderr, "error: cannot write the current chunk.\n"); |
||||
goto err_close_out; |
||||
} |
||||
|
||||
size -= nbytes; |
||||
} |
||||
|
||||
fclose(out); |
||||
fclose(in); |
||||
|
||||
return 0; |
||||
|
||||
err_close_out: |
||||
fclose(out); |
||||
err_close_in: |
||||
fclose(in); |
||||
return -1; |
||||
} |
@ -0,0 +1,237 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <dirent.h> |
||||
#include <getopt.h> |
||||
|
||||
#include <sys/types.h> |
||||
|
||||
#include <openssl/evp.h> |
||||
#include <openssl/pem.h> |
||||
#include <openssl/x509.h> |
||||
#include <openssl/x509v3.h> |
||||
|
||||
#include <image.h> |
||||
#include <macros.h> |
||||
#include <verify.h> |
||||
#include <x509.h> |
||||
|
||||
enum { |
||||
OPTION_HELP = 'h', |
||||
OPTION_IMAGE = 'i', |
||||
OPTION_CERTS = 'c', |
||||
}; |
||||
|
||||
struct args { |
||||
const char *image, *digest, *certs; |
||||
}; |
||||
|
||||
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 }, |
||||
{ "certs", required_argument, 0, OPTION_CERTS }, |
||||
{ NULL, 0, 0, 0 }, |
||||
}; |
||||
int ret; |
||||
|
||||
while ((ret = getopt_long(argc, (char * const *)argv, "hi:d:c:", options, |
||||
NULL)) >= 0) { |
||||
switch (ret) { |
||||
case OPTION_HELP: return -1; |
||||
case OPTION_IMAGE: args->image = optarg; break; |
||||
case OPTION_CERTS: args->certs = optarg; break; |
||||
default: break; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static X509 *X509_find_cert_by_common_name(const char *path, const char *name) |
||||
{ |
||||
DIR *dir; |
||||
struct dirent *dirent; |
||||
X509 *cert = NULL; |
||||
char *fpath, *cn; |
||||
|
||||
if (!(dir = opendir(path))) |
||||
return NULL; |
||||
|
||||
while ((dirent = readdir(dir))) { |
||||
if (asprintf(&fpath, "%s/%s", path, dirent->d_name) < 0) |
||||
continue; |
||||
|
||||
if (!(cert = X509_open_cert(fpath))) |
||||
continue; |
||||
|
||||
if (!(cn = X509_get_common_name(cert))) { |
||||
free(cert); cert = NULL; |
||||
continue; |
||||
} |
||||
|
||||
if (strcmp(cn, name) == 0) |
||||
break; |
||||
} |
||||
|
||||
closedir(dir); |
||||
|
||||
return cert; |
||||
} |
||||
|
||||
static int verify(const char *image, const char *digest_name, EVP_PKEY *key, |
||||
unsigned char *sig, size_t sig_len) |
||||
{ |
||||
char data[512]; |
||||
struct rots_hdr hdr; |
||||
const EVP_MD *digest; |
||||
FILE *fp; |
||||
EVP_MD_CTX *ctx = NULL; |
||||
size_t nbytes, size; |
||||
int ret; |
||||
|
||||
if (!image || !digest_name || !key || !sig || !sig_len) |
||||
return -1; |
||||
|
||||
if (!(fp = fopen(image, "rb"))) |
||||
return -1; |
||||
|
||||
if (rots_read_hdr(fp, &hdr) < 0) |
||||
goto err_close_image; |
||||
|
||||
size = hdr.size; |
||||
|
||||
if (!(ctx = EVP_MD_CTX_create())) |
||||
goto err_close_image; |
||||
|
||||
if (!(digest = EVP_get_digestbyname(digest_name))) |
||||
goto err_destroy_ctx; |
||||
|
||||
if (!(EVP_DigestVerifyInit(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_DigestVerifyUpdate(ctx, data, nbytes))) |
||||
goto err_destroy_ctx; |
||||
|
||||
size -= nbytes; |
||||
} |
||||
|
||||
if (EVP_DigestVerifyFinal(ctx, sig, sig_len) == 1) { |
||||
ret = 0; |
||||
} else { |
||||
ret = -1; |
||||
} |
||||
|
||||
EVP_MD_CTX_destroy(ctx); |
||||
fclose(fp); |
||||
return ret; |
||||
|
||||
err_destroy_ctx: |
||||
EVP_MD_CTX_destroy(ctx); |
||||
err_close_image: |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
|
||||
static int verify_all(size_t *count, size_t *total, const char *ca_path, const char *image) |
||||
{ |
||||
struct rots_hdr hdr; |
||||
struct rots_sig_hdr sig_hdr; |
||||
unsigned char *sig; |
||||
FILE *fp; |
||||
EVP_PKEY *key; |
||||
X509 *cert; |
||||
int ret; |
||||
|
||||
*count = *total = 0; |
||||
|
||||
if (!(fp = fopen(image, "rb"))) |
||||
return -1; |
||||
|
||||
if (rots_read_hdr(fp, &hdr) < 0) |
||||
goto err_close_image; |
||||
|
||||
if (fseek(fp, hdr.size, SEEK_CUR) < 0) |
||||
goto err_close_image; |
||||
|
||||
while (rots_read_sig_hdr(fp, &sig_hdr) == 0) { |
||||
if (!(cert = X509_find_cert_by_common_name(ca_path, sig_hdr.name))) { |
||||
rots_cleanup_sig_hdr(&sig_hdr); |
||||
fseek(fp, sig_hdr.size, SEEK_CUR); |
||||
++*total; |
||||
continue; |
||||
} |
||||
|
||||
if (X509_check_issued(cert, cert) == X509_V_OK) { |
||||
printf("self-signed certificate\n"); |
||||
} else { |
||||
printf("root-signed certificate\n"); |
||||
} |
||||
|
||||
if (!(key = X509_get_pubkey(cert))) { |
||||
free(cert); |
||||
rots_cleanup_sig_hdr(&sig_hdr); |
||||
fseek(fp, sig_hdr.size, SEEK_CUR); |
||||
++*total; |
||||
continue; |
||||
} |
||||
|
||||
if (!(sig = malloc(sig_hdr.size * sizeof *sig))) { |
||||
free(cert); |
||||
rots_cleanup_sig_hdr(&sig_hdr); |
||||
goto err_close_image; |
||||
} |
||||
|
||||
if (fread(sig, sizeof *sig, sig_hdr.size, fp) < sig_hdr.size) { |
||||
free(sig); |
||||
goto err_close_image; |
||||
} |
||||
|
||||
ret = verify(image, sig_hdr.digest, key, sig, sig_hdr.size); |
||||
free(sig); |
||||
|
||||
if (ret == 0) |
||||
++*count; |
||||
|
||||
++*total; |
||||
|
||||
rots_cleanup_sig_hdr(&sig_hdr); |
||||
} |
||||
|
||||
fclose(fp); |
||||
return 0; |
||||
|
||||
err_close_image: |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
|
||||
int do_verify(int argc, char *argv[]) |
||||
{ |
||||
struct args args; |
||||
size_t count, total; |
||||
|
||||
if (parse_args(&args, argc, argv) < 0) { |
||||
fprintf(stderr, "invalid\n"); |
||||
return -1; |
||||
} |
||||
|
||||
OpenSSL_add_all_algorithms(); |
||||
|
||||
if (verify_all(&count, &total, args.certs, args.image) < 0) { |
||||
fprintf(stderr, "error: unable to verify the signature(s).\n"); |
||||
return -1; |
||||
} |
||||
|
||||
printf("%zu/%zu signatures are correct.\n", count, total); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,44 @@ |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <x509.h> |
||||
|
||||
X509 *X509_open_cert(const char *path) |
||||
{ |
||||
X509 *cert; |
||||
FILE *fp; |
||||
|
||||
if (!(fp = fopen(path, "r"))) |
||||
return NULL; |
||||
|
||||
cert = PEM_read_X509(fp, NULL, NULL, NULL); |
||||
fclose(fp); |
||||
|
||||
return cert; |
||||
} |
||||
|
||||
char *X509_get_common_name(X509 *cert) |
||||
{ |
||||
X509_NAME *subj; |
||||
X509_NAME_ENTRY *entry; |
||||
ASN1_STRING *data; |
||||
unsigned char *cn; |
||||
int idx; |
||||
|
||||
if (!(subj = X509_get_subject_name(cert))) |
||||
return NULL; |
||||
|
||||
if ((idx = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)) < 0) |
||||
return NULL; |
||||
|
||||
if (!(entry = X509_NAME_get_entry(subj, idx))) |
||||
return NULL; |
||||
|
||||
if (!(data = X509_NAME_ENTRY_get_data(entry))) |
||||
return NULL; |
||||
|
||||
if (ASN1_STRING_to_UTF8(&cn, data) == 0) |
||||
return NULL; |
||||
|
||||
return (char *)cn; |
||||
} |
Loading…
Reference in new issue