diff options
Diffstat (limited to 'module/storage.c')
-rw-r--r-- | module/storage.c | 200 |
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 */ +} |