summaryrefslogtreecommitdiff
path: root/module/storage.c
diff options
context:
space:
mode:
Diffstat (limited to 'module/storage.c')
-rw-r--r--module/storage.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/module/storage.c b/module/storage.c
new file mode 100644
index 0000000..3cb9cdd
--- /dev/null
+++ b/module/storage.c
@@ -0,0 +1,200 @@
+
+/* Yes, this looks backwards. */
+typedef char nonce_t[40];
+
+struct singleid_board {
+
+ /* The association with our server */
+ char server[256];
+ char handle[256];
+ unsigned char secret[256];
+ char type[64];
+ size_t n_secret;
+ time_t expires;
+
+ /* Book keeping for the records */
+ nonce_t *records;
+ size_t first;
+ size_t total;
+ size_t count;
+ int wrapped;
+};
+
+int
+singleid_board_store_assoc (singleid_board_t *board, const singleid_assoc_t *assoc)
+{
+ assert (board);
+ assert (assoc);
+
+ /* Check that we have enough space to store this information */
+ if ((assoc->server && strlen (assoc->server) > sizeof (board->server)) ||
+ (assoc->handle && strlen (assoc->handle) > sizeof (board->handle)) ||
+ (assoc->n_secret && assoc->n_secret > sizeof (board->secret)) ||
+ (assoc->type && strlen (assoc->type) > sizeof (board->type)))
+ return 0;
+
+ strcpy (board->server, assoc->server ? assoc->server : "");
+ strcpy (board->handle, assoc->handle ? assoc->handle : "");
+ memcpy (board->secret, assoc->secret, assoc->n_secret);
+ strcpy (board->type, assoc->type ? assoc->type : "");
+ board->expires = assoc->expires;
+ return 1;
+}
+
+int
+singleid_board_find_assoc (singleid_board_t *board, const char *server,
+ const char *handle, singleid_assoc_t *assoc)
+{
+ assert (board);
+ assert (assoc);
+
+ if (server && (strlen (server) > sizeof (board->server) ||
+ strcmp (server, board->server) != 0))
+ return 0;
+
+ if (handle && (strlen (handle) > sizeof (board->handle) ||
+ strcmp (handle, board->handle) != 0))
+ return 0;
+
+ assoc->server = board->server;
+ assoc->handle = board->handle;
+ assoc->type = board->type;
+ assoc->secret = board->secret;
+ assoc->n_secret = board->n_secret;
+ assoc->expires = board->expires;
+ return 1;
+}
+
+void
+singleid_board_invalidate_assoc (singleid_board_t *board, const char *server,
+ const char *handle)
+{
+ singleid_assoc_t dummy;
+ assert (board);
+
+ if (singleid_board_find_assoc (board, server, handle, &dummy)) {
+ board->server[0] = 0;
+ board->handle[0] = 0;
+ board->type[0] = 0;
+ memset (board->secret, 0, sizeof (board->secret));
+ board->n_secret = 0;
+ board->expires = 0;
+ board->secret[0] = 0;
+ }
+}
+
+#define nonce_compare(a, b) \
+ memcmp (a, b, sizeof (nonce_t))
+
+static void
+nonce_put (nonce_t rec, const char *nonce)
+{
+ size_t len = strlen (nonce);
+ char *dst = (char*)rec;
+
+ /* If it's short enough, then just board. Fast */
+ if (len < sizeof (rec)) {
+ memcpy (dst, nonce, len);
+ memset (dst + len, 0, sizeof (rec) - len);
+
+ /* Too long, need to compress into the record */
+ } else {
+ apr_sha1_ctx_t ctx;
+
+ assert (sizeof (nonce_t) == APR_SHA1_DIGESTSIZE + 20);
+ assert (len > 20);
+
+ /* The date prefix we just copy in */
+ memcpy (dst, nonce, 20);
+
+ /* Hash the rest into the buffer */
+ apr_sha1_init (&ctx);
+ apr_sha1_update (&ctx, nonce + 20, len - 20);
+ apr_sha1_final (dst + 20, &ctx);
+ }
+}
+
+static void
+insert_nonce_record (singleid_board_t *board, nonce_t rec, size_t at)
+{
+ nonce_t *records = board->records;
+ size_t from, to, num;
+
+ assert (board->total > 2);
+ assert (at < board->total);
+ assert (at != board->first);
+
+ /* Insertion right after latest, either ancient, more likely top */
+ if (at == board->first + 1 % board->total) {
+ /* We can just copy in at this point */
+
+ /* Our ring has empty space in it, so always push forwards, but only until first */
+ } else if (!board->wrapped) {
+ memmove (records + at + 1, records + at, sizeof (rec) * (board->first - at));
+ board->first += 1;
+
+ /* Move data backwards to make space */
+ } else if (board->first < at) {
+ memmove (records + board->first + 2, records + board->first + 1,
+ sizeof (rec) * (at - board->first));
+
+ /* Move data forwards to make space simply */
+ } else {
+ memmove (records + at + 1, records + at, sizeof (rec) * (board->first - at - 1));
+ }
+
+ memcpy (records[at], rec, sizeof (rec));
+ ++board->count;
+
+ /* Track whether we have a full ring or not. */
+ if (!board->wrapped && board->count > board->total)
+ board->wrapped = 1;
+}
+
+int
+singleid_board_check_nonce (singleid_board_t *board, const char *nonce)
+{
+ nonce_t *records;
+ nonce_t rec;
+ size_t at, lower, upper, mid;
+ int res;
+
+ assert (board);
+ assert (nonce);
+ assert (board->records);
+ assert (board->first < board->total);
+
+ nonce_put (rec, nonce);
+ records = board->records;
+
+ /* Best case scenario, new nonce is higher than our latest one */
+ res = nonce_compare (rec, records[top]);
+
+ /* Was the last nonce */
+ if (res == 0) {
+ return 1;
+
+ /* Newer than anything, push on top */
+ } else if (res < 0) {
+ at = (board->first + 1) % board->total;
+ insert_nonce_record (board, rec, at);
+ board->first = at;
+ return 0;
+ }
+
+ /* Do a binary search for the item */
+ for (lower = 0, upper = board->total; lower < upper; ) {
+ mid = lower + ((upper - lower) / 2); // Note: not (low + high) / 2 !!
+ at = at + board->first % board->total;
+ res = nonce_compare (rec, records[at]);
+ if (res == 0)
+ return 1; /* Have the nonce */
+ else if (res > 0)
+ lower = mid + 1;
+ else
+ upper = mid;
+ }
+
+ insert_nonce_record (board, rec, at);
+ return 0; /* Didn't find nonce */
+}