* <- e, ee, se, psk, {}
*/
+noise_local_t *noise_local_pool;
+
/* Private functions */
static noise_keypair_t *noise_remote_keypair_allocate (noise_remote_t *);
static void noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t *,
static void noise_remote_handshake_index_drop (noise_remote_t *);
static uint64_t noise_counter_send (noise_counter_t *);
-static bool noise_counter_recv (noise_counter_t *, uint64_t);
+bool noise_counter_recv (noise_counter_t *, uint64_t);
static void noise_kdf (uint8_t *, uint8_t *, uint8_t *, const uint8_t *,
size_t, size_t, size_t, size_t,
const uint8_t private[NOISE_PUBLIC_KEY_LEN])
{
clib_memcpy (l->l_private, private, NOISE_PUBLIC_KEY_LEN);
- l->l_has_identity = curve25519_gen_public (l->l_public, private);
- return l->l_has_identity;
-}
-
-bool
-noise_local_keys (noise_local_t * l, uint8_t public[NOISE_PUBLIC_KEY_LEN],
- uint8_t private[NOISE_PUBLIC_KEY_LEN])
-{
- if (l->l_has_identity)
- {
- if (public != NULL)
- clib_memcpy (public, l->l_public, NOISE_PUBLIC_KEY_LEN);
- if (private != NULL)
- clib_memcpy (private, l->l_private, NOISE_PUBLIC_KEY_LEN);
- }
- else
- {
- return false;
- }
- return true;
+ return curve25519_gen_public (l->l_public, private);
}
void
noise_remote_init (noise_remote_t * r, uint32_t peer_pool_idx,
const uint8_t public[NOISE_PUBLIC_KEY_LEN],
- noise_local_t * l)
+ u32 noise_local_idx)
{
clib_memset (r, 0, sizeof (*r));
clib_memcpy (r->r_public, public, NOISE_PUBLIC_KEY_LEN);
+ clib_rwlock_init (&r->r_keypair_lock);
r->r_peer_idx = peer_pool_idx;
-
- ASSERT (l != NULL);
- r->r_local = l;
+ r->r_local_idx = noise_local_idx;
r->r_handshake.hs_state = HS_ZEROED;
- noise_remote_precompute (r);
-}
-
-bool
-noise_remote_set_psk (noise_remote_t * r,
- uint8_t psk[NOISE_SYMMETRIC_KEY_LEN])
-{
- int same;
- same = !clib_memcmp (r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN);
- if (!same)
- {
- clib_memcpy (r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN);
- }
- return same == 0;
-}
-bool
-noise_remote_keys (noise_remote_t * r, uint8_t public[NOISE_PUBLIC_KEY_LEN],
- uint8_t psk[NOISE_SYMMETRIC_KEY_LEN])
-{
- static uint8_t null_psk[NOISE_SYMMETRIC_KEY_LEN];
- int ret;
-
- if (public != NULL)
- clib_memcpy (public, r->r_public, NOISE_PUBLIC_KEY_LEN);
-
- if (psk != NULL)
- clib_memcpy (psk, r->r_psk, NOISE_SYMMETRIC_KEY_LEN);
- ret = clib_memcmp (r->r_psk, null_psk, NOISE_SYMMETRIC_KEY_LEN);
-
- return ret;
+ noise_remote_precompute (r);
}
void
noise_remote_precompute (noise_remote_t * r)
{
- noise_local_t *l = r->r_local;
- if (!l->l_has_identity)
- clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN);
- else if (!curve25519_gen_shared (r->r_ss, l->l_private, r->r_public))
+ noise_local_t *l = noise_local_get (r->r_local_idx);
+
+ if (!curve25519_gen_shared (r->r_ss, l->l_private, r->r_public))
clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN);
noise_remote_handshake_index_drop (r);
uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN])
{
noise_handshake_t *hs = &r->r_handshake;
- noise_local_t *l = r->r_local;
+ noise_local_t *l = noise_local_get (r->r_local_idx);
uint8_t _key[NOISE_SYMMETRIC_KEY_LEN];
uint32_t key_idx;
uint8_t *key;
NOISE_SYMMETRIC_KEY_LEN);
key = vnet_crypto_get_key (key_idx)->data;
- if (!l->l_has_identity)
- goto error;
noise_param_init (hs->hs_ck, hs->hs_hash, r->r_public);
/* e */
*s_idx = hs->hs_local_index;
ret = true;
error:
- vnet_crypto_key_del (vm, key_idx);
secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ vnet_crypto_key_del (vm, key_idx);
return ret;
}
NOISE_SYMMETRIC_KEY_LEN);
key = vnet_crypto_get_key (key_idx)->data;
- if (!l->l_has_identity)
- goto error;
noise_param_init (hs.hs_ck, hs.hs_hash, l->l_public);
/* e */
r->r_handshake = hs;
*rp = r;
ret = true;
+
error:
- vnet_crypto_key_del (vm, key_idx);
secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ vnet_crypto_key_del (vm, key_idx);
secure_zero_memory (&hs, sizeof (hs));
return ret;
}
*s_idx = hs->hs_local_index;
ret = true;
error:
- vnet_crypto_key_del (vm, key_idx);
secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ vnet_crypto_key_del (vm, key_idx);
secure_zero_memory (e, NOISE_PUBLIC_KEY_LEN);
return ret;
}
uint32_t r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN],
uint8_t en[0 + NOISE_AUTHTAG_LEN])
{
- noise_local_t *l = r->r_local;
+ noise_local_t *l = noise_local_get (r->r_local_idx);
noise_handshake_t hs;
uint8_t _key[NOISE_SYMMETRIC_KEY_LEN];
uint8_t preshared_key[NOISE_PUBLIC_KEY_LEN];
NOISE_SYMMETRIC_KEY_LEN);
key = vnet_crypto_get_key (key_idx)->data;
- if (!l->l_has_identity)
- goto error;
-
hs = r->r_handshake;
clib_memcpy (preshared_key, r->r_psk, NOISE_SYMMETRIC_KEY_LEN);
ret = true;
}
error:
- vnet_crypto_key_del (vm, key_idx);
secure_zero_memory (&hs, sizeof (hs));
secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ vnet_crypto_key_del (vm, key_idx);
return ret;
}
clib_memset (&kp.kp_ctr, 0, sizeof (kp.kp_ctr));
/* Now we need to add_new_keypair */
+ clib_rwlock_writer_lock (&r->r_keypair_lock);
+ /* Activate barrier to synchronization keys between threads */
+ vlib_worker_thread_barrier_sync (vm);
next = r->r_next;
current = r->r_current;
previous = r->r_previous;
r->r_next = noise_remote_keypair_allocate (r);
*r->r_next = kp;
}
+ vlib_worker_thread_barrier_release (vm);
+ clib_rwlock_writer_unlock (&r->r_keypair_lock);
+
secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
+
secure_zero_memory (&kp, sizeof (kp));
return true;
}
noise_remote_handshake_index_drop (r);
secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
+ clib_rwlock_writer_lock (&r->r_keypair_lock);
noise_remote_keypair_free (vm, r, &r->r_next);
noise_remote_keypair_free (vm, r, &r->r_current);
noise_remote_keypair_free (vm, r, &r->r_previous);
r->r_next = NULL;
r->r_current = NULL;
r->r_previous = NULL;
+ clib_rwlock_writer_unlock (&r->r_keypair_lock);
}
void
noise_remote_expire_current (noise_remote_t * r)
{
+ clib_rwlock_writer_lock (&r->r_keypair_lock);
if (r->r_next != NULL)
r->r_next->kp_valid = 0;
if (r->r_current != NULL)
r->r_current->kp_valid = 0;
+ clib_rwlock_writer_unlock (&r->r_keypair_lock);
}
bool
noise_keypair_t *kp;
int ret;
+ clib_rwlock_reader_lock (&r->r_keypair_lock);
if ((kp = r->r_current) == NULL ||
!kp->kp_valid ||
wg_birthdate_has_expired (kp->kp_birthdate, REJECT_AFTER_TIME) ||
ret = false;
else
ret = true;
+ clib_rwlock_reader_unlock (&r->r_keypair_lock);
return ret;
}
-static void
+static bool
chacha20poly1305_calc (vlib_main_t * vm,
u8 * src,
u32 src_len,
vnet_crypto_op_id_t op_id,
vnet_crypto_key_index_t key_index)
{
+ vnet_crypto_op_t _op, *op = &_op;
u8 iv[12];
+ u8 tag_[NOISE_AUTHTAG_LEN] = { };
+ u8 src_[] = { };
+
clib_memset (iv, 0, 12);
clib_memcpy (iv + 4, &nonce, sizeof (nonce));
- vnet_crypto_op_t _op, *op = &_op;
+ vnet_crypto_op_init (op, op_id);
- u8 _tag[16] = { };
+ op->tag_len = NOISE_AUTHTAG_LEN;
if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC)
{
- clib_memcpy (_tag, src + src_len - NOISE_AUTHTAG_LEN,
- NOISE_AUTHTAG_LEN);
+ op->tag = src + src_len - NOISE_AUTHTAG_LEN;
src_len -= NOISE_AUTHTAG_LEN;
+ op->flags |= VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
}
- vnet_crypto_op_init (op, op_id);
- op->key_index = key_index;
- op->src = src;
- op->dst = dst;
+ else
+ op->tag = tag_;
+
+ op->src = !src ? src_ : src;
op->len = src_len;
+
+ op->dst = dst;
+ op->key_index = key_index;
op->aad = aad;
op->aad_len = aad_len;
op->iv = iv;
- op->tag_len = NOISE_AUTHTAG_LEN;
- op->tag = _tag;
+
vnet_crypto_process_ops (vm, op, 1);
if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC)
{
clib_memcpy (dst + src_len, op->tag, NOISE_AUTHTAG_LEN);
}
+
+ return (op->status == VNET_CRYPTO_OP_STATUS_COMPLETED);
+}
+
+always_inline void
+wg_prepare_sync_op (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops, u8 *src,
+ u32 src_len, u8 *dst, u8 *aad, u32 aad_len, u64 nonce,
+ vnet_crypto_op_id_t op_id,
+ vnet_crypto_key_index_t key_index, u32 bi, u8 *iv)
+{
+ vnet_crypto_op_t _op, *op = &_op;
+ u8 src_[] = {};
+
+ clib_memset (iv, 0, 4);
+ clib_memcpy (iv + 4, &nonce, sizeof (nonce));
+
+ vec_add2_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
+ vnet_crypto_op_init (op, op_id);
+
+ op->tag_len = NOISE_AUTHTAG_LEN;
+ if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC)
+ {
+ op->tag = src + src_len;
+ op->flags |= VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
+ }
+ else
+ op->tag = dst + src_len;
+
+ op->src = !src ? src_ : src;
+ op->len = src_len;
+
+ op->dst = dst;
+ op->key_index = key_index;
+ op->aad = aad;
+ op->aad_len = aad_len;
+ op->iv = iv;
+ op->user_data = bi;
}
enum noise_state_crypt
}
enum noise_state_crypt
-noise_remote_decrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t r_idx,
- uint64_t nonce, uint8_t * src, size_t srclen,
- uint8_t * dst)
+noise_sync_remote_encrypt (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
+ noise_remote_t *r, uint32_t *r_idx, uint64_t *nonce,
+ uint8_t *src, size_t srclen, uint8_t *dst, u32 bi,
+ u8 *iv, f64 time)
{
noise_keypair_t *kp;
enum noise_state_crypt ret = SC_FAILED;
- if (r->r_current != NULL && r->r_current->kp_local_index == r_idx)
- {
- kp = r->r_current;
- }
- else if (r->r_previous != NULL && r->r_previous->kp_local_index == r_idx)
- {
- kp = r->r_previous;
- }
- else if (r->r_next != NULL && r->r_next->kp_local_index == r_idx)
- {
- kp = r->r_next;
- }
- else
+ if ((kp = r->r_current) == NULL)
+ goto error;
+
+ /* We confirm that our values are within our tolerances. We want:
+ * - a valid keypair
+ * - our keypair to be less than REJECT_AFTER_TIME seconds old
+ * - our receive counter to be less than REJECT_AFTER_MESSAGES
+ * - our send counter to be less than REJECT_AFTER_MESSAGES
+ */
+ if (!kp->kp_valid ||
+ wg_birthdate_has_expired_opt (kp->kp_birthdate, REJECT_AFTER_TIME,
+ time) ||
+ kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES ||
+ ((*nonce = noise_counter_send (&kp->kp_ctr)) > REJECT_AFTER_MESSAGES))
+ goto error;
+
+ /* We encrypt into the same buffer, so the caller must ensure that buf
+ * has NOISE_AUTHTAG_LEN bytes to store the MAC. The nonce and index
+ * are passed back out to the caller through the provided data pointer. */
+ *r_idx = kp->kp_remote_index;
+
+ wg_prepare_sync_op (vm, crypto_ops, src, srclen, dst, NULL, 0, *nonce,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC, kp->kp_send_index,
+ bi, iv);
+
+ /* If our values are still within tolerances, but we are approaching
+ * the tolerances, we notify the caller with ESTALE that they should
+ * establish a new keypair. The current keypair can continue to be used
+ * until the tolerances are hit. We notify if:
+ * - our send counter is valid and not less than REKEY_AFTER_MESSAGES
+ * - we're the initiator and our keypair is older than
+ * REKEY_AFTER_TIME seconds */
+ ret = SC_KEEP_KEY_FRESH;
+ if ((kp->kp_valid && *nonce >= REKEY_AFTER_MESSAGES) ||
+ (kp->kp_is_initiator && wg_birthdate_has_expired_opt (
+ kp->kp_birthdate, REKEY_AFTER_TIME, time)))
+ goto error;
+
+ ret = SC_OK;
+error:
+ return ret;
+}
+
+enum noise_state_crypt
+noise_sync_remote_decrypt (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
+ noise_remote_t *r, uint32_t r_idx, uint64_t nonce,
+ uint8_t *src, size_t srclen, uint8_t *dst, u32 bi,
+ u8 *iv, f64 time)
+{
+ noise_keypair_t *kp;
+ enum noise_state_crypt ret = SC_FAILED;
+
+ if ((kp = wg_get_active_keypair (r, r_idx)) == NULL)
{
goto error;
}
* are the same as the encrypt routine.
*
* kp_ctr isn't locked here, we're happy to accept a racy read. */
- if (wg_birthdate_has_expired (kp->kp_birthdate, REJECT_AFTER_TIME) ||
+ if (wg_birthdate_has_expired_opt (kp->kp_birthdate, REJECT_AFTER_TIME,
+ time) ||
kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES)
goto error;
/* Decrypt, then validate the counter. We don't want to validate the
* counter before decrypting as we do not know the message is authentic
* prior to decryption. */
- chacha20poly1305_calc (vm, src, srclen, dst, NULL, 0, nonce,
- VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC,
- kp->kp_recv_index);
-
- if (!noise_counter_recv (&kp->kp_ctr, nonce))
- goto error;
+ wg_prepare_sync_op (vm, crypto_ops, src, srclen, dst, NULL, 0, nonce,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, kp->kp_recv_index,
+ bi, iv);
/* If we've received the handshake confirming data packet then move the
* next keypair into current. If we do slide the next keypair in, then
* we skip the REKEY_AFTER_TIME_RECV check. This is safe to do as a
* data packet can't confirm a session that we are an INITIATOR of. */
- if (kp == r->r_next && kp->kp_local_index == r_idx)
+ if (kp == r->r_next)
{
- noise_remote_keypair_free (vm, r, &r->r_previous);
- r->r_previous = r->r_current;
- r->r_current = r->r_next;
- r->r_next = NULL;
+ clib_rwlock_writer_lock (&r->r_keypair_lock);
+ if (kp == r->r_next && kp->kp_local_index == r_idx)
+ {
+ noise_remote_keypair_free (vm, r, &r->r_previous);
+ r->r_previous = r->r_current;
+ r->r_current = r->r_next;
+ r->r_next = NULL;
- ret = SC_CONN_RESET;
- goto error;
+ ret = SC_CONN_RESET;
+ clib_rwlock_writer_unlock (&r->r_keypair_lock);
+ goto error;
+ }
+ clib_rwlock_writer_unlock (&r->r_keypair_lock);
}
-
/* Similar to when we encrypt, we want to notify the caller when we
* are approaching our tolerances. We notify if:
* - we're the initiator and the current keypair is older than
* REKEY_AFTER_TIME_RECV seconds. */
ret = SC_KEEP_KEY_FRESH;
kp = r->r_current;
- if (kp != NULL &&
- kp->kp_valid &&
- kp->kp_is_initiator &&
- wg_birthdate_has_expired (kp->kp_birthdate, REKEY_AFTER_TIME_RECV))
+ if (kp != NULL && kp->kp_valid && kp->kp_is_initiator &&
+ wg_birthdate_has_expired_opt (kp->kp_birthdate, REKEY_AFTER_TIME_RECV,
+ time))
goto error;
ret = SC_OK;
noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t * r,
noise_keypair_t ** kp)
{
- struct noise_upcall *u = &r->r_local->l_upcall;
+ noise_local_t *local = noise_local_get (r->r_local_idx);
+ struct noise_upcall *u = &local->l_upcall;
if (*kp)
{
u->u_index_drop ((*kp)->kp_local_index);
static uint32_t
noise_remote_handshake_index_get (noise_remote_t * r)
{
- struct noise_upcall *u = &r->r_local->l_upcall;
+ noise_local_t *local = noise_local_get (r->r_local_idx);
+ struct noise_upcall *u = &local->l_upcall;
return u->u_index_set (r);
}
noise_remote_handshake_index_drop (noise_remote_t * r)
{
noise_handshake_t *hs = &r->r_handshake;
- struct noise_upcall *u = &r->r_local->l_upcall;
+ noise_local_t *local = noise_local_get (r->r_local_idx);
+ struct noise_upcall *u = &local->l_upcall;
if (hs->hs_state != HS_ZEROED)
u->u_index_drop (hs->hs_local_index);
}
static uint64_t
noise_counter_send (noise_counter_t * ctr)
{
- uint64_t ret = ctr->c_send++;
- return ret;
-}
-
-static bool
-noise_counter_recv (noise_counter_t * ctr, uint64_t recv)
-{
- uint64_t i, top, index_recv, index_ctr;
- unsigned long bit;
- bool ret = false;
-
-
- /* Check that the recv counter is valid */
- if (ctr->c_recv >= REJECT_AFTER_MESSAGES || recv >= REJECT_AFTER_MESSAGES)
- goto error;
-
- /* If the packet is out of the window, invalid */
- if (recv + COUNTER_WINDOW_SIZE < ctr->c_recv)
- goto error;
-
- /* If the new counter is ahead of the current counter, we'll need to
- * zero out the bitmap that has previously been used */
- index_recv = recv / COUNTER_BITS;
- index_ctr = ctr->c_recv / COUNTER_BITS;
-
- if (recv > ctr->c_recv)
- {
- top = clib_min (index_recv - index_ctr, COUNTER_NUM);
- for (i = 1; i <= top; i++)
- ctr->c_backtrack[(i + index_ctr) & (COUNTER_NUM - 1)] = 0;
- ctr->c_recv = recv;
- }
-
- index_recv %= COUNTER_NUM;
- bit = 1ul << (recv % COUNTER_BITS);
-
- if (ctr->c_backtrack[index_recv] & bit)
- goto error;
-
- ctr->c_backtrack[index_recv] |= bit;
-
- ret = true;
-error:
+ uint64_t ret;
+ ret = ctr->c_send++;
return ret;
}
uint8_t hash[NOISE_HASH_LEN])
{
/* Nonce always zero for Noise_IK */
- chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0,
- VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, key_idx);
+ if (!chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, key_idx))
+ return false;
noise_mix_hash (hash, src, src_len);
return true;
}