| /* |
| * Copyright (c) 2020 Doc.ai and/or its affiliates. |
| * Copyright (c) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. |
| * Copyright (c) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>. |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <openssl/hmac.h> |
| #include <wireguard/wireguard.h> |
| #include <wireguard/wireguard_chachapoly.h> |
| |
| /* This implements Noise_IKpsk2: |
| * |
| * <- s |
| * ****** |
| * -> e, es, s, ss, {t} |
| * <- 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 *, |
| noise_keypair_t **); |
| static uint32_t noise_remote_handshake_index_get (noise_remote_t *); |
| static void noise_remote_handshake_index_drop (noise_remote_t *); |
| |
| static uint64_t noise_counter_send (noise_counter_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[NOISE_HASH_LEN]); |
| static bool noise_mix_dh (uint8_t[NOISE_HASH_LEN], |
| uint8_t[NOISE_SYMMETRIC_KEY_LEN], |
| const uint8_t[NOISE_PUBLIC_KEY_LEN], |
| const uint8_t[NOISE_PUBLIC_KEY_LEN]); |
| static bool noise_mix_ss (uint8_t ck[NOISE_HASH_LEN], |
| uint8_t key[NOISE_SYMMETRIC_KEY_LEN], |
| const uint8_t ss[NOISE_PUBLIC_KEY_LEN]); |
| static void noise_mix_hash (uint8_t[NOISE_HASH_LEN], const uint8_t *, size_t); |
| static void noise_mix_psk (uint8_t[NOISE_HASH_LEN], |
| uint8_t[NOISE_HASH_LEN], |
| uint8_t[NOISE_SYMMETRIC_KEY_LEN], |
| const uint8_t[NOISE_SYMMETRIC_KEY_LEN]); |
| static void noise_param_init (uint8_t[NOISE_HASH_LEN], |
| uint8_t[NOISE_HASH_LEN], |
| const uint8_t[NOISE_PUBLIC_KEY_LEN]); |
| |
| static void noise_msg_encrypt (vlib_main_t * vm, uint8_t *, uint8_t *, size_t, |
| uint32_t key_idx, uint8_t[NOISE_HASH_LEN]); |
| static bool noise_msg_decrypt (vlib_main_t * vm, uint8_t *, uint8_t *, size_t, |
| uint32_t key_idx, uint8_t[NOISE_HASH_LEN]); |
| static void noise_msg_ephemeral (uint8_t[NOISE_HASH_LEN], |
| uint8_t[NOISE_HASH_LEN], |
| const uint8_t src[NOISE_PUBLIC_KEY_LEN]); |
| |
| static void noise_tai64n_now (uint8_t[NOISE_TIMESTAMP_LEN]); |
| |
| /* Set/Get noise parameters */ |
| void |
| noise_local_init (noise_local_t * l, struct noise_upcall *upcall) |
| { |
| clib_memset (l, 0, sizeof (*l)); |
| l->l_upcall = *upcall; |
| } |
| |
| bool |
| noise_local_set_private (noise_local_t * l, |
| const uint8_t private[NOISE_PUBLIC_KEY_LEN]) |
| { |
| clib_memcpy (l->l_private, private, NOISE_PUBLIC_KEY_LEN); |
| |
| 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], |
| 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; |
| r->r_local_idx = noise_local_idx; |
| r->r_handshake.hs_state = HS_ZEROED; |
| |
| noise_remote_precompute (r); |
| } |
| |
| void |
| noise_remote_precompute (noise_remote_t * r) |
| { |
| 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); |
| wg_secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake)); |
| } |
| |
| /* Handshake functions */ |
| bool |
| noise_create_initiation (vlib_main_t * vm, noise_remote_t * r, |
| uint32_t * s_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], |
| uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN], |
| uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN]) |
| { |
| noise_handshake_t *hs = &r->r_handshake; |
| noise_local_t *l = noise_local_get (r->r_local_idx); |
| uint8_t _key[NOISE_SYMMETRIC_KEY_LEN] = { 0 }; |
| uint32_t key_idx; |
| uint8_t *key; |
| int ret = false; |
| |
| key_idx = |
| vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key, |
| NOISE_SYMMETRIC_KEY_LEN); |
| key = vnet_crypto_get_key (key_idx)->data; |
| |
| noise_param_init (hs->hs_ck, hs->hs_hash, r->r_public); |
| |
| /* e */ |
| curve25519_gen_secret (hs->hs_e); |
| if (!curve25519_gen_public (ue, hs->hs_e)) |
| goto error; |
| noise_msg_ephemeral (hs->hs_ck, hs->hs_hash, ue); |
| |
| /* es */ |
| if (!noise_mix_dh (hs->hs_ck, key, hs->hs_e, r->r_public)) |
| goto error; |
| |
| /* s */ |
| noise_msg_encrypt (vm, es, l->l_public, NOISE_PUBLIC_KEY_LEN, key_idx, |
| hs->hs_hash); |
| |
| /* ss */ |
| if (!noise_mix_ss (hs->hs_ck, key, r->r_ss)) |
| goto error; |
| |
| /* {t} */ |
| noise_tai64n_now (ets); |
| noise_msg_encrypt (vm, ets, ets, NOISE_TIMESTAMP_LEN, key_idx, hs->hs_hash); |
| noise_remote_handshake_index_drop (r); |
| hs->hs_state = CREATED_INITIATION; |
| hs->hs_local_index = noise_remote_handshake_index_get (r); |
| *s_idx = hs->hs_local_index; |
| ret = true; |
| error: |
| wg_secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); |
| vnet_crypto_key_del (vm, key_idx); |
| return ret; |
| } |
| |
| bool |
| noise_consume_initiation (vlib_main_t * vm, noise_local_t * l, |
| noise_remote_t ** rp, uint32_t s_idx, |
| uint8_t ue[NOISE_PUBLIC_KEY_LEN], |
| uint8_t es[NOISE_PUBLIC_KEY_LEN + |
| NOISE_AUTHTAG_LEN], |
| uint8_t ets[NOISE_TIMESTAMP_LEN + |
| NOISE_AUTHTAG_LEN]) |
| { |
| noise_remote_t *r; |
| noise_handshake_t hs; |
| uint8_t _key[NOISE_SYMMETRIC_KEY_LEN] = { 0 }; |
| uint8_t r_public[NOISE_PUBLIC_KEY_LEN] = { 0 }; |
| uint8_t timestamp[NOISE_TIMESTAMP_LEN] = { 0 }; |
| u32 key_idx; |
| uint8_t *key; |
| int ret = false; |
| |
| key_idx = |
| vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key, |
| NOISE_SYMMETRIC_KEY_LEN); |
| key = vnet_crypto_get_key (key_idx)->data; |
| |
| noise_param_init (hs.hs_ck, hs.hs_hash, l->l_public); |
| |
| /* e */ |
| noise_msg_ephemeral (hs.hs_ck, hs.hs_hash, ue); |
| |
| /* es */ |
| if (!noise_mix_dh (hs.hs_ck, key, l->l_private, ue)) |
| goto error; |
| |
| /* s */ |
| |
| if (!noise_msg_decrypt (vm, r_public, es, |
| NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN, key_idx, |
| hs.hs_hash)) |
| goto error; |
| |
| /* Lookup the remote we received from */ |
| if ((r = l->l_upcall.u_remote_get (r_public)) == NULL) |
| goto error; |
| |
| /* ss */ |
| if (!noise_mix_ss (hs.hs_ck, key, r->r_ss)) |
| goto error; |
| |
| /* {t} */ |
| if (!noise_msg_decrypt (vm, timestamp, ets, |
| NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN, key_idx, |
| hs.hs_hash)) |
| goto error; |
| ; |
| |
| hs.hs_state = CONSUMED_INITIATION; |
| hs.hs_local_index = 0; |
| hs.hs_remote_index = s_idx; |
| clib_memcpy (hs.hs_e, ue, NOISE_PUBLIC_KEY_LEN); |
| |
| /* Replay */ |
| if (clib_memcmp (timestamp, r->r_timestamp, NOISE_TIMESTAMP_LEN) > 0) |
| clib_memcpy (r->r_timestamp, timestamp, NOISE_TIMESTAMP_LEN); |
| else |
| goto error; |
| |
| /* Flood attack */ |
| if (wg_birthdate_has_expired (r->r_last_init, REJECT_INTERVAL)) |
| r->r_last_init = vlib_time_now (vm); |
| else |
| goto error; |
| |
| /* Ok, we're happy to accept this initiation now */ |
| noise_remote_handshake_index_drop (r); |
| r->r_handshake = hs; |
| *rp = r; |
| ret = true; |
| |
| error: |
| wg_secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); |
| vnet_crypto_key_del (vm, key_idx); |
| wg_secure_zero_memory (&hs, sizeof (hs)); |
| return ret; |
| } |
| |
| bool |
| noise_create_response (vlib_main_t * vm, noise_remote_t * r, uint32_t * s_idx, |
| uint32_t * r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], |
| uint8_t en[0 + NOISE_AUTHTAG_LEN]) |
| { |
| noise_handshake_t *hs = &r->r_handshake; |
| uint8_t _key[NOISE_SYMMETRIC_KEY_LEN] = { 0 }; |
| uint8_t e[NOISE_PUBLIC_KEY_LEN] = { 0 }; |
| uint32_t key_idx; |
| uint8_t *key; |
| int ret = false; |
| |
| key_idx = |
| vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key, |
| NOISE_SYMMETRIC_KEY_LEN); |
| key = vnet_crypto_get_key (key_idx)->data; |
| |
| if (hs->hs_state != CONSUMED_INITIATION) |
| goto error; |
| |
| /* e */ |
| curve25519_gen_secret (e); |
| if (!curve25519_gen_public (ue, e)) |
| goto error; |
| noise_msg_ephemeral (hs->hs_ck, hs->hs_hash, ue); |
| |
| /* ee */ |
| if (!noise_mix_dh (hs->hs_ck, NULL, e, hs->hs_e)) |
| goto error; |
| |
| /* se */ |
| if (!noise_mix_dh (hs->hs_ck, NULL, e, r->r_public)) |
| goto error; |
| |
| /* psk */ |
| noise_mix_psk (hs->hs_ck, hs->hs_hash, key, r->r_psk); |
| |
| /* {} */ |
| noise_msg_encrypt (vm, en, NULL, 0, key_idx, hs->hs_hash); |
| |
| |
| hs->hs_state = CREATED_RESPONSE; |
| hs->hs_local_index = noise_remote_handshake_index_get (r); |
| *r_idx = hs->hs_remote_index; |
| *s_idx = hs->hs_local_index; |
| ret = true; |
| error: |
| wg_secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); |
| vnet_crypto_key_del (vm, key_idx); |
| wg_secure_zero_memory (e, NOISE_PUBLIC_KEY_LEN); |
| return ret; |
| } |
| |
| bool |
| noise_consume_response (vlib_main_t * vm, noise_remote_t * r, uint32_t s_idx, |
| uint32_t r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], |
| uint8_t en[0 + NOISE_AUTHTAG_LEN]) |
| { |
| noise_local_t *l = noise_local_get (r->r_local_idx); |
| noise_handshake_t hs; |
| uint8_t _key[NOISE_SYMMETRIC_KEY_LEN] = { 0 }; |
| uint8_t preshared_key[NOISE_PUBLIC_KEY_LEN] = { 0 }; |
| uint32_t key_idx; |
| uint8_t *key; |
| int ret = false; |
| |
| key_idx = |
| vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key, |
| NOISE_SYMMETRIC_KEY_LEN); |
| key = vnet_crypto_get_key (key_idx)->data; |
| |
| hs = r->r_handshake; |
| clib_memcpy (preshared_key, r->r_psk, NOISE_SYMMETRIC_KEY_LEN); |
| |
| if (hs.hs_state != CREATED_INITIATION || hs.hs_local_index != r_idx) |
| goto error; |
| |
| /* e */ |
| noise_msg_ephemeral (hs.hs_ck, hs.hs_hash, ue); |
| |
| /* ee */ |
| if (!noise_mix_dh (hs.hs_ck, NULL, hs.hs_e, ue)) |
| goto error; |
| |
| /* se */ |
| if (!noise_mix_dh (hs.hs_ck, NULL, l->l_private, ue)) |
| goto error; |
| |
| /* psk */ |
| noise_mix_psk (hs.hs_ck, hs.hs_hash, key, preshared_key); |
| |
| /* {} */ |
| |
| if (!noise_msg_decrypt |
| (vm, NULL, en, 0 + NOISE_AUTHTAG_LEN, key_idx, hs.hs_hash)) |
| goto error; |
| |
| |
| hs.hs_remote_index = s_idx; |
| |
| if (r->r_handshake.hs_state == hs.hs_state && |
| r->r_handshake.hs_local_index == hs.hs_local_index) |
| { |
| r->r_handshake = hs; |
| r->r_handshake.hs_state = CONSUMED_RESPONSE; |
| ret = true; |
| } |
| error: |
| wg_secure_zero_memory (&hs, sizeof (hs)); |
| wg_secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN); |
| vnet_crypto_key_del (vm, key_idx); |
| return ret; |
| } |
| |
| bool |
| noise_remote_begin_session (vlib_main_t * vm, noise_remote_t * r) |
| { |
| noise_handshake_t *hs = &r->r_handshake; |
| noise_keypair_t kp, *next, *current, *previous; |
| |
| uint8_t key_send[NOISE_SYMMETRIC_KEY_LEN]; |
| uint8_t key_recv[NOISE_SYMMETRIC_KEY_LEN]; |
| |
| /* We now derive the keypair from the handshake */ |
| if (hs->hs_state == CONSUMED_RESPONSE) |
| { |
| kp.kp_is_initiator = 1; |
| noise_kdf (key_send, key_recv, NULL, NULL, |
| NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0, |
| hs->hs_ck); |
| } |
| else if (hs->hs_state == CREATED_RESPONSE) |
| { |
| kp.kp_is_initiator = 0; |
| noise_kdf (key_recv, key_send, NULL, NULL, |
| NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0, |
| hs->hs_ck); |
| } |
| else |
| { |
| return false; |
| } |
| |
| kp.kp_valid = 1; |
| kp.kp_send_index = vnet_crypto_key_add (vm, |
| VNET_CRYPTO_ALG_CHACHA20_POLY1305, |
| key_send, NOISE_SYMMETRIC_KEY_LEN); |
| kp.kp_recv_index = vnet_crypto_key_add (vm, |
| VNET_CRYPTO_ALG_CHACHA20_POLY1305, |
| key_recv, NOISE_SYMMETRIC_KEY_LEN); |
| kp.kp_local_index = hs->hs_local_index; |
| kp.kp_remote_index = hs->hs_remote_index; |
| kp.kp_birthdate = vlib_time_now (vm); |
| 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; |
| |
| if (kp.kp_is_initiator) |
| { |
| if (next != NULL) |
| { |
| r->r_next = NULL; |
| r->r_previous = next; |
| noise_remote_keypair_free (vm, r, ¤t); |
| } |
| else |
| { |
| r->r_previous = current; |
| } |
| |
| noise_remote_keypair_free (vm, r, &previous); |
| |
| r->r_current = noise_remote_keypair_allocate (r); |
| *r->r_current = kp; |
| } |
| else |
| { |
| noise_remote_keypair_free (vm, r, &next); |
| r->r_previous = NULL; |
| noise_remote_keypair_free (vm, 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); |
| |
| wg_secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake)); |
| |
| wg_secure_zero_memory (&kp, sizeof (kp)); |
| return true; |
| } |
| |
| void |
| noise_remote_clear (vlib_main_t * vm, noise_remote_t * r) |
| { |
| noise_remote_handshake_index_drop (r); |
| wg_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_remote_ready (noise_remote_t * r) |
| { |
| 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) || |
| kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES || |
| kp->kp_ctr.c_send >= REJECT_AFTER_MESSAGES) |
| ret = false; |
| else |
| ret = true; |
| clib_rwlock_reader_unlock (&r->r_keypair_lock); |
| return ret; |
| } |
| |
| enum noise_state_crypt |
| noise_remote_encrypt (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_keypair_t *kp; |
| enum noise_state_crypt ret = SC_FAILED; |
| |
| 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 (kp->kp_birthdate, REJECT_AFTER_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_chacha20poly1305_calc (vm, src, srclen, dst, NULL, 0, *nonce, |
| VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC, |
| kp->kp_send_index); |
| |
| /* 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 (kp->kp_birthdate, REKEY_AFTER_TIME))) |
| goto error; |
| |
| ret = SC_OK; |
| error: |
| return ret; |
| } |
| |
| /* Private functions - these should not be called outside this file under any |
| * circumstances. */ |
| static noise_keypair_t * |
| noise_remote_keypair_allocate (noise_remote_t * r) |
| { |
| noise_keypair_t *kp; |
| kp = clib_mem_alloc (sizeof (*kp)); |
| return kp; |
| } |
| |
| static uint32_t |
| noise_remote_handshake_index_get (noise_remote_t * r) |
| { |
| noise_local_t *local = noise_local_get (r->r_local_idx); |
| struct noise_upcall *u = &local->l_upcall; |
| return u->u_index_set (r); |
| } |
| |
| static void |
| noise_remote_handshake_index_drop (noise_remote_t * r) |
| { |
| noise_handshake_t *hs = &r->r_handshake; |
| 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 void |
| noise_kdf (uint8_t * a, uint8_t * b, uint8_t * c, const uint8_t * x, |
| size_t a_len, size_t b_len, size_t c_len, size_t x_len, |
| const uint8_t ck[NOISE_HASH_LEN]) |
| { |
| uint8_t out[BLAKE2S_HASH_SIZE + 1]; |
| uint8_t sec[BLAKE2S_HASH_SIZE]; |
| |
| /* Extract entropy from "x" into sec */ |
| u32 l = 0; |
| HMAC (EVP_blake2s256 (), ck, NOISE_HASH_LEN, x, x_len, sec, &l); |
| ASSERT (l == BLAKE2S_HASH_SIZE); |
| if (a == NULL || a_len == 0) |
| goto out; |
| |
| /* Expand first key: key = sec, data = 0x1 */ |
| out[0] = 1; |
| HMAC (EVP_blake2s256 (), sec, BLAKE2S_HASH_SIZE, out, 1, out, &l); |
| ASSERT (l == BLAKE2S_HASH_SIZE); |
| clib_memcpy (a, out, a_len); |
| |
| if (b == NULL || b_len == 0) |
| goto out; |
| |
| /* Expand second key: key = sec, data = "a" || 0x2 */ |
| out[BLAKE2S_HASH_SIZE] = 2; |
| HMAC (EVP_blake2s256 (), sec, BLAKE2S_HASH_SIZE, out, BLAKE2S_HASH_SIZE + 1, |
| out, &l); |
| ASSERT (l == BLAKE2S_HASH_SIZE); |
| clib_memcpy (b, out, b_len); |
| |
| if (c == NULL || c_len == 0) |
| goto out; |
| |
| /* Expand third key: key = sec, data = "b" || 0x3 */ |
| out[BLAKE2S_HASH_SIZE] = 3; |
| HMAC (EVP_blake2s256 (), sec, BLAKE2S_HASH_SIZE, out, BLAKE2S_HASH_SIZE + 1, |
| out, &l); |
| ASSERT (l == BLAKE2S_HASH_SIZE); |
| |
| clib_memcpy (c, out, c_len); |
| |
| out: |
| /* Clear sensitive data from stack */ |
| wg_secure_zero_memory (sec, BLAKE2S_HASH_SIZE); |
| wg_secure_zero_memory (out, BLAKE2S_HASH_SIZE + 1); |
| } |
| |
| static bool |
| noise_mix_dh (uint8_t ck[NOISE_HASH_LEN], |
| uint8_t key[NOISE_SYMMETRIC_KEY_LEN], |
| const uint8_t private[NOISE_PUBLIC_KEY_LEN], |
| const uint8_t public[NOISE_PUBLIC_KEY_LEN]) |
| { |
| uint8_t dh[NOISE_PUBLIC_KEY_LEN]; |
| if (!curve25519_gen_shared (dh, private, public)) |
| return false; |
| noise_kdf (ck, key, NULL, dh, |
| NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, |
| ck); |
| wg_secure_zero_memory (dh, NOISE_PUBLIC_KEY_LEN); |
| return true; |
| } |
| |
| static bool |
| noise_mix_ss (uint8_t ck[NOISE_HASH_LEN], |
| uint8_t key[NOISE_SYMMETRIC_KEY_LEN], |
| const uint8_t ss[NOISE_PUBLIC_KEY_LEN]) |
| { |
| static uint8_t null_point[NOISE_PUBLIC_KEY_LEN]; |
| if (clib_memcmp (ss, null_point, NOISE_PUBLIC_KEY_LEN) == 0) |
| return false; |
| noise_kdf (ck, key, NULL, ss, |
| NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, |
| ck); |
| return true; |
| } |
| |
| static void |
| noise_mix_hash (uint8_t hash[NOISE_HASH_LEN], const uint8_t * src, |
| size_t src_len) |
| { |
| blake2s_state_t blake; |
| |
| blake2s_init (&blake, NOISE_HASH_LEN); |
| blake2s_update (&blake, hash, NOISE_HASH_LEN); |
| blake2s_update (&blake, src, src_len); |
| blake2s_final (&blake, hash, NOISE_HASH_LEN); |
| } |
| |
| static void |
| noise_mix_psk (uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN], |
| uint8_t key[NOISE_SYMMETRIC_KEY_LEN], |
| const uint8_t psk[NOISE_SYMMETRIC_KEY_LEN]) |
| { |
| uint8_t tmp[NOISE_HASH_LEN]; |
| |
| noise_kdf (ck, tmp, key, psk, |
| NOISE_HASH_LEN, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, |
| NOISE_SYMMETRIC_KEY_LEN, ck); |
| noise_mix_hash (hash, tmp, NOISE_HASH_LEN); |
| wg_secure_zero_memory (tmp, NOISE_HASH_LEN); |
| } |
| |
| static void |
| noise_param_init (uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN], |
| const uint8_t s[NOISE_PUBLIC_KEY_LEN]) |
| { |
| blake2s_state_t blake; |
| |
| blake2s (ck, NOISE_HASH_LEN, (uint8_t *) NOISE_HANDSHAKE_NAME, |
| strlen (NOISE_HANDSHAKE_NAME), NULL, 0); |
| |
| blake2s_init (&blake, NOISE_HASH_LEN); |
| blake2s_update (&blake, ck, NOISE_HASH_LEN); |
| blake2s_update (&blake, (uint8_t *) NOISE_IDENTIFIER_NAME, |
| strlen (NOISE_IDENTIFIER_NAME)); |
| blake2s_final (&blake, hash, NOISE_HASH_LEN); |
| |
| noise_mix_hash (hash, s, NOISE_PUBLIC_KEY_LEN); |
| } |
| |
| static void |
| noise_msg_encrypt (vlib_main_t * vm, uint8_t * dst, uint8_t * src, |
| size_t src_len, uint32_t key_idx, |
| uint8_t hash[NOISE_HASH_LEN]) |
| { |
| /* Nonce always zero for Noise_IK */ |
| wg_chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0, |
| VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC, key_idx); |
| noise_mix_hash (hash, dst, src_len + NOISE_AUTHTAG_LEN); |
| } |
| |
| static bool |
| noise_msg_decrypt (vlib_main_t * vm, uint8_t * dst, uint8_t * src, |
| size_t src_len, uint32_t key_idx, |
| uint8_t hash[NOISE_HASH_LEN]) |
| { |
| /* Nonce always zero for Noise_IK */ |
| if (!wg_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; |
| } |
| |
| static void |
| noise_msg_ephemeral (uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN], |
| const uint8_t src[NOISE_PUBLIC_KEY_LEN]) |
| { |
| noise_mix_hash (hash, src, NOISE_PUBLIC_KEY_LEN); |
| noise_kdf (ck, NULL, NULL, src, NOISE_HASH_LEN, 0, 0, |
| NOISE_PUBLIC_KEY_LEN, ck); |
| } |
| |
| static void |
| noise_tai64n_now (uint8_t output[NOISE_TIMESTAMP_LEN]) |
| { |
| uint32_t unix_sec; |
| uint32_t unix_nanosec; |
| |
| uint64_t sec; |
| uint32_t nsec; |
| |
| unix_time_now_nsec_fraction (&unix_sec, &unix_nanosec); |
| |
| /* Round down the nsec counter to limit precise timing leak. */ |
| unix_nanosec &= REJECT_INTERVAL_MASK; |
| |
| /* https://cr.yp.to/libtai/tai64.html */ |
| sec = htobe64 (0x400000000000000aULL + unix_sec); |
| nsec = htobe32 (unix_nanosec); |
| |
| /* memcpy to output buffer, assuming output could be unaligned. */ |
| clib_memcpy (output, &sec, sizeof (sec)); |
| clib_memcpy (output + sizeof (sec), &nsec, sizeof (nsec)); |
| } |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |