aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <me@fwei.tk>2018-06-30 20:01:43 -0400
committerFranklin Wei <me@fwei.tk>2018-06-30 20:01:43 -0400
commitf0519032a59aff4ca9edcb2916e094db93e08942 (patch)
tree2888deb8615fb03f30bab36773a9605a0546160d
parent03a354b8d0f2a8820db9571c639804648d804ac4 (diff)
downloadcsaa-f0519032a59aff4ca9edcb2916e094db93e08942.zip
csaa-f0519032a59aff4ca9edcb2916e094db93e08942.tar.gz
csaa-f0519032a59aff4ca9edcb2916e094db93e08942.tar.bz2
csaa-f0519032a59aff4ca9edcb2916e094db93e08942.tar.xz
Add dummy client/server for comparison; fix bugs and polish database code
-rw-r--r--Makefile18
-rw-r--r--client.c67
-rw-r--r--crypto.c25
-rw-r--r--crypto.h2
-rw-r--r--dummy_client.c417
-rw-r--r--dummy_service.c485
-rw-r--r--iomt.c5
-rw-r--r--main.c13
-rw-r--r--service_provider.c184
-rw-r--r--service_provider.h9
-rw-r--r--sqlinit.c201
-rw-r--r--sqlinit.txt33
-rw-r--r--test.c1
-rw-r--r--test.h2
-rwxr-xr-xtestmodify.sh2
15 files changed, 1359 insertions, 105 deletions
diff --git a/Makefile b/Makefile
index 925c342..f68b16e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,19 @@
-all: client server Makefile
-CFLAGS = -g -Wall -O3 -lsqlite3
+all: client server dummy_client dummy_server
+CFLAGS = -g -Wall -O0 -lsqlite3
+sqlinit.c: sqlinit.txt
+ xxd -i $^ | sed 's/\([0-9a-f]\)$$/\0, 0x00/' > $@
+
+dummy_main.o: main.c
+ cc -c -o $@ $^ -DDUMMY $(CFLAGS)
+
+dummy_client: dummy_client.o crypto.o test.o iomt.o
+ cc -o $@ $^ -lcrypto $(CFLAGS)
+dummy_server: dummy_service.o dummy_main.o sqlinit.o
+ cc -o $@ $^ -lcrypto $(CFLAGS)
client: client.o crypto.o test.o iomt.o
cc -o $@ $^ -lcrypto $(CFLAGS)
-server: service_provider.o crypto.o helper.o trusted_module.o main.o test.o iomt.o
+server: service_provider.o crypto.o helper.o trusted_module.o main.o test.o iomt.o sqlinit.o
cc -o $@ $^ -lcrypto $(CFLAGS)
clean:
- rm -f *.o a.out client server
+ rm -f *.o a.out client server dummy_client dummy_server
diff --git a/client.c b/client.c
index 7c057b1..02a4d63 100644
--- a/client.c
+++ b/client.c
@@ -85,10 +85,6 @@ void print_usage(const char *name)
" retrieveinfo -f FILEIDX [-v VERSION]\n"
"\n"
" retrievefile -f FILEIDX [-v VERSION] -o IMAGE_OUT\n");
- for(int i = 0; i < 10; ++i)
- {
- printf("%d %d\n", i, ilog2(i));
- }
}
bool parse_args(int argc, char *argv[])
@@ -308,7 +304,8 @@ bool parse_args(int argc, char *argv[])
}
}
-static struct tm_request verify_and_sign(int fd, const struct user_request *req)
+/* val is lambda for FILE_UPDATE, ignored for create, and the ACL root for ACL_MODIFY */
+static struct tm_request verify_and_sign(int fd, const struct user_request *req, hash_t val)
{
struct tm_request tmr = req_null;
if(recv(fd, &tmr, sizeof(tmr), MSG_WAITALL) != sizeof(tmr))
@@ -338,12 +335,20 @@ static struct tm_request verify_and_sign(int fd, const struct user_request *req)
}
case MODIFY_FILE:
{
- /* TODO */
+ if(tmr.type != FILE_UPDATE ||
+ tmr.user_id != req->user_id ||
+ tmr.idx != req->file_idx ||
+ !hash_equals(tmr.val, val))
+ return req_null;
break;
}
case MODIFY_ACL:
{
- /* TODO */
+ if(tmr.type != ACL_UPDATE ||
+ tmr.user_id != req->user_id ||
+ tmr.idx != req->file_idx ||
+ !hash_equals(tmr.val, val))
+ return req_null;
break;
}
default:
@@ -417,7 +422,19 @@ bool exec_request(int fd, const struct user_request *req,
case MODIFY_FILE:
{
/* verify module ack */
- struct tm_request tmr = verify_and_sign(fd, req);
+ hash_t val = hash_null;
+ if(req->type == MODIFY_FILE)
+ {
+ hash_t gamma = sha256(new_file_contents, len);
+ val = calc_lambda(gamma,
+ new_buildcode,
+ new_composefile,
+ req->modify_file.kf);
+ }
+ else if(req->type == MODIFY_ACL)
+ val = iomt_getroot(new_acl);
+
+ struct tm_request tmr = verify_and_sign(fd, req, val);
if(tmreq_out)
*tmreq_out = tmr;
return verify_sp_ack(fd, &tmr);
@@ -451,9 +468,14 @@ bool exec_request(int fd, const struct user_request *req,
recv(fd, &encrypted_secret, sizeof(encrypted_secret), MSG_WAITALL);
recv(fd, &kf, sizeof(kf), MSG_WAITALL);
- hash_t pad = hmac_sha256(&kf, sizeof(kf),
- user_key, keylen);
- *secret_out = hash_xor(encrypted_secret, pad);
+ if(!is_zero(kf))
+ {
+ hash_t pad = hmac_sha256(&kf, sizeof(kf),
+ user_key, keylen);
+ *secret_out = hash_xor(encrypted_secret, pad);
+ }
+ else
+ *secret_out = hash_null;
*buildcode = iomt_deserialize(read_from_fd, &fd);
*composefile = iomt_deserialize(read_from_fd, &fd);
@@ -642,6 +664,8 @@ bool server_request(const char *sockpath,
req.type == RETRIEVE_FILE ? &file_contents : NULL,
req.type == RETRIEVE_FILE ? &file_len : NULL);
+ close(fd);
+
printf("Request %s\n",
success ?
"\033[32;1msucceeded\033[0m" :
@@ -678,7 +702,26 @@ bool server_request(const char *sockpath,
printf("Writing image file to %s.\n", image_path);
write_file(image_path, file_contents, file_len);
/* What about build code? We only have the IOMT, not the actual contents. */
- break;
+
+ /* Verify contents */
+ int fd = connect_to_service(sockpath);
+ struct version_info verinfo = request_verinfo(fd, user_id,
+ user_key, strlen(user_key),
+ req.file_idx,
+ 0);
+ close(fd);
+
+ bool success = hash_equals(lambda, verinfo.lambda);
+
+ if(!success)
+ {
+ printf("Could not verify integrity of response (lambda should be %s).\n",
+ hash_format(verinfo.lambda, 4).str);
+ }
+ else
+ printf("Successfully verifed integrity of file.\n");
+
+ return success;
}
default:
break;
diff --git a/crypto.c b/crypto.c
index 6fb1ead..0b626ba 100644
--- a/crypto.c
+++ b/crypto.c
@@ -1,3 +1,5 @@
+/* crypto and other generally useful stuff, shared by all code */
+
#include "crypto.h"
#include "iomt.h"
#include "trusted_module.h"
@@ -350,10 +352,14 @@ hash_t derive_key(const char *passphrase, hash_t nonce)
hash_t calc_kf(hash_t encryption_key, uint64_t file_idx)
{
- if(is_zero(encryption_key))
- return hash_null;
- return hmac_sha256(&encryption_key, sizeof(encryption_key),
- &file_idx, sizeof(file_idx));
+ hash_t kf = hash_null;
+ if(!is_zero(encryption_key))
+ kf = hmac_sha256(&encryption_key, sizeof(encryption_key),
+ &file_idx, sizeof(file_idx));
+ printf("calc_kf: encryption key = %s, file_idx = %lu, kf = %s\n",
+ hash_format(encryption_key, 4).str, file_idx,
+ hash_format(kf, 4).str);
+ return kf;
}
void memxor(unsigned char *dest, const unsigned char *b, size_t len)
@@ -453,6 +459,17 @@ void dump_versioninfo(const struct version_info *verinfo)
hash_format(verinfo->lambda, 4).str);
}
+void warn(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ char buf[256];
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+
+ fprintf(stderr, "\033[31;1mWARNING\033[0m: %s\n", buf);
+}
+
void crypto_test(void)
{
#if 1
diff --git a/crypto.h b/crypto.h
index e82bbbc..7e9d0ed 100644
--- a/crypto.h
+++ b/crypto.h
@@ -87,6 +87,8 @@ hash_t generate_nonce(void);
hash_t derive_key(const char *passphrase, hash_t nonce);
hash_t calc_kf(hash_t encryption_key, uint64_t file_idx);
+void warn(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
/* self-test */
void crypto_test(void);
#endif
diff --git a/dummy_client.c b/dummy_client.c
new file mode 100644
index 0000000..fe66b1b
--- /dev/null
+++ b/dummy_client.c
@@ -0,0 +1,417 @@
+/* Based on:
+ * <https://github.com/troydhanson/network/blob/master/unixdomain/01.basic/cli.c> */
+
+/* A dummy client for use with the dummy service provider, which
+ * provides no assurances. */
+
+/* Usage:
+ *
+ * $ ./client [-s <SOCKET>] -u USERID COMMAND [PARAMS]
+ *
+ * Where COMMAND and PARAMS are one of the following:
+ * create (takes no parameters)
+ *
+ * modifyfile -f FILEIDX -i IMAGE_FILE
+ *
+ * retrievefile -f FILEIDX [-v VERSION] -o IMAGE_OUT
+ */
+
+#define CLIENT
+#include "crypto.h"
+#include "service_provider.h"
+#include "test.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+
+static const char *socket_path = "socket";
+static const char *parse_args_fail = NULL;
+static uint64_t user_id = 0;
+static struct user_request cl_request;
+const char *image_path = NULL;
+
+void print_usage(const char *name)
+{
+ printf("Usage:\n"
+ "\n"
+ "$ ./client [-s <SOCKET>] -u USERID -k USER_KEY COMMAND [PARAMS]\n"
+ "\n"
+ "Where COMMAND and PARAMS are one of the following:\n"
+ " create (takes no parameters)\n"
+ "\n"
+ " modifyfile -f FILEIDX -i IMAGE_FILE [-ib buildcode_file]\n"
+ " [-ic compose_file] [--encrypt, -e]\n"
+ "\n"
+ " retrieveinfo -f FILEIDX [-v VERSION]\n"
+ "\n"
+ " retrievefile -f FILEIDX [-v VERSION] -o IMAGE_OUT\n");
+}
+
+bool parse_args(int argc, char *argv[])
+{
+ for(int i = 1; i < argc; ++i)
+ {
+ char *arg = argv[i];
+ if(!strcmp(arg, "-s") || !strcmp(arg, "--socket"))
+ {
+ if(++i < argc)
+ socket_path = argv[i];
+ else
+ {
+ parse_args_fail = "Expected parameter after -s";
+ return false;
+ }
+ }
+ else if(!strcmp(arg, "-u"))
+ {
+ if(++i < argc)
+ user_id = atol(argv[i]);
+ else
+ {
+ parse_args_fail = "Expected user id";
+ return false;
+ }
+ }
+ else if(!strcmp(arg, "-f"))
+ {
+ if(++i < argc)
+ cl_request.file_idx = atol(argv[i]);
+ else
+ {
+ parse_args_fail = "Expected file index";
+ return false;
+ }
+ }
+ else if(!strcmp(arg, "-v"))
+ {
+ if(++i < argc)
+ cl_request.retrieve.version = atol(argv[i]);
+ else
+ {
+ parse_args_fail = "Expected version number";
+ return false;
+ }
+ }
+ /* -i and -o are handled identically */
+ else if(!strcmp(arg, "-i") || !strcmp(arg, "-o"))
+ {
+ if(++i < argc)
+ image_path = argv[i];
+ else
+ {
+ parse_args_fail = "Expected image path";
+ return false;
+ }
+ }
+ else if(!strcmp(arg, "-h") || !strcmp(arg, "--help"))
+ {
+ print_usage(argv[0]);
+ exit(1);
+ }
+ else if(!strcmp(arg, "create"))
+ {
+ if(cl_request.type != USERREQ_NONE)
+ {
+ parse_args_fail = "Multiple commands";
+ return false;
+ }
+ cl_request.type = CREATE_FILE;
+ }
+ else if(!strcmp(arg, "modifyfile"))
+ {
+ if(cl_request.type != USERREQ_NONE)
+ {
+ parse_args_fail = "Multiple commands";
+ return false;
+ }
+ cl_request.type = MODIFY_FILE;
+ }
+ else if(!strcmp(arg, "retrieveinfo") || !strcmp(arg, "retrievefile"))
+ {
+ if(cl_request.type != USERREQ_NONE)
+ {
+ parse_args_fail = "Multiple commands";
+ return false;
+ }
+ cl_request.type = RETRIEVE_INFO;
+
+ if(!strcmp(arg, "retrievefile"))
+ {
+ cl_request.type = RETRIEVE_FILE;
+ }
+ }
+ else
+ {
+ parse_args_fail = "Unknown parameter";
+ return false;
+ }
+ }
+ if(cl_request.type != USERREQ_NONE && user_id != 0)
+ {
+ if(cl_request.type > CREATE_FILE)
+ {
+ if(!cl_request.file_idx)
+ {
+ parse_args_fail = "No file index specified";
+ return false;
+ }
+ }
+ else
+ {
+ if(cl_request.file_idx)
+ {
+ parse_args_fail = "Index specified for create";
+ return false;
+ }
+ }
+
+ if(cl_request.type == MODIFY_FILE ||
+ cl_request.type == RETRIEVE_FILE)
+ {
+ if(!image_path)
+ {
+ parse_args_fail = "No image file specified";
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else
+ {
+ parse_args_fail = "Missing required parameter (either command, user ID, or user key)";
+ return false;
+ }
+}
+
+/* In case of modifcation or file creation, returns true on successful
+ * completion of request, as acknowledged by module. In case of info
+ * retrieval, returns true if version info is verified by module. The
+ * verinfo_out, user_key, and keylen parameters must not be NULL in
+ * this case (in all other cases they are ignored). */
+bool exec_request(int fd, const struct user_request *req,
+ const void *new_file_contents, size_t len, /* MODIFY_FILE only */
+ struct version_info *verinfo_out, /* RETRIEVE_INFO only */
+ void **file_contents_out, /* RETRIEVE_FILE only */
+ size_t *file_len, /* RETRIEVE_FILE only */
+ uint64_t *new_idx) /* CREATE_FILE only */
+{
+ write(fd, req, sizeof(*req));
+ /* write additional data */
+ switch(req->type)
+ {
+ case MODIFY_FILE:
+ /* prefix file with size */
+ write(fd, &len, sizeof(len));
+ write(fd, new_file_contents, len);
+ break;
+ case CREATE_FILE:
+ case RETRIEVE_INFO:
+ case RETRIEVE_FILE:
+ /* no additional data needed, fall through */
+ default:
+ break;
+ }
+
+ switch(req->type)
+ {
+ case CREATE_FILE:
+ {
+ if(new_idx)
+ recv(fd, new_idx, sizeof(*new_idx), MSG_WAITALL);
+ return true;
+ }
+ case MODIFY_ACL:
+ case MODIFY_FILE:
+ {
+ /* don't verify */
+ return true;
+ }
+ case RETRIEVE_INFO:
+ {
+ hash_t hmac;
+ struct version_info verinfo;
+ recv(fd, &verinfo, sizeof(verinfo), MSG_WAITALL);
+ *verinfo_out = verinfo;
+ return true;
+ }
+ case RETRIEVE_FILE:
+ {
+ recv(fd, file_len, sizeof(*file_len), MSG_WAITALL);
+
+ if(*file_len)
+ {
+ *file_contents_out = malloc(*file_len);
+ recv(fd, *file_contents_out, *file_len, MSG_WAITALL);
+ }
+ else
+ {
+ *file_contents_out = NULL;
+ return false;
+ }
+ return true;
+ }
+ default:
+ assert(false);
+ }
+}
+
+/* set version = 0 to get latest version */
+struct version_info request_verinfo(int fd, uint64_t user_id,
+ uint64_t file_idx, uint64_t version)
+
+{
+ struct user_request req;
+ req.type = RETRIEVE_INFO;
+ req.user_id = user_id;
+ req.retrieve.file_idx = file_idx;
+ req.retrieve.version = version;
+
+ struct version_info verinfo;
+
+ bool rc = exec_request(fd, &req,
+ NULL, 0,
+ &verinfo,
+ NULL,
+ NULL,
+ NULL);
+
+ return verinfo;
+}
+
+int connect_to_service(const char *sockpath)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ perror("socket error");
+ exit(-1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ if (*sockpath == '\0') {
+ *addr.sun_path = '\0';
+ strncpy(addr.sun_path+1, sockpath+1, sizeof(addr.sun_path)-2);
+ } else {
+ strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)-1);
+ }
+
+ if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+ perror("connect error");
+ exit(-1);
+ }
+
+ return fd;
+}
+
+void *load_file(const char *path, size_t *len)
+{
+ FILE *f = fopen(path, "r");
+ fseek(f, 0, SEEK_END);
+ *len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ void *buf = malloc(*len);
+ fread(buf, 1, *len, f);
+ return buf;
+}
+
+void write_file(const char *path, const void *contents, size_t len)
+{
+ if(contents)
+ {
+ FILE *f = fopen(path, "w");
+ fwrite(contents, 1, len, f);
+ fclose(f);
+ }
+}
+
+bool server_request(const char *sockpath,
+ uint64_t user_id,
+ struct user_request req,
+ const char *image_path)
+{
+ void *file_contents = NULL;
+ size_t file_len = 0;
+
+ /* Fill in rest of request structure */
+ req.user_id = user_id;
+
+ if(req.type == MODIFY_FILE)
+ {
+ if(image_path)
+ {
+ file_contents = load_file(image_path, &file_len);
+ }
+ }
+
+ struct version_info verinfo;
+
+ int fd = connect_to_service(sockpath);
+ uint64_t new_idx;
+
+ bool success = exec_request(fd, &req,
+ req.type == MODIFY_FILE ? file_contents : NULL,
+ req.type == MODIFY_FILE ? file_len : 0,
+ req.type == RETRIEVE_INFO ? &verinfo : NULL,
+ req.type == RETRIEVE_FILE ? &file_contents : NULL,
+ req.type == RETRIEVE_FILE ? &file_len : NULL,
+ req.type == CREATE_FILE ? &new_idx : NULL);
+
+ printf("Request %s\n",
+ success ?
+ "\033[32;1msucceeded\033[0m" :
+ "\033[31;1mfailed\033[0m");
+
+ if(!success)
+ return false;
+
+ switch(req.type)
+ {
+ case CREATE_FILE:
+ printf("Created file with index %lu.\n", new_idx);
+ break;
+ case RETRIEVE_INFO:
+ printf("File info: ");
+ dump_versioninfo(&verinfo);
+ break;
+ case RETRIEVE_FILE:
+ {
+ printf("Writing image file to %s.\n", image_path);
+ write_file(image_path, file_contents, file_len);
+ /* What about build code? We only have the IOMT, not the actual contents. */
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[]) {
+ char buf[100];
+ int fd,rc;
+
+ if(!parse_args(argc, argv))
+ {
+ printf("%s\n", parse_args_fail);
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ server_request(socket_path, user_id,
+ cl_request,
+ image_path);
+
+ return 0;
+}
diff --git a/dummy_service.c b/dummy_service.c
new file mode 100644
index 0000000..5a974d1
--- /dev/null
+++ b/dummy_service.c
@@ -0,0 +1,485 @@
+/* implementation of a basic service provider for use with the trusted
+ * module */
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sqlite3.h>
+
+#include "crypto.h"
+#include "helper.h"
+#include "test.h"
+
+/* get type definitions only */
+#define CLIENT
+
+#include "trusted_module.h"
+#include "service_provider.h"
+
+/* arbitrary */
+#define ACL_LOGLEAVES 4
+
+#define MAX_PATH 260
+
+/* should be free'd with free_record */
+struct file_record {
+ uint64_t idx;
+ uint64_t version;
+};
+
+struct service_provider {
+ const char *data_dir;
+
+ void *db; /* sqlite3 handle */
+};
+
+/* write to file data_dir/file_idx/version */
+void write_contents(const struct service_provider *sp,
+ int file_idx, int version,
+ const void *data, size_t len)
+{
+ mkdir(sp->data_dir, 0755);
+
+ char dirname[MAX_PATH];
+ snprintf(dirname, sizeof(dirname), "%s/%d", sp->data_dir, file_idx);
+
+ mkdir(dirname, 0755);
+
+ char filename[MAX_PATH];
+ snprintf(filename, sizeof(filename), "%s/%d/%d", sp->data_dir, file_idx, version);
+
+ FILE *f = fopen(filename, "w");
+
+ fwrite(data, 1, len, f);
+
+ fclose(f);
+}
+
+size_t file_len(FILE *f)
+{
+ off_t orig = ftell(f);
+ fseek(f, 0, SEEK_END);
+ off_t len = ftell(f);
+ fseek(f, orig, SEEK_SET);
+
+ return (size_t)len;
+}
+
+void *read_contents(const struct service_provider *sp,
+ int file_idx, int version,
+ size_t *len)
+{
+ char filename[MAX_PATH];
+ snprintf(filename, sizeof(filename), "%s/%d/%d", sp->data_dir, file_idx, version);
+
+ FILE *f = fopen(filename, "r");
+
+ *len = file_len(f);
+
+ void *buf = malloc(*len);
+ fread(buf, 1, *len, f);
+
+ fclose(f);
+ return buf;
+}
+
+void *db_init(const char *filename)
+{
+ sqlite3 *db;
+ if(sqlite3_open(filename, &db) != SQLITE_OK)
+ return NULL;
+
+ sqlite3_exec(db, "PRAGMA synchronous = 0;", 0, 0, 0);
+ sqlite3_exec(db, "PRAGMA journal_mode = memory;", 0, 0, 0);
+
+ return db;
+}
+
+void begin_transaction(void *db)
+{
+ sqlite3 *handle = db;
+ sqlite3_exec(handle, "BEGIN;", 0, 0, 0);
+}
+
+void commit_transaction(void *db)
+{
+ sqlite3 *handle = db;
+ sqlite3_exec(handle, "COMMIT;", 0, 0, 0);
+}
+
+/* leaf count will be 2^logleaves */
+struct service_provider *sp_new(const void *key, size_t keylen, int logleaves, const char *data_dir, const char *dbpath)
+{
+ assert(logleaves > 0);
+ struct service_provider *sp = calloc(1, sizeof(*sp));
+
+ sp->db = db_init(dbpath);
+
+ sp->data_dir = data_dir;
+
+ return sp;
+}
+
+static void free_record(struct file_record *rec)
+{
+ if(rec)
+ {
+ free(rec);
+ }
+}
+
+void sp_free(struct service_provider *sp)
+{
+ if(sp)
+ {
+ free(sp);
+ }
+}
+
+/* linear search for record given idx */
+static struct file_record *lookup_record(struct service_provider *sp, uint64_t idx)
+{
+ sqlite3 *handle = sp->db;
+
+ const char *sql = "SELECT * FROM FileRecords WHERE Idx = ?1;";
+
+ sqlite3_stmt *st;
+
+ sqlite3_prepare_v2(handle, sql, -1, &st, 0);
+ sqlite3_bind_int(st, 1, idx);
+
+ int rc = sqlite3_step(st);
+ if(rc == SQLITE_ROW)
+ {
+ struct file_record *rec = calloc(1, sizeof(struct file_record));
+
+ rec->idx = sqlite3_column_int(st, 0);
+ rec->version = sqlite3_column_int(st, 1);
+
+ return rec;
+ }
+ //printf("Failed to find file record with index %lu (%s), ret %d\n", idx, sqlite3_errmsg(handle), rc);
+ return NULL;
+}
+
+/* Should we insert sorted (for O(logn) lookup), or just at the end to
+ * avoid copying (O(n) lookup, O(1) insertion)? Eventually this will
+ * be replaced with a SQL backend. We do not check to ensure that
+ * there are no duplicate file indices; that is up to the caller. */
+static void insert_record(struct service_provider *sp, const struct file_record *rec)
+{
+ //printf("Inserting record %lu\n", rec->idx);
+
+ sqlite3 *handle = sp->db;
+
+ const char *sql = "INSERT INTO FileRecords (Idx, Ver) VALUES ( ?1, ?2 );";
+ sqlite3_stmt *st;
+ sqlite3_prepare_v2(handle, sql, -1, &st, 0);
+ sqlite3_bind_int(st, 1, rec->idx);
+ sqlite3_bind_int(st, 2, rec->version);
+
+ assert(sqlite3_step(st) == SQLITE_DONE);
+
+ sqlite3_finalize(st);
+}
+
+/* Should we insert sorted (for O(logn) lookup), or just at the end to
+ * avoid copying (O(n) lookup, O(1) insertion)? Eventually this will
+ * be replaced with a SQL backend. We do not check to ensure that
+ * there are no duplicate file indices; that is up to the caller. */
+static void update_record(struct service_provider *sp,
+ const struct file_record *rec)
+{
+ sqlite3 *handle = sp->db;
+
+ const char *sql = "UPDATE FileRecords SET Idx = ?1, Ver = ?2 WHERE Idx = ?7;";
+
+ sqlite3_stmt *st;
+ sqlite3_prepare_v2(handle, sql, -1, &st, 0);
+ sqlite3_bind_int(st, 1, rec->idx);
+ sqlite3_bind_int(st, 2, rec->version);
+ sqlite3_bind_int(st, 7, rec->idx);
+
+ assert(sqlite3_step(st) == SQLITE_DONE);
+
+ sqlite3_finalize(st);
+}
+
+/* This does the majority of the work that actually modifies or
+ * creates a file. It expects a filled and signed tm_request
+ * structure, req, and will return the resulting FR certificate and
+ * its signature in *hmac_out. Additionally, the module's
+ * authenticated acknowledgement (equal to HMAC(req | 0), where |
+ * indicates concatenation) is output in *ack_hmac_out.
+ *
+ * If the request is to modify the file, the parameters
+ * encrypted_secret, kf, buildcode, composefile, encrypted_contents,
+ * and contents_len are used (otherwise they are
+ * ignored). `encrypted_secret' should be the file encryption key
+ * XOR'd with HMAC(file index | file counter, user_key). kf should be
+ * HMAC(encryption secret, file index).
+ *
+ * If the request is to either modify the ACL or create a file (which
+ * is essentially an ACL update), the ACL will be set to new_acl. This
+ * function will make a copy of new_acl, so it can safely be freed
+ * after calling this function. */
+void sp_request(struct service_provider *sp,
+ const struct tm_request *req,
+ const void *encrypted_contents, size_t contents_len)
+{
+ /* update the corresponding file record */
+ struct file_record *rec = lookup_record(sp, req->idx);
+
+ bool need_insert = false;
+ if(!rec)
+ {
+ rec = calloc(1, sizeof(struct file_record));
+ need_insert = true;
+ }
+
+ rec->idx = req->idx;
+
+ if(req->type == FILE_UPDATE)
+ {
+ rec->version++;
+
+ /* write to disk */
+ write_contents(sp, req->idx, rec->version,
+ encrypted_contents, contents_len);
+ }
+
+ if(need_insert)
+ insert_record(sp, rec);
+ else
+ update_record(sp, rec);
+
+ free_record(rec);
+}
+
+int next_slot(struct service_provider *sp)
+{
+ const char *sql = "SELECT MAX(Idx) from FileRecords;";
+
+ static sqlite3_stmt *st = NULL;
+
+ sqlite3 *handle = sp->db;
+
+ if(!st)
+ sqlite3_prepare_v2(handle, sql, -1, &st, 0);
+
+ sqlite3_reset(st);
+
+ int rc = sqlite3_step(st);
+
+ if(rc == SQLITE_ROW)
+ {
+ return sqlite3_column_int(st, 0) + 1;
+ }
+
+ return 1;
+}
+
+int sp_createfile(struct service_provider *sp,
+ uint64_t user_id)
+{
+ int i = next_slot(sp);
+
+ struct tm_request req;
+ req.idx = i;
+ req.user_id = user_id;
+ req.type = ACL_UPDATE;
+ req.counter = 0;
+
+ sp_request(sp,
+ &req,
+ NULL, 0);
+
+ return i;
+}
+
+struct tm_request sp_modifyfile(struct service_provider *sp,
+ uint64_t user_id,
+ uint64_t file_idx,
+ const void *encrypted_file, size_t filelen)
+{
+ /* modification */
+ struct file_record *rec = lookup_record(sp, file_idx);
+ if(!rec)
+ {
+ printf("Could not find file with index %lu\n", file_idx);
+ return req_null;
+ }
+
+ struct tm_request req;
+ req.idx = file_idx;
+ req.user_id = user_id;
+ req.type = FILE_UPDATE;
+
+ sp_request(sp,
+ &req,
+ encrypted_file, filelen);
+
+ return req;
+}
+
+/* Retrieve authenticated information (using the user's secret as the
+ * key) on a version of a file; if version is zero, default to the
+ * latest version. If the file does not exist, the function will still
+ * succeed, returning an authenticated structure indicating
+ * failure. */
+struct version_info sp_fileinfo(struct service_provider *sp,
+ uint64_t user_id,
+ uint64_t file_idx,
+ uint64_t version)
+{
+ struct file_record *rec = lookup_record(sp, file_idx);
+
+ if(!rec)
+ {
+ return (struct version_info) { file_idx, 0, 0, 0, hash_null, hash_null };
+ }
+
+ if(!version)
+ version = rec->version;
+
+ struct version_info ret = (struct version_info) { rec->idx, 0, version, rec->version, hash_null, hash_null };
+
+ return ret;
+}
+
+/* This file retrieves the file given by file_idx for a given
+ * user. *encrypted_secret will be set to the encryption key XOR'd
+ * with HMAC(kf, K). kf will be returned via the *kf pointer. The
+ * returned value is dynamically allocated and must be freed by the
+ * caller. This function returns NULL upon failure. An authenticated
+ * proof that the request cannot be satisfied can be obtained by
+ * calling sp_fileinfo. */
+void *sp_retrieve_file(struct service_provider *sp,
+ uint64_t user_id,
+ uint64_t file_idx,
+ uint64_t version,
+ size_t *len)
+{
+ struct file_record *rec = lookup_record(sp, file_idx);
+
+ if(!rec || rec->version == 0)
+ {
+ /* Newly created file, no contents. We don't bother to set
+ * *encrypted_secret or *len. Or, file does not exist. */
+ *len = 0;
+ return NULL;
+ }
+
+ if(!version)
+ version = rec->version;
+
+ void *ret = read_contents(sp, file_idx, version, len);
+
+ return ret;
+}
+
+static void sp_handle_client(struct service_provider *sp, int cl)
+{
+ /* We should probably fork() here to avoid blocking */
+ struct user_request user_req;
+ if(recv(cl, &user_req, sizeof(user_req), MSG_WAITALL) != sizeof(user_req))
+ return;
+
+ switch(user_req.type)
+ {
+ case CREATE_FILE:
+ {
+ printf("Client: create file\n");
+ uint64_t slot = sp_createfile(sp, user_req.user_id);
+ write(cl, &slot, sizeof(slot));
+ break;
+ }
+ case MODIFY_FILE:
+ {
+ printf("Client: modify file\n");
+ size_t filelen;
+ recv(cl, &filelen, sizeof(filelen), MSG_WAITALL);
+
+ printf("File is %lu bytes.\n", filelen);
+ void *filebuf = malloc(filelen);
+ recv(cl, filebuf, filelen, MSG_WAITALL);
+
+ sp_modifyfile(sp,
+ user_req.user_id,
+ user_req.modify_file.file_idx,
+ filebuf, filelen);
+ break;
+ }
+ case RETRIEVE_INFO:
+ {
+ printf("Client: retrieve info\n");
+ struct version_info verinfo = sp_fileinfo(sp,
+ user_req.user_id,
+ user_req.retrieve.file_idx,
+ user_req.retrieve.version);
+ write(cl, &verinfo, sizeof(verinfo));
+
+ break;
+ }
+ case RETRIEVE_FILE:
+ {
+ printf("Client: retrieve file\n");
+ size_t len = 0;
+ void *contents = sp_retrieve_file(sp,
+ user_req.user_id,
+ user_req.retrieve.file_idx,
+ user_req.retrieve.version,
+ &len);
+
+ write(cl, &len, sizeof(len));
+ if(contents)
+ write(cl, contents, len);
+
+ break;
+ }
+ case USERREQ_NONE:
+ {
+ printf("null request\n");
+ exit(1);
+ }
+ }
+}
+
+int sp_main(int sockfd, int logleaves, const char *dbpath, bool overwrite)
+{
+ (void) overwrite;
+#define BACKLOG 10
+
+ if(listen(sockfd, BACKLOG) < 0)
+ {
+ perror("listen");
+ return 1;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ struct service_provider *sp = sp_new("a", 1, logleaves, "files", dbpath);
+
+ while(1)
+ {
+ int cl;
+
+ if((cl = accept(sockfd, NULL, NULL)) < 0)
+ {
+ perror("accept");
+ return 1;
+ }
+
+ sp_handle_client(sp, cl);
+ close(cl);
+ }
+}
diff --git a/iomt.c b/iomt.c
index 1a92568..2b1fb33 100644
--- a/iomt.c
+++ b/iomt.c
@@ -306,7 +306,9 @@ void merkle_update(struct iomt *tree, uint64_t leafidx, hash_t newval, hash_t **
hash_t iomt_getroot(const struct iomt *tree)
{
- return iomt_getnode(tree, 0);
+ if(tree)
+ return iomt_getnode(tree, 0);
+ return hash_null;
}
/* find a node with given idx */
@@ -570,6 +572,7 @@ struct iomt *iomt_dup(const struct iomt *oldtree)
newtree->mt_leafcount = oldtree->mt_leafcount;
newtree->mt_logleaves = oldtree->mt_logleaves;
+ newtree->in_memory = true;
newtree->mem.mt_leaves = calloc(oldtree->mt_leafcount, sizeof(struct iomt_node));
newtree->mem.mt_nodes = calloc(2 * oldtree->mt_leafcount - 1, sizeof(hash_t));
diff --git a/main.c b/main.c
index bc8717a..3d2c808 100644
--- a/main.c
+++ b/main.c
@@ -37,23 +37,33 @@ void signal_handler(int sig)
exit(1);
}
+#ifndef DUMMY
void run_tests(void)
{
crypto_test();
tm_test();
sp_test();
}
+#endif
int main(int argc, char *argv[])
{
+#ifndef DUMMY
//run_tests();
+#endif
+ bool overwrite = false;
+
+ /* TODO: real parsing */
const char *dbpath = "csaa.db";
int logleaves = 10;
if(argc >= 2)
logleaves = atol(argv[1]);
if(argc >= 3)
dbpath = argv[2];
+ if(argc >= 4)
+ if(!strcmp(argv[3], "--overwrite"))
+ overwrite = true;
const char *socket_name = "socket";
int sockfd;
@@ -71,6 +81,7 @@ int main(int argc, char *argv[])
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGSEGV, signal_handler);
+ signal(SIGABRT, signal_handler);
- sp_main(sockfd, logleaves, dbpath);
+ sp_main(sockfd, logleaves, dbpath, overwrite);
}
diff --git a/service_provider.c b/service_provider.c
index 96f12d2..d17cd4e 100644
--- a/service_provider.c
+++ b/service_provider.c
@@ -184,7 +184,26 @@ void *read_contents(const struct service_provider *sp,
return buf;
}
-void *db_init(const char *filename)
+int count_rows(void *db, const char *table)
+{
+ char buf[1000];
+ snprintf(buf, sizeof(buf), "SELECT COUNT(*) FROM %s;", table);
+
+ sqlite3_stmt *st;
+ sqlite3_prepare_v2(db, buf, -1, &st, 0);
+
+ /* no table */
+ if(sqlite3_step(st) != SQLITE_ROW)
+ return 0;
+
+ int rows = sqlite3_column_int(st, 0);
+
+ sqlite3_finalize(st);
+
+ return rows;
+}
+
+void *db_init(const char *filename, bool overwrite, bool *need_init)
{
sqlite3 *db;
if(sqlite3_open(filename, &db) != SQLITE_OK)
@@ -193,6 +212,19 @@ void *db_init(const char *filename)
sqlite3_exec(db, "PRAGMA synchronous = 0;", 0, 0, 0);
sqlite3_exec(db, "PRAGMA journal_mode = memory;", 0, 0, 0);
+ if(overwrite || count_rows(db, "FileLeaves") == 0)
+ {
+ extern unsigned char sqlinit_txt[];
+
+ /* create tables */
+ char *msg;
+ assert(sqlite3_exec(db, (const char*)sqlinit_txt, NULL, NULL, &msg) == SQLITE_OK);
+
+ *need_init = true;
+ }
+ else
+ *need_init = false;
+
return db;
}
@@ -209,12 +241,18 @@ void commit_transaction(void *db)
}
/* leaf count will be 2^logleaves */
-struct service_provider *sp_new(const void *key, size_t keylen, int logleaves, const char *data_dir, const char *dbpath)
+/* will use old DB contents unless overwrite_db is true */
+struct service_provider *sp_new(const void *key, size_t keylen,
+ int logleaves,
+ const char *data_dir,
+ const char *dbpath,
+ bool overwrite_db)
{
assert(logleaves > 0);
struct service_provider *sp = calloc(1, sizeof(*sp));
- sp->db = db_init(dbpath);
+ bool iomt_init = true;
+ sp->db = db_init(dbpath, overwrite_db, &iomt_init);
sp->tm = tm_new(key, keylen);
@@ -223,75 +261,79 @@ struct service_provider *sp_new(const void *key, size_t keylen, int logleaves, c
sp->data_dir = data_dir;
- clock_t start = clock();
+ if(iomt_init)
+ {
+ printf("Initializing IOMT with %llu nodes.\n", 1ULL << logleaves);
- /* The trusted module initializes itself with a single placeholder
- * node (1,0,1). We first update our list of IOMT leaves. Then we
- * insert our desired number of nodes by using EQ certificates to
- * update the internal IOMT root. Note that leaf indices are
- * 1-indexed. */
- iomt_update_leaf_full(sp->iomt,
- 0,
- 1, 1, hash_null);
+ clock_t start = clock();
- for(int i = 1; i < sp->iomt->mt_leafcount; ++i)
- {
- /* generate EQ certificate */
- hash_t hmac;
- struct tm_cert eq = cert_eq(sp,
- iomt_getleaf(sp->iomt, i - 1),
- i - 1,
- i, i + 1,
- &hmac);
- assert(eq.type == EQ);
-
- /* update previous leaf's index */
- iomt_update_leaf_nextidx(sp->iomt, i - 1, i + 1);
-
- /* next_idx is set to 1 to keep everything circularly linked;
- * in the next iteration it will be updated to point to the
- * next node, if any */
- iomt_update_leaf_full(sp->iomt, i, i + 1, 1, hash_null);
-
-#if 0
- if(i % 10 == 0)
+ /* The trusted module initializes itself with a single placeholder
+ * node (1,0,1). We first update our list of IOMT leaves. Then we
+ * insert our desired number of nodes by using EQ certificates to
+ * update the internal IOMT root. Note that leaf indices are
+ * 1-indexed. */
+ iomt_update_leaf_full(sp->iomt,
+ 0,
+ 1, 1, hash_null);
+
+ for(int i = 1; i < sp->iomt->mt_leafcount; ++i)
{
- commit_transaction(sp->db);
- begin_transaction(sp->db);
+ /* generate EQ certificate */
+ hash_t hmac;
+ struct tm_cert eq = cert_eq(sp,
+ iomt_getleaf(sp->iomt, i - 1),
+ i - 1,
+ i, i + 1,
+ &hmac);
+ assert(eq.type == EQ);
+
+ /* update previous leaf's index */
+ iomt_update_leaf_nextidx(sp->iomt, i - 1, i + 1);
+
+ /* next_idx is set to 1 to keep everything circularly linked;
+ * in the next iteration it will be updated to point to the
+ * next node, if any */
+ iomt_update_leaf_full(sp->iomt, i, i + 1, 1, hash_null);
+
+ assert(tm_set_equiv_root(sp->tm, &eq, hmac));
+ //printf("%d\n", i);
}
-#endif
- assert(tm_set_equiv_root(sp->tm, &eq, hmac));
- //printf("%d\n", i);
- }
+ /* now transfer to database */
+ printf("IOMT initialized in memory, transferring to DB...\n");
- /* now transfer to database */
- printf("IOMT initialized in memory, transferring to DB...\n");
+ clock_t point1 = clock();
- clock_t point1 = clock();
+ begin_transaction(sp->db);
- begin_transaction(sp->db);
+ /* we must do it this way because cert_eq expects sp->iomt to be
+ * the working tree */
+ struct iomt *old = sp->iomt;
- /* we must do it this way because cert_eq expects sp->iomt to be
- * the working tree */
- struct iomt *old = sp->iomt;
+ sp->iomt = iomt_dup_in_db(sp->db,
+ "FileNodes", "FileLeaves",
+ NULL, 0,
+ NULL, 0,
+ old);
- sp->iomt = iomt_dup_in_db(sp->db,
- "FileNodes", "FileLeaves",
- NULL, 0,
- NULL, 0,
- old);
+ commit_transaction(sp->db);
- commit_transaction(sp->db);
+ iomt_free(old);
- iomt_free(old);
+ clock_t stop = clock();
- clock_t stop = clock();
-
- printf("sp_init(): logleaves=%d, time=%.3fsec, overall_rate=%.3f/sec, db_time=%.3fsec\n",
- logleaves, (double)(stop - start) / CLOCKS_PER_SEC,
- (double)(1ULL << logleaves) * CLOCKS_PER_SEC / ( stop - start ),
- (double)(stop - point1) / CLOCKS_PER_SEC);
+ printf("sp_init(): logleaves=%d, time=%.3fsec, overall_rate=%.3f/sec, db_time=%.3fsec\n",
+ logleaves, (double)(stop - start) / CLOCKS_PER_SEC,
+ (double)(1ULL << logleaves) * CLOCKS_PER_SEC / ( stop - start ),
+ (double)(stop - point1) / CLOCKS_PER_SEC);
+ }
+ else
+ {
+ int leaves = count_rows(sp->db, "FileLeaves");
+ if(leaves != (1ULL << logleaves))
+ warn("logleaves value is inconsistent with leaf count in IOMT! (have %d, expect %d)",
+ leaves, 1 << logleaves);
+ }
return sp;
}
@@ -596,6 +638,7 @@ struct tm_cert sp_request(struct service_provider *sp,
else
{
ver.encrypted_secret = hash_null;
+ ver.kf = hash_null;
}
ver.version = fr.fr.version;
@@ -653,6 +696,7 @@ struct tm_request sp_createfile(struct service_provider *sp,
{
int i;
+ /* TODO: use database? */
/* Find an empty leaf node */
for(i = 0; i < sp->iomt->mt_leafcount; ++i)
{
@@ -816,6 +860,8 @@ struct tm_request sp_modifyfile(struct service_provider *sp,
struct tm_cert vr;
hash_t vr_hmac, fr_hmac;
+ printf("Modifying file with new kf=%s.\n", hash_format(kf, 4).str);
+
struct tm_cert new_fr = sp_request(sp,
&req, req_hmac,
&fr_hmac,
@@ -920,6 +966,8 @@ struct version_info sp_fileinfo(struct service_provider *sp,
struct file_version *ver = lookup_version(sp, rec->idx, version);
+ printf("Version kf=%s\n", hash_format(ver->kf, 4).str);
+
if(acl_out)
*acl_out = iomt_dup(rec->acl);
@@ -967,6 +1015,12 @@ void *sp_retrieve_file(struct service_provider *sp,
struct file_version *ver = lookup_version(sp, file_idx, version);
+ if(!ver)
+ {
+ *len = 0;
+ return NULL;
+ }
+
hash_t rv1_hmac, rv2_hmac;
struct tm_cert rv1 = cert_rv_by_idx(sp->tm, sp->iomt, file_idx, &rv1_hmac);
struct tm_cert rv2 = cert_rv_by_idx(sp->tm, rec->acl, user_id, &rv2_hmac);
@@ -1148,7 +1202,7 @@ static void sp_handle_client(struct service_provider *sp, int cl)
}
}
-int sp_main(int sockfd, int logleaves, const char *dbpath)
+int sp_main(int sockfd, int logleaves, const char *dbpath, bool overwrite)
{
#define BACKLOG 10
@@ -1160,12 +1214,7 @@ int sp_main(int sockfd, int logleaves, const char *dbpath)
signal(SIGPIPE, SIG_IGN);
- printf("Initializing IOMT with logleaves = %d...\n", logleaves);
-
- struct service_provider *sp = sp_new("a", 1, logleaves, "files", dbpath);
-
- /* test init only */
- return 0;
+ struct service_provider *sp = sp_new("a", 1, logleaves, "files", dbpath, overwrite);
while(1)
{
@@ -1191,10 +1240,9 @@ static hash_t test_sign_request(void *userdata, const struct tm_request *req)
void sp_test(void)
{
int logleaves = 1;
- printf("Initializing IOMT with %llu nodes.\n", 1ULL << logleaves);
clock_t start = clock();
- struct service_provider *sp = sp_new("a", 1, logleaves, "files", "csaa.db");
+ struct service_provider *sp = sp_new("a", 1, logleaves, "files", "csaa.db", true);
clock_t stop = clock();
check("Tree initialization", sp != NULL);
diff --git a/service_provider.h b/service_provider.h
index fe7a4cd..fdc7bc0 100644
--- a/service_provider.h
+++ b/service_provider.h
@@ -69,7 +69,8 @@ struct service_provider *sp_new(const void *key,
size_t keylen,
int logleaves,
const char *data_dir,
- const char *dbpath);
+ const char *dbpath,
+ bool overwrite);
void sp_free(struct service_provider *sp);
/* see .c file for documentation */
@@ -129,9 +130,11 @@ void *sp_retrieve_file(struct service_provider *sp,
struct iomt **composefile,
size_t *len);
-int sp_main(int sockfd, int logleaves, const char *dbpath);
-
void sp_test(void);
#endif
+#if defined(DUMMY) || !defined(CLIENT)
+int sp_main(int sockfd, int logleaves, const char *dbpath, bool overwrite);
+#endif
+
#endif
diff --git a/sqlinit.c b/sqlinit.c
new file mode 100644
index 0000000..bfa2599
--- /dev/null
+++ b/sqlinit.c
@@ -0,0 +1,201 @@
+unsigned char sqlinit_txt[] = {
+ 0x50, 0x52, 0x41, 0x47, 0x4d, 0x41, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x69,
+ 0x67, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x4f, 0x46,
+ 0x46, 0x3b, 0x0a, 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42,
+ 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53,
+ 0x20, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73,
+ 0x3b, 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45,
+ 0x20, 0x49, 0x46, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x46,
+ 0x69, 0x6c, 0x65, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3b, 0x0a, 0x44,
+ 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46,
+ 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x46, 0x69, 0x6c, 0x65,
+ 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x3b, 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20,
+ 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x45, 0x58, 0x49,
+ 0x53, 0x54, 0x53, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+ 0x3b, 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45,
+ 0x20, 0x49, 0x46, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x41,
+ 0x43, 0x4c, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3b, 0x0a, 0x44, 0x52,
+ 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20,
+ 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x41, 0x43, 0x4c, 0x4e, 0x6f,
+ 0x64, 0x65, 0x73, 0x3b, 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41,
+ 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54,
+ 0x53, 0x20, 0x42, 0x43, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3b, 0x0a,
+ 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49,
+ 0x46, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x42, 0x43, 0x4e,
+ 0x6f, 0x64, 0x65, 0x73, 0x3b, 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54,
+ 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x45, 0x58, 0x49, 0x53,
+ 0x54, 0x53, 0x20, 0x43, 0x46, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3b,
+ 0x0a, 0x44, 0x52, 0x4f, 0x50, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20,
+ 0x49, 0x46, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x43, 0x46,
+ 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41,
+ 0x54, 0x45, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20,
+ 0x4e, 0x4f, 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x46,
+ 0x69, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x28,
+ 0x0a, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52,
+ 0x2c, 0x0a, 0x56, 0x65, 0x72, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45,
+ 0x52, 0x2c, 0x0a, 0x43, 0x74, 0x72, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47,
+ 0x45, 0x52, 0x2c, 0x0a, 0x43, 0x65, 0x72, 0x74, 0x20, 0x42, 0x4c, 0x4f,
+ 0x42, 0x2c, 0x0a, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x42, 0x4c, 0x4f, 0x42,
+ 0x2c, 0x0a, 0x41, 0x43, 0x4c, 0x5f, 0x6c, 0x6f, 0x67, 0x6c, 0x65, 0x61,
+ 0x76, 0x65, 0x73, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x0a,
+ 0x29, 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x20, 0x54,
+ 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x4e, 0x4f, 0x54, 0x20,
+ 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x4c,
+ 0x65, 0x61, 0x76, 0x65, 0x73, 0x20, 0x28, 0x0a, 0x4c, 0x65, 0x61, 0x66,
+ 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c,
+ 0x0a, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52,
+ 0x2c, 0x0a, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e,
+ 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61, 0x6c, 0x20, 0x42,
+ 0x4c, 0x4f, 0x42, 0x2c, 0x0a, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59,
+ 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x4c, 0x65, 0x61, 0x66, 0x49, 0x64,
+ 0x78, 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54,
+ 0x45, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x4e,
+ 0x4f, 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x46, 0x69,
+ 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x28, 0x0a, 0x4e, 0x6f,
+ 0x64, 0x65, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45,
+ 0x52, 0x2c, 0x0a, 0x56, 0x61, 0x6c, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c,
+ 0x0a, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x20, 0x4b, 0x45, 0x59,
+ 0x20, 0x28, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x78, 0x29, 0x0a, 0x29,
+ 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x20, 0x54, 0x41,
+ 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x45,
+ 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x73, 0x20, 0x28, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78,
+ 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45,
+ 0x52, 0x2c, 0x0a, 0x4b, 0x46, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c, 0x0a,
+ 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65,
+ 0x63, 0x72, 0x65, 0x74, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c, 0x0a, 0x43,
+ 0x65, 0x72, 0x74, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c, 0x0a, 0x48, 0x4d,
+ 0x41, 0x43, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c, 0x0a, 0x42, 0x75, 0x69,
+ 0x6c, 0x64, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x6c, 0x65,
+ 0x61, 0x76, 0x65, 0x73, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52,
+ 0x2c, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x66, 0x69, 0x6c,
+ 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x20,
+ 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x50, 0x52, 0x49,
+ 0x4d, 0x41, 0x52, 0x59, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69,
+ 0x6c, 0x65, 0x49, 0x64, 0x78, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x29, 0x2c, 0x0a, 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e,
+ 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64,
+ 0x78, 0x29, 0x20, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45,
+ 0x53, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
+ 0x73, 0x28, 0x49, 0x64, 0x78, 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43,
+ 0x52, 0x45, 0x41, 0x54, 0x45, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20,
+ 0x49, 0x46, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54,
+ 0x53, 0x20, 0x41, 0x43, 0x4c, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x20,
+ 0x28, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e,
+ 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x4c, 0x65, 0x61, 0x66, 0x49,
+ 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a,
+ 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c,
+ 0x0a, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54,
+ 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61, 0x6c, 0x20, 0x42, 0x4c,
+ 0x4f, 0x42, 0x2c, 0x0a, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x20,
+ 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78,
+ 0x2c, 0x20, 0x4c, 0x65, 0x61, 0x66, 0x49, 0x64, 0x78, 0x29, 0x2c, 0x0a,
+ 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59, 0x20,
+ 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x29, 0x20, 0x52, 0x45,
+ 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x46, 0x69, 0x6c,
+ 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x28, 0x49, 0x64, 0x78,
+ 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
+ 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x4e, 0x4f,
+ 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x41, 0x43, 0x4c,
+ 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x28, 0x0a, 0x46, 0x69, 0x6c, 0x65,
+ 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c,
+ 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54,
+ 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61, 0x6c, 0x20, 0x42, 0x4c,
+ 0x4f, 0x42, 0x2c, 0x0a, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x20,
+ 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78,
+ 0x2c, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x78, 0x29, 0x2c, 0x0a,
+ 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59, 0x20,
+ 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x29, 0x20, 0x52, 0x45,
+ 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x46, 0x69, 0x6c,
+ 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x28, 0x49, 0x64, 0x78,
+ 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
+ 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x4e, 0x4f,
+ 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x42, 0x43, 0x4c,
+ 0x65, 0x61, 0x76, 0x65, 0x73, 0x20, 0x28, 0x0a, 0x46, 0x69, 0x6c, 0x65,
+ 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c,
+ 0x0a, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x4e, 0x54,
+ 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x4c, 0x65, 0x61, 0x66, 0x49, 0x64,
+ 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x49,
+ 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a,
+ 0x4e, 0x65, 0x78, 0x74, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45,
+ 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61, 0x6c, 0x20, 0x42, 0x4c, 0x4f,
+ 0x42, 0x2c, 0x0a, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x20, 0x4b,
+ 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x2c,
+ 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x4c, 0x65,
+ 0x61, 0x66, 0x49, 0x64, 0x78, 0x29, 0x2c, 0x0a, 0x46, 0x4f, 0x52, 0x45,
+ 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c,
+ 0x65, 0x49, 0x64, 0x78, 0x29, 0x20, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45,
+ 0x4e, 0x43, 0x45, 0x53, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x63,
+ 0x6f, 0x72, 0x64, 0x73, 0x28, 0x49, 0x64, 0x78, 0x29, 0x2c, 0x0a, 0x46,
+ 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28,
+ 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x52, 0x45, 0x46,
+ 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x56, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
+ 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x49, 0x46, 0x20, 0x4e, 0x4f,
+ 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x20, 0x42, 0x43, 0x4e,
+ 0x6f, 0x64, 0x65, 0x73, 0x20, 0x28, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x49,
+ 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a,
+ 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x4e, 0x54, 0x45,
+ 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61, 0x6c, 0x20, 0x42, 0x4c, 0x4f,
+ 0x42, 0x2c, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x78, 0x20, 0x49,
+ 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x50, 0x52, 0x49, 0x4d,
+ 0x41, 0x52, 0x59, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c,
+ 0x65, 0x49, 0x64, 0x78, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x2c, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x78, 0x29, 0x2c,
+ 0x0a, 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59,
+ 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x29, 0x20, 0x52,
+ 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x46, 0x69,
+ 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x28, 0x49, 0x64,
+ 0x78, 0x29, 0x2c, 0x0a, 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20,
+ 0x4b, 0x45, 0x59, 0x20, 0x28, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x29, 0x20, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53,
+ 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x56, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43,
+ 0x52, 0x45, 0x41, 0x54, 0x45, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20,
+ 0x49, 0x46, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54,
+ 0x53, 0x20, 0x43, 0x46, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x20, 0x28,
+ 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54,
+ 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x4c,
+ 0x65, 0x61, 0x66, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47,
+ 0x45, 0x52, 0x2c, 0x0a, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45,
+ 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x64, 0x78,
+ 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61,
+ 0x6c, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c, 0x0a, 0x50, 0x52, 0x49, 0x4d,
+ 0x41, 0x52, 0x59, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c,
+ 0x65, 0x49, 0x64, 0x78, 0x2c, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x2c, 0x20, 0x4c, 0x65, 0x61, 0x66, 0x49, 0x64, 0x78, 0x29, 0x2c,
+ 0x0a, 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59,
+ 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x29, 0x20, 0x52,
+ 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x46, 0x69,
+ 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x28, 0x49, 0x64,
+ 0x78, 0x29, 0x2c, 0x0a, 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47, 0x4e, 0x20,
+ 0x4b, 0x45, 0x59, 0x20, 0x28, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x29, 0x20, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x53,
+ 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x56, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x0a, 0x29, 0x3b, 0x0a, 0x0a, 0x43,
+ 0x52, 0x45, 0x41, 0x54, 0x45, 0x20, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x20,
+ 0x49, 0x46, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x45, 0x58, 0x49, 0x53, 0x54,
+ 0x53, 0x20, 0x43, 0x46, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x28, 0x0a,
+ 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45,
+ 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c, 0x0a, 0x56, 0x61,
+ 0x6c, 0x20, 0x42, 0x4c, 0x4f, 0x42, 0x2c, 0x0a, 0x4e, 0x6f, 0x64, 0x65,
+ 0x49, 0x64, 0x78, 0x20, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x2c,
+ 0x0a, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x20, 0x4b, 0x45, 0x59,
+ 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x78, 0x2c, 0x20, 0x56,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x4e, 0x6f, 0x64, 0x65,
+ 0x49, 0x64, 0x78, 0x29, 0x2c, 0x0a, 0x46, 0x4f, 0x52, 0x45, 0x49, 0x47,
+ 0x4e, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x46, 0x69, 0x6c, 0x65, 0x49,
+ 0x64, 0x78, 0x29, 0x20, 0x52, 0x45, 0x46, 0x45, 0x52, 0x45, 0x4e, 0x43,
+ 0x45, 0x53, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72,
+ 0x64, 0x73, 0x28, 0x49, 0x64, 0x78, 0x29, 0x2c, 0x0a, 0x46, 0x4f, 0x52,
+ 0x45, 0x49, 0x47, 0x4e, 0x20, 0x4b, 0x45, 0x59, 0x20, 0x28, 0x56, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x52, 0x45, 0x46, 0x45, 0x52,
+ 0x45, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x73, 0x28, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x0a,
+ 0x29, 0x3b, 0x0a, 0x00
+};
+unsigned int sqlinit_txt_len = 2367;
diff --git a/sqlinit.txt b/sqlinit.txt
index 8f17229..06cd076 100644
--- a/sqlinit.txt
+++ b/sqlinit.txt
@@ -1,4 +1,17 @@
-CREATE TABLE FileRecords (
+PRAGMA foreign_keys = OFF;
+
+DROP TABLE IF EXISTS FileRecords;
+DROP TABLE IF EXISTS FileLeaves;
+DROP TABLE IF EXISTS FileNodes;
+DROP TABLE IF EXISTS Versions;
+DROP TABLE IF EXISTS ACLLeaves;
+DROP TABLE IF EXISTS ACLNodes;
+DROP TABLE IF EXISTS BCLeaves;
+DROP TABLE IF EXISTS BCNodes;
+DROP TABLE IF EXISTS CFLeaves;
+DROP TABLE IF EXISTS CFNodes;
+
+CREATE TABLE IF NOT EXISTS FileRecords (
Idx INTEGER,
Ver INTEGER,
Ctr INTEGER,
@@ -7,7 +20,7 @@ HMAC BLOB,
ACL_logleaves INTEGER
);
-CREATE TABLE FileLeaves (
+CREATE TABLE IF NOT EXISTS FileLeaves (
LeafIdx INTEGER,
Idx INTEGER,
NextIdx INTEGER,
@@ -15,13 +28,13 @@ Val BLOB,
PRIMARY KEY (LeafIdx)
);
-CREATE TABLE FileNodes (
+CREATE TABLE IF NOT EXISTS FileNodes (
NodeIdx INTEGER,
Val BLOB,
PRIMARY KEY (NodeIdx)
);
-CREATE TABLE Versions (
+CREATE TABLE IF NOT EXISTS Versions (
FileIdx INTEGER,
Version INTEGER,
KF BLOB,
@@ -34,7 +47,7 @@ PRIMARY KEY (FileIdx, Version),
FOREIGN KEY (FileIdx) REFERENCES FileRecords(Idx)
);
-CREATE TABLE ACLLeaves (
+CREATE TABLE IF NOT EXISTS ACLLeaves (
FileIdx INTEGER,
LeafIdx INTEGER,
Idx INTEGER,
@@ -44,7 +57,7 @@ PRIMARY KEY (FileIdx, LeafIdx),
FOREIGN KEY (FileIdx) REFERENCES FileRecords(Idx)
);
-CREATE TABLE ACLNodes (
+CREATE TABLE IF NOT EXISTS ACLNodes (
FileIdx INTEGER,
NodeIdx INTEGER,
Val BLOB,
@@ -52,7 +65,7 @@ PRIMARY KEY (FileIdx, NodeIdx),
FOREIGN KEY (FileIdx) REFERENCES FileRecords(Idx)
);
-CREATE TABLE BCLeaves (
+CREATE TABLE IF NOT EXISTS BCLeaves (
FileIdx INTEGER,
Version INTEGER,
LeafIdx INTEGER,
@@ -64,7 +77,7 @@ FOREIGN KEY (FileIdx) REFERENCES FileRecords(Idx),
FOREIGN KEY (Version) REFERENCES Versions(Version)
);
-CREATE TABLE BCNodes (
+CREATE TABLE IF NOT EXISTS BCNodes (
FileIdx INTEGER,
Version INTEGER,
Val BLOB,
@@ -74,7 +87,7 @@ FOREIGN KEY (FileIdx) REFERENCES FileRecords(Idx),
FOREIGN KEY (Version) REFERENCES Versions(Version)
);
-CREATE TABLE CFLeaves (
+CREATE TABLE IF NOT EXISTS CFLeaves (
FileIdx INTEGER,
Version INTEGER,
LeafIdx INTEGER,
@@ -86,7 +99,7 @@ FOREIGN KEY (FileIdx) REFERENCES FileRecords(Idx),
FOREIGN KEY (Version) REFERENCES Versions(Version)
);
-CREATE TABLE CFNodes (
+CREATE TABLE IF NOT EXISTS CFNodes (
FileIdx INTEGER,
Version INTEGER,
Val BLOB,
diff --git a/test.c b/test.c
index e8bf44a..98c674b 100644
--- a/test.c
+++ b/test.c
@@ -1,5 +1,6 @@
#include <stdio.h>
+#include "crypto.h"
#include "service_provider.h"
#include "trusted_module.h"
diff --git a/test.h b/test.h
index 46cfa6f..0e55bda 100644
--- a/test.h
+++ b/test.h
@@ -1,5 +1,5 @@
/* testing */
void check(const char *name, int condition);
-/* in main.c */
+/* in test.c or dummy_service.c */
void run_tests(void);
diff --git a/testmodify.sh b/testmodify.sh
index 1e03f29..c836ad4 100755
--- a/testmodify.sh
+++ b/testmodify.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-./client -u 1 -k a create
+./client -u 1 -k a create > /dev/null
for i in $(seq 1 100)
do
./client -u 1 -k a modifyfile -f 1 -i container1/hello-world.tar > /dev/null