diff options
Diffstat (limited to 'src/lib/crypt')
| -rw-r--r-- | src/lib/crypt/keyrot.c | 741 | ||||
| -rw-r--r-- | src/lib/crypt/keyrot.h | 74 | ||||
| -rw-r--r-- | src/lib/crypt/openssl.c | 504 | ||||
| -rw-r--r-- | src/lib/crypt/openssl.h | 42 |
4 files changed, 1077 insertions, 284 deletions
diff --git a/src/lib/crypt/keyrot.c b/src/lib/crypt/keyrot.c new file mode 100644 index 00000000..8b0d9429 --- /dev/null +++ b/src/lib/crypt/keyrot.c @@ -0,0 +1,741 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * Data-plane key-rotation schedule (node/leaf keys, selector) + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#include <config.h> + +#include <ouroboros/atomics.h> +#include <ouroboros/crypt.h> +#include <ouroboros/pthread.h> +#include <ouroboros/rcu.h> + +#include "crypt/keyrot.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +/* + * Per-flow keys are addressed by (epoch, node, leaf) and derived as: + * root = per-batch HKDF PRK from the OAP exchange, wiped once expanded + * nodes = HKDF-Expand(root, "o7s-keyrot-node") -> KEY_NODE_COUNT keys + * leaf = HKDF-Expand(node, "o7s-keyrot-leaf"|dir|leaf) -> AEAD key + * The epoch is a small wrapping counter, carried in the selector, that picks + * the live batch; a Tier-2 OAP re-key advances it. The "dir" byte forks the + * leaf keys per direction. + * + * Concurrency: cur/prev batch pointers are published by a re-key and read on + * the data path under an rcu_guard (lock-free RCU with liburcu, else a per- + * keyrot rwlock). The per-batch TX counter is atomic, so the (epoch, counter) + * nonce is unique without serialising TX. Leaf caches are THREAD-LOCAL (an app + * writer and the FRCT retransmit timer never share cache state), keyed on a + * global batch id and direct-mapped. + */ + +#define KR_WITHIN_BITS (KEY_LEAF_BITS + KEY_NODE_BITS) +#define KR_WITHIN_MASK (((uint64_t) 1 << KR_WITHIN_BITS) - 1) +#define KR_N (KEY_NODE_COUNT) +#define KR_LEAVES (1u << KEY_NODE_BITS) +#define KR_BATCH_MAX ((uint64_t) KR_N << KR_WITHIN_BITS) +#define KR_NODES_SZ ((size_t) KR_N * SYMMKEYSZ) +#define KR_TCACHE_WAYS 16 /* per-thread cache slots per direction (pow2) */ +#define KR_EPOCHS 16 /* 4-bit wire epoch: gens before wrap */ + +#define KR_RP_WORDS (KEY_REPLAY_WINDOW / 64) /* pow2; RFC 6479 bitmap */ +#define KR_RP_SHIFT 6 +#define KR_RP_MASK 63 +#define KR_RP_WINDOW (KEY_REPLAY_WINDOW - 64) /* reserve 1 slack word */ + +static const char kr_node_label[] = "o7s-keyrot-node"; +static const char kr_leaf_label[] = "o7s-keyrot-leaf"; + +struct kr_batch { + uint64_t id; /* process-global, unique; cache key (no ABA) */ + uint8_t epoch; /* 4-bit wire selector */ + uint8_t * nodes; /* KR_NODES_SZ in secure heap; NULL if empty */ + uint64_t tx_ctr; /* atomic; per-batch so nonces never collide */ + + struct { /* RFC 6479-like anti-replay window */ + uint64_t last; /* highest accepted ctr + 1 */ + uint64_t bits[KR_RP_WORDS]; + pthread_mutex_t mtx; + } rp; +}; + +struct kr_keycache { + uint8_t * key; /* SYMMKEYSZ, points into the per-thread slab */ + uint64_t id; /* batch the cached key belongs to */ + uint16_t node; + uint8_t leaf; + uint8_t dir; + bool valid; +}; + +struct keyrot { + struct kr_batch * cur; /* published; read on data path */ + struct kr_batch * prev; /* NULL = none */ + struct rcu_guard guard; /* re-key vs readers */ + uint8_t role; + uint8_t tx_epoch; /* epoch TX currently stamps */ + bool peer_switched; /* peer is on the cur epoch */ +}; + +/* Per-thread leaf-key caches, freed by the thread-exit destructor. */ +struct kr_tcache { + struct kr_keycache tx[KR_TCACHE_WAYS]; + struct kr_keycache rx[KR_TCACHE_WAYS]; + uint8_t * slab; /* 2*KR_TCACHE_WAYS*SYMMKEYSZ secure heap */ +}; + +static struct { + uint64_t next_id; /* batch-id allocator (atomic) */ + pthread_key_t tcache_key; /* per-thread leaf-key caches */ + pthread_once_t tcache_once; +} kr_g = { 0, 0, PTHREAD_ONCE_INIT }; + +static void kr_tcache_free(void * p) +{ + struct kr_tcache * t = p; + + if (t == NULL) + return; + + crypt_secure_free(t->slab, 2 * KR_TCACHE_WAYS * SYMMKEYSZ); + free(t); +} + +static void kr_tcache_init(void) +{ + pthread_key_create(&kr_g.tcache_key, kr_tcache_free); +} + +static struct kr_tcache * kr_tcache_get(void) +{ + struct kr_tcache * t; + size_t i; + + pthread_once(&kr_g.tcache_once, kr_tcache_init); + + t = pthread_getspecific(kr_g.tcache_key); + if (t != NULL) + return t; + + t = malloc(sizeof(*t)); + if (t == NULL) + goto fail_alloc; + + memset(t, 0, sizeof(*t)); + + t->slab = crypt_secure_malloc(2 * KR_TCACHE_WAYS * SYMMKEYSZ); + if (t->slab == NULL) + goto fail_slab; + + for (i = 0; i < KR_TCACHE_WAYS; i++) { + t->tx[i].key = t->slab + i * SYMMKEYSZ; + t->rx[i].key = t->slab + (KR_TCACHE_WAYS + i) * SYMMKEYSZ; + } + + if (pthread_setspecific(kr_g.tcache_key, t) != 0) + goto fail_set; + + return t; + + fail_set: + crypt_secure_free(t->slab, 2 * KR_TCACHE_WAYS * SYMMKEYSZ); + fail_slab: + free(t); + fail_alloc: + return NULL; +} + +static uint8_t * kr_expand_nodes(const uint8_t * root) +{ + uint8_t * nodes; + buffer_t prk; + buffer_t info; + buffer_t okm; + + nodes = crypt_secure_malloc(KR_NODES_SZ); + if (nodes == NULL) + return NULL; + + prk.len = SYMMKEYSZ; + prk.data = (uint8_t *) root; + info.len = sizeof(kr_node_label) - 1; + info.data = (uint8_t *) kr_node_label; + okm.len = KR_NODES_SZ; + okm.data = nodes; + + if (crypt_hkdf_expand(prk, info, okm) != 0) + goto fail_expand; + + return nodes; + + fail_expand: + crypt_secure_free(nodes, KR_NODES_SZ); + return NULL; +} + +static int kr_leaf_key(const uint8_t * node, + uint8_t leaf, + uint8_t dir, + uint8_t * out) +{ + uint8_t info_buf[sizeof(kr_leaf_label) - 1 + 2]; + buffer_t prk; + buffer_t info; + buffer_t okm; + size_t n = sizeof(kr_leaf_label) - 1; + + memcpy(info_buf, kr_leaf_label, n); + info_buf[n] = dir; + info_buf[n + 1] = leaf; + + prk.len = SYMMKEYSZ; + prk.data = (uint8_t *) node; + info.len = n + 2; + info.data = info_buf; + okm.len = SYMMKEYSZ; + okm.data = out; + + return crypt_hkdf_expand(prk, info, okm); +} + +static __inline__ bool kr_kc_hit(const struct kr_keycache * kc, + const struct kr_batch * b, + uint16_t node, + uint8_t leaf, + uint8_t dir) +{ + if (!kc->valid) + return false; + + if (kc->id != b->id) + return false; + + if (kc->node != node) + return false; + + if (kc->leaf != leaf) + return false; + + return kc->dir == dir; +} + +/* Fetch the leaf key; derive into the (direct-mapped) slot on a miss. */ +static const uint8_t * kr_kc_get(struct kr_keycache * cache, + const struct kr_batch * b, + uint16_t node, + uint8_t leaf, + uint8_t dir) +{ + struct kr_keycache * kc; + uint8_t * nkey; + + kc = &cache[b->id & (KR_TCACHE_WAYS - 1)]; + + if (kr_kc_hit(kc, b, node, leaf, dir)) + return kc->key; + + nkey = b->nodes + (size_t) node * SYMMKEYSZ; + if (kr_leaf_key(nkey, leaf, dir, kc->key) != 0) + return NULL; + + kc->valid = true; + kc->id = b->id; + kc->node = node; + kc->leaf = leaf; + kc->dir = dir; + + return kc->key; +} + +static void kr_sel_enc(uint8_t epoch, + uint16_t node, + uint32_t seq, + uint8_t sel[KR_SELECTOR_LEN]) +{ + sel[0] = (uint8_t) ((epoch << 4) | ((node >> 8) & 0x0F)); + sel[1] = (uint8_t) (node & 0xFF); + sel[2] = (uint8_t) (seq >> 24); + sel[3] = (uint8_t) (seq >> 16); + sel[4] = (uint8_t) (seq >> 8); + sel[5] = (uint8_t) (seq); +} + +static void kr_sel_dec(const uint8_t sel[KR_SELECTOR_LEN], + uint8_t * epoch, + uint16_t * node, + uint32_t * seq) +{ + *epoch = (uint8_t) (sel[0] >> 4); + *node = (uint16_t) (((sel[0] & 0x0F) << 8) | sel[1]); + *seq = ((uint32_t) sel[2] << 24) | ((uint32_t) sel[3] << 16) | + ((uint32_t) sel[4] << 8) | (uint32_t) sel[5]; +} + +static uint64_t kr_ctr(uint16_t node, + uint32_t seq) +{ + return ((uint64_t) node << KR_WITHIN_BITS) | + ((uint64_t) seq & KR_WITHIN_MASK); +} + +static void kr_nonce(uint64_t ctr, + uint8_t * nonce) +{ + size_t i; + + memset(nonce, 0, KR_NONCE_LEN); + + /* ctr big-endian in the low 8 bytes; high bytes stay zero */ + for (i = 0; i < 8; i++) + nonce[i] = (uint8_t) (ctr >> (56 - 8 * i)); +} + +static struct kr_batch * kr_batch_create(uint8_t epoch, + const uint8_t * root) +{ + struct kr_batch * b; + + b = malloc(sizeof(*b)); + if (b == NULL) + goto fail_alloc; + + b->nodes = kr_expand_nodes(root); + if (b->nodes == NULL) + goto fail_nodes; + + b->id = FETCH_ADD_RELAXED(&kr_g.next_id, 1); + b->epoch = epoch; + b->tx_ctr = 0; + if (pthread_mutex_init(&b->rp.mtx, NULL) != 0) + goto fail_lock; + + b->rp.last = 0; + memset(b->rp.bits, 0, sizeof(b->rp.bits)); + + return b; + + fail_lock: + crypt_secure_free(b->nodes, KR_NODES_SZ); + free(b); + return NULL; + fail_nodes: + free(b); + fail_alloc: + return NULL; +} + +static void kr_batch_free(struct kr_batch * b) +{ + if (b == NULL) + return; + + pthread_mutex_destroy(&b->rp.mtx); + crypt_secure_free(b->nodes, KR_NODES_SZ); + free(b); +} + +/* + * RFC 6479 anti-replay window keyed on the per-batch counter, with + * seq = ctr + 1 so 0 means "nothing accepted yet". Returns 0 if the + * packet is fresh (and records it), -1 on a replay or a too-old ctr. + */ +static int kr_rp_commit(struct kr_batch * b, + uint64_t ctr) +{ + uint64_t seq; + uint64_t idx; + uint64_t cur; + uint64_t diff; + + seq = ctr + 1; + + pthread_mutex_lock(&b->rp.mtx); + + if (seq > b->rp.last) { + idx = seq >> KR_RP_SHIFT; + cur = b->rp.last >> KR_RP_SHIFT; + diff = idx - cur; + if (diff > KR_RP_WORDS) + diff = KR_RP_WORDS; + + while (diff-- > 0) { + cur++; + b->rp.bits[cur & (KR_RP_WORDS - 1)] = 0; + } + + b->rp.bits[idx & (KR_RP_WORDS - 1)] |= + (uint64_t) 1 << (seq & KR_RP_MASK); + b->rp.last = seq; + goto finish; + } + + if (b->rp.last - seq >= KR_RP_WINDOW) + goto fail; + + idx = seq >> KR_RP_SHIFT; + if (b->rp.bits[idx & (KR_RP_WORDS - 1)] + & ((uint64_t) 1 << (seq & KR_RP_MASK))) + goto fail; + + b->rp.bits[idx & (KR_RP_WORDS - 1)] |= + (uint64_t) 1 << (seq & KR_RP_MASK); + finish: + pthread_mutex_unlock(&b->rp.mtx); + + return 0; + fail: + pthread_mutex_unlock(&b->rp.mtx); + + return -1; +} + +struct keyrot * keyrot_create(const uint8_t * root, + uint8_t epoch, + uint8_t role) +{ + struct keyrot * kr; + + assert(root != NULL); + assert(role <= 1); + + if (epoch >= KR_EPOCHS) + goto fail_kr; + + kr = malloc(sizeof(*kr)); + if (kr == NULL) + goto fail_kr; + + memset(kr, 0, sizeof(*kr)); + + kr->role = role; + kr->tx_epoch = epoch; + kr->peer_switched = true; + kr->prev = NULL; + + kr->cur = kr_batch_create(epoch, root); + if (kr->cur == NULL) + goto fail_cur; + + if (rcu_guard_init(&kr->guard)) + goto fail_guard; + + return kr; + + fail_guard: + kr_batch_free(kr->cur); + fail_cur: + free(kr); + fail_kr: + return NULL; +} + +void keyrot_destroy(struct keyrot * kr) +{ + if (kr == NULL) + return; + + /* Wait out any in-flight reader before freeing batches. */ + rcu_drain(&kr->guard); + + kr_batch_free(kr->cur); + kr_batch_free(kr->prev); + + rcu_guard_fini(&kr->guard); + + free(kr); +} + +int keyrot_rekey(struct keyrot * kr, + const uint8_t * root, + uint8_t epoch) +{ + struct kr_batch * nb; + struct kr_batch * old_prev; + + assert(kr != NULL); + assert(root != NULL); + + if (epoch >= KR_EPOCHS) + return -1; + + nb = kr_batch_create(epoch, root); + if (nb == NULL) + return -1; + + rcu_wrlock(&kr->guard); + + old_prev = kr->prev; + rcu_assign(kr->prev, kr->cur); + rcu_publish(nb); + rcu_assign(kr->cur, nb); + + /* TX keeps the old epoch until the peer is seen on the new one. */ + STORE_RELEASE(&kr->peer_switched, false); + + rcu_wrunlock(&kr->guard); + + /* old_prev is unreachable now; reclaim past any live reader. */ + rcu_reclaim(&kr->guard); + kr_batch_free(old_prev); + + return 0; +} + +void keyrot_tx_promote(struct keyrot * kr) +{ + assert(kr != NULL); + + /* Serialise with keyrot_rekey so tx_epoch tracks a consistent cur. */ + rcu_wrlock(&kr->guard); + STORE_RELAXED(&kr->tx_epoch, rcu_deref(kr->cur)->epoch); + rcu_wrunlock(&kr->guard); +} + +int keyrot_tx_next(struct keyrot * kr, + uint8_t sel[KR_SELECTOR_LEN], + const uint8_t ** key, + uint8_t nonce[KR_NONCE_LEN]) +{ + struct kr_tcache * tc; + struct kr_batch * cur; + struct kr_batch * prev; + struct kr_batch * b; + uint64_t ctr; + uint16_t node; + uint8_t leaf; + uint8_t txe; + uint8_t epoch; + uint32_t seq; + const uint8_t * k; + + assert(kr != NULL); + assert(key != NULL); + + tc = kr_tcache_get(); + if (tc == NULL) + return -1; + + rcu_rdlock(&kr->guard); + + cur = rcu_deref(kr->cur); + prev = rcu_deref(kr->prev); + rcu_consume(cur); + rcu_consume(prev); + txe = LOAD_RELAXED(&kr->tx_epoch); + + if (cur->epoch == txe) + b = cur; + else if (prev != NULL && prev->epoch == txe) + b = prev; + else + b = NULL; + + if (b == NULL) { + rcu_rdunlock(&kr->guard); + return -1; /* tx_epoch batch gone; next promote resyncs */ + } + + /* Slot reserved even if exhausted; tx_nodes_left clamps the count. */ + ctr = FETCH_ADD_RELAXED(&b->tx_ctr, 1); + if (ctr >= KR_BATCH_MAX) { + rcu_rdunlock(&kr->guard); + return -1; /* batch exhausted */ + } + + node = (uint16_t) (ctr >> KR_WITHIN_BITS); + leaf = (uint8_t) ((ctr >> KEY_LEAF_BITS) & (KR_LEAVES - 1)); + seq = (uint32_t) (ctr & KR_WITHIN_MASK); + epoch = b->epoch; + + k = kr_kc_get(tc->tx, b, node, leaf, kr->role); + + rcu_rdunlock(&kr->guard); + + if (k == NULL) + return -1; + + kr_sel_enc(epoch, node, seq, sel); + kr_nonce(ctr, nonce); + + *key = k; + + return 0; +} + +int keyrot_rx_lookup(struct keyrot * kr, + const uint8_t sel[KR_SELECTOR_LEN], + const uint8_t ** key, + uint8_t nonce[KR_NONCE_LEN], + struct kr_rx * rx) +{ + struct kr_tcache * tc; + struct kr_batch * cur; + struct kr_batch * prev; + struct kr_batch * b; + uint8_t epoch; + uint16_t node; + uint32_t seq; + uint64_t ctr; + uint8_t leaf; + const uint8_t * k; + + assert(kr != NULL); + assert(key != NULL); + + kr_sel_dec(sel, &epoch, &node, &seq); + + if (node >= KR_N) + return -1; + + tc = kr_tcache_get(); + if (tc == NULL) + return -1; + + rcu_rdlock(&kr->guard); + + cur = rcu_deref(kr->cur); + prev = rcu_deref(kr->prev); + rcu_consume(cur); + rcu_consume(prev); + + if (epoch == cur->epoch) { + b = cur; + } else if (prev != NULL && epoch == prev->epoch) { + b = prev; + } else { + rcu_rdunlock(&kr->guard); + return -1; /* unknown epoch */ + } + + ctr = kr_ctr(node, seq); + leaf = (uint8_t) ((ctr >> KEY_LEAF_BITS) & (KR_LEAVES - 1)); + + /* peer's tx direction */ + k = kr_kc_get(tc->rx, b, node, leaf, (uint8_t) (kr->role ^ 1)); + + rx->id = b->id; + rx->ctr = ctr; + + rcu_rdunlock(&kr->guard); + + if (k == NULL) + return -1; + + kr_nonce(ctr, nonce); + + *key = k; + + return 0; +} + +/* + * Commit a packet that authenticated under the batch keyrot_rx_lookup + * selected. Re-finds that batch by id (epoch may have advanced) and, + * if still resident, advances the replay window and records that the + * peer is on the current batch. Runs only post-AEAD so a forged or + * replayed packet can mutate no receiver state. Returns -1 on replay. + */ +int keyrot_rx_commit(struct keyrot * kr, + const struct kr_rx * rx) +{ + struct kr_batch * cur; + struct kr_batch * prev; + struct kr_batch * b; + int rc; + + assert(kr != NULL); + assert(rx != NULL); + + rcu_rdlock(&kr->guard); + + cur = rcu_deref(kr->cur); + prev = rcu_deref(kr->prev); + rcu_consume(cur); + rcu_consume(prev); + + if (cur->id == rx->id) + b = cur; + else if (prev != NULL && prev->id == rx->id) + b = prev; + else + b = NULL; + + if (b == NULL) { + rcu_rdunlock(&kr->guard); + return 0; /* batch evicted post-auth; nothing to protect */ + } + + rc = kr_rp_commit(b, rx->ctr); + if (rc == 0 && b == cur) + STORE_RELEASE(&kr->peer_switched, true); + + rcu_rdunlock(&kr->guard); + + return rc; +} + +bool keyrot_peer_switched(const struct keyrot * kr) +{ + assert(kr != NULL); + + return LOAD_ACQUIRE(&kr->peer_switched); +} + +unsigned keyrot_tx_nodes_left(struct keyrot * kr) +{ + struct kr_batch * cur; + struct kr_batch * prev; + struct kr_batch * b; + uint64_t ctr; + unsigned used; + uint8_t txe; + + assert(kr != NULL); + + rcu_rdlock(&kr->guard); + cur = rcu_deref(kr->cur); + prev = rcu_deref(kr->prev); + rcu_consume(cur); + rcu_consume(prev); + txe = LOAD_RELAXED(&kr->tx_epoch); + + if (cur->epoch == txe) + b = cur; + else if (prev != NULL && prev->epoch == txe) + b = prev; + else + b = NULL; + + ctr = b != NULL ? LOAD_RELAXED(&b->tx_ctr) : KR_BATCH_MAX; + rcu_rdunlock(&kr->guard); + + used = (unsigned) (ctr >> KR_WITHIN_BITS); + if (used >= KR_N) + return 0; + + return KR_N - used; +} diff --git a/src/lib/crypt/keyrot.h b/src/lib/crypt/keyrot.h new file mode 100644 index 00000000..6a598f76 --- /dev/null +++ b/src/lib/crypt/keyrot.h @@ -0,0 +1,74 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * Data-plane key-rotation schedule (node/leaf keys, selector) + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_LIB_CRYPT_KEYROT_H +#define OUROBOROS_LIB_CRYPT_KEYROT_H + +#include <ouroboros/crypt.h> /* SYMMKEYSZ, NONCESZ */ + +#include <stdbool.h> +#include <stdint.h> + +#define KR_SELECTOR_LEN 6 +#define KR_NONCE_LEN NONCESZ + +struct keyrot; + +struct kr_rx { + uint64_t id; /* batch id of the matched epoch */ + uint64_t ctr; /* packet counter for replay check */ +}; + +struct keyrot * keyrot_create(const uint8_t * root, + uint8_t epoch, + uint8_t role); + +void keyrot_destroy(struct keyrot * kr); + +int keyrot_rekey(struct keyrot * kr, + const uint8_t * root, + uint8_t epoch); + +/* Promote TX to the installed (new) batch once the peer is on it. */ +void keyrot_tx_promote(struct keyrot * kr); + +int keyrot_tx_next(struct keyrot * kr, + uint8_t sel[KR_SELECTOR_LEN], + const uint8_t ** key, + uint8_t nonce[KR_NONCE_LEN]); + +int keyrot_rx_lookup(struct keyrot * kr, + const uint8_t sel[KR_SELECTOR_LEN], + const uint8_t ** key, + uint8_t nonce[KR_NONCE_LEN], + struct kr_rx * rx); + +/* Commit an authenticated packet: replay window + peer-switched. */ +int keyrot_rx_commit(struct keyrot * kr, + const struct kr_rx * rx); + +/* True once an RX packet under the current batch has been observed. */ +bool keyrot_peer_switched(const struct keyrot * kr); + +unsigned keyrot_tx_nodes_left(struct keyrot * kr); + +#endif /* OUROBOROS_LIB_CRYPT_KEYROT_H */ diff --git a/src/lib/crypt/openssl.c b/src/lib/crypt/openssl.c index d4ffc00b..7a4abec9 100644 --- a/src/lib/crypt/openssl.c +++ b/src/lib/crypt/openssl.c @@ -53,27 +53,14 @@ #define HKDF_INFO_DHE "o7s-ossl-dhe" #define HKDF_INFO_ENCAP "o7s-ossl-encap" -#define HKDF_INFO_ROTATION "o7s-key-rotation" #define HKDF_SALT_LEN 32 /* SHA-256 output size */ +#define AEAD_NONCE_LEN 12 /* 96-bit deterministic IV (SP 800-38D) */ +#define AEAD_TAG_LEN 16 /* 128-bit AEAD authentication tag */ struct ossl_crypt_ctx { EVP_CIPHER_CTX * evp_ctx; const EVP_CIPHER * cipher; - int ivsz; int tagsz; - - struct { - uint8_t * cur; /* current key */ - uint8_t * prv; /* rotated key */ - } keys; - - struct { - uint32_t cntr; /* counter */ - uint32_t mask; /* phase mask */ - uint32_t age; /* counter within epoch */ - uint8_t phase; /* current key phase */ - uint8_t salt[HKDF_SALT_LEN]; - } rot; /* rotation logic */ }; struct kdf_info { @@ -84,17 +71,6 @@ struct kdf_info { buffer_t key; }; -/* Key rotation macros */ -#define HAS_PHASE_BIT_TOGGLED(ctx) \ - (((ctx)->rot.cntr & (ctx)->rot.mask) != \ - (((ctx)->rot.cntr - 1) & (ctx)->rot.mask)) - -#define HAS_GRACE_EXPIRED(ctx) \ - ((ctx)->rot.age >= ((ctx)->rot.mask >> 1)) - -#define ROTATION_TOO_RECENT(ctx) \ - ((ctx)->rot.age < ((ctx)->rot.mask - ((ctx)->rot.mask >> 2))) - /* Convert hash NID to OpenSSL digest name string for HKDF */ static const char * hash_nid_to_digest_name(int nid) { @@ -145,21 +121,20 @@ static int get_pk_bytes_from_key(EVP_PKEY * key, } /* Derive salt from public key bytes by hashing them */ -static int derive_salt_from_pk_bytes(buffer_t pk, - uint8_t * salt, - size_t salt_len) +static int derive_salt_from_pk_bytes(buffer_t pk, + buffer_t salt) { uint8_t hash[EVP_MAX_MD_SIZE]; unsigned hash_len; assert(pk.data != NULL); - assert(salt != NULL); + assert(salt.data != NULL); if (EVP_Digest(pk.data, pk.len, hash, &hash_len, EVP_sha256(), NULL) != 1) goto fail_digest; - memcpy(salt, hash, salt_len < hash_len ? salt_len : hash_len); + memcpy(salt.data, hash, salt.len < hash_len ? salt.len : hash_len); return 0; fail_digest: @@ -167,10 +142,9 @@ static int derive_salt_from_pk_bytes(buffer_t pk, } /* Derive salt from two public key byte buffers (DHE) in canonical order */ -static int derive_salt_from_pk_bytes_dhe(buffer_t local, - buffer_t remote, - uint8_t * salt, - size_t salt_len) +static int derive_salt_from_pk_bytes_dhe(buffer_t local, + buffer_t remote, + buffer_t salt) { uint8_t * concat; size_t concat_len; @@ -181,7 +155,7 @@ static int derive_salt_from_pk_bytes_dhe(buffer_t local, assert(local.data != NULL); assert(remote.data != NULL); - assert(salt != NULL); + assert(salt.data != NULL); concat_len = local.len + remote.len; concat = OPENSSL_malloc(concat_len); @@ -205,7 +179,7 @@ static int derive_salt_from_pk_bytes_dhe(buffer_t local, OPENSSL_free(concat); - memcpy(salt, hash, salt_len < hash_len ? salt_len : hash_len); + memcpy(salt.data, hash, salt.len < hash_len ? salt.len : hash_len); return 0; fail_digest: @@ -259,117 +233,144 @@ static int derive_key_hkdf(struct kdf_info * ki) return -ECRYPT; } -/* Key rotation helper functions implementation */ -static int should_rotate_key_rx(struct ossl_crypt_ctx * ctx, - uint8_t rx_phase) +int openssl_hkdf_expand(buffer_t key, + buffer_t info, + buffer_t out) { - assert(ctx != NULL); + EVP_KDF * kdf; + EVP_KDF_CTX * kctx; + OSSL_PARAM params[5]; + int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + int idx = 0; + int ret = -1; + + kdf = EVP_KDF_fetch(NULL, "HKDF", NULL); + if (kdf == NULL) + goto fail_fetch; - /* Phase must have changed */ - if (rx_phase == ctx->rot.phase) - return 0; + kctx = EVP_KDF_CTX_new(kdf); + if (kctx == NULL) + goto fail_ctx; + + params[idx++] = OSSL_PARAM_construct_utf8_string( + "digest", (char *) "SHA256", 0); + params[idx++] = OSSL_PARAM_construct_int("mode", &mode); + params[idx++] = OSSL_PARAM_construct_octet_string( + "key", key.data, key.len); + params[idx++] = OSSL_PARAM_construct_octet_string( + "info", info.data, info.len); + params[idx] = OSSL_PARAM_construct_end(); - if (ROTATION_TOO_RECENT(ctx)) - return 0; + if (EVP_KDF_derive(kctx, out.data, out.len, params) == 1) + ret = 0; - return 1; + EVP_KDF_CTX_free(kctx); + fail_ctx: + EVP_KDF_free(kdf); + fail_fetch: + return ret; } -static int rotate_key(struct ossl_crypt_ctx * ctx) +/* AEAD seal: encrypt in with key/nonce, bind aad, append tag */ +int openssl_seal(struct ossl_crypt_ctx * ctx, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + uint8_t * out, + uint8_t * tag) { - struct kdf_info ki; - uint8_t * tmp; + int out_sz; + int tmp_sz; assert(ctx != NULL); + assert(ctx->tagsz > 0); /* AEAD mandated at ctx creation */ - /* Swap keys - move current to prev */ - tmp = ctx->keys.prv; - ctx->keys.prv = ctx->keys.cur; + EVP_CIPHER_CTX_reset(ctx->evp_ctx); - if (tmp != NULL) { - /* Reuse old prev_key memory for new key */ - ctx->keys.cur = tmp; - } else { - /* First rotation - allocate new memory */ - ctx->keys.cur = OPENSSL_secure_malloc(SYMMKEYSZ); - if (ctx->keys.cur == NULL) - return -ECRYPT; - } + if (EVP_EncryptInit_ex(ctx->evp_ctx, ctx->cipher, NULL, + NULL, NULL) != 1) + return -1; - /* Derive new key from previous key using HKDF */ - ki.secret.data = ctx->keys.prv; - ki.secret.len = SYMMKEYSZ; - ki.nid = NID_sha256; - ki.salt.data = ctx->rot.salt; - ki.salt.len = HKDF_SALT_LEN; - ki.info.data = (uint8_t *) HKDF_INFO_ROTATION; - ki.info.len = strlen(HKDF_INFO_ROTATION); - ki.key.data = ctx->keys.cur; - ki.key.len = SYMMKEYSZ; + /* Pin the AEAD nonce to 96 bits (SP 800-38D deterministic IV). */ + if (EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_SET_IVLEN, + AEAD_NONCE_LEN, NULL) != 1) + return -1; - if (derive_key_hkdf(&ki) != 0) - return -ECRYPT; + if (EVP_EncryptInit_ex(ctx->evp_ctx, NULL, NULL, + key, nonce) != 1) + return -1; - ctx->rot.age = 0; - ctx->rot.phase = !ctx->rot.phase; + if (EVP_EncryptUpdate(ctx->evp_ctx, NULL, &tmp_sz, + aad.data, (int) aad.len) != 1) + return -1; - return 0; -} + if (EVP_EncryptUpdate(ctx->evp_ctx, out, &out_sz, + in.data, (int) in.len) != 1) + return -1; -static void cleanup_old_key(struct ossl_crypt_ctx * ctx) -{ - assert(ctx != NULL); + if (EVP_EncryptFinal_ex(ctx->evp_ctx, out + out_sz, &tmp_sz) != 1) + return -1; - if (ctx->keys.prv == NULL) - return; + out_sz += tmp_sz; - if (!HAS_GRACE_EXPIRED(ctx)) - return; + if (EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_GET_TAG, + ctx->tagsz, tag) != 1) + return -1; - OPENSSL_secure_clear_free(ctx->keys.prv, SYMMKEYSZ); - ctx->keys.prv = NULL; + return out_sz; } -static int try_decrypt(struct ossl_crypt_ctx * ctx, - uint8_t * key, - uint8_t * iv, - uint8_t * input, - int in_sz, - uint8_t * out, - int * out_sz) +/* AEAD open: decrypt in with key/nonce, verify aad and tag */ +int openssl_open(struct ossl_crypt_ctx * ctx, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + const uint8_t * tag, + buffer_t * out) { - uint8_t * tag; - int tmp_sz; - int ret; + int out_sz; + int tmp_sz; - tag = input + in_sz; + assert(ctx != NULL); + assert(ctx->tagsz > 0); /* AEAD mandated at ctx creation */ EVP_CIPHER_CTX_reset(ctx->evp_ctx); - ret = EVP_DecryptInit_ex(ctx->evp_ctx, ctx->cipher, NULL, key, iv); - if (ret != 1) + if (EVP_DecryptInit_ex(ctx->evp_ctx, ctx->cipher, NULL, + NULL, NULL) != 1) return -1; - if (ctx->tagsz > 0) { - ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_SET_TAG, - ctx->tagsz, tag); - if (ret != 1) - return -1; - } + /* Pin the AEAD nonce to 96 bits (SP 800-38D deterministic IV). */ + if (EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_SET_IVLEN, + AEAD_NONCE_LEN, NULL) != 1) + return -1; - ret = EVP_DecryptUpdate(ctx->evp_ctx, out, &tmp_sz, input, in_sz); - if (ret != 1) + if (EVP_DecryptInit_ex(ctx->evp_ctx, NULL, NULL, key, nonce) != 1) return -1; - *out_sz = tmp_sz; + if (EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_SET_TAG, + ctx->tagsz, (void *) tag) != 1) + return -1; - ret = EVP_DecryptFinal_ex(ctx->evp_ctx, out + tmp_sz, &tmp_sz); - if (ret != 1) + if (EVP_DecryptUpdate(ctx->evp_ctx, NULL, &tmp_sz, + aad.data, (int) aad.len) != 1) return -1; - *out_sz += tmp_sz; + if (EVP_DecryptUpdate(ctx->evp_ctx, out->data, &out_sz, + in.data, (int) in.len) != 1) + return -1; - return 0; + if (EVP_DecryptFinal_ex(ctx->evp_ctx, out->data + out_sz, + &tmp_sz) != 1) + return -1; + + out_sz += tmp_sz; + + out->len = (size_t) out_sz; + + return out_sz; } /* @@ -397,11 +398,14 @@ static int __openssl_dhe_derive(EVP_PKEY * pkp, ret = i2d_PUBKEY(pkp, &local_pk.data); if (ret <= 0) goto fail_local; + local_pk.len = (size_t) ret; + ki.salt.len = HKDF_SALT_LEN; + ki.salt.data = salt_buf; + /* Derive salt from both public keys */ - if (derive_salt_from_pk_bytes_dhe(local_pk, remote_pk, salt_buf, - HKDF_SALT_LEN) < 0) + if (derive_salt_from_pk_bytes_dhe(local_pk, remote_pk, ki.salt) < 0) goto fail_salt; ctx = EVP_PKEY_CTX_new(pkp, NULL); @@ -438,8 +442,6 @@ static int __openssl_dhe_derive(EVP_PKEY * pkp, ki.info.data = (uint8_t *) HKDF_INFO_DHE; ki.key.len = SYMMKEYSZ; ki.key.data = s; - ki.salt.len = HKDF_SALT_LEN; - ki.salt.data = salt_buf; /* Derive symmetric key from shared secret using HKDF */ ret = derive_key_hkdf(&ki); @@ -718,13 +720,17 @@ ssize_t openssl_kem_encap(buffer_t pk, EVP_PKEY * pub; uint8_t * pos; uint8_t salt[HKDF_SALT_LEN]; + buffer_t salt_b; ssize_t ret; assert(pk.data != NULL); assert(ct != NULL); assert(s != NULL); - if (derive_salt_from_pk_bytes(pk, salt, HKDF_SALT_LEN) < 0) + salt_b.len = HKDF_SALT_LEN; + salt_b.data = salt; + + if (derive_salt_from_pk_bytes(pk, salt_b) < 0) goto fail_salt; pos = pk.data; @@ -750,13 +756,17 @@ ssize_t openssl_kem_encap_raw(buffer_t pk, EVP_PKEY * pub; const char * algo; uint8_t salt[HKDF_SALT_LEN]; + buffer_t salt_b; ssize_t ret; assert(pk.data != NULL); assert(ct != NULL); assert(s != NULL); - if (derive_salt_from_pk_bytes(pk, salt, HKDF_SALT_LEN) < 0) + salt_b.len = HKDF_SALT_LEN; + salt_b.data = salt; + + if (derive_salt_from_pk_bytes(pk, salt_b) < 0) goto fail_salt; algo = __openssl_hybrid_algo_from_len(pk.len); @@ -790,12 +800,16 @@ int openssl_kem_decap(EVP_PKEY * priv, size_t secret_len; int ret; uint8_t salt[HKDF_SALT_LEN]; + buffer_t salt_b; /* Extract public key bytes from private key */ if (get_pk_bytes_from_key(priv, &pk) < 0) goto fail_pk; - if (derive_salt_from_pk_bytes(pk, salt, HKDF_SALT_LEN) < 0) + salt_b.len = HKDF_SALT_LEN; + salt_b.data = salt; + + if (derive_salt_from_pk_bytes(pk, salt_b) < 0) goto fail_salt; ctx = EVP_PKEY_CTX_new(priv, NULL); @@ -858,13 +872,14 @@ void openssl_pkp_destroy(EVP_PKEY * pkp) EVP_PKEY_free(pkp); } -int __openssl_get_curve(EVP_PKEY * pub, - char * algo) +static int openssl_get_curve(EVP_PKEY * pub, + char * algo) { int ret; size_t len = KEX_ALGO_BUFSZ; ret = EVP_PKEY_get_utf8_string_param(pub, "group", algo, len, &len); + return ret == 1 ? 0 : -ECRYPT; } @@ -889,9 +904,10 @@ int openssl_get_algo_from_pk_der(buffer_t pk, strcpy(algo, type_str); - if ((IS_EC_GROUP(algo) || IS_DH_GROUP(algo)) && - __openssl_get_curve(pub, algo) < 0) - goto fail_pub; + if (IS_EC_GROUP(algo) || IS_DH_GROUP(algo)) { + if (openssl_get_curve(pub, algo) < 0) + goto fail_pub; + } EVP_PKEY_free(pub); return 0; @@ -949,141 +965,122 @@ int openssl_dhe_derive(EVP_PKEY * pkp, return -ECRYPT; } -int openssl_encrypt(struct ossl_crypt_ctx * ctx, - buffer_t in, - buffer_t * out) +/* Set up a fresh AEAD cipher ctx for nid: reject non-AEAD / oversized IV. */ +static int ossl_cipher_ctx_init(struct ossl_crypt_ctx * ctx, + int nid) { - uint8_t * ptr; - uint8_t * iv; - int in_sz; - int out_sz; - int tmp_sz; - int ret; - - assert(ctx != NULL); - - in_sz = (int) in.len; - - out->data = malloc(in.len + EVP_MAX_BLOCK_LENGTH + \ - ctx->ivsz + ctx->tagsz); - if (out->data == NULL) - goto fail_malloc; - - iv = out->data; - ptr = out->data + ctx->ivsz; + ctx->cipher = EVP_get_cipherbynid(nid); + if (ctx->cipher == NULL) + return -1; - if (random_buffer(iv, ctx->ivsz) < 0) - goto fail_encrypt; + /* IV must fit the NONCESZ nonce buffer. */ + if (EVP_CIPHER_get_iv_length(ctx->cipher) > NONCESZ) + return -1; - /* Set IV bit 7 to current key phase (KEY_ROTATION_BIT of counter) */ - if (ctx->rot.cntr & ctx->rot.mask) - iv[0] |= 0x80; - else - iv[0] &= 0x7F; + /* Authenticated encryption is mandatory; reject non-AEAD ciphers. */ + if ((EVP_CIPHER_flags(ctx->cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) == 0) + return -1; - EVP_CIPHER_CTX_reset(ctx->evp_ctx); + ctx->tagsz = AEAD_TAG_LEN; - ret = EVP_EncryptInit_ex(ctx->evp_ctx, ctx->cipher, NULL, - ctx->keys.cur, iv); - if (ret != 1) - goto fail_encrypt; + ctx->evp_ctx = EVP_CIPHER_CTX_new(); + if (ctx->evp_ctx == NULL) + return -1; - ret = EVP_EncryptUpdate(ctx->evp_ctx, ptr, &tmp_sz, in.data, in_sz); - if (ret != 1) - goto fail_encrypt; + return 0; +} - out_sz = tmp_sz; - ret = EVP_EncryptFinal_ex(ctx->evp_ctx, ptr + tmp_sz, &tmp_sz); - if (ret != 1) - goto fail_encrypt; +/* One-shot AEAD seal over an explicit key/nonce (no keyrot). out = ct ‖ tag. */ +int openssl_oneshot_seal(int nid, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + buffer_t * out) +{ + struct ossl_crypt_ctx ctx; + int out_sz; - out_sz += tmp_sz; + assert(key != NULL); + assert(nonce != NULL); + assert(out != NULL); - /* For AEAD ciphers, get and append the authentication tag */ - if (ctx->tagsz > 0) { - ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_AEAD_GET_TAG, - ctx->tagsz, ptr + out_sz); - if (ret != 1) - goto fail_encrypt; - out_sz += ctx->tagsz; - } + memset(&ctx, 0, sizeof(ctx)); - assert(out_sz >= in_sz); + if (ossl_cipher_ctx_init(&ctx, nid) < 0) + goto fail_cipher; - out->len = (size_t) out_sz + ctx->ivsz; + out->data = malloc(in.len + EVP_MAX_BLOCK_LENGTH + ctx.tagsz); + if (out->data == NULL) + goto fail_ctx; - /* Increment packet counter and check for key rotation */ - ctx->rot.cntr++; - ctx->rot.age++; + out_sz = openssl_seal(&ctx, key, nonce, aad, in, + out->data, out->data + in.len); + if (out_sz < 0) + goto fail_seal; - if (HAS_PHASE_BIT_TOGGLED(ctx)) { - if (rotate_key(ctx) != 0) - goto fail_encrypt; - } + out->len = (size_t) out_sz + ctx.tagsz; - cleanup_old_key(ctx); + EVP_CIPHER_CTX_free(ctx.evp_ctx); return 0; - fail_encrypt: + + fail_seal: free(out->data); - fail_malloc: + fail_ctx: + EVP_CIPHER_CTX_free(ctx.evp_ctx); + fail_cipher: clrbuf(*out); return -ECRYPT; } -int openssl_decrypt(struct ossl_crypt_ctx * ctx, - buffer_t in, - buffer_t * out) +/* One-shot AEAD open; in = ct ‖ tag, verifies aad and tag. */ +int openssl_oneshot_open(int nid, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + buffer_t * out) { - uint8_t * iv; - uint8_t * input; - uint8_t rx_phase; - int out_sz; - int in_sz; - - assert(ctx != NULL); - - in_sz = (int) in.len - ctx->ivsz; - if (in_sz < ctx->tagsz) - return -ECRYPT; + struct ossl_crypt_ctx ctx; + buffer_t ct; + const uint8_t * tag; + int in_sz; - in_sz -= ctx->tagsz; + assert(key != NULL); + assert(nonce != NULL); + assert(out != NULL); - out->data = malloc(in_sz + EVP_MAX_BLOCK_LENGTH); - if (out->data == NULL) - goto fail_malloc; + memset(&ctx, 0, sizeof(ctx)); - iv = in.data; - input = in.data + ctx->ivsz; + if (ossl_cipher_ctx_init(&ctx, nid) < 0) + goto fail_cipher; - /* Extract phase from IV bit 7 and check for key rotation */ - rx_phase = (iv[0] & 0x80) ? 1 : 0; + if (in.len < (size_t) ctx.tagsz) + goto fail_ctx; - if (should_rotate_key_rx(ctx, rx_phase)) { - if (rotate_key(ctx) != 0) - goto fail_decrypt; - } + in_sz = (int) in.len - ctx.tagsz; - ctx->rot.cntr++; - ctx->rot.age++; + out->data = malloc((size_t) in_sz + EVP_MAX_BLOCK_LENGTH); + if (out->data == NULL) + goto fail_ctx; - if (try_decrypt(ctx, ctx->keys.cur, iv, input, in_sz, out->data, - &out_sz) != 0) { - if (ctx->keys.prv == NULL) - goto fail_decrypt; - if (try_decrypt(ctx, ctx->keys.prv, iv, input, in_sz, - out->data, &out_sz) != 0) - goto fail_decrypt; - } + ct.data = in.data; + ct.len = (size_t) in_sz; + tag = in.data + in_sz; - assert(out_sz <= in_sz); + if (openssl_open(&ctx, key, nonce, aad, ct, tag, out) < 0) + goto fail_open; - out->len = (size_t) out_sz; + EVP_CIPHER_CTX_free(ctx.evp_ctx); return 0; - fail_decrypt: + + fail_open: free(out->data); - fail_malloc: + fail_ctx: + EVP_CIPHER_CTX_free(ctx.evp_ctx); + fail_cipher: clrbuf(*out); return -ECRYPT; } @@ -1094,51 +1091,19 @@ struct ossl_crypt_ctx * openssl_crypt_create_ctx(struct crypt_sk * sk) assert(sk != NULL); assert(sk->key != NULL); - assert(sk->rot_bit > 0 && sk->rot_bit < 32); ctx = malloc(sizeof(*ctx)); if (ctx == NULL) - goto fail_malloc; + goto fail_malloc; memset(ctx, 0, sizeof(*ctx)); - ctx->keys.cur = OPENSSL_secure_malloc(SYMMKEYSZ); - if (ctx->keys.cur == NULL) - goto fail_key; - - memcpy(ctx->keys.cur, sk->key, SYMMKEYSZ); - - ctx->keys.prv = NULL; - - /* Derive rotation salt from initial shared secret */ - if (EVP_Digest(sk->key, SYMMKEYSZ, ctx->rot.salt, NULL, - EVP_sha256(), NULL) != 1) - goto fail_cipher; - - ctx->cipher = EVP_get_cipherbynid(sk->nid); - if (ctx->cipher == NULL) - goto fail_cipher; - - ctx->ivsz = EVP_CIPHER_iv_length(ctx->cipher); - - /* Set tag size for AEAD ciphers (GCM, CCM, OCB, ChaCha20-Poly1305) */ - if (EVP_CIPHER_flags(ctx->cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) - ctx->tagsz = 16; /* Standard AEAD tag length (128 bits) */ - - ctx->rot.cntr = 0; - ctx->rot.mask = (1U << sk->rot_bit); - ctx->rot.age = 0; - ctx->rot.phase = 0; - - ctx->evp_ctx = EVP_CIPHER_CTX_new(); - if (ctx->evp_ctx == NULL) + if (ossl_cipher_ctx_init(ctx, sk->nid) < 0) goto fail_cipher; return ctx; fail_cipher: - OPENSSL_secure_clear_free(ctx->keys.cur, SYMMKEYSZ); - fail_key: free(ctx); fail_malloc: return NULL; @@ -1149,23 +1114,10 @@ void openssl_crypt_destroy_ctx(struct ossl_crypt_ctx * ctx) if (ctx == NULL) return; - if (ctx->keys.cur != NULL) - OPENSSL_secure_clear_free(ctx->keys.cur, SYMMKEYSZ); - - if (ctx->keys.prv != NULL) - OPENSSL_secure_clear_free(ctx->keys.prv, SYMMKEYSZ); - EVP_CIPHER_CTX_free(ctx->evp_ctx); free(ctx); } -int openssl_crypt_get_ivsz(struct ossl_crypt_ctx * ctx) -{ - assert(ctx != NULL); - - return ctx->ivsz; -} - int openssl_crypt_get_tagsz(struct ossl_crypt_ctx * ctx) { assert(ctx != NULL); @@ -1937,9 +1889,10 @@ void * openssl_secure_malloc(size_t size) return OPENSSL_secure_malloc(size); } -void openssl_secure_free(void * ptr) +void openssl_secure_free(void * ptr, + size_t size) { - OPENSSL_secure_free(ptr); + OPENSSL_secure_clear_free(ptr, size); } void openssl_secure_clear(void * ptr, @@ -1947,6 +1900,7 @@ void openssl_secure_clear(void * ptr, { OPENSSL_cleanse(ptr, size); } + void openssl_cleanup(void) { OPENSSL_cleanup(); diff --git a/src/lib/crypt/openssl.h b/src/lib/crypt/openssl.h index 2578a0d2..e5cc35f7 100644 --- a/src/lib/crypt/openssl.h +++ b/src/lib/crypt/openssl.h @@ -61,20 +61,44 @@ int openssl_get_algo_from_pk_der(buffer_t pk, int openssl_get_algo_from_pk_raw(buffer_t pk, char * algo); -int openssl_encrypt(struct ossl_crypt_ctx * ctx, - buffer_t in, - buffer_t * out); - -int openssl_decrypt(struct ossl_crypt_ctx * ctx, - buffer_t in, - buffer_t * out); +int openssl_seal(struct ossl_crypt_ctx * ctx, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + uint8_t * out, + uint8_t * tag); + +int openssl_open(struct ossl_crypt_ctx * ctx, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + const uint8_t * tag, + buffer_t * out); + +int openssl_oneshot_seal(int nid, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + buffer_t * out); + +int openssl_oneshot_open(int nid, + const uint8_t * key, + const uint8_t * nonce, + buffer_t aad, + buffer_t in, + buffer_t * out); + +int openssl_hkdf_expand(buffer_t key, + buffer_t info, + buffer_t out); struct ossl_crypt_ctx * openssl_crypt_create_ctx(struct crypt_sk * sk); void openssl_crypt_destroy_ctx(struct ossl_crypt_ctx * ctx); -int openssl_crypt_get_ivsz(struct ossl_crypt_ctx * ctx); - int openssl_crypt_get_tagsz(struct ossl_crypt_ctx * ctx); /* AUTHENTICATION */ |
