/** * @return lifetime in seconds of the security tokens we generate. */ time_delta_t sectoken_lifetime(const sectoken_gen_t *stg) { sectoken_gen_check(stg); return stg->refresh * stg->keycnt; }
/* * Is specified token still valid for this address/port tuple? */ static bool sectoken_is_valid_internal(sectoken_gen_t *stg, const sectoken_t *tok, host_addr_t addr, uint16 port, const void *data, size_t len) { size_t i; sectoken_gen_check(stg); g_assert(tok != NULL); g_assert((NULL != data) == (len != 0)); /* * We can't decrypt, we just generate a new token with the set of * keys and say the token is valid if it matches with the one we're * generating. * * We try the most recent key first as it is the most likely to succeed. */ for (i = 0; i < stg->keycnt; i++) { sectoken_t gen; sectoken_generate_n(stg, i, &gen, addr, port, data, len); if (0 == memcmp(gen.v, tok->v, sizeof(tok->v))) return TRUE; } return FALSE; }
/** * Destroy the security token generator and nullify its pointer. */ void sectoken_gen_free_null(sectoken_gen_t **stg_ptr) { sectoken_gen_t *stg = *stg_ptr; if (stg != NULL) { sectoken_gen_check(stg); cq_cancel(&stg->rotate_ev); WFREE_NULL(stg->keys, stg->keycnt * sizeof stg->keys[0]); stg->magic = 0; WFREE(stg); *stg_ptr = NULL; } }
/** * Token key rotating event. */ static void sectoken_rotate(cqueue_t *cq, void *obj) { size_t i; sectoken_gen_t *stg = obj; sectoken_gen_check(stg); cq_zero(cq, &stg->rotate_ev); stg->rotate_ev = cq_main_insert(stg->refresh * 1000, sectoken_rotate, stg); for (i = 0; i < stg->keycnt - 1; i++) stg->keys[i + 1] = stg->keys[i]; /* 0 is most recent key */ random_strong_bytes(&stg->keys[0], sizeof(stg->keys[0])); }
/** * Create a security token from host address and port using specified key. * * Optionally, extra contextual data may be given (i.e. the token is not * only based on the address and port) to make the token more unique to * a specific context. * * @param stg the security token generator * @param n key index to use * @param tok where security token is written * @param addr address of the host for which we're generating a token * @param port port of the host for which we're generating a token * @param data optional contextual data * @param len length of contextual data */ static void sectoken_generate_n(sectoken_gen_t *stg, size_t n, sectoken_t *tok, host_addr_t addr, uint16 port, const void *data, size_t len) { char block[8]; char enc[8]; char *p = block; sectoken_gen_check(stg); g_assert(tok != NULL); g_assert(size_is_non_negative(n)); g_assert(n < stg->keycnt); g_assert((NULL != data) == (len != 0)); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: p = poke_be32(p, host_addr_ipv4(addr)); break; case NET_TYPE_IPV6: { uint val; val = binary_hash(host_addr_ipv6(&addr), 16); p = poke_be32(p, val); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_error("unexpected address for security token generation: %s", host_addr_to_string(addr)); } p = poke_be16(p, port); p = poke_be16(p, 0); /* Filler */ g_assert(p == &block[8]); STATIC_ASSERT(sizeof(tok->v) == sizeof(uint32)); STATIC_ASSERT(sizeof(block) == sizeof(enc)); tea_encrypt(&stg->keys[n], enc, block, sizeof block); /* * If they gave contextual data, encrypt them by block of 8 bytes, * filling the last partial block with zeroes if needed. */ if (data != NULL) { const void *q = data; size_t remain = len; char denc[8]; STATIC_ASSERT(sizeof(denc) == sizeof(enc)); while (remain != 0) { size_t fill = MIN(remain, 8U); unsigned i; if (fill != 8U) ZERO(&block); memcpy(block, q, fill); remain -= fill; q = const_ptr_add_offset(q, fill); /* * Encrypt block of contextual data (possibly filled with trailing * zeroes) and merge back the result into the main encryption * output with XOR. */ tea_encrypt(&stg->keys[n], denc, block, sizeof block); for (i = 0; i < sizeof denc; i++) enc[i] ^= denc[i]; } } poke_be32(tok->v, tea_squeeze(enc, sizeof enc)); }