rots-utils: initial commit
This commit is contained in:
commit
5494d66eeb
17 changed files with 1048 additions and 0 deletions
45
Makefile
Normal file
45
Makefile
Normal file
|
@ -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
|
3
include/file.h
Normal file
3
include/file.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
int get_file_size(size_t *size, const char *path);
|
29
include/image.h
Normal file
29
include/image.h
Normal file
|
@ -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);
|
4
include/macros.h
Normal file
4
include/macros.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define min(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
3
include/pack.h
Normal file
3
include/pack.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
int do_pack(int argc, char *argv[]);
|
3
include/sign.h
Normal file
3
include/sign.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
int do_sign(int argc, char *argv[]);
|
3
include/unpack.h
Normal file
3
include/unpack.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
int do_unpack(int argc, char *argv[]);
|
3
include/verify.h
Normal file
3
include/verify.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
int do_verify(int argc, char *argv[]);
|
9
include/x509.h
Normal file
9
include/x509.h
Normal file
|
@ -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);
|
21
source/file.c
Normal file
21
source/file.c
Normal file
|
@ -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;
|
||||
}
|
205
source/image.c
Normal file
205
source/image.c
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
39
source/main.c
Normal file
39
source/main.c
Normal file
|
@ -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;
|
||||
}
|
104
source/pack.c
Normal file
104
source/pack.c
Normal file
|
@ -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;
|
||||
}
|
196
source/sign.c
Normal file
196
source/sign.c
Normal file
|
@ -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;
|
||||
}
|
100
source/unpack.c
Normal file
100
source/unpack.c
Normal file
|
@ -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;
|
||||
}
|
237
source/verify.c
Normal file
237
source/verify.c
Normal file
|
@ -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;
|
||||
}
|
44
source/x509.c
Normal file
44
source/x509.c
Normal file
|
@ -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…
Add table
Reference in a new issue