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