mesh: Isolate cryptographic material

This is a major refactoring of the handling of the cryptographic
material of both the network and transport layers. The aim is to
encapsulate the key object manipulation, and improve overall modularity.

Pulls Applications and Subnets out of the bt_mesh and into separate
modules, with static storage types on the data. This has several
side-effects:
- The Config Server no longer operates directly on the bt_mesh.subs and
  bt_mesh.apps lists, but goes through a public configuration interface,
  following the pattern set in #27908.
- All iteration through the keys is done through iteration APIs
- Key resolution on RX and TX is centralized.
- Changes to the keys triggers events the other modules can register
  handlers for.
- Friendship credentials are stored in the lpn and friend structures.

this is port of eca014115287bdb8a8a57355514fbd5e1d27b75b
diff --git a/nimble/host/mesh/include/mesh/access.h b/nimble/host/mesh/include/mesh/access.h
index fcd796b..48ab62b 100644
--- a/nimble/host/mesh/include/mesh/access.h
+++ b/nimble/host/mesh/include/mesh/access.h
@@ -28,6 +28,7 @@
 #define BT_MESH_ADDR_RELAYS       0xfffe
 
 #define BT_MESH_KEY_UNUSED        0xffff
+#define BT_MESH_KEY_ANY           0xffff
 #define BT_MESH_KEY_DEV           0xfffe
 #define BT_MESH_KEY_DEV_LOCAL     BT_MESH_KEY_DEV
 #define BT_MESH_KEY_DEV_REMOTE    0xfffd
@@ -392,7 +393,7 @@
 	/** @brief Publication buffer, containing the publication message.
 	 *
 	 *  The application is expected to initialize this with
-	 *  a valid net_buf_simple pointer, with the help of e.g.
+	 *  a valid os_mbuf pointer, with the help of e.g.
 	 *  the NET_BUF_SIMPLE() macro. The publication buffer must
 	 *  contain a valid publication message before calling the
 	 *  bt_mesh_model_publish() API or after the publication's
diff --git a/nimble/host/mesh/include/mesh/cfg.h b/nimble/host/mesh/include/mesh/cfg.h
new file mode 100644
index 0000000..0cef42a
--- /dev/null
+++ b/nimble/host/mesh/include/mesh/cfg.h
@@ -0,0 +1,320 @@
+/** @file
+ *  @brief Bluetooth Mesh Runtime Configuration APIs.
+ */
+
+/*
+ * Copyright (c) 2020 Nordic Semiconductor
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef _BT_MESH_CFG_H_
+#define _BT_MESH_CFG_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+/**
+ * @brief Bluetooth Mesh Runtime Configuration API
+ * @defgroup bt_mesh_cfg Bluetooth Mesh Runtime Configuration
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Bluetooth Mesh Feature states */
+enum bt_mesh_feat_state {
+	/** Feature is supported, but disabled. */
+	BT_MESH_FEATURE_DISABLED,
+	/** Feature is supported and enabled. */
+	BT_MESH_FEATURE_ENABLED,
+	/** Feature is not supported, and cannot be enabled. */
+	BT_MESH_FEATURE_NOT_SUPPORTED,
+};
+
+/**
+ * @brief Bluetooth Mesh Subnet Configuration
+ * @defgroup bt_mesh_cfg_subnet Bluetooth Mesh Subnet Configuration
+ * @{
+ */
+
+/** @brief Add a Subnet.
+ *
+ *  Adds a subnet with the given network index and network key to the list of
+ *  known Subnets. All messages sent on the given Subnet will be processed by
+ *  this node, and the node may send and receive Network Beacons on the given
+ *  Subnet.
+ *
+ *  @param net_idx Network index.
+ *  @param key     Root network key of the Subnet. All other keys are derived
+ *                 from this.
+ *
+ *  @retval STATUS_SUCCESS The Subnet was successfully added.
+ *  @retval STATUS_INSUFF_RESOURCES No room for this Subnet.
+ *  @retval STATUS_UNSPECIFIED The Subnet couldn't be created for some reason.
+ */
+uint8_t bt_mesh_subnet_add(uint16_t net_idx, const uint8_t key[16]);
+
+/** @brief Update the given Subnet.
+ *
+ *  Starts the Key Refresh procedure for this Subnet by adding a second set of
+ *  encryption keys. The Subnet will continue sending with the old key (but
+ *  receiving messages using both) until the Subnet enters Key Refresh phase 2.
+ *
+ *  This allows a network configurator to replace old network and application
+ *  keys for the entire network, effectively removing access for all nodes that
+ *  aren't given the new keys.
+ *
+ *  @param net_idx Network index.
+ *  @param key     New root network key of the Subnet.
+ *
+ *  @retval STATUS_SUCCESS The Subnet was updated with a second key.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ *  @retval STATUS_IDX_ALREADY_STORED The @c key value is the same as the
+ *                                    current key.
+ *  @retval STATUS_CANNOT_UPDATE The Subnet cannot be updated for some reason.
+ */
+uint8_t bt_mesh_subnet_update(uint16_t net_idx, const uint8_t key[16]);
+
+/** @brief Delete a Subnet.
+ *
+ *  Removes the Subnet with the given network index from the node. The node will
+ *  stop sending Network Beacons with the given Subnet, and can no longer
+ *  process messages on this Subnet.
+ *
+ *  All Applications bound to this Subnet are also deleted.
+ *
+ *  @param net_idx Network index.
+ *
+ *  @retval STATUS_SUCCESS The Subnet was deleted.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ */
+uint8_t bt_mesh_subnet_del(uint16_t net_idx);
+
+/** @brief Check whether a Subnet is known.
+ *
+ *  @param net_idx Network index
+ *
+ *  @return true if a Subnet with the given index exists, false otherwise.
+ */
+bool bt_mesh_subnet_exists(uint16_t net_idx);
+
+/** @brief Set the Subnet's Key Refresh phase.
+ *
+ *  The Key Refresh procedure is started by updating the Subnet keys through
+ *  @ref bt_mesh_subnet_update. This puts the Subnet in Key Refresh Phase 1.
+ *  Once all nodes have received the new Subnet key, Key Refresh Phase 2 can be
+ *  activated through this function to start transmitting with the new network
+ *  key. Finally, to revoke the old key, set the Key Refresh Phase to 3. This
+ *  removes the old keys from the node, and returns the Subnet back to normal
+ *  single-key operation with the new key set.
+ *
+ *  @param net_idx Network index.
+ *  @param phase   Pointer to the new Key Refresh phase. Will return the actual
+ *                 Key Refresh phase after updating.
+ *
+ *  @retval STATUS_SUCCESS The Key Refresh phase of the Subnet was successfully
+ *                         changed.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ *  @retval STATUS_CANNOT_UPDATE The given phase change is invalid.
+ */
+uint8_t bt_mesh_subnet_kr_phase_set(uint16_t net_idx, uint8_t *phase);
+
+/** @brief Get the Subnet's Key Refresh phase.
+ *
+ *  @param net_idx Network index.
+ *  @param phase   Pointer to the Key Refresh variable to fill.
+ *
+ *  @retval STATUS_SUCCESS Successfully populated the @c phase variable.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ */
+uint8_t bt_mesh_subnet_kr_phase_get(uint16_t net_idx, uint8_t *phase);
+
+/** @brief Set the Node Identity state of the Subnet.
+ *
+ *  The Node Identity state of a Subnet determines whether the Subnet advertises
+ *  connectable Node Identity beacons for Proxy Clients to connect to.
+ *  Once started, the Node Identity beacon runs for 60 seconds, or until it is
+ *  stopped.
+ *
+ *  This function serves the same purpose as @ref bt_mesh_proxy_identity_enable,
+ *  but only acts on a single Subnet.
+ *
+ *  GATT Proxy support must be enabled through
+ *  @option{CONFIG_BT_MESH_GATT_PROXY}.
+ *
+ *  @param net_idx Network index.
+ *  @param node_id New Node Identity state, must be either @ref
+ *                 BT_MESH_FEATURE_ENABLED or @ref BT_MESH_FEATURE_DISABLED.
+ *
+ *  @retval STATUS_SUCCESS Successfully set the Node Identity state of the
+ *                         Subnet.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ *  @retval STATUS_FEAT_NOT_SUPP The Node Identity feature is not supported.
+ *  @retval STATUS_CANNOT_SET Couldn't set the Node Identity state.
+ */
+uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx,
+				   enum bt_mesh_feat_state node_id);
+
+/** @brief Get the Node Identity state of the Subnet.
+ *
+ *  @param net_idx Network index.
+ *  @param node_id Node Identity variable to fill.
+ *
+ *  @retval STATUS_SUCCESS Successfully populated the @c node_id variable.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ */
+uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx,
+				   enum bt_mesh_feat_state *node_id);
+
+/** @brief Get a list of all known Subnet indexes.
+ *
+ *  Builds a list of all known Subnet indexes in the @c net_idxs array.
+ *  If the @c net_idxs array is smaller than the list of known Subnets, this
+ *  function fills all available entries and returns @c -ENOMEM. In this
+ *  case, the next @c max entries of the list can be read out by calling
+ *  @code
+ *  bt_mesh_subnets_get(list, max, max);
+ *  @endcode
+ *
+ *  Note that any changes to the Subnet list between calls to this function
+ *  could change the order and number of entries in the list.
+ *
+ *  @param net_idxs Array to fill.
+ *  @param max      Max number of indexes to return.
+ *  @param skip     Number of indexes to skip. Enables batched processing of the
+ *                  list.
+ *
+ *  @return The number of indexes added to the @c net_idxs array, or @c -ENOMEM
+ *          if the number of known Subnets exceeds the @c max parameter.
+ */
+ssize_t bt_mesh_subnets_get(uint16_t net_idxs[], size_t max, off_t skip);
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Bluetooth Mesh Application Configuration
+ * @defgroup bt_mesh_cfg_app Bluetooth Mesh Application Configuration
+ * @{
+ */
+
+/** @brief Add an Application key.
+ *
+ *  Adds the Application with the given index to the list of known applications.
+ *  Allows the node to send and receive model messages encrypted with this
+ *  Application key.
+ *
+ *  Every Application is bound to a specific Subnet. The node must know the
+ *  Subnet the Application is bound to before it can add the Application.
+ *
+ *  @param app_idx Application index.
+ *  @param net_idx Network index the Application is bound to.
+ *  @param key     Application key value.
+ *
+ *  @retval STATUS_SUCCESS The Application was successfully added.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ *  @retval STATUS_INSUFF_RESOURCES There's no room for storing this
+ *                                  Application.
+ *  @retval STATUS_INVALID_BINDING This AppIdx is already bound to another
+ *                                 Subnet.
+ *  @retval STATUS_IDX_ALREADY_STORED This AppIdx is already stored with a
+ *                                    different key value.
+ *  @retval STATUS_CANNOT_SET Cannot set the Application key for some reason.
+ */
+uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
+			    const uint8_t key[16]);
+
+/** @brief Update an Application key.
+ *
+ *  Update an Application with a second Application key, as part of the
+ *  Key Refresh procedure of the bound Subnet. The node will continue
+ *  transmitting with the old application key (but receiving on both) until the
+ *  Subnet enters Key Refresh phase 2. Once the Subnet enters Key Refresh phase
+ *  3, the old application key will be deleted.
+ *
+ *  @note The Application key can only be updated if the bound Subnet is in Key
+ *        Refresh phase 1.
+ *
+ *  @param app_idx Application index.
+ *  @param net_idx Network index the Application is bound to, or
+ *                 @ref BT_MESH_KEY_ANY to skip the binding check.
+ *  @param key     New key value.
+ *
+ *  @retval STATUS_SUCCESS The Application key was successfully updated.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ *  @retval STATUS_INVALID_BINDING This AppIdx is not bound to the given NetIdx.
+ *  @retval STATUS_CANNOT_UPDATE The Application key cannot be updated for some
+ *                               reason.
+ *  @retval STATUS_IDX_ALREADY_STORED This AppIdx is already updated with a
+ *                                    different key value.
+ */
+uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
+			       const uint8_t key[16]);
+
+/** @brief Delete an Application key.
+ *
+ *  All models bound to this application will remove this binding.
+ *  All models publishing with this application will stop publishing.
+ *
+ *  @param app_idx Application index.
+ *  @param net_idx Network index.
+ *
+ *  @retval STATUS_SUCCESS The Application key was successfully deleted.
+ *  @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
+ *  @retval STATUS_INVALID_BINDING This AppIdx is not bound to the given NetIdx.
+ */
+uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx);
+
+/** @brief Check if an Application key is known.
+ *
+ *  @param app_idx Application index.
+ *
+ *  @return true if the Application is known, false otherwise.
+ */
+bool bt_mesh_app_key_exists(uint16_t app_idx);
+
+/** @brief Get a list of all known Application key indexes.
+ *
+ *  Builds a list of all Application indexes for the given network index in the
+ *  @c app_idxs array. If the @c app_idxs array cannot fit all bound
+ *  Applications, this function fills all available entries and returns @c
+ *  -ENOMEM. In this case, the next @c max entries of the list can be read out
+ *  by calling
+ *  @code
+ *  bt_mesh_app_keys_get(net_idx, list, max, max);
+ *  @endcode
+ *
+ *  Note that any changes to the Application key list between calls to this
+ *  function could change the order and number of entries in the list.
+ *
+ *  @param net_idx  Network Index to get the Applications of, or @ref
+ *                  BT_MESH_KEY_ANY to get all Applications.
+ *  @param app_idxs Array to fill.
+ *  @param max      Max number of indexes to return.
+ *  @param skip     Number of indexes to skip. Enables batched processing of the
+ *                  list.
+ *
+ *  @return The number of indexes added to the @c app_idxs array, or @c -ENOMEM
+ *          if the number of known Applications exceeds the @c max parameter.
+ */
+ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
+			     off_t skip);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* _BT_MESH_CFG_H_ */
\ No newline at end of file
diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h
index 651bdea..c7d104a 100644
--- a/nimble/host/mesh/include/mesh/glue.h
+++ b/nimble/host/mesh/include/mesh/glue.h
@@ -234,10 +234,9 @@
 }
 
 #define net_buf_simple_init_with_data(buf, data, size)  \
-    buf = NET_BUF_SIMPLE(size); \
     os_mbuf_copyinto(buf, 0, data, size);
 
-static inline void net_buf_simple_reset(struct os_mbuf *om)
+static inline void os_mbuf_reset(struct os_mbuf *om)
 {
     om->om_len = 0;
     om->om_data = om->om_databuf;
@@ -262,7 +261,7 @@
 void net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val);
 void net_buf_simple_push_be24(struct os_mbuf *om, uint32_t val);
 void net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val);
-void *net_buf_simple_pull(struct os_mbuf *om, uint8_t len);
+void *net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len);
 void *net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len);
 void *net_buf_simple_add(struct os_mbuf *om, uint8_t len);
 bool k_fifo_is_empty(struct ble_npl_eventq *q);
@@ -278,7 +277,7 @@
 #define net_buf_clone(a, b) os_mbuf_dup(a)
 #define net_buf_add_be32(a, b) net_buf_simple_add_be32(a, b)
 #define net_buf_add_be16(a, b) net_buf_simple_add_be16(a, b)
-#define net_buf_pull(a, b) net_buf_simple_pull(a, b)
+#define net_buf_pull(a, b) net_buf_simple_pull_mem(a, b)
 #define net_buf_pull_mem(a, b) net_buf_simple_pull_mem(a, b)
 #define net_buf_pull_u8(a) net_buf_simple_pull_u8(a)
 #define net_buf_pull_be16(a) net_buf_simple_pull_be16(a)
@@ -346,14 +345,14 @@
 void k_delayed_work_add_arg(struct k_delayed_work *w, void *arg);
 uint32_t k_delayed_work_remaining_get(struct k_delayed_work *w);
 
-static inline void net_buf_simple_save(struct os_mbuf *buf,
+static inline void os_mbuf_save(struct os_mbuf *buf,
                        struct net_buf_simple_state *state)
 {
     state->offset = net_buf_simple_headroom(buf);
     state->len = buf->om_len;
 }
 
-static inline void net_buf_simple_restore(struct os_mbuf *buf,
+static inline void os_mbuf_restore(struct os_mbuf *buf,
                                           struct net_buf_simple_state *state)
 {
       buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + state->offset;
diff --git a/nimble/host/mesh/include/mesh/health_srv.h b/nimble/host/mesh/include/mesh/health_srv.h
index 5d1c0de..ad79e36 100644
--- a/nimble/host/mesh/include/mesh/health_srv.h
+++ b/nimble/host/mesh/include/mesh/health_srv.h
@@ -52,7 +52,7 @@
  *
  *  @param max_faults Maximum number of faults the element can have.
  *
- *  @return a New net_buf_simple of the needed size.
+ *  @return a New os_mbuf of the needed size.
  */
 #define BT_MESH_HEALTH_FAULT_MSG(max_faults) \
 	NET_BUF_SIMPLE(1 + 3 + (max_faults))
diff --git a/nimble/host/mesh/include/mesh/mesh.h b/nimble/host/mesh/include/mesh/mesh.h
index c67bf91..77b37d7 100644
--- a/nimble/host/mesh/include/mesh/mesh.h
+++ b/nimble/host/mesh/include/mesh/mesh.h
@@ -23,5 +23,6 @@
 #include "health_cli.h"
 #include "proxy.h"
 #include "cdb.h"
+#include "cfg.h"
 
 #endif /* __BT_MESH_H */
diff --git a/nimble/host/mesh/src/access.c b/nimble/host/mesh/src/access.c
index e87151d..952ab01 100644
--- a/nimble/host/mesh/src/access.c
+++ b/nimble/host/mesh/src/access.c
@@ -156,30 +156,18 @@
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct bt_mesh_model_pub *pub = mod->pub;
-	struct bt_mesh_app_key *key;
 	struct bt_mesh_msg_ctx ctx = {
 		.addr = pub->addr,
 		.send_ttl = pub->ttl,
+		.app_idx = pub->key,
 	};
 	struct bt_mesh_net_tx tx = {
 		.ctx = &ctx,
 		.src = bt_mesh_model_elem(mod)->addr,
-		.xmit = bt_mesh_net_transmit_get(),
 		.friend_cred = pub->cred,
 	};
 	int err;
 
-	key = bt_mesh_app_key_find(pub->key);
-	if (!key) {
-		err = -EADDRNOTAVAIL;
-		goto done;
-	}
-
-	tx.sub = bt_mesh_subnet_get(key->net_idx);
-
-	ctx.net_idx = key->net_idx;
-	ctx.app_idx = key->app_idx;
-
 	net_buf_simple_init(sdu, 0);
 	net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len);
 
@@ -187,7 +175,6 @@
 
 	err = bt_mesh_trans_send(&tx, sdu, &pub_sent_cb, mod);
 
-done:
 	os_mbuf_free_chain(sdu);
 	return err;
 }
@@ -630,9 +617,9 @@
 		 * store the parsing state in case multiple models
 		 * receive the message.
 		 */
-		net_buf_simple_save(buf, &state);
+		os_mbuf_save(buf, &state);
 		op->func(model, &rx->ctx, buf);
-		net_buf_simple_restore(buf, &state);
+		os_mbuf_restore(buf, &state);
 	}
 }
 
@@ -698,24 +685,9 @@
 		       struct os_mbuf *msg,
 		       const struct bt_mesh_send_cb *cb, void *cb_data)
 {
-	struct bt_mesh_app_key *app_key;
-
-	if (!BT_MESH_IS_DEV_KEY(ctx->app_idx)) {
-		app_key = bt_mesh_app_key_find(ctx->app_idx);
-		if (!app_key) {
-			BT_ERR("Unknown app_idx 0x%04x", ctx->app_idx);
-			return -EINVAL;
-		}
-
-		ctx->net_idx = app_key->net_idx;
-	}
-
 	struct bt_mesh_net_tx tx = {
-		.sub = bt_mesh_subnet_get(ctx->net_idx),
 		.ctx = ctx,
 		.src = bt_mesh_model_elem(model)->addr,
-		.xmit = bt_mesh_net_transmit_get(),
-		.friend_cred = 0,
 	};
 
 	return model_send(model, &tx, false, msg, cb, cb_data);
@@ -725,13 +697,15 @@
 {
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
 	struct bt_mesh_model_pub *pub = model->pub;
-	struct bt_mesh_app_key *key;
 	struct bt_mesh_msg_ctx ctx = {
+		.addr = pub->addr,
+		.send_ttl = pub->ttl,
+		.send_rel = pub->send_rel,
+		.app_idx = pub->key,
 	};
 	struct bt_mesh_net_tx tx = {
 		.ctx = &ctx,
 		.src = bt_mesh_model_elem(model)->addr,
-		.xmit = bt_mesh_net_transmit_get(),
 	};
 	int err;
 
@@ -747,12 +721,6 @@
 		goto done;
 	}
 
-	key = bt_mesh_app_key_find(pub->key);
-	if (!key) {
-		err = -EADDRNOTAVAIL;
-		goto done;
-	}
-
 	if (pub->msg->om_len + 4 > BT_MESH_TX_SDU_MAX) {
 		BT_ERR("Message does not fit maximum SDU size");
 		err = -EMSGSIZE;
@@ -767,14 +735,7 @@
 	net_buf_simple_init(sdu, 0);
 	net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len);
 
-	ctx.addr = pub->addr;
-	ctx.send_ttl = pub->ttl;
-	ctx.send_rel = pub->send_rel;
-	ctx.net_idx = key->net_idx;
-	ctx.app_idx = key->app_idx;
-
 	tx.friend_cred = pub->cred;
-	tx.sub = bt_mesh_subnet_get(ctx.net_idx),
 
 	pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit);
 
diff --git a/nimble/host/mesh/src/adv.c b/nimble/host/mesh/src/adv.c
index 5cb0aa6..3143838 100644
--- a/nimble/host/mesh/src/adv.c
+++ b/nimble/host/mesh/src/adv.c
@@ -281,7 +281,7 @@
 			return;
 		}
 
-		net_buf_simple_save(buf, &state);
+		os_mbuf_save(buf, &state);
 
 		type = net_buf_simple_pull_u8(buf);
 
@@ -301,8 +301,8 @@
 			break;
 		}
 
-		net_buf_simple_restore(buf, &state);
-		net_buf_simple_pull(buf, len);
+		os_mbuf_restore(buf, &state);
+		net_buf_simple_pull_mem(buf, len);
 	}
 }
 
@@ -360,7 +360,7 @@
 	case BLE_GAP_EVENT_EXT_DISC:
 		ext_desc = &event->ext_disc;
 		buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0);
-		if (!buf || os_mbuf_append(buf, ext_desc->data, ext_desc->length_data)) {
+		if (!buf || os_mbuf_append(buf, ext_desc->om_data, ext_desc->length_data)) {
 			BT_ERR("Could not append data");
 			goto done;
 		}
diff --git a/nimble/host/mesh/src/app_keys.c b/nimble/host/mesh/src/app_keys.c
new file mode 100644
index 0000000..ccdaad3
--- /dev/null
+++ b/nimble/host/mesh/src/app_keys.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "mesh/mesh.h"
+#include "mesh_priv.h"
+#include "net.h"
+#include "app_keys.h"
+#include "rpl.h"
+#include "settings.h"
+#include "crypto.h"
+#include "adv.h"
+#include "proxy.h"
+#include "friend.h"
+#include "foundation.h"
+#include "access.h"
+#include "subnet.h"
+
+#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_KEYS)
+#define LOG_MODULE_NAME bt_mesh_app_keys
+#include "log/log.h"
+
+static struct bt_mesh_app_key apps[CONFIG_BT_MESH_APP_KEY_COUNT] = {
+	[0 ... (CONFIG_BT_MESH_APP_KEY_COUNT - 1)] = {
+		.app_idx = BT_MESH_KEY_UNUSED,
+		.net_idx = BT_MESH_KEY_UNUSED,
+	}
+};
+
+static void app_key_evt(struct bt_mesh_app_key *app, enum bt_mesh_key_evt evt)
+{
+	int i;
+
+	for (i = 0; i < (sizeof(bt_mesh_app_key_cb_list)/sizeof(void *)); i++) {
+		bt_mesh_app_key_cb_list[i] (app->app_idx, app->net_idx, evt);
+	}
+}
+
+struct bt_mesh_app_key *app_get(uint16_t app_idx)
+{
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		if (apps[i].app_idx == app_idx) {
+			return &apps[i];
+		}
+	}
+
+	return NULL;
+}
+
+static struct bt_mesh_app_key *app_key_alloc(uint16_t app_idx)
+{
+	struct bt_mesh_app_key *app = NULL;
+
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		/* Check for already existing app_key */
+		if (apps[i].app_idx == app_idx) {
+			return &apps[i];
+		}
+
+		if (!app && apps[i].app_idx == BT_MESH_KEY_UNUSED) {
+			app = &apps[i];
+		}
+	}
+
+	return app;
+}
+
+static void app_key_del(struct bt_mesh_app_key *app)
+{
+	BT_DBG("AppIdx 0x%03x", app->app_idx);
+
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_clear_app_key(app->app_idx);
+	}
+
+	app_key_evt(app, BT_MESH_KEY_DELETED);
+
+	app->net_idx = BT_MESH_KEY_UNUSED;
+	app->app_idx = BT_MESH_KEY_UNUSED;
+	(void)memset(app->keys, 0, sizeof(app->keys));
+}
+
+static void app_key_revoke(struct bt_mesh_app_key *app)
+{
+	if (!app->updated) {
+		return;
+	}
+
+	memcpy(&app->keys[0], &app->keys[1], sizeof(app->keys[0]));
+	memset(&app->keys[1], 0, sizeof(app->keys[1]));
+	app->updated = false;
+
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_store_app_key(app->app_idx);
+	}
+
+	app_key_evt(app, BT_MESH_KEY_REVOKED);
+}
+
+uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
+			const uint8_t key[16])
+{
+	struct bt_mesh_app_key *app;
+
+	BT_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx,
+	       bt_hex(key, 16));
+
+	if (!bt_mesh_subnet_get(net_idx)) {
+		return STATUS_INVALID_NETKEY;
+	}
+
+	app = app_key_alloc(app_idx);
+	if (!app) {
+		return STATUS_INSUFF_RESOURCES;
+	}
+
+	if (app->app_idx == app_idx) {
+		if (app->net_idx != net_idx) {
+			return STATUS_INVALID_BINDING;
+		}
+
+		if (memcmp(key, app->keys[0].val, 16)) {
+			return STATUS_IDX_ALREADY_STORED;
+		}
+
+		return STATUS_SUCCESS;
+	}
+
+	if (bt_mesh_app_id(key, &app->keys[0].id)) {
+		return STATUS_CANNOT_SET;
+	}
+
+	BT_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id);
+
+	app->net_idx = net_idx;
+	app->app_idx = app_idx;
+	app->updated = false;
+	memcpy(app->keys[0].val, key, 16);
+
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		BT_DBG("Storing AppKey persistently");
+		bt_mesh_store_app_key(app->app_idx);
+	}
+
+	app_key_evt(app, BT_MESH_KEY_ADDED);
+
+	return STATUS_SUCCESS;
+}
+
+struct bt_mesh_app_key *bt_mesh_app_key_get(uint16_t app_idx)
+{
+	struct bt_mesh_app_key *app;
+
+	app = app_get(app_idx);
+	if (app) {
+		return app;
+	}
+
+	return NULL;
+}
+
+uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
+			       const uint8_t key[16])
+{
+	struct bt_mesh_app_key *app;
+	struct bt_mesh_subnet *sub;
+
+	BT_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx,
+	       bt_hex(key, 16));
+
+	app = app_get(app_idx);
+	if (!app) {
+		return STATUS_INVALID_APPKEY;
+	}
+
+	if (net_idx != BT_MESH_KEY_UNUSED && app->net_idx != net_idx) {
+		return STATUS_INVALID_BINDING;
+	}
+
+	sub = bt_mesh_subnet_get(app->net_idx);
+	if (!sub) {
+		return STATUS_INVALID_NETKEY;
+	}
+
+	/* The AppKey Update message shall generate an error when node
+	 * is in normal operation, Phase 2, or Phase 3 or in Phase 1
+	 * when the AppKey Update message on a valid AppKeyIndex when
+	 * the AppKey value is different.
+	 */
+	if (sub->kr_phase != BT_MESH_KR_PHASE_1) {
+		return STATUS_CANNOT_UPDATE;
+	}
+
+	if (app->updated) {
+		if (memcmp(app->keys[1].val, key, 16)) {
+			return STATUS_IDX_ALREADY_STORED;
+		}
+
+		return STATUS_SUCCESS;
+	}
+
+	if (bt_mesh_app_id(key, &app->keys[1].id)) {
+		return STATUS_CANNOT_UPDATE;
+	}
+
+	BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, app->keys[1].id);
+
+	app->updated = true;
+	memcpy(app->keys[1].val, key, 16);
+
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		BT_DBG("Storing AppKey persistently");
+		bt_mesh_store_app_key(app->app_idx);
+	}
+
+	app_key_evt(app, BT_MESH_KEY_UPDATED);
+
+	return STATUS_SUCCESS;
+}
+
+uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx)
+{
+	struct bt_mesh_app_key *app;
+
+	BT_DBG("AppIdx 0x%03x", app_idx);
+
+	if (net_idx != BT_MESH_KEY_UNUSED && !bt_mesh_subnet_get(net_idx)) {
+		return STATUS_INVALID_NETKEY;
+	}
+
+	app = app_get(app_idx);
+	if (!app) {
+		/* This could be a retry of a previous attempt that had its
+		 * response lost, so pretend that it was a success.
+		 */
+		return STATUS_SUCCESS;
+	}
+
+	if (net_idx != BT_MESH_KEY_UNUSED && net_idx != app->net_idx) {
+		return STATUS_INVALID_BINDING;
+	}
+
+	app_key_del(app);
+
+	return STATUS_SUCCESS;
+}
+
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	if (evt == BT_MESH_KEY_UPDATED || evt == BT_MESH_KEY_ADDED) {
+		return;
+	}
+
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		struct bt_mesh_app_key *app = &apps[i];
+
+		if (app->app_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		if (app->net_idx != sub->net_idx) {
+			continue;
+		}
+
+		if (evt == BT_MESH_KEY_DELETED) {
+			app_key_del(app);
+		} else if (evt == BT_MESH_KEY_REVOKED) {
+			app_key_revoke(app);
+		} else if (evt == BT_MESH_KEY_SWAPPED && app->updated) {
+			app_key_evt(app, BT_MESH_KEY_SWAPPED);
+		}
+	}
+}
+
+int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx,
+		    const uint8_t old_key[16], const uint8_t new_key[16])
+{
+	bt_mesh_subnet_cb_list[0] = subnet_evt;
+	struct bt_mesh_app_key *app;
+
+	app = app_key_alloc(app_idx);
+	if (!app) {
+		return -ENOMEM;
+	}
+
+	if (app->app_idx == app_idx) {
+		return 0;
+	}
+
+	BT_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id);
+
+	memcpy(app->keys[0].val, old_key, 16);
+	if (bt_mesh_app_id(old_key, &app->keys[0].id)) {
+		return -EIO;
+	}
+
+	if (new_key) {
+		memcpy(app->keys[1].val, new_key, 16);
+		if (bt_mesh_app_id(new_key, &app->keys[1].id)) {
+			return -EIO;
+		}
+	}
+
+	app->net_idx = net_idx;
+	app->app_idx = app_idx;
+	app->updated = !!new_key;
+
+	return 0;
+}
+
+bool bt_mesh_app_key_exists(uint16_t app_idx)
+{
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		if (apps[i].app_idx == app_idx) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
+			     off_t skip)
+{
+	size_t count = 0;
+
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		struct bt_mesh_app_key *app = &apps[i];
+
+		if (app->app_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		if (net_idx != BT_MESH_KEY_ANY && app->net_idx != net_idx) {
+			continue;
+		}
+
+		if (skip) {
+			skip--;
+			continue;
+		}
+
+		if (count >= max) {
+			return -ENOMEM;
+		}
+
+		app_idxs[count++] = app->app_idx;
+	}
+
+	return count;
+}
+
+int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
+			 struct bt_mesh_subnet **sub,
+			 const uint8_t *app_key[16], uint8_t *aid)
+{
+	struct bt_mesh_app_key *app = NULL;
+
+	if (BT_MESH_IS_DEV_KEY(ctx->app_idx)) {
+		/* With device keys, the application has to decide which subnet
+		 * to send on.
+		 */
+		*sub = bt_mesh_subnet_get(ctx->net_idx);
+		if (!*sub) {
+			BT_WARN("Unknown NetKey 0x%03x", ctx->net_idx);
+			return -EINVAL;
+		}
+
+		if (ctx->app_idx == BT_MESH_KEY_DEV_REMOTE &&
+		    !bt_mesh_elem_find(ctx->addr)) {
+			struct bt_mesh_cdb_node *node;
+
+			if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) {
+				BT_WARN("No DevKey for 0x%04x", ctx->addr);
+				return -EINVAL;
+			}
+
+			node = bt_mesh_cdb_node_get(ctx->addr);
+			if (!node) {
+				BT_WARN("No DevKey for 0x%04x", ctx->addr);
+				return -EINVAL;
+			}
+
+			*app_key = node->dev_key;
+		} else {
+			*app_key = bt_mesh.dev_key;
+		}
+
+		*aid = 0;
+		return 0;
+	}
+
+	app = app_get(ctx->app_idx);
+	if (!app) {
+		BT_WARN("Unknown AppKey 0x%03x", ctx->app_idx);
+		return -EINVAL;
+	}
+
+	*sub = bt_mesh_subnet_get(app->net_idx);
+	if (!*sub) {
+		BT_WARN("Unknown NetKey 0x%03x", app->net_idx);
+		return -EINVAL;
+	}
+
+	if ((*sub)->kr_phase == BT_MESH_KR_PHASE_2 && app->updated) {
+		*aid = app->keys[1].id;
+		*app_key = app->keys[1].val;
+	} else {
+		*aid = app->keys[0].id;
+		*app_key = app->keys[0].val;
+	}
+
+	return 0;
+}
+
+uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
+			      struct bt_mesh_net_rx *rx,
+			      int (*cb)(struct bt_mesh_net_rx *rx,
+					const uint8_t key[16], void *cb_data),
+			      void *cb_data)
+{
+	int err, i;
+
+	if (dev_key) {
+		/* Attempt remote dev key first, as that is only available for
+		 * provisioner devices, which normally don't interact with nodes
+		 * that know their local dev key.
+		 */
+		if (IS_ENABLED(CONFIG_BT_MESH_CDB) &&
+		    rx->net_if != BT_MESH_NET_IF_LOCAL) {
+			struct bt_mesh_cdb_node *node;
+
+			node = bt_mesh_cdb_node_get(rx->ctx.addr);
+			if (node && !cb(rx, node->dev_key, cb_data)) {
+				return BT_MESH_KEY_DEV_REMOTE;
+			}
+		}
+
+		/** Bluetooth Mesh Specification v1.0.1, section 3.4.3:
+		 *  The Device key is only valid for unicast addresses.
+		 */
+		if (BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) {
+			err = cb(rx, bt_mesh.dev_key, cb_data);
+			if (!err) {
+				return BT_MESH_KEY_DEV_LOCAL;
+			}
+		}
+
+		return BT_MESH_KEY_UNUSED;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(apps); i++) {
+		const struct bt_mesh_app_key *app = &apps[i];
+		const struct bt_mesh_app_cred *cred;
+
+		if (app->app_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		if (app->net_idx != rx->sub->net_idx) {
+			continue;
+		}
+
+		if (rx->new_key && app->updated) {
+			cred = &app->keys[1];
+		} else {
+			cred = &app->keys[0];
+		}
+
+		if (cred->id != aid) {
+			continue;
+		}
+
+		err = cb(rx, cred->val, cb_data);
+		if (err) {
+			continue;
+		}
+
+		return app->app_idx;
+	}
+
+	return BT_MESH_KEY_UNUSED;
+}
+
+
+void bt_mesh_app_keys_reset(void)
+{
+	for (int i = 0; i < ARRAY_SIZE(apps); i++) {
+		struct bt_mesh_app_key *app = &apps[i];
+
+		if (app->app_idx != BT_MESH_KEY_UNUSED) {
+			app_key_del(app);
+		}
+	}
+}
\ No newline at end of file
diff --git a/nimble/host/mesh/src/app_keys.h b/nimble/host/mesh/src/app_keys.h
new file mode 100644
index 0000000..d007b78
--- /dev/null
+++ b/nimble/host/mesh/src/app_keys.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _BT_MESH_APP_KEYS_H_
+#define _BT_MESH_APP_KEYS_H_
+
+#include "mesh/mesh.h"
+#include "subnet.h"
+
+/** Mesh Application. */
+struct bt_mesh_app_key {
+	uint16_t net_idx;
+	uint16_t app_idx;
+	bool updated;
+	struct bt_mesh_app_cred {
+		uint8_t id;
+		uint8_t val[16];
+	} keys[2];
+};
+
+/** @brief Reset the app keys module. */
+void bt_mesh_app_keys_reset(void);
+
+/** @brief Get the application key with the given AppIdx.
+ *
+ *  @param app_idx App index.
+ *
+ *  @return The matching application, or NULL if the application isn't known.
+ */
+struct bt_mesh_app_key *bt_mesh_app_key_get(uint16_t app_idx);
+
+/** @brief Initialize a new application key with the given parameters.
+ *
+ *  @param app_idx AppIndex.
+ *  @param net_idx NetIndex the application is bound to.
+ *  @param old_key Current application key.
+ *  @param new_key Updated application key, or NULL if not known.
+ *
+ *  @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx,
+			const uint8_t old_key[16], const uint8_t new_key[16]);
+
+/** @brief Resolve the message encryption keys, given a message context.
+ *
+ *  Will use the @c ctx::app_idx and @c ctx::net_idx fields to find a pair of
+ *  message encryption keys. If @c ctx::app_idx represents a device key, the
+ *  @c ctx::net_idx will be used to determine the net key. Otherwise, the
+ *  @c ctx::net_idx parameter will be ignored.
+ *
+ *  @param ctx     Message context.
+ *  @param sub     Subnet return parameter.
+ *  @param app_key Application return parameter.
+ *  @param aid     Application ID return parameter.
+ *
+ *  @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
+			 struct bt_mesh_subnet **sub,
+			 const uint8_t *app_key[16], uint8_t *aid);
+
+/** @brief Iterate through all matching application keys and call @c cb on each.
+ *
+ *  @param dev_key Whether to return device keys.
+ *  @param aid     7 bit application ID to match.
+ *  @param rx      RX structure to match against.
+ *  @param cb      Callback to call for every valid app key.
+ *  @param cb_data Callback data to pass to the callback.
+ *
+ *  @return The AppIdx that yielded a 0-return from the callback.
+ */
+uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
+			      struct bt_mesh_net_rx *rx,
+			      int (*cb)(struct bt_mesh_net_rx *rx,
+					const uint8_t key[16], void *cb_data),
+			      void *cb_data);
+
+struct bt_mesh_app_key *app_get(uint16_t app_idx);
+
+extern void (*bt_mesh_app_key_cb_list[1]) (uint16_t app_idx, uint16_t net_idx,
+			enum bt_mesh_key_evt evt);
+
+#endif /* _BT_MESH_APP_KEYS_H_ */
\ No newline at end of file
diff --git a/nimble/host/mesh/src/beacon.c b/nimble/host/mesh/src/beacon.c
index 2db03cf..f83ed55 100644
--- a/nimble/host/mesh/src/beacon.c
+++ b/nimble/host/mesh/src/beacon.c
@@ -36,23 +36,9 @@
 
 static struct k_delayed_work beacon_timer;
 
-static struct bt_mesh_subnet *cache_check(uint8_t data[21])
+static int cache_check(struct bt_mesh_subnet *sub, void *beacon_data)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		if (!memcmp(sub->beacon_cache, data, 21)) {
-			return sub;
-		}
-	}
-
-	return NULL;
+	return !memcmp(sub->beacon_cache, beacon_data, 21);
 }
 
 static void cache_add(uint8_t data[21], struct bt_mesh_subnet *sub)
@@ -77,11 +63,7 @@
 
 	net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE);
 
-	if (sub->kr_flag) {
-		keys = &sub->keys[1];
-	} else {
-		keys = &sub->keys[0];
-	}
+	keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
 
 	net_buf_simple_add_u8(buf, flags);
 
@@ -103,44 +85,34 @@
 #define BEACON_THRESHOLD(sub) (K_SECONDS(10 * ((sub)->beacons_last + 1)) - \
 			       K_SECONDS(5))
 
-static int secure_beacon_send(void)
+static int secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data)
 {
 	static const struct bt_mesh_send_cb send_cb = {
 		.end = beacon_complete,
 	};
 	uint32_t now = k_uptime_get_32();
-	int i;
+	struct os_mbuf *buf;
+	uint32_t time_diff;
 
 	BT_DBG("");
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-		struct os_mbuf *buf;
-		uint32_t time_diff;
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		time_diff = now - sub->beacon_sent;
-		if (time_diff < K_SECONDS(600) &&
-		    time_diff < BEACON_THRESHOLD(sub)) {
-			continue;
-		}
-
-		buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT,
-					 K_NO_WAIT);
-		if (!buf) {
-			BT_ERR("Unable to allocate beacon buffer");
-			return -ENOBUFS;
-		}
-
-		bt_mesh_beacon_create(sub, buf);
-
-		bt_mesh_adv_send(buf, &send_cb, sub);
-		net_buf_unref(buf);
+	time_diff = now - sub->beacon_sent;
+	if (time_diff < (600 * MSEC_PER_SEC) &&
+		time_diff < BEACON_THRESHOLD(sub)) {
+		return 0;
 	}
 
+	buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT, K_NO_WAIT);
+	if (!buf) {
+		BT_ERR("Unable to allocate beacon buffer");
+		return -ENOMEM;
+	}
+
+	bt_mesh_beacon_create(sub, buf);
+
+	bt_mesh_adv_send(buf, &send_cb, sub);
+	net_buf_unref(buf);
+
 	return 0;
 }
 
@@ -232,10 +204,15 @@
 	}
 }
 
+static void sub_update_beacon_observation(struct bt_mesh_subnet *sub)
+{
+	sub->beacons_last = sub->beacons_cur;
+	sub->beacons_cur = 0U;
+}
+
 static void update_beacon_observation(void)
 {
 	static bool first_half;
-	int i;
 
 	/* Observation period is 20 seconds, whereas the beacon timer
 	 * runs every 10 seconds. We process what's happened during the
@@ -246,16 +223,7 @@
 		return;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		sub->beacons_last = sub->beacons_cur;
-		sub->beacons_cur = 0;
-	}
+	bt_mesh_subnet_foreach(sub_update_beacon_observation);
 }
 
 static void beacon_send(struct ble_npl_event *work)
@@ -271,7 +239,7 @@
 
 	if (bt_mesh_is_provisioned()) {
 		update_beacon_observation();
-		secure_beacon_send();
+		(void)bt_mesh_subnet_find(secure_beacon_send, NULL);
 
 		/* Only resubmit if beaconing is still enabled */
 		if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED ||
@@ -290,20 +258,62 @@
 	}
 }
 
+struct beacon_params {
+	const uint8_t *net_id;
+	const uint8_t *auth;
+	uint32_t iv_index;
+	uint8_t flags;
+
+	bool new_key;
+};
+
+static bool auth_match(struct bt_mesh_subnet_keys *keys,
+		       const struct beacon_params *params)
+{
+	uint8_t net_auth[8];
+
+	if (memcmp(params->net_id, keys->net_id, 8)) {
+		return false;
+	}
+
+	bt_mesh_beacon_auth(keys->beacon, params->flags, keys->net_id,
+			    params->iv_index, net_auth);
+
+	if (memcmp(params->auth, net_auth, 8)) {
+		BT_WARN("Authentication Value %s != %s",
+			bt_hex(params->auth, 8), bt_hex(net_auth, 8));
+		return false;
+	}
+
+	return true;
+}
+
+static int subnet_by_id(struct bt_mesh_subnet *sub, void *cb_data)
+{
+	struct beacon_params *params = cb_data;
+
+	for (int i = 0; i < ARRAY_SIZE(sub->keys); i++) {
+		if (sub->keys[i].valid && auth_match(&sub->keys[i], params)) {
+			params->new_key = (i > 0);
+			return true;
+		}
+	}
+
+	return false;
+}
+
 static void secure_beacon_recv(struct os_mbuf *buf)
 {
-	uint8_t *data, *net_id, *auth;
+	struct beacon_params params;
 	struct bt_mesh_subnet *sub;
-	uint32_t iv_index;
-	bool new_key, kr_change, iv_change;
-	uint8_t flags;
+	uint8_t *data;
 
 	if (buf->om_len < 21) {
 		BT_ERR("Too short secure beacon (len %u)", buf->om_len);
 		return;
 	}
 
-	sub = cache_check(buf->om_data);
+	sub = bt_mesh_subnet_find(cache_check, buf->om_data);
 	if (sub) {
 		/* We've seen this beacon before - just update the stats */
 		goto update_stats;
@@ -312,33 +322,29 @@
 	/* So we can add to the cache if auth matches */
 	data = buf->om_data;
 
-	flags = net_buf_simple_pull_u8(buf);
-	net_id = net_buf_simple_pull_mem(buf, 8);
-	iv_index = net_buf_simple_pull_be32(buf);
-	auth = buf->om_data;
+	params.flags = net_buf_simple_pull_u8(buf);
+	params.net_id = net_buf_simple_pull_mem(buf, 8);
+	params.iv_index = net_buf_simple_pull_be32(buf);
+	params.auth = buf->om_data;
 
 	BT_DBG("flags 0x%02x id %s iv_index 0x%08x",
-	       flags, bt_hex(net_id, 8), (unsigned) iv_index);
+	       params.flags, bt_hex(params.net_id, 8), params.iv_index);
 
-	sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key);
+	sub = bt_mesh_subnet_find(subnet_by_id, &params);
 	if (!sub) {
 		BT_DBG("No subnet that matched beacon");
 		return;
 	}
 
-	if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !new_key) {
+	if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !params.new_key) {
 		BT_WARN("Ignoring Phase 2 KR Update secured using old key");
 		return;
 	}
 
 	cache_add(data, sub);
 
-	kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key);
-	if (kr_change) {
-		bt_mesh_net_beacon_update(sub);
-		/* Key Refresh without IV Update only impacts one subnet */
-		bt_mesh_net_sec_update(sub);
-	}
+	bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(params.flags),
+			  params.new_key);
 
 	/* If we have NetKey0 accept initiation only from it */
 	if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
@@ -348,20 +354,15 @@
 	}
 
 	BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x",
-	       sub->net_idx, (unsigned) iv_index, (unsigned) bt_mesh.iv_index);
+	       sub->net_idx, params.iv_index, bt_mesh.iv_index);
 
 	if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
 	    (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
-	     BT_MESH_IV_UPDATE(flags))) {
+	     BT_MESH_IV_UPDATE(params.flags))) {
 		bt_mesh_beacon_ivu_initiator(false);
 	}
 
-	iv_change = bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(flags));
-
-	if (iv_change) {
-		/* Update all subnets */
-		bt_mesh_net_sec_update(NULL);
-	}
+	bt_mesh_net_iv_update(params.iv_index, BT_MESH_IV_UPDATE(params.flags));
 
 update_stats:
 	if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED &&
@@ -397,8 +398,36 @@
 	}
 }
 
+void bt_mesh_beacon_update(struct bt_mesh_subnet *sub)
+{
+	uint8_t flags = bt_mesh_net_flags(sub);
+	struct bt_mesh_subnet_keys *keys;
+	int err;
+
+	keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
+
+	BT_DBG("NetIndex 0x%03x Using %s key", sub->net_idx,
+	       SUBNET_KEY_TX_IDX(sub) ? "new" : "current");
+	BT_DBG("flags 0x%02x, IVI 0x%08x", flags, bt_mesh.iv_index);
+
+	err = bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id,
+				   bt_mesh.iv_index, sub->auth);
+	if (err) {
+		BT_ERR("Failed updating net beacon for 0x%03x", sub->net_idx);
+	}
+}
+
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	if (evt == BT_MESH_KEY_ADDED || evt == BT_MESH_KEY_SWAPPED) {
+		bt_mesh_beacon_update(sub);
+	}
+}
+
 void bt_mesh_beacon_init(void)
 {
+	bt_mesh_subnet_cb_list[1] = subnet_evt;
+
 	k_delayed_work_init(&beacon_timer, beacon_send);
 }
 
@@ -413,27 +442,22 @@
 	}
 }
 
+static void subnet_beacon_enable(struct bt_mesh_subnet *sub)
+{
+	sub->beacons_last = 0U;
+	sub->beacons_cur = 0U;
+
+	bt_mesh_beacon_update(sub);
+}
+
 void bt_mesh_beacon_enable(void)
 {
-	int i;
-
 	if (!bt_mesh_is_provisioned()) {
 		k_work_submit(&beacon_timer.work);
 		return;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		sub->beacons_last = 0;
-		sub->beacons_cur = 0;
-
-		bt_mesh_net_beacon_update(sub);
-	}
+	bt_mesh_subnet_foreach(subnet_beacon_enable);
 
 	k_work_submit(&beacon_timer.work);
 }
diff --git a/nimble/host/mesh/src/beacon.h b/nimble/host/mesh/src/beacon.h
index ac4bfed..a6aa8cb 100644
--- a/nimble/host/mesh/src/beacon.h
+++ b/nimble/host/mesh/src/beacon.h
@@ -22,5 +22,6 @@
 			   struct os_mbuf *buf);
 
 void bt_mesh_beacon_init(void);
+void bt_mesh_beacon_update(struct bt_mesh_subnet *sub);
 
 #endif
diff --git a/nimble/host/mesh/src/cfg_srv.c b/nimble/host/mesh/src/cfg_srv.c
index f9ae62f..3b52f86 100644
--- a/nimble/host/mesh/src/cfg_srv.c
+++ b/nimble/host/mesh/src/cfg_srv.c
@@ -17,6 +17,7 @@
 
 #include "mesh_priv.h"
 #include "adv.h"
+#include "app_keys.h"
 #include "net.h"
 #include "rpl.h"
 #include "lpn.h"
@@ -40,6 +41,9 @@
 static uint8_t va_del(uint8_t *label_uuid, uint16_t *addr);
 #endif
 
+void (*bt_mesh_app_key_cb_list[1]) (uint16_t app_idx, uint16_t net_idx,
+			enum bt_mesh_key_evt evt);
+
 static int comp_add_elem(struct os_mbuf *buf, struct bt_mesh_elem *elem,
 			 bool primary)
 {
@@ -174,22 +178,6 @@
 	}
 }
 
-static bool app_key_is_valid(uint16_t app_idx)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
-
-		if (key->net_idx != BT_MESH_KEY_UNUSED &&
-		    key->app_idx == app_idx) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
 static uint8_t _mod_pub_set(struct bt_mesh_model *model, uint16_t pub_addr,
 			 uint16_t app_idx, uint8_t cred_flag, uint8_t ttl, uint8_t period,
 			 uint8_t retransmit, bool store)
@@ -230,7 +218,7 @@
 		return STATUS_SUCCESS;
 	}
 
-	if (!bt_mesh_app_key_find(app_idx)) {
+	if (!bt_mesh_app_key_exists(app_idx)) {
 		return STATUS_INVALID_APPKEY;
 	}
 
@@ -277,7 +265,7 @@
 
 	BT_DBG("model %p key_idx 0x%03x", model, key_idx);
 
-	if (!app_key_is_valid(key_idx)) {
+	if (!bt_mesh_app_key_exists(key_idx)) {
 		return STATUS_INVALID_APPKEY;
 	}
 
@@ -309,7 +297,7 @@
 
 	BT_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store);
 
-	if (!app_key_is_valid(key_idx)) {
+	if (!bt_mesh_app_key_exists(key_idx)) {
 		return STATUS_INVALID_APPKEY;
 	}
 
@@ -333,115 +321,27 @@
 	return STATUS_SUCCESS;
 }
 
-struct bt_mesh_app_key *bt_mesh_app_key_alloc(uint16_t app_idx)
+static void send_app_key_status(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				uint8_t status,
+				uint16_t app_idx, uint16_t net_idx)
 {
-	int i;
+	struct os_mbuf *msg = NET_BUF_SIMPLE(
+		BT_MESH_MODEL_BUF_LEN(OP_APP_KEY_STATUS, 4));
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+	bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
+	net_buf_simple_add_u8(msg, status);
+	key_idx_pack(msg, net_idx, app_idx);
 
-		if (key->net_idx == BT_MESH_KEY_UNUSED) {
-			return key;
-		}
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		BT_ERR("Unable to send App Key Status response");
 	}
-
-	return NULL;
-}
-
-static uint8_t app_key_set(uint16_t net_idx, uint16_t app_idx, const uint8_t val[16],
-			bool update)
-{
-	struct bt_mesh_app_keys *keys;
-	struct bt_mesh_app_key *key;
-	struct bt_mesh_subnet *sub;
-
-	BT_DBG("net_idx 0x%04x app_idx %04x update %u val %s",
-	       net_idx, app_idx, update, bt_hex(val, 16));
-
-	sub = bt_mesh_subnet_get(net_idx);
-	if (!sub) {
-		return STATUS_INVALID_NETKEY;
-	}
-
-	key = bt_mesh_app_key_find(app_idx);
-	if (update) {
-		if (!key) {
-			return STATUS_INVALID_APPKEY;
-		}
-
-		if (key->net_idx != net_idx) {
-			return STATUS_INVALID_BINDING;
-		}
-
-		keys = &key->keys[1];
-
-		/* The AppKey Update message shall generate an error when node
-		 * is in normal operation, Phase 2, or Phase 3 or in Phase 1
-		 * when the AppKey Update message on a valid AppKeyIndex when
-		 * the AppKey value is different.
-		 */
-		if (sub->kr_phase != BT_MESH_KR_PHASE_1) {
-			return STATUS_CANNOT_UPDATE;
-		}
-
-		if (key->updated) {
-			if (memcmp(keys->val, val, 16)) {
-				return STATUS_CANNOT_UPDATE;
-			} else {
-				return STATUS_SUCCESS;
-			}
-		}
-
-		key->updated = true;
-	} else {
-		if (key) {
-			if (key->net_idx == net_idx &&
-			    !memcmp(key->keys[0].val, val, 16)) {
-				return STATUS_SUCCESS;
-			}
-
-			if (key->net_idx == net_idx) {
-				return STATUS_IDX_ALREADY_STORED;
-			} else {
-				return STATUS_INVALID_NETKEY;
-			}
-		}
-
-		key = bt_mesh_app_key_alloc(app_idx);
-		if (!key) {
-			return STATUS_INSUFF_RESOURCES;
-		}
-
-		keys = &key->keys[0];
-	}
-
-	if (bt_mesh_app_id(val, &keys->id)) {
-		if (update) {
-			key->updated = false;
-		}
-
-		return STATUS_STORAGE_FAIL;
-	}
-
-	BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, keys->id);
-
-	key->net_idx = net_idx;
-	key->app_idx = app_idx;
-	memcpy(keys->val, val, 16);
-
-	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		BT_DBG("Storing AppKey persistently");
-		bt_mesh_store_app_key(key);
-	}
-
-	return STATUS_SUCCESS;
 }
 
 static void app_key_add(struct bt_mesh_model *model,
 			struct bt_mesh_msg_ctx *ctx,
 			struct os_mbuf *buf)
 {
-	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4);
 	uint16_t key_net_idx, key_app_idx;
 	uint8_t status;
 
@@ -449,26 +349,15 @@
 
 	BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
 
-	bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
+	status = bt_mesh_app_key_add(key_app_idx, key_net_idx, buf->om_data);
 
-	status = app_key_set(key_net_idx, key_app_idx, buf->om_data, false);
-	BT_DBG("status 0x%02x", status);
-	net_buf_simple_add_u8(msg, status);
-
-	key_idx_pack(msg, key_net_idx, key_app_idx);
-
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
-		BT_ERR("Unable to send App Key Status response");
-	}
-
-	os_mbuf_free_chain(msg);
+	send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
 }
 
 static void app_key_update(struct bt_mesh_model *model,
 			   struct bt_mesh_msg_ctx *ctx,
 			   struct os_mbuf *buf)
 {
-	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4);
 	uint16_t key_net_idx, key_app_idx;
 	uint8_t status;
 
@@ -476,97 +365,42 @@
 
 	BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
 
-	bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
-
-	status = app_key_set(key_net_idx, key_app_idx, buf->om_data, true);
+	status = bt_mesh_app_key_update(key_app_idx, key_net_idx, buf->om_data);
 	BT_DBG("status 0x%02x", status);
-	net_buf_simple_add_u8(msg, status);
-
-	key_idx_pack(msg, key_net_idx, key_app_idx);
-
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
-		BT_ERR("Unable to send App Key Status response");
-	}
-
-	os_mbuf_free_chain(msg);
+	send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
 }
 
-struct unbind_data {
-	uint16_t app_idx;
-	bool store;
-};
-
-static void _mod_unbind(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
-			bool vnd, bool primary, void *user_data)
+static void mod_app_key_del(struct bt_mesh_model *mod,
+			    struct bt_mesh_elem *elem, bool vnd, bool primary,
+			    void *user_data)
 {
-	struct unbind_data *data = user_data;
+	uint16_t *app_idx = user_data;
 
-	mod_unbind(mod, data->app_idx, data->store);
+	mod_unbind(mod, *app_idx, true);
 }
 
-void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store)
+static void app_key_evt(uint16_t app_idx, uint16_t net_idx,
+			enum bt_mesh_key_evt evt)
 {
-	struct unbind_data data = { .app_idx = key->app_idx, .store = store };
-
-	BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store);
-
-	bt_mesh_model_foreach(_mod_unbind, &data);
-
-	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-		bt_mesh_clear_app_key(key);
+	if (evt == BT_MESH_KEY_DELETED) {
+		bt_mesh_model_foreach(&mod_app_key_del, &app_idx);
 	}
-
-	key->net_idx = BT_MESH_KEY_UNUSED;
-	memset(key->keys, 0, sizeof(key->keys));
 }
 
 static void app_key_del(struct bt_mesh_model *model,
 			struct bt_mesh_msg_ctx *ctx,
 			struct os_mbuf *buf)
 {
-	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4);
 	uint16_t key_net_idx, key_app_idx;
-	struct bt_mesh_app_key *key;
 	uint8_t status;
 
 	key_idx_unpack(buf, &key_net_idx, &key_app_idx);
 
 	BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
 
-	if (!bt_mesh_subnet_get(key_net_idx)) {
-		status = STATUS_INVALID_NETKEY;
-		goto send_status;
-	}
+	status = bt_mesh_app_key_del(key_net_idx, key_net_idx);
 
-	key = bt_mesh_app_key_find(key_app_idx);
-	if (!key) {
-		/* Treat as success since the client might have missed a
-		 * previous response and is resending the request.
-		 */
-		status = STATUS_SUCCESS;
-		goto send_status;
-	}
-
-	if (key->net_idx != key_net_idx) {
-		status = STATUS_INVALID_BINDING;
-		goto send_status;
-	}
-
-	bt_mesh_app_key_del(key, true);
-	status = STATUS_SUCCESS;
-
-send_status:
-	bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
-
-	net_buf_simple_add_u8(msg, status);
-
-	key_idx_pack(msg, key_net_idx, key_app_idx);
-
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
-		BT_ERR("Unable to send App Key Status response");
-	}
-
-	os_mbuf_free_chain(msg);
+	send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
 }
 
 /* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */
@@ -579,8 +413,11 @@
 	struct os_mbuf *msg =
 		BT_MESH_MODEL_BUF(OP_APP_KEY_LIST,
 				  3 + IDX_LEN(CONFIG_BT_MESH_APP_KEY_COUNT));
-	uint16_t get_idx, i, prev;
+	uint16_t app_idx[CONFIG_BT_MESH_APP_KEY_COUNT];
+	uint16_t get_idx;
 	uint8_t status;
+	ssize_t count;
+	int i;
 
 	get_idx = net_buf_simple_pull_le16(buf);
 	if (get_idx > 0xfff) {
@@ -592,7 +429,7 @@
 
 	bt_mesh_model_msg_init(msg, OP_APP_KEY_LIST);
 
-	if (!bt_mesh_subnet_get(get_idx)) {
+	if (!bt_mesh_subnet_exists(get_idx)) {
 		status = STATUS_INVALID_NETKEY;
 	} else {
 		status = STATUS_SUCCESS;
@@ -605,25 +442,17 @@
 		goto send_status;
 	}
 
-	prev = BT_MESH_KEY_UNUSED;
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
-
-		if (key->net_idx != get_idx) {
-			continue;
-		}
-
-		if (prev == BT_MESH_KEY_UNUSED) {
-			prev = key->app_idx;
-			continue;
-		}
-
-		key_idx_pack(msg, prev, key->app_idx);
-		prev = BT_MESH_KEY_UNUSED;
+	count = bt_mesh_app_keys_get(get_idx, app_idx, ARRAY_SIZE(app_idx), 0);
+	if (count < 0 || count > ARRAY_SIZE(app_idx)) {
+		count = ARRAY_SIZE(app_idx);
 	}
 
-	if (prev != BT_MESH_KEY_UNUSED) {
-		net_buf_simple_add_le16(msg, prev);
+	for (i = 0; i < count - 1; i += 2) {
+		key_idx_pack(msg, app_idx[i], app_idx[i + 1]);
+	}
+
+	if (i < count) {
+		net_buf_simple_add_le16(msg, app_idx[i]);
 	}
 
 send_status:
@@ -1334,8 +1163,8 @@
 		return;
 	}
 
-	net_buf_simple_pull(buf, 16);
-	mod_id = net_buf_simple_pull(buf, 4);
+	net_buf_simple_pull_mem(buf, 16);
+	mod_id = net_buf_simple_pull_mem(buf, 4);
 
 	BT_DBG("elem_addr 0x%04x", elem_addr);
 
@@ -2044,7 +1873,7 @@
 		return;
 	}
 
-	net_buf_simple_pull(buf, 16);
+	net_buf_simple_pull_mem(buf, 16);
 
 	mod_id = buf->om_data;
 
@@ -2085,7 +1914,7 @@
 		return;
 	}
 
-	net_buf_simple_pull(buf, 16);
+	net_buf_simple_pull_mem(buf, 16);
 
 	mod_id = buf->om_data;
 
@@ -2124,7 +1953,7 @@
 		return;
 	}
 
-	net_buf_simple_pull(buf, 18);
+	net_buf_simple_pull_mem(buf, 18);
 
 	mod_id = buf->om_data;
 
@@ -2170,9 +1999,8 @@
 			struct bt_mesh_msg_ctx *ctx,
 			struct os_mbuf *buf)
 {
-	struct bt_mesh_subnet *sub;
+	uint8_t status;
 	uint16_t idx;
-	int err;
 
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
@@ -2182,72 +2010,16 @@
 
 	BT_DBG("idx 0x%04x", idx);
 
-	sub = bt_mesh_subnet_get(idx);
-	if (!sub) {
-		int i;
-
-		for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-			if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
-				sub = &bt_mesh.sub[i];
-				break;
-			}
-		}
-
-		if (!sub) {
-			send_net_key_status(model, ctx, idx,
-					    STATUS_INSUFF_RESOURCES);
-			return;
-		}
-	}
-
-	/* Check for already existing subnet */
-	if (sub->net_idx == idx) {
-		uint8_t status;
-
-		if (memcmp(buf->om_data, sub->keys[0].net, 16)) {
-			status = STATUS_IDX_ALREADY_STORED;
-		} else {
-			status = STATUS_SUCCESS;
-		}
-
-		send_net_key_status(model, ctx, idx, status);
-		return;
-	}
-
-	err = bt_mesh_net_keys_create(&sub->keys[0], buf->om_data);
-	if (err) {
-		send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED);
-		return;
-	}
-
-	sub->net_idx = idx;
-
-	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		BT_DBG("Storing NetKey persistently");
-		bt_mesh_store_subnet(sub);
-	}
-
-	/* Make sure we have valid beacon data to be sent */
-	bt_mesh_net_beacon_update(sub);
-
-	if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
-		sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
-		bt_mesh_proxy_beacon_send(sub);
-		bt_mesh_adv_update();
-	} else {
-		sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
-	}
-
-	send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
+	status = bt_mesh_subnet_add(idx, buf->om_data);
+	send_net_key_status(model, ctx, idx, status);
 }
 
 static void net_key_update(struct bt_mesh_model *model,
 			   struct bt_mesh_msg_ctx *ctx,
 			   struct os_mbuf *buf)
 {
-	struct bt_mesh_subnet *sub;
+	uint8_t status;
 	uint16_t idx;
-	int err;
 
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
@@ -2255,59 +2027,9 @@
 		return;
 	}
 
-	BT_DBG("idx 0x%04x", idx);
+	status = bt_mesh_subnet_update(idx, buf->om_data);
 
-	sub = bt_mesh_subnet_get(idx);
-	if (!sub) {
-		send_net_key_status(model, ctx, idx, STATUS_INVALID_NETKEY);
-		return;
-	}
-
-	/* The node shall successfully process a NetKey Update message on a
-	 * valid NetKeyIndex when the NetKey value is different and the Key
-	 * Refresh procedure has not been started, or when the NetKey value is
-	 * the same in Phase 1. The NetKey Update message shall generate an
-	 * error when the node is in Phase 2, or Phase 3.
-	 */
-	switch (sub->kr_phase) {
-	case BT_MESH_KR_NORMAL:
-		if (!memcmp(buf->om_data, sub->keys[0].net, 16)) {
-			return;
-		}
-		break;
-	case BT_MESH_KR_PHASE_1:
-		if (!memcmp(buf->om_data, sub->keys[1].net, 16)) {
-			send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
-			return;
-		}
-		/* fall through */
-	case BT_MESH_KR_PHASE_2:
-	case BT_MESH_KR_PHASE_3:
-		send_net_key_status(model, ctx, idx, STATUS_CANNOT_UPDATE);
-		return;
-	}
-
-	err = bt_mesh_net_keys_create(&sub->keys[1], buf->om_data);
-	if (!err && ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) ||
-		     (MYNEWT_VAL(BLE_MESH_FRIEND)))) {
-		err = friend_cred_update(sub);
-	}
-
-	if (err) {
-		send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED);
-		return;
-	}
-
-	sub->kr_phase = BT_MESH_KR_PHASE_1;
-
-	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		BT_DBG("Storing NetKey persistently");
-		bt_mesh_store_subnet(sub);
-	}
-
-	bt_mesh_net_beacon_update(sub);
-
-	send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
+	send_net_key_status(model, ctx, idx, status);
 }
 
 static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg)
@@ -2326,9 +2048,7 @@
 			struct bt_mesh_msg_ctx *ctx,
 			struct os_mbuf *buf)
 {
-	struct bt_mesh_subnet *sub;
 	uint16_t del_idx;
-	uint8_t status;
 
 	del_idx = net_buf_simple_pull_le16(buf);
 	if (del_idx > 0xfff) {
@@ -2338,28 +2058,18 @@
 
 	BT_DBG("idx 0x%04x", del_idx);
 
-	sub = bt_mesh_subnet_get(del_idx);
-	if (!sub) {
-		/* This could be a retry of a previous attempt that had its
-		 * response lost, so pretend that it was a success.
-		 */
-		status = STATUS_SUCCESS;
-		goto send_status;
-	}
-
 	/* The key that the message was encrypted with cannot be removed.
 	 * The NetKey List must contain a minimum of one NetKey.
 	 */
 	if (ctx->net_idx == del_idx) {
-		status = STATUS_CANNOT_REMOVE;
-		goto send_status;
+		send_net_key_status(model, ctx, del_idx,
+				    STATUS_CANNOT_REMOVE);
+		return;
 	}
 
-	bt_mesh_subnet_del(sub, true);
-	status = STATUS_SUCCESS;
+	bt_mesh_subnet_del(del_idx);
 
-send_status:
-	send_net_key_status(model, ctx, del_idx, status);
+	send_net_key_status(model, ctx, del_idx, STATUS_SUCCESS);
 }
 
 static void net_key_get(struct bt_mesh_model *model,
@@ -2369,29 +2079,23 @@
 	struct os_mbuf *msg =
 		BT_MESH_MODEL_BUF(OP_NET_KEY_LIST,
 				  IDX_LEN(CONFIG_BT_MESH_SUBNET_COUNT));
-	uint16_t prev, i;
+	uint16_t net_idx[CONFIG_BT_MESH_SUBNET_COUNT];
+	ssize_t count;
+	int i;
 
 	bt_mesh_model_msg_init(msg, OP_NET_KEY_LIST);
 
-	prev = BT_MESH_KEY_UNUSED;
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		if (prev == BT_MESH_KEY_UNUSED) {
-			prev = sub->net_idx;
-			continue;
-		}
-
-		key_idx_pack(msg, prev, sub->net_idx);
-		prev = BT_MESH_KEY_UNUSED;
+	count = bt_mesh_subnets_get(net_idx, ARRAY_SIZE(net_idx), 0);
+	if (count < 0 || count > ARRAY_SIZE(net_idx)) {
+		count = ARRAY_SIZE(net_idx);
 	}
 
-	if (prev != BT_MESH_KEY_UNUSED) {
-		net_buf_simple_add_le16(msg, prev);
+	for (i = 0; i < count - 1; i += 2) {
+		key_idx_pack(msg, net_idx[i], net_idx[i + 1]);
+	}
+
+	if (i < count) {
+		net_buf_simple_add_le16(msg, net_idx[i]);
 	}
 
 	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
@@ -2401,13 +2105,29 @@
 	os_mbuf_free_chain(msg);
 }
 
+static void send_node_id_status(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				uint8_t status,
+				uint16_t net_idx, uint8_t node_id)
+{
+	struct os_mbuf *msg = NET_BUF_SIMPLE(4);
+
+	bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS);
+	net_buf_simple_add_u8(msg, status);
+	net_buf_simple_add_le16(msg, net_idx);
+	net_buf_simple_add_u8(msg, node_id);
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		BT_ERR("Unable to send Node Identity Status");
+	}
+}
+
 static void node_identity_get(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
-	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4);
-	struct bt_mesh_subnet *sub;
-	uint8_t node_id;
+	enum bt_mesh_feat_state node_id;
+	uint8_t status;
 	uint16_t idx;
 
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
@@ -2417,38 +2137,19 @@
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
-		goto done;
+		return;
 	}
 
-	bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS);
+	status = bt_mesh_subnet_node_id_get(idx, &node_id);
 
-	sub = bt_mesh_subnet_get(idx);
-	if (!sub) {
-		net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY);
-		node_id = 0x00;
-	} else {
-		net_buf_simple_add_u8(msg, STATUS_SUCCESS);
-		node_id = sub->node_id;
-	}
-
-	net_buf_simple_add_le16(msg, idx);
-	net_buf_simple_add_u8(msg, node_id);
-
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
-		BT_ERR("Unable to send Node Identity Status");
-	}
-
-done:
-    os_mbuf_free_chain(msg);
+	send_node_id_status(model, ctx, status, idx, node_id);
 }
 
 static void node_identity_set(struct bt_mesh_model *model,
 			      struct bt_mesh_msg_ctx *ctx,
 			      struct os_mbuf *buf)
 {
-	struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4);
-	struct bt_mesh_subnet *sub;
-	uint8_t node_id;
+	uint8_t node_id, status;
 	uint16_t idx;
 
 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
@@ -2458,45 +2159,30 @@
 	idx = net_buf_simple_pull_le16(buf);
 	if (idx > 0xfff) {
 		BT_WARN("Invalid NetKeyIndex 0x%04x", idx);
-		goto done;
+		return;
 	}
 
 	node_id = net_buf_simple_pull_u8(buf);
 	if (node_id != 0x00 && node_id != 0x01) {
 		BT_WARN("Invalid Node ID value 0x%02x", node_id);
-		goto done;
+		return;
 	}
 
-	bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS);
-
-	sub = bt_mesh_subnet_get(idx);
-	if (!sub) {
-		net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY);
-		net_buf_simple_add_le16(msg, idx);
-		net_buf_simple_add_u8(msg, node_id);
-	} else  {
-		net_buf_simple_add_u8(msg, STATUS_SUCCESS);
-		net_buf_simple_add_le16(msg, idx);
-
-		if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) {
-			if (node_id) {
-				bt_mesh_proxy_identity_start(sub);
-			} else {
-				bt_mesh_proxy_identity_stop(sub);
-			}
-			bt_mesh_adv_update();
-		}
-
-		net_buf_simple_add_u8(msg, sub->node_id);
+	status = bt_mesh_subnet_node_id_set(idx, node_id);
+	if (status == STATUS_INVALID_NETKEY) {
+		send_node_id_status(model, ctx, status, idx,
+				    BT_MESH_NODE_IDENTITY_STOPPED);
+		return;
 	}
 
-	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
-		BT_ERR("Unable to send Node Identity Status");
+	if (status == STATUS_FEAT_NOT_SUPP) {
+		/* Should return success, even if feature isn't supported: */
+		send_node_id_status(model, ctx, STATUS_SUCCESS, idx,
+				    BT_MESH_NODE_IDENTITY_NOT_SUPPORTED);
+		return;
 	}
 
-done:
-	os_mbuf_free_chain(msg);
-
+	send_node_id_status(model, ctx, status, idx, node_id);
 }
 
 static void create_mod_app_status(struct os_mbuf *msg,
@@ -2797,7 +2483,7 @@
 		}
 
 		if (cfg->frnd == BT_MESH_FRIEND_DISABLED) {
-			bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
+			bt_mesh_friends_clear();
 		}
 	}
 
@@ -2878,7 +2564,7 @@
 static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
 		    struct os_mbuf *buf)
 {
-	struct bt_mesh_subnet *sub;
+	uint8_t kr_phase, status;
 	uint16_t idx;
 
 	idx = net_buf_simple_pull_le16(buf);
@@ -2889,20 +2575,15 @@
 
 	BT_DBG("idx 0x%04x", idx);
 
-	sub = bt_mesh_subnet_get(idx);
-	if (!sub) {
-		send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY);
-	} else {
-		send_krp_status(model, ctx, idx, sub->kr_phase,
-				STATUS_SUCCESS);
-	}
+	status = bt_mesh_subnet_kr_phase_get(idx, &kr_phase);
+
+	send_krp_status(model, ctx, idx, kr_phase, status);
 }
 
 static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
 		    struct os_mbuf *buf)
 {
-	struct bt_mesh_subnet *sub;
-	uint8_t phase;
+	uint8_t phase, status;
 	uint16_t idx;
 
 	idx = net_buf_simple_pull_le16(buf);
@@ -2913,47 +2594,13 @@
 		return;
 	}
 
-	BT_DBG("idx 0x%04x transition 0x%02x", idx, phase);
-
-	sub = bt_mesh_subnet_get(idx);
-	if (!sub) {
-		send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY);
+	status = bt_mesh_subnet_kr_phase_set(idx, &phase);
+	if (status == STATUS_CANNOT_UPDATE) {
+		BT_ERR("Invalid kr phase transition 0x%02x", phase);
 		return;
 	}
 
-	BT_DBG("%u -> %u", sub->kr_phase, phase);
-
-	if (phase < BT_MESH_KR_PHASE_2 || phase > BT_MESH_KR_PHASE_3 ||
-	    (sub->kr_phase == BT_MESH_KR_NORMAL &&
-	     phase == BT_MESH_KR_PHASE_2)) {
-		BT_WARN("Prohibited transition %u -> %u", sub->kr_phase, phase);
-		return;
-	}
-
-	if (sub->kr_phase == BT_MESH_KR_PHASE_1 &&
-	    phase == BT_MESH_KR_PHASE_2) {
-		sub->kr_phase = BT_MESH_KR_PHASE_2;
-		sub->kr_flag = 1;
-		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		    BT_DBG("Storing krp phase persistently");
-		    bt_mesh_store_subnet(sub);
-		}
-
-		bt_mesh_net_beacon_update(sub);
-	} else if ((sub->kr_phase == BT_MESH_KR_PHASE_1 ||
-		    sub->kr_phase == BT_MESH_KR_PHASE_2) &&
-		   phase == BT_MESH_KR_PHASE_3) {
-		bt_mesh_net_revoke_keys(sub);
-		if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) ||
-		    (MYNEWT_VAL(BLE_MESH_FRIEND))) {
-			friend_cred_refresh(ctx->net_idx);
-		}
-		sub->kr_phase = BT_MESH_KR_NORMAL;
-		sub->kr_flag = 0;
-		bt_mesh_net_beacon_update(sub);
-	}
-
-	send_krp_status(model, ctx, idx, sub->kr_phase, STATUS_SUCCESS);
+	send_krp_status(model, ctx, idx, phase, status);
 }
 
 static uint8_t hb_log(uint16_t val)
@@ -3388,6 +3035,8 @@
 {
 	struct bt_mesh_cfg_srv *cfg = model->user_data;
 
+	bt_mesh_app_key_cb_list[0] = app_key_evt;
+
 	BT_DBG("");
 
 	if (!bt_mesh_model_in_primary(model)) {
@@ -3466,7 +3115,6 @@
 void bt_mesh_cfg_reset(void)
 {
 	struct bt_mesh_cfg_srv *cfg = conf;
-	int i;
 
 	BT_DBG("");
 
@@ -3476,17 +3124,6 @@
 	cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED;
 	cfg->hb_sub.expiry = 0;
 
-	/* Delete all net keys, which also takes care of all app keys which
-	 * are associated with each net key.
-	 */
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx != BT_MESH_KEY_UNUSED) {
-			bt_mesh_subnet_del(sub, true);
-		}
-	}
-
 	bt_mesh_model_foreach(mod_reset, NULL);
 
 	memset(labels, 0, sizeof(labels));
@@ -3619,40 +3256,3 @@
 {
 	return conf;
 }
-
-void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store)
-{
-	int i;
-
-	BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store);
-
-	if (conf->hb_pub.net_idx == sub->net_idx) {
-		hb_pub_disable(conf);
-
-		if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-			bt_mesh_store_hb_pub();
-		}
-	}
-
-	/* Delete any app keys bound to this NetKey index */
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
-
-		if (key->net_idx == sub->net_idx) {
-			bt_mesh_app_key_del(key, store);
-		}
-	}
-
-	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
-		bt_mesh_friend_clear_net_idx(sub->net_idx);
-	}
-
-	bt_mesh_net_loopback_clear(sub->net_idx);
-
-	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
-		bt_mesh_clear_subnet(sub);
-	}
-
-	memset(sub, 0, sizeof(*sub));
-	sub->net_idx = BT_MESH_KEY_UNUSED;
-}
diff --git a/nimble/host/mesh/src/crypto.c b/nimble/host/mesh/src/crypto.c
index c9bf41e..3111a6e 100644
--- a/nimble/host/mesh/src/crypto.c
+++ b/nimble/host/mesh/src/crypto.c
@@ -325,114 +325,70 @@
 				   NULL, 0, &buf->om_data[7], mic_len);
 }
 
-static void create_app_nonce(uint8_t nonce[13], bool dev_key, uint8_t aszmic,
-			     uint16_t src, uint16_t dst, uint32_t seq_num,
-			     uint32_t iv_index)
+static void create_app_nonce(uint8_t nonce[13],
+			     const struct bt_mesh_app_crypto_ctx *ctx)
 {
-	if (dev_key) {
+	if (ctx->dev_key) {
 		nonce[0] = 0x02;
 	} else {
 		nonce[0] = 0x01;
 	}
 
-	sys_put_be32((seq_num | ((uint32_t)aszmic << 31)), &nonce[1]);
+	sys_put_be32((ctx->seq_num | ((uint32_t)ctx->aszmic << 31)), &nonce[1]);
 
-	sys_put_be16(src, &nonce[5]);
-	sys_put_be16(dst, &nonce[7]);
+	sys_put_be16(ctx->src, &nonce[5]);
+	sys_put_be16(ctx->dst, &nonce[7]);
 
-	sys_put_be32(iv_index, &nonce[9]);
+	sys_put_be32(ctx->iv_index, &nonce[9]);
 }
 
-static int mesh_app_encrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-			    struct os_mbuf *buf, const uint8_t *ad,
-			    uint16_t src, uint16_t dst, uint32_t seq_num, uint32_t iv_index)
+int bt_mesh_app_encrypt(const uint8_t key[16],
+			const struct bt_mesh_app_crypto_ctx *ctx,
+			struct os_mbuf *buf)
 {
+	int err;
 	uint8_t nonce[13];
 
 	BT_DBG("AppKey %s", bt_hex(key, 16));
-	BT_DBG("dev_key %u src 0x%04x dst 0x%04x", dev_key, src, dst);
-	BT_DBG("seq_num 0x%08x iv_index 0x%08x", (unsigned) seq_num,
-	       (unsigned) iv_index);
+	BT_DBG("dev_key %u src 0x%04x dst 0x%04x", ctx->dev_key, ctx->src,
+	       ctx->dst);
+	BT_DBG("seq_num 0x%08x iv_index 0x%08x", ctx->seq_num, ctx->iv_index);
 	BT_DBG("Clear: %s", bt_hex(buf->om_data, buf->om_len));
 
-	create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index);
+	create_app_nonce(nonce, ctx);
 
 	BT_DBG("Nonce  %s", bt_hex(nonce, 13));
 
-	return bt_ccm_encrypt(key, nonce, buf->om_data, buf->om_len, ad,
-				   ad ? 16 : 0, buf->om_data,
-				   APP_MIC_LEN(aszmic));
-}
+	err = bt_ccm_encrypt(key, nonce, buf->om_data, buf->om_len, ctx->ad,
+			     ctx->ad ? 16 : 0, buf->om_data,
+			     APP_MIC_LEN(ctx->aszmic));
 
-int bt_mesh_app_encrypt_in_place(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-				 struct os_mbuf *buf, const uint8_t *ad, uint16_t src,
-				 uint16_t dst, uint32_t seq_num, uint32_t iv_index)
-{
-	int err;
-
-	err = mesh_app_encrypt(key, dev_key, aszmic, buf, ad, src, dst,
-			       seq_num, iv_index);
 	if (!err) {
+		net_buf_simple_add(buf, APP_MIC_LEN(ctx->aszmic));
 		BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len));
 	}
 
 	return err;
 }
 
-int bt_mesh_app_encrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-			struct os_mbuf *buf, const uint8_t *ad,
-			uint16_t src, uint16_t dst, uint32_t seq_num, uint32_t iv_index)
-{
-	int err;
-
-	err = mesh_app_encrypt(key, dev_key, aszmic, buf, ad, src, dst,
-			       seq_num, iv_index);
-
-	if (!err) {
-		net_buf_simple_add(buf, APP_MIC_LEN(aszmic));
-		BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len));
-	}
-
-	return err;
-}
-
-static int mesh_app_decrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-			    struct os_mbuf *buf, struct os_mbuf *out,
-			    const uint8_t *ad, uint16_t src, uint16_t dst,
-			    uint32_t seq_num, uint32_t iv_index)
+int bt_mesh_app_decrypt(const uint8_t key[16],
+			const struct bt_mesh_app_crypto_ctx *ctx,
+			struct os_mbuf *buf, struct os_mbuf *out)
 {
 	uint8_t nonce[13];
+	int err;
 
 	BT_DBG("EncData (len %u) %s", buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
-	create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index);
+	create_app_nonce(nonce, ctx);
 
 	BT_DBG("AppKey %s", bt_hex(key, 16));
 	BT_DBG("Nonce  %s", bt_hex(nonce, 13));
 
-	return bt_ccm_decrypt(key, nonce, buf->om_data, buf->om_len, ad,
-				   ad ? 16 : 0, out->om_data,
-				   APP_MIC_LEN(aszmic));
-}
-
-int bt_mesh_app_decrypt_in_place(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-				 struct os_mbuf *buf, const uint8_t *ad, uint16_t src,
-				 uint16_t dst, uint32_t seq_num, uint32_t iv_index)
-{
-	return mesh_app_decrypt(key, dev_key, aszmic, buf, buf,
-				ad, src, dst, seq_num, iv_index);
-}
-
-int bt_mesh_app_decrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-			struct os_mbuf *buf, struct os_mbuf *out,
-			const uint8_t *ad, uint16_t src, uint16_t dst, uint32_t seq_num,
-			uint32_t iv_index)
-{
-	int err;
-
-	err = mesh_app_decrypt(key, dev_key, aszmic, buf, out,
-			       ad, src, dst, seq_num, iv_index);
+	err = bt_ccm_decrypt(key, nonce, buf->om_data, buf->om_len, ctx->ad,
+			     ctx->ad ? 16 : 0, out->om_data,
+			     APP_MIC_LEN(ctx->aszmic));
 	if (!err) {
 		net_buf_simple_add(out, buf->om_len);
 	}
diff --git a/nimble/host/mesh/src/crypto.h b/nimble/host/mesh/src/crypto.h
index 35fabe5..4b2961d 100644
--- a/nimble/host/mesh/src/crypto.h
+++ b/nimble/host/mesh/src/crypto.h
@@ -135,18 +135,27 @@
 				 struct os_mbuf*buf, const uint8_t *ad, uint16_t src,
 				 uint16_t dst, uint32_t seq_num, uint32_t iv_index);
 
-int bt_mesh_app_encrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-			struct os_mbuf*buf, const uint8_t *ad,
-			uint16_t src, uint16_t dst, uint32_t seq_num, uint32_t iv_index);
-
 int bt_mesh_app_decrypt_in_place(const uint8_t key[16], bool dev_key, uint8_t aszmic,
 				 struct os_mbuf *buf, const uint8_t *ad, uint16_t src,
 				 uint16_t dst, uint32_t seq_num, uint32_t iv_index);
 
-int bt_mesh_app_decrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
-			struct os_mbuf*buf, struct os_mbuf*out,
-			const uint8_t *ad, uint16_t src, uint16_t dst, uint32_t seq_num,
-			uint32_t iv_index);
+struct bt_mesh_app_crypto_ctx {
+	bool dev_key;
+	uint8_t aszmic;
+	uint16_t src;
+	uint16_t dst;
+	uint32_t seq_num;
+	uint32_t iv_index;
+	const uint8_t *ad;
+};
+
+int bt_mesh_app_encrypt(const uint8_t key[16],
+			const struct bt_mesh_app_crypto_ctx *ctx,
+			struct os_mbuf *buf);
+
+int bt_mesh_app_decrypt(const uint8_t key[16],
+			const struct bt_mesh_app_crypto_ctx *ctx,
+			struct os_mbuf *buf, struct os_mbuf *out);
 
 uint8_t bt_mesh_fcs_calc(const uint8_t *data, uint8_t data_len);
 
diff --git a/nimble/host/mesh/src/foundation.h b/nimble/host/mesh/src/foundation.h
index 2679bdf..985894b 100644
--- a/nimble/host/mesh/src/foundation.h
+++ b/nimble/host/mesh/src/foundation.h
@@ -148,11 +148,6 @@
 uint8_t bt_mesh_gatt_proxy_get(void);
 uint8_t bt_mesh_default_ttl_get(void);
 
-void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store);
-
-struct bt_mesh_app_key *bt_mesh_app_key_alloc(uint16_t app_idx);
-void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store);
-
 static inline void key_idx_pack(struct os_mbuf *buf,
 				uint16_t idx1, uint16_t idx2)
 {
@@ -165,7 +160,7 @@
 {
 	*idx1 = sys_get_le16(&buf->om_data[0]) & 0xfff;
 	*idx2 = sys_get_le16(&buf->om_data[1]) >> 4;
-	net_buf_simple_pull(buf, 3);
+	net_buf_simple_pull_mem(buf, 3);
 }
 
 #endif
diff --git a/nimble/host/mesh/src/friend.c b/nimble/host/mesh/src/friend.c
index 4915e38..ed6e69d 100644
--- a/nimble/host/mesh/src/friend.c
+++ b/nimble/host/mesh/src/friend.c
@@ -21,10 +21,12 @@
 #include "crypto.h"
 #include "adv.h"
 #include "net.h"
+#include "app_keys.h"
 #include "transport.h"
 #include "access.h"
 #include "foundation.h"
 #include "friend.h"
+#include "subnet.h"
 
 /* We reserve one extra buffer for each friendship, since we need to be able
  * to resend the last sent PDU, which sits separately outside of the queue.
@@ -89,7 +91,7 @@
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 
-		if (valid && !frnd->valid) {
+		if (valid && !frnd->subnet) {
 			continue;
 		}
 
@@ -97,7 +99,8 @@
 			continue;
 		}
 
-		if (net_idx != BT_MESH_KEY_ANY && frnd->net_idx != net_idx) {
+		if (net_idx != BT_MESH_KEY_ANY &&
+		    (!frnd->subnet || frnd->subnet->net_idx != net_idx)) {
 			continue;
 		}
 
@@ -109,6 +112,14 @@
 	return NULL;
 }
 
+static int friend_cred_create(struct bt_mesh_friend *frnd, uint8_t idx)
+{
+	return bt_mesh_friend_cred_create(&frnd->cred[idx], frnd->lpn,
+					  bt_mesh_primary_addr(),
+					  frnd->lpn_counter, frnd->counter,
+					  frnd->subnet->keys[idx].net);
+}
+
 static void purge_buffers(struct net_buf_slist_t *list)
 {
 	struct os_mbuf *buf;
@@ -142,7 +153,7 @@
 
 	k_delayed_work_cancel(&frnd->timer);
 
-	friend_cred_del(frnd->net_idx, frnd->lpn);
+	memset(frnd->cred, 0, sizeof(frnd->cred));
 
 	if (frnd->last) {
 		/* Cancel the sending if necessary */
@@ -163,7 +174,7 @@
 		seg->seg_count = 0U;
 	}
 
-	frnd->valid = 0;
+	frnd->subnet = NULL;
 	frnd->established = 0;
 	frnd->pending_buf = 0;
 	frnd->fsn = 0;
@@ -172,22 +183,20 @@
 	memset(frnd->sub_list, 0, sizeof(frnd->sub_list));
 }
 
-void bt_mesh_friend_clear_net_idx(uint16_t net_idx)
+void bt_mesh_friends_clear(void)
 {
 	int i;
 
-	BT_DBG("net_idx 0x%04x", net_idx);
+	BT_DBG("");
 
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 
-		if (frnd->net_idx == BT_MESH_KEY_UNUSED) {
+		if (!frnd->subnet) {
 			continue;
 		}
 
-		if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) {
-			friend_clear(frnd);
-		}
+		friend_clear(frnd);
 	}
 }
 
@@ -202,11 +211,12 @@
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 
-		if (frnd->net_idx == BT_MESH_KEY_UNUSED) {
+		if (!frnd->subnet) {
 			continue;
 		}
 
-		if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) {
+		if (net_idx == BT_MESH_KEY_ANY ||
+		    frnd->subnet->net_idx == net_idx) {
 			enqueue_update(frnd, 0x00);
 		}
 	}
@@ -324,12 +334,10 @@
 }
 
 struct unseg_app_sdu_meta {
-	struct bt_mesh_net_rx net;
+	struct bt_mesh_app_crypto_ctx crypto;
 	const uint8_t *key;
 	struct bt_mesh_subnet *subnet;
-	bool is_dev_key;
 	uint8_t aid;
-	uint8_t *ad;
 };
 
 static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd,
@@ -337,25 +345,30 @@
 				struct unseg_app_sdu_meta *meta)
 {
 	uint16_t app_idx = FRIEND_ADV(buf)->app_idx;
+	struct bt_mesh_net_rx net;
 	int err;
 
-	meta->subnet = bt_mesh_subnet_get(frnd->net_idx);
-	meta->is_dev_key = (app_idx == BT_MESH_KEY_DEV);
-	meta->is_dev_key = BT_MESH_IS_DEV_KEY(app_idx);
-	bt_mesh_net_header_parse(buf, &meta->net);
-	err = bt_mesh_app_key_get(meta->subnet, app_idx, meta->net.ctx.recv_dst,
-				  &meta->key, &meta->aid);
+	meta->subnet = frnd->subnet;
+	bt_mesh_net_header_parse(buf, &net);
+	err = bt_mesh_keys_resolve(&net.ctx, &net.sub, &meta->key, &meta->aid);
 	if (err) {
 		return err;
 	}
 
-	if (BT_MESH_ADDR_IS_VIRTUAL(meta->net.ctx.recv_dst)) {
-		meta->ad = bt_mesh_label_uuid_get(meta->net.ctx.recv_dst);
-		if (!meta->ad) {
+	meta->crypto.src = net.ctx.addr;
+	meta->crypto.dst = net.ctx.recv_dst;
+	meta->crypto.iv_index = BT_MESH_NET_IVI_TX;
+	meta->crypto.dev_key = BT_MESH_IS_DEV_KEY(app_idx);
+	meta->crypto.seq_num = net.seq;
+	meta->crypto.aszmic = 0;
+
+	if (BT_MESH_ADDR_IS_VIRTUAL(meta->crypto.dst)) {
+		meta->crypto.ad = bt_mesh_label_uuid_get(meta->crypto.dst);
+		if (!meta->crypto.ad) {
 			return -ENOENT;
 		}
 	} else {
-		meta->ad = NULL;
+		meta->crypto.ad = NULL;
 	}
 
 	return 0;
@@ -370,16 +383,14 @@
 
 	BT_DBG("");
 
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 	net_buf_simple_pull_mem(buf, 10);
 	buf->om_len -= 4;
 
-	err = bt_mesh_app_decrypt_in_place(meta->key, meta->is_dev_key,
-					   0, buf, meta->ad, meta->net.ctx.addr,
-					   meta->net.ctx.recv_dst, meta->net.seq,
-					   BT_MESH_NET_IVI_TX);
+	err = bt_mesh_app_decrypt(meta->key, &meta->crypto, buf, buf);
 
-	net_buf_simple_restore(buf, &state);
+	os_mbuf_restore(buf, &state);
+	net_buf_unref(buf);
 	return err;
 }
 
@@ -392,16 +403,13 @@
 
 	BT_DBG("");
 
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 	net_buf_simple_pull_mem(buf, 10);
 	buf->om_len -= 4;
 
-	err = bt_mesh_app_encrypt_in_place(meta->key, meta->is_dev_key, 0, buf,
-					   meta->ad, meta->net.ctx.addr,
-					   meta->net.ctx.recv_dst, bt_mesh.seq,
-					   BT_MESH_NET_IVI_TX);
+	err = bt_mesh_app_encrypt(meta->key, &meta->crypto, buf);
 
-	net_buf_simple_restore(buf, &state);
+	os_mbuf_restore(buf, &state);
 	return err;
 }
 
@@ -425,7 +433,7 @@
 	/* No need to reencrypt the message if the sequence number is
 	 * unchanged.
 	 */
-	if (meta.net.seq == bt_mesh.seq) {
+	if (meta.crypto.seq_num == bt_mesh.seq) {
 		return 0;
 	}
 
@@ -446,22 +454,16 @@
 static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct os_mbuf *buf,
 			      bool master_cred)
 {
-	struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
-	const uint8_t *enc, *priv;
+	const struct bt_mesh_net_cred *cred;
 	uint32_t iv_index;
 	uint16_t src;
-	uint8_t nid;
 	int err;
 
 	if (master_cred) {
-		enc = sub->keys[sub->kr_flag].enc;
-		priv = sub->keys[sub->kr_flag].privacy;
-		nid = sub->keys[sub->kr_flag].nid;
+		cred = &frnd->subnet->keys[SUBNET_KEY_TX_IDX(frnd->subnet)]
+				.msg;
 	} else {
-		if (friend_cred_get(sub, frnd->lpn, &nid, &enc, &priv)) {
-			BT_ERR("friend_cred_get failed");
-			return -ENOENT;
-		}
+		cred = &frnd->cred[SUBNET_KEY_TX_IDX(frnd->subnet)];
 	}
 
 	src = sys_get_be16(&buf->om_data[5]);
@@ -486,14 +488,14 @@
 		iv_index = (bt_mesh.iv_index - ((bt_mesh.iv_index & 1) != ivi));
 	}
 
-	buf->om_data[0] = (nid | (iv_index & 1) << 7);
+	buf->om_data[0] = (cred->nid | (iv_index & 1) << 7);
 
-	if (bt_mesh_net_encrypt(enc, buf, iv_index, false)) {
+	if (bt_mesh_net_encrypt(cred->enc, buf, iv_index, false)) {
 		BT_ERR("Encrypting failed");
 		return -EINVAL;
 	}
 
-	if (bt_mesh_net_obfuscate(buf->om_data, iv_index, priv)) {
+	if (bt_mesh_net_obfuscate(buf->om_data, iv_index, cred->privacy)) {
 		BT_ERR("Obfuscating failed");
 		return -EINVAL;
 	}
@@ -528,17 +530,16 @@
 {
 	struct bt_mesh_ctl_friend_update *upd;
 	struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*upd));
-	struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
 	struct os_mbuf *buf;
 
-	__ASSERT_NO_MSG(sub != NULL);
+	__ASSERT_NO_MSG(frnd->subnet);
 
 	BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md);
 
 	net_buf_simple_init(sdu, 1);
 
 	upd = net_buf_simple_add(sdu, sizeof(*upd));
-	upd->flags = bt_mesh_net_flags(sub);
+	upd->flags = bt_mesh_net_flags(frnd->subnet);
 	upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index);
 	upd->md = md;
 
@@ -765,13 +766,13 @@
 static void send_friend_clear(struct bt_mesh_friend *frnd)
 {
 	struct bt_mesh_msg_ctx ctx = {
-		.net_idx  = frnd->net_idx,
+		.net_idx  = frnd->subnet->net_idx,
 		.app_idx  = BT_MESH_KEY_UNUSED,
 		.addr     = frnd->clear.frnd,
 		.send_ttl = BT_MESH_TTL_MAX,
 	};
 	struct bt_mesh_net_tx tx = {
-		.sub  = bt_mesh_subnet_get(frnd->net_idx),
+		.sub  = frnd->subnet,
 		.ctx  = &ctx,
 		.src  = bt_mesh_primary_addr(),
 		.xmit = bt_mesh_net_transmit_get(),
@@ -932,7 +933,7 @@
 	struct bt_mesh_ctl_friend_req *msg = (void *)buf->om_data;
 	struct bt_mesh_friend *frnd = NULL;
 	uint32_t poll_to;
-	int i;
+	int i, err;
 
 	if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
 		BT_WARN("Ignoring Friend request from local interface");
@@ -986,9 +987,8 @@
 	}
 
 	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
-		if (!bt_mesh.frnd[i].valid) {
+		if (!bt_mesh.frnd[i].subnet) {
 			frnd = &bt_mesh.frnd[i];
-			frnd->valid = 1;
 			break;
 		}
 	}
@@ -1001,12 +1001,19 @@
 init_friend:
 	frnd->lpn = rx->ctx.addr;
 	frnd->num_elem = msg->num_elem;
-	frnd->net_idx = rx->sub->net_idx;
+	frnd->subnet = rx->sub;
 	frnd->recv_delay = msg->recv_delay;
 	frnd->poll_to = poll_to * 100;
 	frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
 	frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr);
 
+	err = friend_cred_create(frnd, SUBNET_KEY_TX_IDX(frnd->subnet));
+	if (err) {
+		BT_ERR("Failed to create friend credentials");
+		friend_clear(frnd);
+		return -EIO;
+	}
+
 	BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums",
 	       frnd->lpn, rx->ctx.recv_rssi, frnd->recv_delay,
 	       (unsigned) frnd->poll_to);
@@ -1020,9 +1027,6 @@
 			      offer_delay(frnd, rx->ctx.recv_rssi,
 					  msg->criteria));
 
-	friend_cred_create(rx->sub, frnd->lpn, frnd->lpn_counter,
-			   frnd->counter);
-
 	enqueue_offer(frnd, rx->ctx.recv_rssi);
 
 	return 0;
@@ -1039,12 +1043,12 @@
 		return false;
 	}
 
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 	net_buf_skip(buf, 5);   /* skip IVI, NID, CTL, TTL, SEQ */
 	buf_src = net_buf_pull_be16(buf);
 	net_buf_skip(buf, 3);   /* skip DST, OP/AID */
 	buf_seq_zero = ((net_buf_pull_be16(buf) >> 2) & TRANS_SEQ_ZERO_MASK);
-	net_buf_simple_restore(buf, &state);
+	os_mbuf_restore(buf, &state);
 
 	return ((src == buf_src) && (seq_zero == buf_seq_zero));
 }
@@ -1157,7 +1161,7 @@
 		return;
 	}
 
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 
 	net_buf_skip(buf, 1); /* skip IVI, NID */
 
@@ -1177,7 +1181,7 @@
 	upd->md = md;
 
 end:
-	net_buf_simple_restore(buf, &state);
+	os_mbuf_restore(buf, &state);
 }
 
 static void friend_timeout(struct ble_npl_event *work)
@@ -1236,8 +1240,54 @@
 	bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd);
 }
 
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	int i, err;
+
+	if (evt == BT_MESH_KEY_ADDED) {
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+		if (frnd->subnet != sub) {
+			continue;
+		}
+
+		switch (evt) {
+		case BT_MESH_KEY_DELETED:
+			BT_DBG("Cleared network for 0x%04x", frnd->lpn);
+			friend_clear(frnd);
+			break;
+		case BT_MESH_KEY_UPDATED:
+			BT_DBG("Generating new keys for 0x%04x", frnd->lpn);
+			err = friend_cred_create(frnd, 1);
+			if (err) {
+				BT_ERR("Failed updating friend cred for 0x%04x",
+				       frnd->lpn);
+				friend_clear(frnd);
+				break;
+			}
+
+			enqueue_update(frnd, 0);
+			break;
+		case BT_MESH_KEY_REVOKED:
+			BT_DBG("Revoking old keys for 0x%04x", frnd->lpn);
+			memcpy(&frnd->cred[0], &frnd->cred[1],
+			       sizeof(frnd->cred[0]));
+			memset(&frnd->cred[1], 0, sizeof(frnd->cred[1]));
+			enqueue_update(frnd, 0);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 int bt_mesh_friend_init(void)
 {
+	bt_mesh_subnet_cb_list[3] = subnet_evt;
 	int rc;
 	int i;
 
@@ -1255,8 +1305,6 @@
 		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
 		int j;
 
-		frnd->net_idx = BT_MESH_KEY_UNUSED;
-
 		net_buf_slist_init(&frnd->queue);
 
 		k_delayed_work_init(&frnd->timer, friend_timeout);
@@ -1281,7 +1329,7 @@
 		return false;
 	}
 
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 
 	net_buf_skip(buf, 1); /* skip IVI, NID */
 
@@ -1304,7 +1352,7 @@
 	found = ((net_buf_pull_be16(buf) >> 2) & TRANS_SEQ_ZERO_MASK) ==
 		(*seqauth & TRANS_SEQ_ZERO_MASK);
 end:
-	net_buf_simple_restore(buf, &state);
+	os_mbuf_restore(buf, &state);
 	return found;
 }
 
@@ -1435,7 +1483,7 @@
 		return false;
 	}
 
-	if (net_idx != frnd->net_idx) {
+	if (net_idx != frnd->subnet->net_idx) {
 		return false;
 	}
 
diff --git a/nimble/host/mesh/src/friend.h b/nimble/host/mesh/src/friend.h
index 48acc22..ee783f3 100644
--- a/nimble/host/mesh/src/friend.h
+++ b/nimble/host/mesh/src/friend.h
@@ -39,7 +39,7 @@
 
 void bt_mesh_friend_sec_update(uint16_t net_idx);
 
-void bt_mesh_friend_clear_net_idx(uint16_t net_idx);
+void bt_mesh_friends_clear(void);
 
 int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
 int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
diff --git a/nimble/host/mesh/src/glue.c b/nimble/host/mesh/src/glue.c
index 6d046eb..e1c2ae3 100644
--- a/nimble/host/mesh/src/glue.c
+++ b/nimble/host/mesh/src/glue.c
@@ -336,7 +336,7 @@
 {
     void *data = om->om_data;
 
-    net_buf_simple_pull(om, len);
+    net_buf_simple_pull_mem(om, len);
     return data;
 }
 
diff --git a/nimble/host/mesh/src/lpn.c b/nimble/host/mesh/src/lpn.c
index 783e031..df0c6c2 100644
--- a/nimble/host/mesh/src/lpn.c
+++ b/nimble/host/mesh/src/lpn.c
@@ -171,20 +171,20 @@
 static int send_friend_clear(void)
 {
 	struct bt_mesh_msg_ctx ctx = {
-		.net_idx     = bt_mesh.sub[0].net_idx,
+		.net_idx     = bt_mesh.lpn.sub->net_idx,
 		.app_idx     = BT_MESH_KEY_UNUSED,
 		.addr        = bt_mesh.lpn.frnd,
 		.send_ttl    = 0,
 	};
 	struct bt_mesh_net_tx tx = {
-		.sub = &bt_mesh.sub[0],
+		.sub = bt_mesh.lpn.sub,
 		.ctx = &ctx,
 		.src = bt_mesh_primary_addr(),
 		.xmit = bt_mesh_net_transmit_get(),
 	};
 	struct bt_mesh_ctl_friend_clear req = {
 		.lpn_addr    = sys_cpu_to_be16(tx.src),
-		.lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter),
+		.lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.lpn_counter),
 	};
 
 	BT_DBG("");
@@ -211,8 +211,6 @@
 
 	k_delayed_work_cancel(&lpn->timer);
 
-	friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd);
-
 	if (lpn->clear_success) {
 		lpn->old_friend = BT_MESH_ADDR_UNASSIGNED;
 	} else {
@@ -232,6 +230,7 @@
 	lpn->sent_req = 0;
 	lpn->established = 0;
 	lpn->clear_success = 0;
+	lpn->sub = NULL;
 
 	group_zero(lpn->added);
 	group_zero(lpn->pending);
@@ -285,13 +284,13 @@
 {
 	const struct bt_mesh_comp *comp = bt_mesh_comp_get();
 	struct bt_mesh_msg_ctx ctx = {
-		.net_idx  = bt_mesh.sub[0].net_idx,
+		.net_idx  = lpn->sub->net_idx,
 		.app_idx  = BT_MESH_KEY_UNUSED,
 		.addr     = BT_MESH_ADDR_FRIENDS,
 		.send_ttl = 0,
 	};
 	struct bt_mesh_net_tx tx = {
-		.sub = &bt_mesh.sub[0],
+		.sub = bt_mesh.lpn.sub,
 		.ctx = &ctx,
 		.src = bt_mesh_primary_addr(),
 		.xmit = POLL_XMIT,
@@ -302,7 +301,7 @@
 		.poll_to     = LPN_POLL_TO,
 		.prev_addr   = sys_cpu_to_be16(lpn->old_friend),
 		.num_elem    = comp->elem_count,
-		.lpn_counter = sys_cpu_to_be16(lpn->counter),
+		.lpn_counter = sys_cpu_to_be16(lpn->lpn_counter),
 	};
 
 	BT_DBG("");
@@ -351,13 +350,13 @@
 static int send_friend_poll(void)
 {
 	struct bt_mesh_msg_ctx ctx = {
-		.net_idx     = bt_mesh.sub[0].net_idx,
+		.net_idx     = bt_mesh.lpn.sub->net_idx,
 		.app_idx     = BT_MESH_KEY_UNUSED,
 		.addr        = bt_mesh.lpn.frnd,
 		.send_ttl    = 0,
 	};
 	struct bt_mesh_net_tx tx = {
-		.sub = &bt_mesh.sub[0],
+		.sub = bt_mesh.lpn.sub,
 		.ctx = &ctx,
 		.src = bt_mesh_primary_addr(),
 		.xmit = POLL_XMIT,
@@ -478,13 +477,21 @@
 	send_friend_poll();
 }
 
+static int friend_cred_create(struct bt_mesh_net_cred *cred,
+			      const uint8_t key[16])
+{
+	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+	return bt_mesh_friend_cred_create(cred, bt_mesh_primary_addr(),
+					  lpn->frnd, lpn->lpn_counter,
+					  lpn->frnd_counter, key);
+}
+
 int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
 			     struct os_mbuf *buf)
 {
 	struct bt_mesh_ctl_friend_offer *msg = (void *)buf->om_data;
 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
-	struct bt_mesh_subnet *sub = rx->sub;
-	struct friend_cred *cred;
 	uint16_t frnd_counter;
 	int err;
 
@@ -511,14 +518,21 @@
 
 	lpn->frnd = rx->ctx.addr;
 
-	cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter);
-	if (!cred) {
-		lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
-		return -ENOMEM;
+	/* Create friend credentials for each of the valid keys in the
+	 * friendship subnet:
+	 */
+	for (int i = 0; i < ARRAY_SIZE(lpn->cred); i++) {
+		if (!lpn->sub->keys[i].valid) {
+			continue;
+		}
+
+		err = friend_cred_create(&lpn->cred[i], lpn->sub->keys[i].net);
+		if (err) {
+			lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
+			return err;
+		}
 	}
-
 	/* TODO: Add offer acceptance criteria check */
-
 	k_delayed_work_cancel(&lpn->timer);
 
 	lpn->recv_win = msg->recv_win;
@@ -526,14 +540,14 @@
 
 	err = send_friend_poll();
 	if (err) {
-		friend_cred_clear(cred);
+		lpn->sub = NULL;
 		lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
 		lpn->recv_win = 0;
 		lpn->queue_size = 0;
 		return err;
 	}
 
-	lpn->counter++;
+	lpn->lpn_counter++;
 
 	return 0;
 }
@@ -560,7 +574,7 @@
 
 	BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter);
 
-	if (addr != bt_mesh_primary_addr() || counter != lpn->counter) {
+	if (addr != bt_mesh_primary_addr() || counter != lpn->lpn_counter) {
 		BT_WARN("Invalid parameters in Friend Clear Confirm");
 		return 0;
 	}
@@ -633,13 +647,13 @@
 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
 	int added_count = group_popcount(lpn->added);
 	struct bt_mesh_msg_ctx ctx = {
-		.net_idx     = bt_mesh.sub[0].net_idx,
+		.net_idx     = lpn->sub->net_idx,
 		.app_idx     = BT_MESH_KEY_UNUSED,
 		.addr        = lpn->frnd,
 		.send_ttl    = 0,
 	};
 	struct bt_mesh_net_tx tx = {
-		.sub = &bt_mesh.sub[0],
+		.sub = lpn->sub,
 		.ctx = &ctx,
 		.src = bt_mesh_primary_addr(),
 		.xmit = POLL_XMIT,
@@ -760,7 +774,7 @@
 		if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
 			bt_mesh_scan_disable();
 		}
-		lpn->counter++;
+		lpn->lpn_counter++;
 		lpn_set_state(BT_MESH_LPN_ENABLED);
 		lpn->sent_req = 0U;
 		k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
@@ -981,11 +995,7 @@
 	BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags,
 	       (unsigned) iv_index, msg->md);
 
-	if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags),
-			      rx->new_key)) {
-		bt_mesh_net_beacon_update(sub);
-	}
-
+	bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags), rx->new_key);
 	bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(msg->flags));
 
 	if (lpn->groups_changed) {
@@ -1025,10 +1035,30 @@
 	lpn_cb = cb;
 }
 
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	switch (evt) {
+	case BT_MESH_KEY_DELETED:
+		if (sub == bt_mesh.lpn.sub) {
+			BT_DBG("NetKey deleted");
+			clear_friendship(true, false);
+		}
+		break;
+	case BT_MESH_KEY_UPDATED:
+		BT_DBG("NetKey updated");
+		friend_cred_create(&bt_mesh.lpn.cred[1], sub->keys[1].net);
+		break;
+	default:
+		break;
+	}
+}
+
 int bt_mesh_lpn_init(void)
 {
 	struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
 
+	bt_mesh_subnet_cb_list[2] = subnet_evt;
+
 	BT_DBG("");
 
 	k_delayed_work_init(&lpn->timer, lpn_timeout);
diff --git a/nimble/host/mesh/src/mesh.c b/nimble/host/mesh/src/mesh.c
index da68884..0d68c25 100644
--- a/nimble/host/mesh/src/mesh.c
+++ b/nimble/host/mesh/src/mesh.c
@@ -19,6 +19,7 @@
 #include "adv.h"
 #include "prov.h"
 #include "net.h"
+#include "app_keys.h"
 #include "rpl.h"
 #include "beacon.h"
 #include "lpn.h"
@@ -129,13 +130,6 @@
 
 	memcpy(bt_mesh.dev_key, dev_key, 16);
 
-	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		BT_DBG("Storing network information persistently");
-		bt_mesh_store_net();
-		bt_mesh_store_subnet(&bt_mesh.sub[0]);
-		bt_mesh_store_iv(false);
-	}
-
 	bt_mesh_start();
 
 	return 0;
@@ -178,6 +172,8 @@
 
 	bt_mesh_rx_reset();
 	bt_mesh_tx_reset();
+	bt_mesh_app_keys_reset();
+	bt_mesh_net_keys_reset();
 
 	bt_mesh_net_loopback_clear(BT_MESH_KEY_ANY);
 
@@ -192,7 +188,7 @@
 	}
 
 	if ((MYNEWT_VAL(BLE_MESH_FRIEND))) {
-		bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
+		bt_mesh_friends_clear();
 	}
 
 	if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
diff --git a/nimble/host/mesh/src/mesh_priv.h b/nimble/host/mesh/src/mesh_priv.h
index 03e61c8..11efcac 100644
--- a/nimble/host/mesh/src/mesh_priv.h
+++ b/nimble/host/mesh/src/mesh_priv.h
@@ -8,7 +8,8 @@
 #ifndef __MESH_PRIV_H
 #define __MESH_PRIV_H
 
-#include <stdbool.h> 
+#include <stdbool.h>
+#include <stdint.h>
 
 #define BT_MESH_KEY_PRIMARY 0x0000
 #define BT_MESH_KEY_ANY     0xffff
@@ -17,6 +18,21 @@
 #define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
 #define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000)
 #define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb)
+
+enum bt_mesh_key_evt {
+	BT_MESH_KEY_ADDED,   /* New key added */
+	BT_MESH_KEY_DELETED, /* Existing key deleted */
+	BT_MESH_KEY_UPDATED, /* KR phase 1, second key added */
+	BT_MESH_KEY_SWAPPED, /* KR phase 2, now sending on second key */
+	BT_MESH_KEY_REVOKED, /* KR phase 3, old key removed */
+};
+
+/** Appkey callback. Instantiate with @ref BT_MESH_APP_KEY_CB */
+struct bt_mesh_app_key_cb {
+	void (*evt_handler)(uint16_t app_idx, uint16_t net_idx,
+			    enum bt_mesh_key_evt evt);
+};
+
 struct bt_mesh_net;
 int bt_mesh_start(void);
 
diff --git a/nimble/host/mesh/src/net.c b/nimble/host/mesh/src/net.c
index 7e7cc93..c10fb61 100644
--- a/nimble/host/mesh/src/net.c
+++ b/nimble/host/mesh/src/net.c
@@ -56,17 +56,6 @@
 #define SRC(pdu)           (sys_get_be16(&(pdu)[5]))
 #define DST(pdu)           (sys_get_be16(&(pdu)[7]))
 
-/* Determine how many friendship credentials we need */
-#if (MYNEWT_VAL(BLE_MESH_FRIEND))
-#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)
-#elif (MYNEWT_VAL(BLE_MESH_LOW_POWER))
-#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)
-#else
-#define FRIEND_CRED_COUNT 0
-#endif
-
-static struct friend_cred friend_cred[FRIEND_CRED_COUNT];
-
 static struct {
 	uint32_t src : 15, /* MSb of source is always 0 */
 	      seq : 17;
@@ -76,16 +65,6 @@
 /* Singleton network context (the implementation only supports one) */
 struct bt_mesh_net bt_mesh = {
 	.local_queue = STAILQ_HEAD_INITIALIZER(bt_mesh.local_queue),
-	.sub = {
-		[0 ... (MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) - 1)] = {
-			.net_idx = BT_MESH_KEY_UNUSED,
-		}
-	},
-	.app_keys = {
-		[0 ... (MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) - 1)] = {
-			.net_idx = BT_MESH_KEY_UNUSED,
-		}
-	},
 };
 
 static struct os_mempool loopback_buf_pool_mem;
@@ -137,331 +116,29 @@
 	msg_cache_next %= ARRAY_SIZE(msg_cache);
 }
 
-struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx)
-{
-	int i;
-
-	if (net_idx == BT_MESH_KEY_ANY) {
-		return &bt_mesh.sub[0];
-	}
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		if (bt_mesh.sub[i].net_idx == net_idx) {
-			return &bt_mesh.sub[i];
-		}
-	}
-
-	return NULL;
-}
-
-int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys,
-			    const uint8_t key[16])
-{
-	uint8_t p[] = { 0 };
-	uint8_t nid;
-	int err;
-
-	err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy);
-	if (err) {
-		BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
-		return err;
-	}
-
-	memcpy(keys->net, key, 16);
-
-	keys->nid = nid;
-
-	BT_DBG("NID 0x%02x EncKey %s", keys->nid, bt_hex(keys->enc, 16));
-	BT_DBG("PrivacyKey %s", bt_hex(keys->privacy, 16));
-
-	err = bt_mesh_k3(key, keys->net_id);
-	if (err) {
-		BT_ERR("Unable to generate Net ID");
-		return err;
-	}
-
-	BT_DBG("NetID %s", bt_hex(keys->net_id, 8));
-
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-	err = bt_mesh_identity_key(key, keys->identity);
-	if (err) {
-		BT_ERR("Unable to generate IdentityKey");
-		return err;
-	}
-
-	BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16));
-#endif /* GATT_PROXY */
-
-	err = bt_mesh_beacon_key(key, keys->beacon);
-	if (err) {
-		BT_ERR("Unable to generate beacon key");
-		return err;
-	}
-
-	BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16));
-
-	return 0;
-}
-
-int friend_cred_set(struct friend_cred *cred, uint8_t idx, const uint8_t net_key[16])
-{
-	uint16_t lpn_addr, frnd_addr;
-	int err;
-	uint8_t p[9];
-
-#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
-	if (cred->addr == bt_mesh.lpn.frnd) {
-		lpn_addr = bt_mesh_primary_addr();
-		frnd_addr = cred->addr;
-	} else {
-		lpn_addr = cred->addr;
-		frnd_addr = bt_mesh_primary_addr();
-	}
-#else
-	lpn_addr = cred->addr;
-	frnd_addr = bt_mesh_primary_addr();
-#endif
-
-	BT_DBG("LPNAddress 0x%04x FriendAddress 0x%04x", lpn_addr, frnd_addr);
-	BT_DBG("LPNCounter 0x%04x FriendCounter 0x%04x", cred->lpn_counter,
-	       cred->frnd_counter);
-
-	p[0] = 0x01;
-	sys_put_be16(lpn_addr, p + 1);
-	sys_put_be16(frnd_addr, p + 3);
-	sys_put_be16(cred->lpn_counter, p + 5);
-	sys_put_be16(cred->frnd_counter, p + 7);
-
-	err = bt_mesh_k2(net_key, p, sizeof(p), &cred->cred[idx].nid,
-			 cred->cred[idx].enc, cred->cred[idx].privacy);
-	if (err) {
-		BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
-		return err;
-	}
-
-	BT_DBG("Friend NID 0x%02x EncKey %s", cred->cred[idx].nid,
-	       bt_hex(cred->cred[idx].enc, 16));
-	BT_DBG("Friend PrivacyKey %s", bt_hex(cred->cred[idx].privacy, 16));
-
-	return 0;
-}
-
-void friend_cred_refresh(uint16_t net_idx)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
-		struct friend_cred *cred = &friend_cred[i];
-
-		if (cred->addr != BT_MESH_ADDR_UNASSIGNED &&
-		    cred->net_idx == net_idx) {
-			memcpy(&cred->cred[0], &cred->cred[1],
-			       sizeof(cred->cred[0]));
-		}
-	}
-}
-
-int friend_cred_update(struct bt_mesh_subnet *sub)
-{
-	int err, i;
-
-	BT_DBG("net_idx 0x%04x", sub->net_idx);
-
-	for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
-		struct friend_cred *cred = &friend_cred[i];
-
-		if (cred->addr == BT_MESH_ADDR_UNASSIGNED ||
-		    cred->net_idx != sub->net_idx) {
-			continue;
-		}
-
-		err = friend_cred_set(cred, 1, sub->keys[1].net);
-		if (err) {
-			return err;
-		}
-	}
-
-	return 0;
-}
-
-struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, uint16_t addr,
-				       uint16_t lpn_counter, uint16_t frnd_counter)
-{
-	struct friend_cred *cred;
-	int i, err;
-
-	BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr);
-
-	for (cred = NULL, i = 0; i < ARRAY_SIZE(friend_cred); i++) {
-		if ((friend_cred[i].addr == BT_MESH_ADDR_UNASSIGNED) ||
-		    (friend_cred[i].addr == addr &&
-		     friend_cred[i].net_idx == sub->net_idx)) {
-			cred = &friend_cred[i];
-			break;
-		}
-	}
-
-	if (!cred) {
-		BT_WARN("No free friend credential slots");
-		return NULL;
-	}
-
-	cred->net_idx = sub->net_idx;
-	cred->addr = addr;
-	cred->lpn_counter = lpn_counter;
-	cred->frnd_counter = frnd_counter;
-
-	err = friend_cred_set(cred, 0, sub->keys[0].net);
-	if (err) {
-		friend_cred_clear(cred);
-		return NULL;
-	}
-
-	if (sub->kr_flag) {
-		err = friend_cred_set(cred, 1, sub->keys[1].net);
-		if (err) {
-			friend_cred_clear(cred);
-			return NULL;
-		}
-	}
-
-	return cred;
-}
-
-void friend_cred_clear(struct friend_cred *cred)
-{
-	cred->net_idx = BT_MESH_KEY_UNUSED;
-	cred->addr = BT_MESH_ADDR_UNASSIGNED;
-	cred->lpn_counter = 0;
-	cred->frnd_counter = 0;
-	memset(cred->cred, 0, sizeof(cred->cred));
-}
-
-int friend_cred_del(uint16_t net_idx, uint16_t addr)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
-		struct friend_cred *cred = &friend_cred[i];
-
-		if (cred->addr == addr && cred->net_idx == net_idx) {
-			friend_cred_clear(cred);
-			return 0;
-		}
-	}
-
-	return -ENOENT;
-}
-
-int friend_cred_get(struct bt_mesh_subnet *sub, uint16_t addr, uint8_t *nid,
-		    const uint8_t **enc, const uint8_t **priv)
-{
-	int i;
-
-	BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr);
-
-	for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
-		struct friend_cred *cred = &friend_cred[i];
-
-		if (cred->net_idx != sub->net_idx) {
-			continue;
-		}
-
-		if (addr != BT_MESH_ADDR_UNASSIGNED && cred->addr != addr) {
-			continue;
-		}
-
-		if (nid) {
-			*nid = cred->cred[sub->kr_flag].nid;
-		}
-
-		if (enc) {
-			*enc = cred->cred[sub->kr_flag].enc;
-		}
-
-		if (priv) {
-			*priv = cred->cred[sub->kr_flag].privacy;
-		}
-
-		return 0;
-	}
-
-	return -ENOENT;
-}
-
-uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
-{
-	uint8_t flags = 0x00;
-
-	if (sub && sub->kr_flag) {
-		flags |= BT_MESH_NET_FLAG_KR;
-	}
-
-	if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
-		flags |= BT_MESH_NET_FLAG_IVU;
-	}
-
-	return flags;
-}
-
-int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub)
-{
-	uint8_t flags = bt_mesh_net_flags(sub);
-	struct bt_mesh_subnet_keys *keys;
-
-	if (sub->kr_flag) {
-		BT_DBG("NetIndex %u Using new key", sub->net_idx);
-		keys = &sub->keys[1];
-	} else {
-		BT_DBG("NetIndex %u Using current key", sub->net_idx);
-		keys = &sub->keys[0];
-	}
-
-	BT_DBG("flags 0x%02x, IVI 0x%08x", flags, (unsigned) bt_mesh.iv_index);
-
-	return bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id,
-				   bt_mesh.iv_index, sub->auth);
-}
-
 int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
 		       uint32_t iv_index)
 {
-	struct bt_mesh_subnet *sub;
 	int err;
 
-	BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags,
-	       (unsigned) iv_index);
+	BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags, iv_index);
 
 	BT_DBG("NetKey %s", bt_hex(key, 16));
 
+	if (BT_MESH_KEY_REFRESH(flags)) {
+		err = bt_mesh_subnet_set(idx, BT_MESH_KR_PHASE_2, NULL, key);
+	} else {
+		err = bt_mesh_subnet_set(idx, BT_MESH_KR_NORMAL, key, NULL);
+	}
+
+	if (err) {
+		BT_ERR("Failed creating subnet");
+		return err;
+	}
+
 	(void)memset(msg_cache, 0, sizeof(msg_cache));
 	msg_cache_next = 0U;
 
-	sub = &bt_mesh.sub[0];
-
-	sub->kr_flag = BT_MESH_KEY_REFRESH(flags);
-	if (sub->kr_flag) {
-		err = bt_mesh_net_keys_create(&sub->keys[1], key);
-		if (err) {
-			return -EIO;
-		}
-
-		sub->kr_phase = BT_MESH_KR_PHASE_2;
-	} else {
-		err = bt_mesh_net_keys_create(&sub->keys[0], key);
-		if (err) {
-			return -EIO;
-		}
-	}
-
-	sub->net_idx = idx;
-
-	if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
-		sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
-	} else {
-		sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
-	}
-
 	bt_mesh.iv_index = iv_index;
 	atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS,
 			  BT_MESH_IV_UPDATE(flags));
@@ -472,89 +149,16 @@
 	 */
 	bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS;
 
-	/* Make sure we have valid beacon data to be sent */
-	bt_mesh_net_beacon_update(sub);
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		BT_DBG("Storing network information persistently");
+		bt_mesh_store_net();
+		bt_mesh_store_subnet(idx);
+		bt_mesh_store_iv(false);
+	}
 
 	return 0;
 }
 
-void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub)
-{
-	int i;
-
-	BT_DBG("idx 0x%04x", sub->net_idx);
-
-	memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0]));
-	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-		BT_DBG("Storing Updated NetKey persistently");
-		bt_mesh_store_subnet(sub);
-	}
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
-
-		if (key->net_idx != sub->net_idx || !key->updated) {
-			continue;
-		}
-
-		memcpy(&key->keys[0], &key->keys[1], sizeof(key->keys[0]));
-		key->updated = false;
-		if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-			BT_DBG("Storing Updated AppKey persistently");
-			bt_mesh_store_app_key(key);
-		}
-	}
-}
-
-bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, uint8_t new_kr, bool new_key)
-{
-	if (new_kr != sub->kr_flag && sub->kr_phase == BT_MESH_KR_NORMAL) {
-		BT_WARN("KR change in normal operation. Are we blacklisted?");
-		return false;
-	}
-
-	sub->kr_flag = new_kr;
-
-	if (sub->kr_flag) {
-		if (sub->kr_phase == BT_MESH_KR_PHASE_1) {
-			BT_DBG("Phase 1 -> Phase 2");
-			sub->kr_phase = BT_MESH_KR_PHASE_2;
-			if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
-				BT_DBG("Storing krp phase persistently");
-				bt_mesh_store_subnet(sub);
-			}
-
-			return true;
-		}
-	} else {
-		switch (sub->kr_phase) {
-		case BT_MESH_KR_PHASE_1:
-			if (!new_key) {
-				/* Ignore */
-				break;
-			}
-		/* Upon receiving a Secure Network beacon with the KR flag set
-		 * to 0 using the new NetKey in Phase 1, the node shall
-		 * immediately transition to Phase 3, which effectively skips
-		 * Phase 2.
-		 *
-		 * Intentional fall-through.
-		 */
-		case BT_MESH_KR_PHASE_2:
-			BT_DBG("KR Phase 0x%02x -> Normal", sub->kr_phase);
-			bt_mesh_net_revoke_keys(sub);
-			if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) ||
-			    (MYNEWT_VAL(BLE_MESH_FRIEND))) {
-				friend_cred_refresh(sub->net_idx);
-			}
-			sub->kr_phase = BT_MESH_KR_NORMAL;
-			return true;
-		}
-	}
-
-	return false;
-}
-
 #if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST)
 void bt_mesh_iv_update_test(bool enable)
 {
@@ -576,29 +180,12 @@
 		bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
 	}
 
-	bt_mesh_net_sec_update(NULL);
-
 	return atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
 }
 #endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */
 
-/* Used for sending immediate beacons to Friend queues and GATT clients */
-void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub)
-{
-	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
-		bt_mesh_friend_sec_update(sub ? sub->net_idx : BT_MESH_KEY_ANY);
-	}
-
-	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
-	    bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
-		bt_mesh_proxy_beacon_send(sub);
-	}
-}
-
 bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update)
 {
-	int i;
-
 	if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
 		/* We're currently in IV Update mode */
 
@@ -682,12 +269,18 @@
 
 	k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
-			bt_mesh_net_beacon_update(&bt_mesh.sub[i]);
-		}
+	/* Notify other modules */
+	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+		bt_mesh_friend_sec_update(BT_MESH_KEY_ANY);
 	}
 
+	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
+	    bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
+		bt_mesh_proxy_beacon_send(NULL);
+	}
+
+	bt_mesh_subnet_foreach(bt_mesh_beacon_update);
+
 	if (MYNEWT_VAL(BLE_MESH_CDB)) {
 		bt_mesh_cdb_iv_update(iv_index, iv_update);
 	}
@@ -712,7 +305,6 @@
 	    bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY)) {
 		bt_mesh_beacon_ivu_initiator(true);
 		bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
-		bt_mesh_net_sec_update(NULL);
 	}
 
 	return seq;
@@ -741,7 +333,7 @@
 			.old_iv = (IVI(buf->om_data) != (bt_mesh.iv_index & 0x01)),
 			.ctl = CTL(buf->om_data),
 			.seq = SEQ(buf->om_data),
-			.new_key = sub->kr_flag,
+			.new_key = SUBNET_KEY_TX_IDX(sub),
 			.local_match = 1U,
 			.friend_match = 0U,
 		};
@@ -754,26 +346,16 @@
 	}
 }
 
-static void net_tx_cred_get(struct bt_mesh_net_tx *tx, uint8_t *nid,
-			    const uint8_t **enc, const uint8_t **priv)
+static const struct bt_mesh_net_cred *net_tx_cred_get(struct bt_mesh_net_tx *tx)
 {
-	if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) || !tx->friend_cred) {
-		tx->friend_cred = 0U;
-		*nid = tx->sub->keys[tx->sub->kr_flag].nid;
-		*enc = tx->sub->keys[tx->sub->kr_flag].enc;
-		*priv = tx->sub->keys[tx->sub->kr_flag].privacy;
-		return;
+#if defined(BLE_MESH_LOW_POWER)
+	if (tx->friend_cred && bt_mesh_lpn_established()) {
+		return &bt_mesh.lpn.cred[SUBNET_KEY_TX_IDX(tx->sub)];
 	}
+#endif
 
-	if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED, nid, enc, priv)) {
-		BT_WARN("Falling back to master credentials");
-
-		tx->friend_cred = 0U;
-
-		*nid = tx->sub->keys[tx->sub->kr_flag].nid;
-		*enc = tx->sub->keys[tx->sub->kr_flag].enc;
-		*priv = tx->sub->keys[tx->sub->kr_flag].privacy;
-	}
+	tx->friend_cred = 0U;
+	return &tx->sub->keys[SUBNET_KEY_TX_IDX(tx->sub)].msg;
 }
 
 static int net_header_encode(struct bt_mesh_net_tx *tx, uint8_t nid,
@@ -808,26 +390,33 @@
 	return 0;
 }
 
+static int net_encrypt(struct os_mbuf *buf,
+		       const struct bt_mesh_net_cred *cred, uint32_t iv_index,
+		       bool proxy)
+{
+	int err;
+
+	err = bt_mesh_net_encrypt(cred->enc, buf, iv_index, proxy);
+	if (err) {
+		return err;
+	}
+
+	return bt_mesh_net_obfuscate(buf->om_data, iv_index, cred->privacy);
+}
+
 int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
 		       bool proxy)
 {
-	const uint8_t *enc, *priv;
-	uint8_t nid;
+	const struct bt_mesh_net_cred *cred;
 	int err;
 
-	net_tx_cred_get(tx, &nid, &enc, &priv);
-
-	err = net_header_encode(tx, nid, buf);
+	cred = net_tx_cred_get(tx);
+	err = net_header_encode(tx, cred->nid, buf);
 	if (err) {
 		return err;
 	}
 
-	err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, proxy);
-	if (err) {
-		return err;
-	}
-
-	return bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv);
+	return net_encrypt(buf, cred, BT_MESH_NET_IVI_TX, proxy);
 }
 
 static int loopback(const struct bt_mesh_net_tx *tx, const uint8_t *data,
@@ -857,8 +446,7 @@
 int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
 		     const struct bt_mesh_send_cb *cb, void *cb_data)
 {
-	const uint8_t *enc, *priv;
-	uint8_t nid;
+	const struct bt_mesh_net_cred *cred;
 	int err;
 
 	BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu",
@@ -871,8 +459,8 @@
 		tx->ctx->send_ttl = bt_mesh_default_ttl_get();
 	}
 
-	net_tx_cred_get(tx, &nid, &enc, &priv);
-	err = net_header_encode(tx, nid, buf);
+	cred = net_tx_cred_get(tx);
+	err = net_header_encode(tx, cred->nid, buf);
 	if (err) {
 		goto done;
 	}
@@ -904,12 +492,7 @@
 		goto done;
 	}
 
-	err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, false);
-	if (err) {
-		goto done;
-	}
-
-	err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv);
+	err = net_encrypt(buf, cred, BT_MESH_NET_IVI_TX, false);
 	if (err) {
 		goto done;
 	}
@@ -956,187 +539,47 @@
 	bt_mesh.local_queue = new_list;
 }
 
-static bool auth_match(struct bt_mesh_subnet_keys *keys,
-		       const uint8_t net_id[8], uint8_t flags,
-		       uint32_t iv_index, const uint8_t auth[8])
+static bool net_decrypt(struct bt_mesh_net_rx *rx, struct os_mbuf *in,
+			struct os_mbuf *out,
+			const struct bt_mesh_net_cred *cred)
 {
-	uint8_t net_auth[8];
+	bool proxy = (rx->net_if == BT_MESH_NET_IF_PROXY_CFG);
 
-	if (memcmp(net_id, keys->net_id, 8)) {
+	if (NID(in->om_data) != cred->nid) {
 		return false;
 	}
 
-	bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, iv_index,
-			    net_auth);
+	BT_DBG("NID 0x%02x", NID(in->om_data));
+	BT_DBG("IVI %u net->iv_index 0x%08x", IVI(in->om_data), bt_mesh.iv_index);
 
-	if (memcmp(auth, net_auth, 8)) {
-		BT_WARN("Authentication Value %s != %s",
-			bt_hex(auth, 8), bt_hex(net_auth, 8));
+	rx->old_iv = (IVI(in->om_data) != (bt_mesh.iv_index & 0x01));
+	os_mbuf_reset(out);
+	net_buf_simple_add_mem(out, in->om_data, in->om_len);
+
+	if (bt_mesh_net_obfuscate(out->om_data, BT_MESH_NET_IVI_RX(rx),
+				  cred->privacy)) {
 		return false;
 	}
 
-	return true;
-}
-
-struct bt_mesh_subnet *bt_mesh_subnet_find(const uint8_t net_id[8], uint8_t flags,
-					   uint32_t iv_index, const uint8_t auth[8],
-					   bool *new_key)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) {
-			*new_key = false;
-			return sub;
-		}
-
-		if (sub->kr_phase == BT_MESH_KR_NORMAL) {
-			continue;
-		}
-
-		if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) {
-			*new_key = true;
-			return sub;
-		}
-	}
-
-	return NULL;
-}
-
-static int net_decrypt(struct bt_mesh_subnet *sub, const uint8_t *enc,
-		       const uint8_t *priv, const uint8_t *data,
-		       size_t data_len, struct bt_mesh_net_rx *rx,
-		       struct os_mbuf *buf)
-{
-	BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx);
-	BT_DBG("IVI %u net->iv_index 0x%08x", IVI(data),
-	       (unsigned) bt_mesh.iv_index);
-
-	rx->old_iv = (IVI(data) != (bt_mesh.iv_index & 0x01));
-
-	net_buf_simple_init(buf, 0);
-	memcpy(net_buf_simple_add(buf, data_len), data, data_len);
-
-	if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) {
-		return -ENOENT;
-	}
-
-	rx->ctx.addr = SRC(buf->om_data);
+	rx->ctx.addr = SRC(out->om_data);
 	if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) {
 		BT_DBG("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr);
-		return -EINVAL;
+		return false;
 	}
 
 	if (bt_mesh_elem_find(rx->ctx.addr)) {
 		BT_DBG("Dropping locally originated packet");
-		return -EBADMSG;
+		return false;
 	}
 
-	if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(buf)) {
+	if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(out)) {
 		BT_DBG("Duplicate found in Network Message Cache");
-		return -EALREADY;
+		return false;
 	}
 
 	BT_DBG("src 0x%04x", rx->ctx.addr);
-
-	if ((MYNEWT_VAL(BLE_MESH_PROXY)) &&
-	    rx->net_if == BT_MESH_NET_IF_PROXY_CFG) {
-		return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx),
-					   true);
-	}
-
-	return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false);
-}
-
-static int friend_decrypt(struct bt_mesh_subnet *sub, const uint8_t *data,
-			  size_t data_len, struct bt_mesh_net_rx *rx,
-			  struct os_mbuf *buf)
-{
-	int i;
-
-	BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx);
-
-	for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
-		struct friend_cred *cred = &friend_cred[i];
-
-		if (cred->net_idx != sub->net_idx) {
-			continue;
-		}
-
-		if (NID(data) == cred->cred[0].nid &&
-		    !net_decrypt(sub, cred->cred[0].enc, cred->cred[0].privacy,
-				 data, data_len, rx, buf)) {
-			return 0;
-		}
-
-		if (sub->kr_phase == BT_MESH_KR_NORMAL) {
-			continue;
-		}
-
-		if (NID(data) == cred->cred[1].nid &&
-		    !net_decrypt(sub, cred->cred[1].enc, cred->cred[1].privacy,
-				 data, data_len, rx, buf)) {
-			rx->new_key = 1;
-			return 0;
-		}
-	}
-
-	return -ENOENT;
-}
-
-static bool net_find_and_decrypt(const uint8_t *data, size_t data_len,
-				 struct bt_mesh_net_rx *rx,
-				 struct os_mbuf *buf)
-{
-	struct bt_mesh_subnet *sub;
-	unsigned int i;
-
-	BT_DBG("");
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		sub = &bt_mesh.sub[i];
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		if ((IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) ||
-		     IS_ENABLED(CONFIG_BT_MESH_FRIEND)) &&
-		    !friend_decrypt(sub, data, data_len, rx, buf)) {
-			rx->friend_cred = 1U;
-			rx->ctx.net_idx = sub->net_idx;
-			rx->sub = sub;
-			return true;
-		}
-
-		if (NID(data) == sub->keys[0].nid &&
-		    !net_decrypt(sub, sub->keys[0].enc, sub->keys[0].privacy,
-				 data, data_len, rx, buf)) {
-			rx->ctx.net_idx = sub->net_idx;
-			rx->sub = sub;
-			return true;
-		}
-
-		if (sub->kr_phase == BT_MESH_KR_NORMAL) {
-			continue;
-		}
-
-		if (NID(data) == sub->keys[1].nid &&
-		    !net_decrypt(sub, sub->keys[1].enc, sub->keys[1].privacy,
-				 data, data_len, rx, buf)) {
-			rx->new_key = 1;
-			rx->ctx.net_idx = sub->net_idx;
-			rx->sub = sub;
-			return true;
-		}
-	}
-
-	return false;
+	return bt_mesh_net_decrypt(cred->enc, out, BT_MESH_NET_IVI_RX(rx),
+				   proxy) == 0;
 }
 
 /* Relaying from advertising to the advertising bearer should only happen
@@ -1159,9 +602,9 @@
 static void bt_mesh_net_relay(struct os_mbuf *sbuf,
 			      struct bt_mesh_net_rx *rx)
 {
-	const uint8_t *enc, *priv;
+	const struct bt_mesh_net_cred *cred;
 	struct os_mbuf *buf;
-	uint8_t nid, transmit;
+	uint8_t transmit;
 
 	if (rx->ctx.recv_ttl <= 1U) {
 		return;
@@ -1198,32 +641,25 @@
 
 	net_buf_add_mem(buf, sbuf->om_data, sbuf->om_len);
 
-	enc = rx->sub->keys[rx->sub->kr_flag].enc;
-	priv = rx->sub->keys[rx->sub->kr_flag].privacy;
-	nid = rx->sub->keys[rx->sub->kr_flag].nid;
+	cred = &rx->sub->keys[SUBNET_KEY_TX_IDX(rx->sub)].msg;
 
 	BT_DBG("Relaying packet. TTL is now %u", TTL(buf->om_data));
 
 	/* Update NID if RX or RX was with friend credentials */
 	if (rx->friend_cred) {
 		buf->om_data[0] &= 0x80; /* Clear everything except IVI */
-		buf->om_data[0] |= nid;
+		buf->om_data[0] |= cred->nid;
 	}
 
 	/* We re-encrypt and obfuscate using the received IVI rather than
 	 * the normal TX IVI (which may be different) since the transport
 	 * layer nonce includes the IVI.
 	 */
-	if (bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false)) {
+	if (net_encrypt(buf, cred, BT_MESH_NET_IVI_RX(rx), false)) {
 		BT_ERR("Re-encrypting failed");
 		goto done;
 	}
 
-	if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) {
-		BT_ERR("Re-obfuscating failed");
-		goto done;
-	}
-
 	BT_DBG("encoded %u bytes: %s", buf->om_len,
 	       bt_hex(buf->om_data, buf->om_len));
 
@@ -1257,26 +693,26 @@
 	rx->ctx.recv_dst = DST(buf->om_data);
 }
 
-int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if,
-		       struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+int bt_mesh_net_decode(struct os_mbuf *in, enum bt_mesh_net_if net_if,
+		       struct bt_mesh_net_rx *rx, struct os_mbuf *out)
 {
-	if (data->om_len < BT_MESH_NET_MIN_PDU_LEN) {
-		BT_WARN("Dropping too short mesh packet (len %u)", data->om_len);
-		BT_WARN("%s", bt_hex(data->om_data, data->om_len));
+	if (in->om_len < BT_MESH_NET_MIN_PDU_LEN) {
+		BT_WARN("Dropping too short mesh packet (len %u)", in->om_len);
+		BT_WARN("%s", bt_hex(in->om_data, in->om_len));
 		return -EINVAL;
 	}
 
-	if (net_if == BT_MESH_NET_IF_ADV && check_dup(data)) {
-		BT_DBG("duplicate packet; dropping %u bytes: %s", data->om_len,
-			   bt_hex(data->om_data, data->om_len));
+	if (net_if == BT_MESH_NET_IF_ADV && check_dup(in)) {
+		BT_DBG("duplicate packet; dropping %u bytes: %s", in->om_len,
+			   bt_hex(in->om_data, in->om_len));
 		return -EINVAL;
 	}
 
-	BT_DBG("%u bytes: %s", data->om_len, bt_hex(data->om_data, data->om_len));
+	BT_DBG("%u bytes: %s", in->om_len, bt_hex(in->om_data, in->om_len));
 
 	rx->net_if = net_if;
 
-	if (!net_find_and_decrypt(data->om_data, data->om_len, rx, buf)) {
+	if (!bt_mesh_net_cred_find(rx, in, out, net_decrypt)) {
 		BT_DBG("Unable to find matching net for packet");
 		return -ENOENT;
 	}
@@ -1284,7 +720,7 @@
 	/* Initialize AppIdx to a sane value */
 	rx->ctx.app_idx = BT_MESH_KEY_UNUSED;
 
-	rx->ctx.recv_ttl = TTL(buf->om_data);
+	rx->ctx.recv_ttl = TTL(out->om_data);
 
 	/* Default to responding with TTL 0 for non-routed messages */
 	if (rx->ctx.recv_ttl == 0) {
@@ -1293,12 +729,12 @@
 		rx->ctx.send_ttl = BT_MESH_TTL_DEFAULT;
 	}
 
-	rx->ctl = CTL(buf->om_data);
-	rx->seq = SEQ(buf->om_data);
-	rx->ctx.recv_dst = DST(buf->om_data);
+	rx->ctl = CTL(out->om_data);
+	rx->seq = SEQ(out->om_data);
+	rx->ctx.recv_dst = DST(out->om_data);
 
-	BT_DBG("Decryption successful. Payload len %u: %s", buf->om_len,
-		   bt_hex(buf->om_data, buf->om_len));
+	BT_DBG("Decryption successful. Payload len %u: %s", out->om_len,
+		   bt_hex(out->om_data, out->om_len));
 
 	if (net_if != BT_MESH_NET_IF_PROXY_CFG &&
 	    rx->ctx.recv_dst == BT_MESH_ADDR_UNASSIGNED) {
@@ -1308,7 +744,7 @@
 
 	BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst,
 	       rx->ctx.recv_ttl);
-	BT_DBG("PDU: %s", bt_hex(buf->om_data, buf->om_len));
+	BT_DBG("PDU: %s", bt_hex(out->om_data, out->om_len));
 
 	msg_cache_add(rx);
 
@@ -1334,7 +770,7 @@
 	}
 
 	/* Save the state so the buffer can later be relayed */
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 
 	rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) ||
 			  bt_mesh_elem_find(rx.ctx.recv_dst));
@@ -1369,7 +805,7 @@
 	 */
 	if (!BT_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) ||
 	    (!rx.local_match && !rx.friend_match)) {
-		net_buf_simple_restore(buf, &state);
+		os_mbuf_restore(buf, &state);
 		bt_mesh_net_relay(buf, &rx);
 	}
 
@@ -1428,10 +864,10 @@
 	}
 
 	if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
-		uint16_t net_idx = bt_mesh.sub[0].net_idx;
+		struct bt_mesh_subnet *sub = bt_mesh_subnet_next(NULL);
 		uint16_t addr = bt_mesh_primary_addr();
 
-		bt_mesh_prov_complete(net_idx, addr);
+		bt_mesh_prov_complete(sub->net_idx, addr);
 	}
 }
 
diff --git a/nimble/host/mesh/src/net.h b/nimble/host/mesh/src/net.h
index 1f73e59..b3f2457 100644
--- a/nimble/host/mesh/src/net.h
+++ b/nimble/host/mesh/src/net.h
@@ -9,13 +9,7 @@
 #ifndef __NET_H__
 #define __NET_H__
 
-#define BT_MESH_NET_FLAG_KR       BIT(0)
-#define BT_MESH_NET_FLAG_IVU      BIT(1)
-
-#define BT_MESH_KR_NORMAL         0x00
-#define BT_MESH_KR_PHASE_1        0x01
-#define BT_MESH_KR_PHASE_2        0x02
-#define BT_MESH_KR_PHASE_3        0x03
+#include "subnet.h"
 
 #define BT_MESH_IV_UPDATE(flags)   ((flags >> 1) & 0x01)
 #define BT_MESH_KEY_REFRESH(flags) (flags & 0x01)
@@ -31,15 +25,7 @@
 				    CONFIG_BT_MESH_IVU_DIVIDER)
 #define BT_MESH_IVU_TIMEOUT        K_HOURS(BT_MESH_IVU_HOURS)
 
-struct bt_mesh_app_key {
-	uint16_t net_idx;
-	uint16_t app_idx;
-	bool  updated;
-	struct bt_mesh_app_keys {
-		uint8_t id;
-		uint8_t val[16];
-	} keys[2];
-};
+struct bt_mesh_net_cred;
 
 struct bt_mesh_node {
 	uint16_t addr;
@@ -48,40 +34,6 @@
 	uint8_t  num_elem;
 };
 
-struct bt_mesh_subnet {
-	uint32_t beacon_sent;        /* Timestamp of last sent beacon */
-	uint8_t  beacons_last;       /* Number of beacons during last
-				   * observation window
-				   */
-	uint8_t  beacons_cur;        /* Number of beaconds observed during
-				   * currently ongoing window.
-				   */
-
-	uint8_t  beacon_cache[21];   /* Cached last authenticated beacon */
-
-	uint16_t net_idx;            /* NetKeyIndex */
-
-	bool  kr_flag;            /* Key Refresh Flag */
-	uint8_t  kr_phase;           /* Key Refresh Phase */
-
-	uint8_t  node_id;            /* Node Identity State */
-	uint32_t node_id_start;      /* Node Identity started timestamp */
-
-	uint8_t  auth[8];            /* Beacon Authentication Value */
-
-	struct bt_mesh_subnet_keys {
-		uint8_t net[16];       /* NetKey */
-		uint8_t nid;           /* NID */
-		uint8_t enc[16];       /* EncKey */
-		uint8_t net_id[8];     /* Network ID */
-#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
-		uint8_t identity[16];  /* IdentityKey */
-#endif
-		uint8_t privacy[16];   /* PrivacyKey */
-		uint8_t beacon[16];    /* BeaconKey */
-	} keys[2];
-};
-
 #if MYNEWT_VAL(BLE_MESH_FRIEND)
 #define FRIEND_SEG_RX MYNEWT_VAL(BLE_MESH_FRIEND_SEG_RX)
 #define FRIEND_SUB_LIST_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_SUB_LIST_SIZE)
@@ -97,14 +49,15 @@
 	      send_last:1,
 	      pending_req:1,
 	      pending_buf:1,
-	      valid:1,
 	      established:1;
 	int32_t poll_to;
 	uint8_t  num_elem;
 	uint16_t lpn_counter;
 	uint16_t counter;
 
-	uint16_t net_idx;
+	struct bt_mesh_subnet *subnet;
+
+	struct bt_mesh_net_cred cred[2];
 
 	uint16_t sub_list[FRIEND_SUB_LIST_SIZE];
 
@@ -181,8 +134,11 @@
 	/* Friend Queue Size */
 	uint8_t  queue_size;
 
+	/* FriendCounter */
+	uint16_t frnd_counter;
+
 	/* LPNCounter */
-	uint16_t counter;
+	uint16_t lpn_counter;
 
 	/* Previous Friend of this LPN */
 	uint16_t old_friend;
@@ -196,6 +152,10 @@
 	/* Subscribed groups */
 	uint16_t groups[LPN_GROUPS];
 
+	struct bt_mesh_subnet *sub;
+
+	struct bt_mesh_net_cred cred[2];
+
 	/* Bit fields for tracking which groups the Friend knows about */
 	ATOMIC_DEFINE(added, LPN_GROUPS);
 	ATOMIC_DEFINE(pending, LPN_GROUPS);
@@ -252,10 +212,6 @@
 	struct k_delayed_work ivu_timer;
 
 	uint8_t dev_key[16];
-
-	struct bt_mesh_app_key app_keys[MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT)];
-
-	struct bt_mesh_subnet sub[MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)];
 };
 
 /* Network interface */
@@ -306,39 +262,20 @@
 	return (void *)buf->om_data;
 }
 
-int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys,
-			    const uint8_t key[16]);
-
 int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
 		       uint32_t iv_index);
 
-uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);
-
-bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, uint8_t new_kr, bool new_key);
-
-void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub);
-
-int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub);
-
 bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update);
 
-void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub);
-
-struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx);
-
-struct bt_mesh_subnet *bt_mesh_subnet_find(const uint8_t net_id[8], uint8_t flags,
-					   uint32_t iv_index, const uint8_t auth[8],
-					   bool *new_key);
-
 int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
 		       bool proxy);
 
+int bt_mesh_net_decode(struct os_mbuf *in, enum bt_mesh_net_if net_if,
+		       struct bt_mesh_net_rx *rx, struct os_mbuf *out);
+
 int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
 		     const struct bt_mesh_send_cb *cb, void *cb_data);
 
-int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if,
-		       struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
-
 void bt_mesh_net_recv(struct os_mbuf *data, int8_t rssi,
 		      enum bt_mesh_net_if net_if);
 
@@ -352,30 +289,6 @@
 void bt_mesh_net_header_parse(struct os_mbuf *buf,
 			      struct bt_mesh_net_rx *rx);
 
-/* Friendship Credential Management */
-struct friend_cred {
-	uint16_t net_idx;
-	uint16_t addr;
-
-	uint16_t lpn_counter;
-	uint16_t frnd_counter;
-
-	struct {
-		uint8_t nid;         /* NID */
-		uint8_t enc[16];     /* EncKey */
-		uint8_t privacy[16]; /* PrivacyKey */
-	} cred[2];
-};
-
-int friend_cred_get(struct bt_mesh_subnet *sub, uint16_t addr, uint8_t *nid,
-			    const uint8_t **enc, const uint8_t **priv);
-int friend_cred_set(struct friend_cred *cred, uint8_t idx, const uint8_t net_key[16]);
-void friend_cred_refresh(uint16_t net_idx);
-int friend_cred_update(struct bt_mesh_subnet *sub);
-struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, uint16_t addr,
-				       uint16_t lpn_counter, uint16_t frnd_counter);
-void friend_cred_clear(struct friend_cred *cred);
-int friend_cred_del(uint16_t net_idx, uint16_t addr);
 
 static inline void send_cb_finalize(const struct bt_mesh_send_cb *cb,
 				    void *cb_data)
diff --git a/nimble/host/mesh/src/pb_adv.c b/nimble/host/mesh/src/pb_adv.c
index 0514293..26c6565 100644
--- a/nimble/host/mesh/src/pb_adv.c
+++ b/nimble/host/mesh/src/pb_adv.c
@@ -205,7 +205,7 @@
 	}
 	link.tx.pending_ack = XACT_ID_NVAL;
 	link.rx.buf = &rx_buf;
-	net_buf_simple_reset(link.rx.buf);
+	os_mbuf_reset(link.rx.buf);
 }
 
 static void close_link(enum prov_bearer_link_status reason)
@@ -340,7 +340,7 @@
 
 		link.rx.id = rx->xact_id;
 
-		net_buf_simple_reset(link.rx.buf);
+		os_mbuf_reset(link.rx.buf);
 		link.rx.seg = SEG_NVAL;
 		link.rx.last_seg = SEG_NVAL;
 
@@ -427,7 +427,7 @@
 		return;
 	}
 
-	net_buf_simple_reset(link.rx.buf);
+	os_mbuf_reset(link.rx.buf);
 	link.rx.buf->om_len = net_buf_simple_pull_be16(buf);
 	link.rx.id = rx->xact_id;
 	link.rx.fcs = net_buf_simple_pull_u8(buf);
@@ -673,7 +673,7 @@
 	seg_len = MIN(msg->om_len, START_PAYLOAD_MAX);
 	BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->om_data, seg_len));
 	net_buf_add_mem(start, msg->om_data, seg_len);
-	net_buf_simple_pull(msg, seg_len);
+	net_buf_simple_pull_mem(msg, seg_len);
 
 	buf = start;
 	for (seg_id = 1U; msg->om_len > 0; seg_id++) {
@@ -700,7 +700,7 @@
 		net_buf_add_u8(buf, link.tx.id);
 		net_buf_add_u8(buf, GPC_CONT(seg_id));
 		net_buf_add_mem(buf, msg->om_data, seg_len);
-		net_buf_simple_pull(msg, seg_len);
+		net_buf_simple_pull_mem(msg, seg_len);
 	}
 
 	send_reliable();
@@ -740,7 +740,7 @@
 
 	link.id = rx->link_id;
 	atomic_set_bit(link.flags, LINK_ACTIVE);
-	net_buf_simple_reset(link.rx.buf);
+	os_mbuf_reset(link.rx.buf);
 
 	bearer_ctl_send(LINK_ACK, NULL, 0, false);
 
@@ -820,7 +820,7 @@
 	link.cb = cb;
 	link.cb_data = cb_data;
 
-	net_buf_simple_reset(link.rx.buf);
+	os_mbuf_reset(link.rx.buf);
 
 	bearer_ctl_send(LINK_OPEN, uuid, 16, true);
 
diff --git a/nimble/host/mesh/src/proxy.c b/nimble/host/mesh/src/proxy.c
index 64c9d5b..f99aa91 100644
--- a/nimble/host/mesh/src/proxy.c
+++ b/nimble/host/mesh/src/proxy.c
@@ -202,7 +202,7 @@
 
 #if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
 /* Next subnet in queue to be advertised */
-static int next_idx;
+static struct bt_mesh_subnet *beacon_sub;
 
 static int proxy_segment_and_send(uint16_t conn_handle, uint8_t type,
 				  struct os_mbuf *msg);
@@ -341,7 +341,7 @@
 	}
 
 	/* Remove network headers */
-	net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
+	net_buf_simple_pull_mem(buf, BT_MESH_NET_HDR_LEN);
 
 	BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
 
@@ -396,21 +396,20 @@
 	return rc;
 }
 
+static int send_beacon_cb(struct bt_mesh_subnet *sub, void *cb_data)
+{
+	struct bt_mesh_proxy_client *client = cb_data;
+
+	return beacon_send(client->conn_handle, sub);
+}
+
 static void proxy_send_beacons(struct ble_npl_event *work)
 {
 	struct bt_mesh_proxy_client *client;
-	int i;
-
 
 	client = ble_npl_event_get_arg(work);
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx != BT_MESH_KEY_UNUSED) {
-			beacon_send(client->conn_handle, sub);
-		}
-	}
+	(void)bt_mesh_subnet_find(send_beacon_cb, client);
 }
 
 static void proxy_sar_timeout(struct ble_npl_event *work)
@@ -436,12 +435,7 @@
 
 	if (!sub) {
 		/* NULL means we send on all subnets */
-		for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-			if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
-				bt_mesh_proxy_beacon_send(&bt_mesh.sub[i]);
-			}
-		}
-
+		bt_mesh_subnet_foreach(bt_mesh_proxy_beacon_send);
 		return;
 	}
 
@@ -452,13 +446,17 @@
 	}
 }
 
-void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
+static void node_id_start(struct bt_mesh_subnet *sub)
 {
 	sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
 	sub->node_id_start = k_uptime_get_32();
+}
 
+void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
+{
+	node_id_start(sub);
 	/* Prioritize the recently enabled subnet */
-	next_idx = sub - bt_mesh.sub;
+	beacon_sub = sub;
 }
 
 void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub)
@@ -469,30 +467,13 @@
 
 int bt_mesh_proxy_identity_enable(void)
 {
-	int i, count = 0;
-
 	BT_DBG("");
 
 	if (!bt_mesh_is_provisioned()) {
 		return -EAGAIN;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		if (sub->node_id == BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) {
-			continue;
-		}
-
-		bt_mesh_proxy_identity_start(sub);
-		count++;
-	}
-
-	if (count) {
+	if (bt_mesh_subnet_foreach(node_id_start)) {
 		bt_mesh_adv_update();
 	}
 
@@ -1045,7 +1026,7 @@
 
 	net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
 	proxy_send(conn_handle, msg->om_data, mtu);
-	net_buf_simple_pull(msg, mtu);
+	net_buf_simple_pull_mem(msg, mtu);
 
 	while (msg->om_len) {
 		if (msg->om_len + 1 < mtu) {
@@ -1056,7 +1037,7 @@
 
 		net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
 		proxy_send(conn_handle, msg->om_data, mtu);
-		net_buf_simple_pull(msg, mtu);
+		net_buf_simple_pull_mem(msg, mtu);
 	}
 
 	return 0;
@@ -1139,7 +1120,8 @@
 	memcpy(tmp + 6, proxy_svc_data + 11, 8);
 	sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
 
-	err = bt_encrypt_be(sub->keys[sub->kr_flag].identity, tmp, tmp);
+	err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp,
+			    tmp);
 	if (err) {
 		return err;
 	}
@@ -1167,9 +1149,9 @@
 	proxy_svc_data[2] = ID_TYPE_NET;
 
 	BT_DBG("Advertising with NetId %s",
-	       bt_hex(sub->keys[sub->kr_flag].net_id, 8));
+	       bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8));
 
-	memcpy(proxy_svc_data + 3, sub->keys[sub->kr_flag].net_id, 8);
+	memcpy(proxy_svc_data + 3, sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8);
 
 	err = bt_le_adv_start(&slow_adv_param, net_id_ad,
 			      ARRAY_SIZE(net_id_ad), NULL, 0);
@@ -1195,33 +1177,47 @@
 
 static struct bt_mesh_subnet *next_sub(void)
 {
-	int i;
+	struct bt_mesh_subnet *sub = NULL;
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub;
-
-		sub = &bt_mesh.sub[(i + next_idx) % ARRAY_SIZE(bt_mesh.sub)];
-		if (advertise_subnet(sub)) {
-			next_idx = (next_idx + 1) % ARRAY_SIZE(bt_mesh.sub);
-			return sub;
+	if (!beacon_sub) {
+		beacon_sub = bt_mesh_subnet_next(NULL);
+		if (!beacon_sub) {
+			/* No valid subnets */
+			return NULL;
 		}
 	}
 
+	sub = beacon_sub;
+	do {
+		if (advertise_subnet(sub)) {
+			beacon_sub = sub;
+			return sub;
+		}
+
+	sub = bt_mesh_subnet_next(sub);
+	} while (sub != beacon_sub);
+
+	/* No subnets to advertise on */
+
 	return NULL;
 }
 
-static int sub_count(void)
+static int sub_count_cb(struct bt_mesh_subnet *sub, void *cb_data)
 {
-	int i, count = 0;
+	int *count = cb_data;
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-
-		if (advertise_subnet(sub)) {
-			count++;
-		}
+	if (advertise_subnet(sub)) {
+		(*count)++;
 	}
 
+	return 0;
+}
+
+static int sub_count(void)
+{
+	int count = 0;
+
+	(void)bt_mesh_subnet_find(sub_count_cb, &count);
 	return count;
 }
 
@@ -1237,6 +1233,7 @@
 		return remaining;
 	}
 
+	sub = beacon_sub ? beacon_sub : bt_mesh_subnet_next(beacon_sub);
 	if (!sub) {
 		BT_WARN("No subnets to advertise on");
 		return remaining;
@@ -1281,6 +1278,8 @@
 	BT_DBG("Advertising %d ms for net_idx 0x%04x",
 	       (int) remaining, sub->net_idx);
 
+	beacon_sub = bt_mesh_subnet_next(beacon_sub);
+
 	return remaining;
 }
 #endif /* GATT_PROXY */
@@ -1393,6 +1392,19 @@
 	}
 }
 
+#if defined(CONFIG_BT_MESH_GATT_PROXY)
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	if (evt == BT_MESH_KEY_DELETED) {
+		if (sub == beacon_sub) {
+			beacon_sub = NULL;
+		}
+	} else {
+		bt_mesh_proxy_beacon_send(sub);
+	}
+}
+#endif
+
 static void ble_mesh_handle_connect(struct ble_gap_event *event, void *arg)
 {
 #if MYNEWT_VAL(BLE_EXT_ADV)
@@ -1509,6 +1521,10 @@
 {
 	int i;
 
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+	bt_mesh_subnet_cb_list[4] = subnet_evt;
+#endif
+
 	for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) {
 #if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
 		k_work_init(&clients[i].send_beacons, proxy_send_beacons);
diff --git a/nimble/host/mesh/src/settings.c b/nimble/host/mesh/src/settings.c
index febe5e6..6f26f27 100644
--- a/nimble/host/mesh/src/settings.c
+++ b/nimble/host/mesh/src/settings.c
@@ -12,6 +12,8 @@
 #include "mesh_priv.h"
 #include "mesh/mesh.h"
 #include "mesh/glue.h"
+#include "subnet.h"
+#include "app_keys.h"
 #include "net.h"
 #include "rpl.h"
 #include "crypto.h"
@@ -356,26 +358,13 @@
 
 static int net_key_set(int argc, char **argv, char *val)
 {
-	struct bt_mesh_subnet *sub;
 	struct net_key_val key;
-	int len, i, err;
+	int len, err;
 	uint16_t net_idx;
 
 	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
 
 	net_idx = strtol(argv[0], NULL, 16);
-	sub = bt_mesh_subnet_get(net_idx);
-
-	if (!val) {
-		if (!sub) {
-			BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx);
-			return -ENOENT;
-		}
-
-		BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx);
-		bt_mesh_subnet_del(sub, false);
-		return 0;
-	}
 
 	len = sizeof(key);
 	err = settings_bytes_from_str(val, &key, &len);
@@ -389,93 +378,41 @@
 		return -EINVAL;
 	}
 
-	if (sub) {
-		BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx);
-
-		sub->kr_flag = key.kr_flag;
-		sub->kr_phase = key.kr_phase;
-		memcpy(sub->keys[0].net, &key.val[0], 16);
-		memcpy(sub->keys[1].net, &key.val[1], 16);
-
-		return 0;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
-			sub = &bt_mesh.sub[i];
-			break;
-		}
-	}
-
-	if (!sub) {
-		BT_ERR("No space to allocate a new subnet");
-		return -ENOMEM;
-	}
-
-	sub->net_idx = net_idx;
-	sub->kr_flag = key.kr_flag;
-	sub->kr_phase = key.kr_phase;
-	memcpy(sub->keys[0].net, &key.val[0], 16);
-	memcpy(sub->keys[1].net, &key.val[1], 16);
-
 	BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
 
-	return 0;
+	return bt_mesh_subnet_set(
+		net_idx, key.kr_phase, key.val[0],
+		(key.kr_phase != BT_MESH_KR_NORMAL) ? key.val[1] : NULL);
 }
 
 static int app_key_set(int argc, char **argv, char *val)
 {
-	struct bt_mesh_app_key *app;
 	struct app_key_val key;
 	uint16_t app_idx;
-	int len, err;
+	int len_rd, err;
 
 	BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
 
 	app_idx = strtol(argv[0], NULL, 16);
+	len_rd = strtol(argv[1], NULL, 16);
 
-	if (!val) {
-		BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx);
-
-		app = bt_mesh_app_key_find(app_idx);
-		if (app) {
-			bt_mesh_app_key_del(app, false);
-		}
-
+	if (!len_rd) {
 		return 0;
 	}
 
-	len = sizeof(key);
-	err = settings_bytes_from_str(val, &key, &len);
+	err = settings_bytes_from_str(val, &key, &len_rd);
 	if (err) {
 		BT_ERR("Failed to decode value %s (err %d)", val, err);
 		return err;
 	}
 
-	if (len != sizeof(key)) {
-		BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key));
-		return -EINVAL;
+	err = bt_mesh_app_key_set(app_idx, key.net_idx, key.val[0],
+			      key.updated ? key.val[1] : NULL);
+	if (err) {
+		BT_ERR("Failed to set \'app-key\'");
+		return err;
 	}
 
-	app = bt_mesh_app_key_find(app_idx);
-	if (!app) {
-		app = bt_mesh_app_key_alloc(app_idx);
-	}
-
-	if (!app) {
-		BT_ERR("No space for a new app key");
-		return -ENOMEM;
-	}
-
-	app->net_idx = key.net_idx;
-	app->app_idx = app_idx;
-	app->updated = key.updated;
-	memcpy(app->keys[0].val, key.val[0], 16);
-	memcpy(app->keys[1].val, key.val[1], 16);
-
-	bt_mesh_app_id(app->keys[0].val, &app->keys[0].id);
-	bt_mesh_app_id(app->keys[1].val, &app->keys[1].id);
-
 	BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
 
 	return 0;
@@ -1069,37 +1006,6 @@
 	return -ENOENT;
 }
 
-static int subnet_init(struct bt_mesh_subnet *sub)
-{
-	int err;
-
-	err = bt_mesh_net_keys_create(&sub->keys[0], sub->keys[0].net);
-	if (err) {
-		BT_ERR("Unable to generate keys for subnet");
-		return -EIO;
-	}
-
-	if (sub->kr_phase != BT_MESH_KR_NORMAL) {
-		err = bt_mesh_net_keys_create(&sub->keys[1], sub->keys[1].net);
-		if (err) {
-			BT_ERR("Unable to generate keys for subnet");
-			memset(&sub->keys[0], 0, sizeof(sub->keys[0]));
-			return -EIO;
-		}
-	}
-
-	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
-		sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
-	} else {
-		sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
-	}
-
-	/* Make sure we have valid beacon data to be sent */
-	bt_mesh_net_beacon_update(sub);
-
-	return 0;
-}
-
 static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
 		       bool vnd, bool primary, void *user_data)
 {
@@ -1128,11 +1034,8 @@
 {
 	struct bt_mesh_hb_pub *hb_pub;
 	struct bt_mesh_cfg_srv *cfg;
-	int i;
 
-	BT_DBG("sub[0].net_idx 0x%03x", bt_mesh.sub[0].net_idx);
-
-	if (bt_mesh.sub[0].net_idx == BT_MESH_KEY_UNUSED) {
+	if (!bt_mesh_subnet_next(NULL)) {
 		/* Nothing to do since we're not yet provisioned */
 		return 0;
 	}
@@ -1141,20 +1044,6 @@
 		bt_mesh_proxy_prov_disable(true);
 	}
 
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
-		struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
-		int err;
-
-		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
-			continue;
-		}
-
-		err = subnet_init(sub);
-		if (err) {
-			BT_ERR("Failed to init subnet 0x%03x", sub->net_idx);
-		}
-	}
-
 	if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
 		k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
 	}
@@ -1532,31 +1421,36 @@
 	}
 }
 
-static void store_net_key(struct bt_mesh_subnet *sub)
+static void store_subnet(uint16_t net_idx)
 {
-	char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))];
+	const struct bt_mesh_subnet *sub;
 	struct net_key_val key;
+	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
 	char path[20];
 	char *str;
 	int err;
 
-	BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx,
-	       bt_hex(sub->keys[0].net, 16));
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		BT_WARN("NetKeyIndex 0x%03x not found", net_idx);
+		return;
+	}
+
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
+
+	snprintk(path, sizeof(path), "bt/mesh/NetKey/%x", net_idx);
 
 	memcpy(&key.val[0], sub->keys[0].net, 16);
 	memcpy(&key.val[1], sub->keys[1].net, 16);
-	key.kr_flag = sub->kr_flag;
+	key.kr_flag = 0U; /* Deprecated */
 	key.kr_phase = sub->kr_phase;
 
 	str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
 	if (!str) {
-		BT_ERR("Unable to encode NetKey as value");
+		BT_ERR("Unable to encode AppKey as value");
 		return;
 	}
 
-	snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", sub->net_idx);
-
-	BT_DBG("Saving NetKey %s as value %s", path, str);
 	err = settings_save_one(path, str);
 	if (err) {
 		BT_ERR("Failed to store NetKey");
@@ -1565,16 +1459,26 @@
 	}
 }
 
-static void store_app_key(struct bt_mesh_app_key *app)
+static void store_app(uint16_t app_idx)
 {
+	const struct bt_mesh_app_key *app;
 	char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
 	struct app_key_val key;
 	char path[20];
 	char *str;
 	int err;
 
-	key.net_idx = app->net_idx;
-	key.updated = app->updated;
+	snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
+
+	app = bt_mesh_app_key_get(app_idx);
+	if (!app) {
+		BT_WARN("ApKeyIndex 0x%03x not found", app_idx);
+		return;
+	}
+
+	key.net_idx = app->net_idx,
+	key.updated = app->updated,
+
 	memcpy(key.val[0], app->keys[0].val, 16);
 	memcpy(key.val[1], app->keys[1].val, 16);
 
@@ -1613,28 +1517,7 @@
 				clear_net_key(update->key_idx);
 			}
 		} else {
-			if (update->app_key) {
-				struct bt_mesh_app_key *key;
-
-				key = bt_mesh_app_key_find(update->key_idx);
-				if (key) {
-					store_app_key(key);
-				} else {
-					BT_WARN("AppKeyIndex 0x%03x not found",
-					       update->key_idx);
-				}
-
-			} else {
-				struct bt_mesh_subnet *sub;
-
-				sub = bt_mesh_subnet_get(update->key_idx);
-				if (sub) {
-					store_net_key(sub);
-				} else {
-					BT_WARN("NetKeyIndex 0x%03x not found",
-					       update->key_idx);
-				}
-			}
+			store_subnet(update->key_idx);
 		}
 
 		update->valid = 0;
@@ -2223,13 +2106,13 @@
 	return match;
 }
 
-void bt_mesh_store_subnet(struct bt_mesh_subnet *sub)
+void bt_mesh_store_subnet(uint16_t net_idx)
 {
 	struct key_update *update, *free_slot;
 
-	BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
 
-	update = key_update_find(false, sub->net_idx, &free_slot);
+	update = key_update_find(false, net_idx, &free_slot);
 	if (update) {
 		update->clear = 0;
 		schedule_store(BT_MESH_KEYS_PENDING);
@@ -2237,25 +2120,25 @@
 	}
 
 	if (!free_slot) {
-		store_net_key(sub);
+		store_subnet(net_idx);
 		return;
 	}
 
 	free_slot->valid = 1;
-	free_slot->key_idx = sub->net_idx;
+	free_slot->key_idx = net_idx;
 	free_slot->app_key = 0;
 	free_slot->clear = 0;
 
 	schedule_store(BT_MESH_KEYS_PENDING);
 }
 
-void bt_mesh_store_app_key(struct bt_mesh_app_key *key)
+void bt_mesh_store_app_key(uint16_t app_idx)
 {
 	struct key_update *update, *free_slot;
 
-	BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
+	BT_DBG("AppKeyIndex 0x%03x", app_idx);
 
-	update = key_update_find(true, key->app_idx, &free_slot);
+	update = key_update_find(true, app_idx, &free_slot);
 	if (update) {
 		update->clear = 0;
 		schedule_store(BT_MESH_KEYS_PENDING);
@@ -2263,12 +2146,12 @@
 	}
 
 	if (!free_slot) {
-		store_app_key(key);
+		store_app(app_idx);
 		return;
 	}
 
 	free_slot->valid = 1;
-	free_slot->key_idx = key->app_idx;
+	free_slot->key_idx = app_idx;
 	free_slot->app_key = 1;
 	free_slot->clear = 0;
 
@@ -2292,13 +2175,13 @@
 	schedule_store(BT_MESH_CFG_PENDING);
 }
 
-void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub)
+void bt_mesh_clear_subnet(uint16_t net_idx)
 {
 	struct key_update *update, *free_slot;
 
-	BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
+	BT_DBG("NetKeyIndex 0x%03x", net_idx);
 
-	update = key_update_find(false, sub->net_idx, &free_slot);
+	update = key_update_find(false, net_idx, &free_slot);
 	if (update) {
 		update->clear = 1;
 		schedule_store(BT_MESH_KEYS_PENDING);
@@ -2306,25 +2189,25 @@
 	}
 
 	if (!free_slot) {
-		clear_net_key(sub->net_idx);
+		clear_net_key(net_idx);
 		return;
 	}
 
 	free_slot->valid = 1;
-	free_slot->key_idx = sub->net_idx;
+	free_slot->key_idx = net_idx;
 	free_slot->app_key = 0;
 	free_slot->clear = 1;
 
 	schedule_store(BT_MESH_KEYS_PENDING);
 }
 
-void bt_mesh_clear_app_key(struct bt_mesh_app_key *key)
+void bt_mesh_clear_app_key(uint16_t app_idx)
 {
 	struct key_update *update, *free_slot;
 
-	BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
+	BT_DBG("AppKeyIndex 0x%03x", app_idx);
 
-	update = key_update_find(true, key->app_idx, &free_slot);
+	update = key_update_find(true, app_idx, &free_slot);
 	if (update) {
 		update->clear = 1;
 		schedule_store(BT_MESH_KEYS_PENDING);
@@ -2332,12 +2215,12 @@
 	}
 
 	if (!free_slot) {
-		clear_app_key(key->app_idx);
+		clear_app_key(app_idx);
 		return;
 	}
 
 	free_slot->valid = 1;
-	free_slot->key_idx = key->app_idx;
+	free_slot->key_idx = app_idx;
 	free_slot->app_key = 1;
 	free_slot->clear = 1;
 
diff --git a/nimble/host/mesh/src/settings.h b/nimble/host/mesh/src/settings.h
index 2718cb5..9060a14 100644
--- a/nimble/host/mesh/src/settings.h
+++ b/nimble/host/mesh/src/settings.h
@@ -8,8 +8,8 @@
 void bt_mesh_store_iv(bool only_duration);
 void bt_mesh_store_seq(void);
 void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
-void bt_mesh_store_subnet(struct bt_mesh_subnet *sub);
-void bt_mesh_store_app_key(struct bt_mesh_app_key *key);
+void bt_mesh_store_subnet(uint16_t net_idx);
+void bt_mesh_store_app_key(uint16_t app_idx);
 void bt_mesh_store_hb_pub(void);
 void bt_mesh_store_cfg(void);
 void bt_mesh_store_mod_bind(struct bt_mesh_model *mod);
@@ -22,8 +22,8 @@
 void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *app);
 
 void bt_mesh_clear_net(void);
-void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub);
-void bt_mesh_clear_app_key(struct bt_mesh_app_key *key);
+void bt_mesh_clear_subnet(uint16_t net_idx);
+void bt_mesh_clear_app_key(uint16_t app_idx);
 void bt_mesh_clear_rpl(void);
 void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node);
 void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub);
diff --git a/nimble/host/mesh/src/shell.c b/nimble/host/mesh/src/shell.c
index d2f4ccb..e845418 100644
--- a/nimble/host/mesh/src/shell.c
+++ b/nimble/host/mesh/src/shell.c
@@ -833,8 +833,6 @@
 	struct bt_mesh_net_tx tx = {
 		.ctx = &ctx,
 		.src = net.local,
-		.xmit = bt_mesh_net_transmit_get(),
-		.sub = bt_mesh_subnet_get(net.net_idx),
 	};
 	size_t len;
 	int err = 0;
@@ -844,12 +842,6 @@
 		goto done;
 	}
 
-	if (!tx.sub) {
-		printk("No matching subnet for NetKey Index 0x%04x\n",
-		       net.net_idx);
-		goto done;
-	}
-
 	net_buf_simple_init(msg, 0);
 	len = hex2bin(argv[1], msg->om_data, net_buf_simple_tailroom(msg) - 4);
 	net_buf_simple_add(msg, len);
diff --git a/nimble/host/mesh/src/subnet.c b/nimble/host/mesh/src/subnet.c
new file mode 100644
index 0000000..626cb3c
--- /dev/null
+++ b/nimble/host/mesh/src/subnet.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_NET_KEYS_LOG
+
+#include "log/log.h"
+
+#include "crypto.h"
+#include "adv.h"
+#include "mesh/mesh.h"
+#include "net.h"
+#include "mesh_priv.h"
+#include "lpn.h"
+#include "friend.h"
+#include "proxy.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+#include "beacon.h"
+#include "rpl.h"
+#include "settings.h"
+#include "prov.h"
+
+#ifdef CONFIG_BT_MESH_GATT_PROXY
+void (*bt_mesh_subnet_cb_list[5]) (struct bt_mesh_subnet *sub,
+									      enum bt_mesh_key_evt evt);
+#else
+void (*bt_mesh_subnet_cb_list[4]) (struct bt_mesh_subnet *sub,
+									      enum bt_mesh_key_evt evt);
+#endif
+
+static struct bt_mesh_subnet subnets[CONFIG_BT_MESH_SUBNET_COUNT] = {
+	[0 ... (CONFIG_BT_MESH_SUBNET_COUNT - 1)] = {
+		.net_idx = BT_MESH_KEY_UNUSED,
+	},
+};
+
+static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
+{
+	int i;
+	for (i = 0; i < (sizeof(bt_mesh_subnet_cb_list)/sizeof(void *)); i++) {
+		bt_mesh_subnet_cb_list[i] (sub, evt);
+	}
+}
+
+uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
+{
+	uint8_t flags = 0x00;
+
+	if (sub && (sub->kr_phase == BT_MESH_KR_PHASE_2)) {
+		flags |= BT_MESH_NET_FLAG_KR;
+	}
+
+	if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
+		flags |= BT_MESH_NET_FLAG_IVU;
+	}
+
+	return flags;
+}
+
+static void key_refresh(struct bt_mesh_subnet *sub, uint8_t new_phase)
+{
+	BT_DBG("Phase 0x%02x -> 0x%02x", sub->kr_phase, new_phase);
+
+	switch (new_phase) {
+	/* Added second set of keys */
+	case BT_MESH_KR_PHASE_1:
+		sub->kr_phase = new_phase;
+		subnet_evt(sub, BT_MESH_KEY_UPDATED);
+		break;
+	/* Now using new keys for TX */
+	case BT_MESH_KR_PHASE_2:
+		sub->kr_phase = new_phase;
+		subnet_evt(sub, BT_MESH_KEY_SWAPPED);
+		break;
+	/* Revoking keys */
+	case BT_MESH_KR_PHASE_3:
+	case BT_MESH_KR_NORMAL:
+		sub->kr_phase = BT_MESH_KR_NORMAL;
+		memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0]));
+		sub->keys[1].valid = 0U;
+		subnet_evt(sub, BT_MESH_KEY_REVOKED);
+		break;
+	}
+
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		BT_DBG("Storing Updated NetKey persistently");
+		bt_mesh_store_subnet(sub->net_idx);
+	}
+}
+
+void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key)
+{
+	if (!new_key) {
+		return;
+	}
+
+	if (sub->kr_phase == BT_MESH_KR_PHASE_1) {
+		/* Bluetooth Mesh Profile Specification Section 3.10.4.1:
+		 * Can skip phase 2 if we get KR=0 on new key.
+		 */
+		key_refresh(sub, (kr_flag ? BT_MESH_KR_PHASE_2 :
+					    BT_MESH_KR_PHASE_3));
+	} else if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !kr_flag) {
+		key_refresh(sub, BT_MESH_KR_PHASE_3);
+	}
+}
+
+static struct bt_mesh_subnet *subnet_alloc(uint16_t net_idx)
+{
+	struct bt_mesh_subnet *sub = NULL;
+
+	for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
+		/* Check for already existing subnet */
+		if (subnets[i].net_idx == net_idx) {
+			return &subnets[i];
+		}
+
+		if (!sub && subnets[i].net_idx == BT_MESH_KEY_UNUSED) {
+			sub = &subnets[i];
+		}
+	}
+
+	return sub;
+}
+
+static void subnet_del(struct bt_mesh_subnet *sub)
+{
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		bt_mesh_clear_subnet(sub->net_idx);
+	}
+
+	bt_mesh_net_loopback_clear(sub->net_idx);
+
+	subnet_evt(sub, BT_MESH_KEY_DELETED);
+	(void)memset(sub, 0, sizeof(*sub));
+	sub->net_idx = BT_MESH_KEY_UNUSED;
+}
+
+static int msg_cred_create(struct bt_mesh_net_cred *cred, const uint8_t *p,
+			   size_t p_len, const uint8_t key[16])
+{
+	return bt_mesh_k2(key, p, p_len, &cred->nid, cred->enc, cred->privacy);
+}
+
+static int net_keys_create(struct bt_mesh_subnet_keys *keys,
+			   const uint8_t key[16])
+{
+	uint8_t p = 0;
+	int err;
+
+	err = msg_cred_create(&keys->msg, &p, 1, key);
+	if (err) {
+		BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
+		return err;
+	}
+
+	memcpy(keys->net, key, 16);
+
+	BT_DBG("NID 0x%02x EncKey %s", keys->msg.nid,
+	       bt_hex(keys->msg.enc, 16));
+	BT_DBG("PrivacyKey %s", bt_hex(keys->msg.privacy, 16));
+
+	err = bt_mesh_k3(key, keys->net_id);
+	if (err) {
+		BT_ERR("Unable to generate Net ID");
+		return err;
+	}
+
+	BT_DBG("NetID %s", bt_hex(keys->net_id, 8));
+
+#if defined(CONFIG_BT_MESH_GATT_PROXY)
+	err = bt_mesh_identity_key(key, keys->identity);
+	if (err) {
+		BT_ERR("Unable to generate IdentityKey");
+		return err;
+	}
+
+	BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16));
+#endif /* GATT_PROXY */
+
+	err = bt_mesh_beacon_key(key, keys->beacon);
+	if (err) {
+		BT_ERR("Unable to generate beacon key");
+		return err;
+	}
+
+	BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16));
+
+	keys->valid = 1U;
+
+	return 0;
+}
+
+uint8_t bt_mesh_subnet_add(uint16_t net_idx, const uint8_t key[16])
+{
+	struct bt_mesh_subnet *sub = NULL;
+	int err;
+
+	BT_DBG("0x%03x", net_idx);
+
+	sub = subnet_alloc(net_idx);
+	if (!sub) {
+		return STATUS_INSUFF_RESOURCES;
+	}
+
+	if (sub->net_idx == net_idx) {
+		if (memcmp(key, sub->keys[0].net, 16)) {
+			return STATUS_IDX_ALREADY_STORED;
+		}
+
+		return STATUS_SUCCESS;
+	}
+
+	err = net_keys_create(&sub->keys[0], key);
+	if (err) {
+		return STATUS_UNSPECIFIED;
+	}
+
+	sub->net_idx = net_idx;
+	sub->kr_phase = BT_MESH_KR_NORMAL;
+
+	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+		sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+	} else {
+		sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
+	}
+
+	subnet_evt(sub, BT_MESH_KEY_ADDED);
+
+	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+		BT_DBG("Storing NetKey persistently");
+		bt_mesh_store_subnet(sub->net_idx);
+	}
+
+	return STATUS_SUCCESS;
+}
+
+bool bt_mesh_subnet_exists(uint16_t net_idx)
+{
+	return !!bt_mesh_subnet_get(net_idx);
+}
+
+uint8_t bt_mesh_subnet_update(uint16_t net_idx, const uint8_t key[16])
+{
+	struct bt_mesh_subnet *sub;
+	int err;
+
+	BT_DBG("0x%03x", net_idx);
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		return STATUS_INVALID_NETKEY;
+	}
+
+	/* The node shall successfully process a NetKey Update message on a
+	 * valid NetKeyIndex when the NetKey value is different and the Key
+	 * Refresh procedure has not been started, or when the NetKey value is
+	 * the same in Phase 1. The NetKey Update message shall generate an
+	 * error when the node is in Phase 2, or Phase 3.
+	 */
+	switch (sub->kr_phase) {
+	case BT_MESH_KR_NORMAL:
+		if (!memcmp(key, sub->keys[0].net, 16)) {
+			return STATUS_IDX_ALREADY_STORED;
+		}
+		break;
+	case BT_MESH_KR_PHASE_1:
+		if (!memcmp(key, sub->keys[1].net, 16)) {
+			return STATUS_SUCCESS;
+		}
+		/* __fallthrough; */
+	case BT_MESH_KR_PHASE_2:
+	case BT_MESH_KR_PHASE_3:
+		return STATUS_CANNOT_UPDATE;
+	}
+
+	err = net_keys_create(&sub->keys[1], key);
+	if (err) {
+		return STATUS_CANNOT_UPDATE;
+	}
+
+	key_refresh(sub, BT_MESH_KR_PHASE_1);
+
+	return STATUS_SUCCESS;
+}
+
+uint8_t bt_mesh_subnet_del(uint16_t net_idx)
+{
+	struct bt_mesh_subnet *sub;
+
+	BT_DBG("0x%03x", net_idx);
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		/* This could be a retry of a previous attempt that had its
+		 * response lost, so pretend that it was a success.
+		 */
+		return STATUS_INVALID_NETKEY;
+	}
+
+	subnet_del(sub);
+
+	return STATUS_SUCCESS;
+}
+
+int bt_mesh_friend_cred_create(struct bt_mesh_net_cred *cred, uint16_t lpn_addr,
+			       uint16_t frnd_addr, uint16_t lpn_counter,
+			       uint16_t frnd_counter, const uint8_t key[16])
+{
+	uint8_t p[9];
+
+	p[0] = 0x01;
+	sys_put_be16(lpn_addr, p + 1);
+	sys_put_be16(frnd_addr, p + 3);
+	sys_put_be16(lpn_counter, p + 5);
+	sys_put_be16(frnd_counter, p + 7);
+
+	return msg_cred_create(cred, p, sizeof(p), key);
+}
+
+uint8_t bt_mesh_subnet_kr_phase_set(uint16_t net_idx, uint8_t *phase)
+{
+	/* Table in Bluetooth Mesh Profile Specification Section 4.2.14: */
+	const uint8_t valid_transitions[] = {
+		0x00, /* Normal phase: KR is started by key update */
+		BIT(BT_MESH_KR_PHASE_2) | BIT(BT_MESH_KR_PHASE_3), /* Phase 1 */
+		BIT(BT_MESH_KR_PHASE_3), /* Phase 2 */
+		/* Subnet is never in Phase 3 */
+	};
+	struct bt_mesh_subnet *sub;
+
+	BT_DBG("0x%03x", net_idx);
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		*phase = 0x00;
+		return STATUS_INVALID_NETKEY;
+	}
+
+	if (*phase == sub->kr_phase) {
+		return STATUS_SUCCESS;
+	}
+
+	if (sub->kr_phase < ARRAY_SIZE(valid_transitions) &&
+	    valid_transitions[sub->kr_phase] & BIT(*phase)) {
+		key_refresh(sub, *phase);
+
+		*phase = sub->kr_phase;
+
+		return STATUS_SUCCESS;
+	}
+
+	BT_WARN("Invalid KR transition: 0x%02x -> 0x%02x", sub->kr_phase,
+		*phase);
+
+	*phase = sub->kr_phase;
+
+	return STATUS_CANNOT_UPDATE;
+}
+
+uint8_t bt_mesh_subnet_kr_phase_get(uint16_t net_idx, uint8_t *phase)
+{
+	struct bt_mesh_subnet *sub;
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		*phase = BT_MESH_KR_NORMAL;
+		return STATUS_INVALID_NETKEY;
+	}
+
+	*phase = sub->kr_phase;
+
+	return STATUS_SUCCESS;
+}
+
+uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx,
+				   enum bt_mesh_feat_state node_id)
+{
+	struct bt_mesh_subnet *sub;
+
+	if (node_id == BT_MESH_FEATURE_NOT_SUPPORTED) {
+		return STATUS_CANNOT_SET;
+	}
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		return STATUS_INVALID_NETKEY;
+	}
+
+	if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+		return STATUS_FEAT_NOT_SUPP;
+	}
+
+	if (node_id) {
+		bt_mesh_proxy_identity_start(sub);
+	} else {
+		bt_mesh_proxy_identity_stop(sub);
+	}
+
+	bt_mesh_adv_update();
+
+	return STATUS_SUCCESS;
+}
+
+uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx,
+				   enum bt_mesh_feat_state *node_id)
+{
+	struct bt_mesh_subnet *sub;
+
+	sub = bt_mesh_subnet_get(net_idx);
+	if (!sub) {
+		*node_id = 0x00;
+		return STATUS_INVALID_NETKEY;
+	}
+
+	*node_id = sub->node_id;
+
+	return STATUS_SUCCESS;
+}
+
+ssize_t bt_mesh_subnets_get(uint16_t net_idxs[], size_t max, off_t skip)
+{
+	size_t count = 0;
+
+	for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
+		struct bt_mesh_subnet *sub = &subnets[i];
+
+		if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		if (skip) {
+			skip--;
+			continue;
+		}
+
+		if (count >= max) {
+			return -ENOMEM;
+		}
+
+		net_idxs[count++] = sub->net_idx;
+	}
+
+	return count;
+}
+
+struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx)
+{
+	for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
+		struct bt_mesh_subnet *sub = &subnets[i];
+
+		if (sub->net_idx == net_idx) {
+			return sub;
+		}
+	}
+
+	return NULL;
+}
+
+int bt_mesh_subnet_set(uint16_t net_idx, uint8_t kr_phase,
+		       const uint8_t old_key[16], const uint8_t new_key[16])
+{
+	const uint8_t *keys[] = { old_key, new_key };
+	struct bt_mesh_subnet *sub;
+
+	sub = subnet_alloc(net_idx);
+	if (!sub) {
+		return -ENOMEM;
+	}
+
+	if (sub->net_idx == net_idx) {
+		return -EALREADY;
+	}
+
+	for (int i = 0; i < ARRAY_SIZE(keys); i++) {
+		if (!keys[i]) {
+			continue;
+		}
+
+		if (net_keys_create(&sub->keys[i], keys[i])) {
+			return -EIO;
+		}
+	}
+
+	sub->net_idx = net_idx;
+	sub->kr_phase = kr_phase;
+
+	if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+		sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+	} else {
+		sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
+	}
+
+	/* Make sure we have valid beacon data to be sent */
+	bt_mesh_beacon_update(sub);
+
+	return 0;
+}
+
+struct bt_mesh_subnet *bt_mesh_subnet_find(int (*cb)(struct bt_mesh_subnet *sub,
+						     void *cb_data),
+					   void *cb_data)
+{
+	for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
+		if (subnets[i].net_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		if (!cb || cb(&subnets[i], cb_data)) {
+			return &subnets[i];
+		}
+	}
+
+	return NULL;
+}
+
+size_t bt_mesh_subnet_foreach(void (*cb)(struct bt_mesh_subnet *sub))
+{
+	size_t count = 0;
+
+	for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
+		if (subnets[i].net_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		cb(&subnets[i]);
+		count++;
+	}
+
+	return count;
+}
+
+struct bt_mesh_subnet *bt_mesh_subnet_next(struct bt_mesh_subnet *sub)
+{
+	if (sub) {
+		sub++;
+	} else {
+		sub = &subnets[0];
+	}
+
+	for (int i = 0; i < ARRAY_SIZE(subnets); i++, sub++) {
+		/* Roll over once we reach the end */
+		if (sub == &subnets[ARRAY_SIZE(subnets)]) {
+			sub = &subnets[0];
+		}
+
+		if (sub->net_idx != BT_MESH_KEY_UNUSED) {
+			return sub;
+		}
+	}
+
+	return NULL;
+}
+
+void bt_mesh_net_keys_reset(void)
+{
+	int i;
+
+	/* Delete all net keys, which also takes care of all app keys which
+	 * are associated with each net key.
+	 */
+	for (i = 0; i < ARRAY_SIZE(subnets); i++) {
+		struct bt_mesh_subnet *sub = &subnets[i];
+
+		if (sub->net_idx != BT_MESH_KEY_UNUSED) {
+			subnet_del(sub);
+		}
+	}
+}
+
+bool bt_mesh_net_cred_find(struct bt_mesh_net_rx *rx, struct os_mbuf *in,
+			   struct os_mbuf *out,
+			   bool (*cb)(struct bt_mesh_net_rx *rx,
+				      struct os_mbuf *in,
+				      struct os_mbuf *out,
+				      const struct bt_mesh_net_cred *cred))
+{
+	int i, j;
+
+	BT_DBG("");
+
+	rx->friend_cred = 1U;
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+	if (bt_mesh_lpn_established()) {
+		rx->sub = bt_mesh.lpn.sub;
+
+		for (j = 0; j < ARRAY_SIZE(bt_mesh.lpn.cred); j++) {
+			if (!rx->sub->keys[j].valid) {
+				continue;
+			}
+
+			if (cb(rx, in, out, &bt_mesh.lpn.cred[j])) {
+				rx->new_key = (j > 0);
+				return true;
+			}
+		}
+
+		/* LPN Should only receive on the friendship credentials when in
+		 * a friendship.
+		 */
+		return false;
+	}
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+	/** Each friendship has unique friendship credentials */
+	for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+		struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+		if (!frnd->established) {
+			continue;
+		}
+
+		if (!frnd->subnet) {
+			continue;
+		}
+
+		rx->sub = frnd->subnet;
+
+		for (j = 0; j < ARRAY_SIZE(frnd->cred); j++) {
+			if (!rx->sub->keys[j].valid) {
+				continue;
+			}
+
+			if (cb(rx, in, out, &frnd->cred[j])) {
+				rx->new_key = (j > 0);
+				return true;
+			}
+		}
+	}
+#endif
+
+	for (i = 0; i < ARRAY_SIZE(subnets); i++) {
+		rx->sub = &subnets[i];
+		if (rx->sub->net_idx == BT_MESH_KEY_UNUSED) {
+			continue;
+		}
+
+		for (j = 0; j < ARRAY_SIZE(rx->sub->keys); j++) {
+			if (!rx->sub->keys[j].valid) {
+				continue;
+			}
+
+			if (cb(rx, in, out, &rx->sub->keys[j].msg)) {
+				rx->new_key = (j > 0);
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
diff --git a/nimble/host/mesh/src/subnet.h b/nimble/host/mesh/src/subnet.h
new file mode 100644
index 0000000..da0a1df
--- /dev/null
+++ b/nimble/host/mesh/src/subnet.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _BLUETOOTH_MESH_SUBNET_H_
+#define _BLUETOOTH_MESH_SUBNET_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#define BT_MESH_NET_FLAG_KR       BIT(0)
+#define BT_MESH_NET_FLAG_IVU      BIT(1)
+
+#define BT_MESH_KR_NORMAL         0x00
+#define BT_MESH_KR_PHASE_1        0x01
+#define BT_MESH_KR_PHASE_2        0x02
+#define BT_MESH_KR_PHASE_3        0x03
+
+/** Which of the two subnet.keys should be used for sending. */
+#define SUBNET_KEY_TX_IDX(sub) ((sub)->kr_phase == BT_MESH_KR_PHASE_2)
+
+struct bt_mesh_net_rx;
+enum bt_mesh_key_evt;
+
+/** Network message encryption credentials */
+struct bt_mesh_net_cred {
+	uint8_t nid;         /* NID */
+	uint8_t enc[16];     /* EncKey */
+	uint8_t privacy[16]; /* PrivacyKey */
+};
+
+/** Subnet instance. */
+struct bt_mesh_subnet {
+	uint32_t beacon_sent;        /* Timestamp of last sent beacon */
+	uint8_t  beacons_last;       /* Number of beacons during last
+				      * observation window
+				      */
+	uint8_t  beacons_cur;        /* Number of beaconds observed during
+				      * currently ongoing window.
+				      */
+
+	uint8_t  beacon_cache[21];   /* Cached last authenticated beacon */
+
+	uint16_t net_idx;            /* NetKeyIndex */
+
+	uint8_t  kr_phase;           /* Key Refresh Phase */
+
+	uint8_t  node_id;            /* Node Identity State */
+	uint32_t node_id_start;      /* Node Identity started timestamp */
+
+	uint8_t  auth[8];            /* Beacon Authentication Value */
+
+	struct bt_mesh_subnet_keys {
+		bool valid;
+		uint8_t net[16];       /* NetKey */
+		struct bt_mesh_net_cred msg;
+		uint8_t net_id[8];     /* Network ID */
+	#if defined(CONFIG_BT_MESH_GATT_PROXY)
+		uint8_t identity[16];  /* IdentityKey */
+	#endif
+		uint8_t beacon[16];    /* BeaconKey */
+	} keys[2];
+};
+
+#ifdef CONFIG_BT_MESH_GATT_PROXY
+extern void (*bt_mesh_subnet_cb_list[5]) (struct bt_mesh_subnet *sub,
+									      enum bt_mesh_key_evt evt);
+#else
+extern void (*bt_mesh_subnet_cb_list[4]) (struct bt_mesh_subnet *sub,
+									      enum bt_mesh_key_evt evt);
+#endif
+
+/** Subnet callback structure. Instantiate with @ref BT_MESH_SUBNET_CB */
+struct bt_mesh_subnet_cb {
+	void (*evt_handler)(struct bt_mesh_subnet *subnet,
+			    enum bt_mesh_key_evt evt);
+};
+
+/** @brief Reset all Network keys. */
+void bt_mesh_net_keys_reset(void);
+
+/** @brief Call cb on every valid Subnet until it returns a non-zero value.
+ *
+ *  @param cb Callback to call, or NULL to return first valid subnet.
+ *  @param cb_data Callback data to pass to callback.
+ *
+ *  @return Subnet that returned non-zero value.
+ */
+struct bt_mesh_subnet *bt_mesh_subnet_find(int (*cb)(struct bt_mesh_subnet *sub,
+						     void *cb_data),
+					   void *cb_data);
+
+/** @brief Iterate through all valid Subnets.
+ *
+ *  @param cb Callback to call on every Subnet.
+ *
+ *  @returns The number of valid subnets.
+ */
+size_t bt_mesh_subnet_foreach(void (*cb)(struct bt_mesh_subnet *sub));
+
+/** @brief Get the next valid Subnet.
+ *
+ *  If there's only one valid Subnet, this will be returned on every call.
+ *
+ *  @param sub Previous Subnet, or NULL to get the first valid.
+ *
+ *  @returns Gets the next valid Subnet after @c sub, or NULL if there are no
+ *           valid Subnets.
+ */
+struct bt_mesh_subnet *bt_mesh_subnet_next(struct bt_mesh_subnet *sub);
+
+/** @brief Get a pointer to the Subnet with the given index.
+ *
+ *  @param net_idx Network index to look for.
+ *
+ *  @returns Subnet with index @c net_idx, or NULL if no such Subnet is known.
+ */
+struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx);
+
+/** @brief Initialize a new Subnet.
+ *
+ *  @param net_idx Network index of the Subnet.
+ *  @param kr_phase Key refresh phase the Subnet should be in.
+ *  @param key The current network key for the Subnet.
+ *  @param new_key New network key, if available.
+ *
+ *  @returns 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_subnet_set(uint16_t net_idx, uint8_t kr_phase,
+		       const uint8_t key[16], const uint8_t new_key[16]);
+
+/** @brief Create Friendship credentials.
+ *
+ *  @param cred Credential object to create.
+ *  @param lpn_addr Address of the LPN node in the friendship.
+ *  @param frnd_addr Address of the Friend node in the friendship.
+ *  @param lpn_counter The LPN's counter parameter.
+ *  @param frnd_counter The Friend node's counter parameter.
+ *  @param key Network key to create the Friendship credentials for.
+ *
+ *  @returns 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_friend_cred_create(struct bt_mesh_net_cred *cred,
+			       uint16_t lpn_addr, uint16_t frnd_addr,
+			       uint16_t lpn_counter, uint16_t frnd_counter,
+			       const uint8_t key[16]);
+
+/** @brief Iterate through all valid network credentials to decrypt a message.
+ *
+ *  @param rx Network RX parameters, passed to the callback.
+ *  @param in Input message buffer, passed to the callback.
+ *  @param out Output message buffer, passed to the callback.
+ *  @param cb Callback to call for each known network credential. Iteration
+ *            stops when this callback returns @c true.
+ *
+ *  @returns Whether any of the credentials got a @c true return from the
+ *           callback.
+ */
+bool bt_mesh_net_cred_find(struct bt_mesh_net_rx *rx, struct os_mbuf *in,
+			   struct os_mbuf *out,
+			   bool (*cb)(struct bt_mesh_net_rx *rx,
+				      struct os_mbuf *in,
+				      struct os_mbuf *out,
+				      const struct bt_mesh_net_cred *cred));
+
+/** @brief Get the network flags of the given Subnet.
+ *
+ *  @param sub Subnet to get the network flags of.
+ *
+ *  @returns A bitmap of @ref BT_MESH_NET_FLAG_KR and @ref BT_MESH_NET_FLAG_IVU.
+ */
+uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);
+
+/** @brief Process a Key Refresh event from a beacon.
+ *
+ *  @param sub Subnet the Key Refresh was received on.
+ *  @param kr_flag Key Refresh flag.
+ *  @param new_key Whether the Key Refresh event was received on the new key
+ *                 set.
+ */
+void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key);
+
+/** @brief Check whether the Subnet has the refreshed keys.
+ *
+ *  @param sub Subnet.
+ *
+ *  @returns Whether the Subnet's second key is valid.
+ */
+static inline bool
+bt_mesh_subnet_has_new_key(const struct bt_mesh_subnet *sub)
+{
+	return sub->kr_phase != BT_MESH_KR_NORMAL;
+}
+
+#endif /* _BLUETOOTH_MESH_SUBNET_H_ */
\ No newline at end of file
diff --git a/nimble/host/mesh/src/testing.c b/nimble/host/mesh/src/testing.c
index c9e3e09..2bef38e 100644
--- a/nimble/host/mesh/src/testing.c
+++ b/nimble/host/mesh/src/testing.c
@@ -13,6 +13,7 @@
 #include "mesh/access.h"
 
 #include "net.h"
+#include "app_keys.h"
 #include "rpl.h"
 #include "testing.h"
 #include "access.h"
@@ -115,38 +116,37 @@
 void bt_test_print_credentials(void)
 {
 	int i;
-	uint8_t nid;
-	const uint8_t *enc;
-	const uint8_t *priv;
-	struct bt_mesh_subnet *sub;
-	struct bt_mesh_app_key *app_key;
+	struct bt_mesh_cdb_subnet *sub;
+	struct bt_mesh_cdb_app_key *app_key;
+	struct bt_mesh_subnet *subnet;
 
 	console_printf("IV Index: %08lx\n", (long) bt_mesh.iv_index);
 	console_printf("Dev key: %s\n", bt_hex(bt_mesh.dev_key, 16));
 
-	for (i = 0; i < MYNEWT_VAL(BLE_MESH_SUBNET_COUNT); ++i)
+	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i)
 	{
-		if (bt_mesh.app_keys[i].net_idx == BT_MESH_KEY_UNUSED) {
+		app_key = &bt_mesh_cdb.app_keys[i];
+		if (app_key->net_idx == BT_MESH_KEY_UNUSED) {
 			continue;
 		}
 
-		sub = &bt_mesh.sub[i];
+		sub = bt_mesh_cdb_subnet_get(i);
 
 		console_printf("Subnet: %d\n", i);
 		console_printf("\tNetKeyIdx: %04x\n",
 			       sub->net_idx);
 		console_printf("\tNetKey: %s\n",
-			       bt_hex(sub->keys[sub->kr_flag].net, 16));
+			       bt_hex(sub->keys[sub->kr_flag].net_key, 16));
 	}
 
-	for (i = 0; i < MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT); ++i)
+	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i)
 	{
-		if (bt_mesh.app_keys[i].net_idx == BT_MESH_KEY_UNUSED) {
+		if (app_key->net_idx == BT_MESH_KEY_UNUSED) {
 			continue;
 		}
 
-		sub = &bt_mesh.sub[i];
-		app_key = &bt_mesh.app_keys[i];
+		sub = bt_mesh_cdb_subnet_get(i);
+		app_key = &bt_mesh_cdb.app_keys[i];
 
 		console_printf("AppKey: %d\n", i);
 		console_printf("\tNetKeyIdx: %04x\n",
@@ -154,28 +154,26 @@
 		console_printf("\tAppKeyIdx: %04x\n",
 			       app_key->app_idx);
 		console_printf("\tAppKey: %s\n",
-			       bt_hex(app_key->keys[sub->kr_flag].val, 16));
+			       bt_hex(app_key->keys[sub->kr_flag].app_key, 16));
 	}
 
-	for (i = 0; i < MYNEWT_VAL(BLE_MESH_SUBNET_COUNT); ++i)
+	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i)
 	{
-		if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
+		sub = bt_mesh_cdb_subnet_get(i);
+		if (sub[i].net_idx == BT_MESH_KEY_UNUSED) {
 			continue;
 		}
 
-		if (friend_cred_get(&bt_mesh.sub[i], BT_MESH_ADDR_UNASSIGNED,
-				&nid, &enc, &priv)) {
-			return;
-		}
+		subnet = bt_mesh_subnet_get(app_key->net_idx);
 
 		console_printf("Friend cred: %d\n", i);
 		console_printf("\tNetKeyIdx: %04x\n",
-			       bt_mesh.sub[i].net_idx);
-		console_printf("\tNID: %02x\n", nid);
+			           sub[i].net_idx);
+		console_printf("\tNID: %02x\n", subnet->keys->msg.nid);
 		console_printf("\tEncKey: %s\n",
-			       bt_hex(enc, 16));
+			       bt_hex(subnet->keys->msg.enc, 16));
 		console_printf("\tPrivKey: %s\n",
-			       bt_hex(priv, 16));
+			       bt_hex(subnet->keys->msg.privacy, 16));
 	}
 }
 
diff --git a/nimble/host/mesh/src/transport.c b/nimble/host/mesh/src/transport.c
index 33a1557..b9f3276 100644
--- a/nimble/host/mesh/src/transport.c
+++ b/nimble/host/mesh/src/transport.c
@@ -19,6 +19,7 @@
 #include "crypto.h"
 #include "adv.h"
 #include "net.h"
+#include "app_keys.h"
 #include "rpl.h"
 #include "lpn.h"
 #include "friend.h"
@@ -265,9 +266,7 @@
 		/* bt_mesh_net_iv_update() will re-enable the flag if this
 		 * wasn't the only transfer.
 		 */
-		if (bt_mesh_net_iv_update(bt_mesh.iv_index, false)) {
-			bt_mesh_net_sec_update(NULL);
-		}
+		bt_mesh_net_iv_update(bt_mesh.iv_index, false);
 	}
 }
 
@@ -443,7 +442,6 @@
 		    const struct bt_mesh_send_cb *cb, void *cb_data,
 		    uint8_t *ctl_op)
 {
-{
 	bool blocked = false;
 	struct seg_tx *tx;
 	uint8_t seg_o;
@@ -582,8 +580,6 @@
 
 	seg_tx_send_unacked(tx);
 
-	}
-
 	if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
 	    bt_mesh_lpn_established()) {
 		bt_mesh_lpn_poll();
@@ -592,27 +588,18 @@
 	return 0;
 }
 
-struct bt_mesh_app_key *bt_mesh_app_key_find(uint16_t app_idx)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
-
-		if (key->net_idx != BT_MESH_KEY_UNUSED &&
-		    key->app_idx == app_idx) {
-			return key;
-		}
-	}
-
-	return NULL;
-}
-
 int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg,
 		       const struct bt_mesh_send_cb *cb, void *cb_data)
 {
+	struct bt_mesh_app_crypto_ctx crypto = {
+		.dev_key = BT_MESH_IS_DEV_KEY(tx->ctx->app_idx),
+		.aszmic = tx->aszmic,
+		.src = tx->src,
+		.dst = tx->ctx->addr,
+		.seq_num = bt_mesh.seq,
+		.iv_index = BT_MESH_NET_IVI_TX,
+	};
 	const uint8_t *key;
-	uint8_t *ad;
 	uint8_t aid;
 	int err;
 
@@ -640,12 +627,12 @@
 	       tx->ctx->app_idx, tx->ctx->addr);
 	BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
 
-	err = bt_mesh_app_key_get(tx->sub, tx->ctx->app_idx,
-				  tx->ctx->addr, &key, &aid);
+	err = bt_mesh_keys_resolve(tx->ctx, &tx->sub, &key, &aid);
 	if (err) {
 		return err;
 	}
 
+	tx->xmit = bt_mesh_net_transmit_get();
 	tx->aid = aid;
 
 	if (!tx->ctx->send_rel || net_buf_simple_tailroom(msg) < 8) {
@@ -655,14 +642,10 @@
 	}
 
 	if (BT_MESH_ADDR_IS_VIRTUAL(tx->ctx->addr)) {
-		ad = bt_mesh_label_uuid_get(tx->ctx->addr);
-	} else {
-		ad = NULL;
+		crypto.ad = bt_mesh_label_uuid_get(tx->ctx->addr);
 	}
 
-	err = bt_mesh_app_encrypt(key, BT_MESH_IS_DEV_KEY(tx->ctx->app_idx),
-				  tx->aszmic, msg, ad, tx->src, tx->ctx->addr,
-				  bt_mesh.seq, BT_MESH_NET_IVI_TX);
+	err = bt_mesh_app_encrypt(key, &crypto, msg);
 	if (err) {
 		return err;
 	}
@@ -681,7 +664,7 @@
 {
 	int i;
 
-	net_buf_simple_reset(buf);
+	os_mbuf_reset(buf);
 
 	for (i = 0; i <= rx->seg_n; i++) {
 		net_buf_simple_add_mem(buf, rx->seg[i],
@@ -695,178 +678,47 @@
 	}
 }
 
-static int remote_devkey_decrypt(struct bt_mesh_net_rx *rx, uint32_t seq, uint8_t *ad,
-				 uint8_t aszmic, struct os_mbuf *buf,
-				 struct os_mbuf *sdu)
-{
-	struct bt_mesh_cdb_node *node;
-	int err;
-
-	if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) {
-		return -ENOENT;
-	}
-
-	/* We will try our local devkey separately. */
-	if (bt_mesh_elem_find(rx->ctx.addr)) {
-		return -ENOENT;
-	}
-
-	/*
-	 * There is no way of knowing if we should use our
-	 * local DevKey or the remote DevKey to decrypt the
-	 * message so we must try both.
-	 */
-	node = bt_mesh_cdb_node_get(rx->ctx.addr);
-	if (node == NULL) {
-		BT_ERR("No node found for addr 0x%04x", rx->ctx.addr);
-		return -EINVAL;
-	}
-
-	err = bt_mesh_app_decrypt(node->dev_key, true, aszmic, buf, sdu, ad,
-				  rx->ctx.addr, rx->ctx.recv_dst, seq,
-				  BT_MESH_NET_IVI_RX(rx));
-	if (err) {
-		BT_DBG("Unable to decrypt with node DevKey");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int app_key_decrypt(struct bt_mesh_net_rx *rx,
-			   struct bt_mesh_app_key *key, uint32_t seq, uint8_t *ad,
-			   uint8_t hdr, uint8_t aszmic, struct os_mbuf *buf,
-			   struct os_mbuf *sdu)
-{
-	struct bt_mesh_app_keys *keys;
-	int err;
-
-	/* Check that this AppKey matches received net_idx */
-	if (key->net_idx != rx->sub->net_idx) {
-		return -EINVAL;
-	}
-
-	if (rx->new_key && key->updated) {
-		keys = &key->keys[1];
-	} else {
-		keys = &key->keys[0];
-	}
-
-	/* Check that the AppKey ID matches */
-	if (AID(&hdr) != keys->id) {
-		return -EINVAL;
-	}
-
-	err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf, sdu, ad,
-				  rx->ctx.addr, rx->ctx.recv_dst, seq,
-				  BT_MESH_NET_IVI_RX(rx));
-	if (err) {
-		BT_WARN("Unable to decrypt with AppKey 0x%03x", key->app_idx);
-	}
-
-	return err;
-}
-
-static int sdu_recv_unseg(struct bt_mesh_net_rx *rx, uint8_t hdr,
-			  struct os_mbuf *buf)
-{
-	struct os_mbuf *sdu =
-		NET_BUF_SIMPLE(BT_MESH_SDU_UNSEG_MAX);
-	uint8_t *ad;
-	uint16_t i;
-	int err = 0;
-
-	BT_DBG("AKF %u AID 0x%02x", AKF(&hdr), AID(&hdr));
-	BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
-
-	if (buf->om_len < 1 + APP_MIC_LEN(0)) {
-		BT_ERR("Too short SDU + MIC");
-		return -EINVAL;
-	}
-
-	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) {
-		BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend",
-		       rx->ctx.recv_dst);
-		return 0;
-	}
-
-	if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) {
-		ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
-	} else {
-		ad = NULL;
-	}
-
-	/* Adjust the length to not contain the MIC at the end */
-	buf->om_len -= APP_MIC_LEN(0);
-
-	if (!AKF(&hdr)) {
-		/* Attempt remote dev key first, as that is only available for
-		 * provisioner devices, which normally don't interact with nodes
-		 * that know their local dev key.
-		 */
-		net_buf_simple_reset(sdu);
-		err = remote_devkey_decrypt(rx, rx->seq, ad, 0, buf, sdu);
-		if (!err) {
-			rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE;
-			bt_mesh_model_recv(rx, sdu);
-			return 0;
-		}
-
-		net_buf_simple_reset(sdu);
-		err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, 0, buf, sdu,
-					  ad, rx->ctx.addr, rx->ctx.recv_dst,
-					  rx->seq, BT_MESH_NET_IVI_RX(rx));
-		if (err) {
-			BT_ERR("Unable to decrypt with local DevKey");
-			return err;
-		}
-
-		rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL;
-		bt_mesh_model_recv(rx, sdu);
-		return 0;
-	}
-
-	for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
-
-		net_buf_simple_reset(sdu);
-		err = app_key_decrypt(rx, &bt_mesh.app_keys[i], rx->seq, ad,
-				      hdr, 0, buf, sdu);
-
-		if (err) {
-			continue;
-		}
-
-		rx->ctx.app_idx = key->app_idx;
-
-		bt_mesh_model_recv(rx, sdu);
-		return 0;
-	}
-
-	if (rx->local_match) {
-		BT_WARN("No matching AppKey");
-	}
-
-	return 0;
-}
-
-static int sdu_recv_seg(struct seg_rx *seg, uint8_t hdr, uint8_t aszmic,
-			struct bt_mesh_net_rx *rx)
-{
-	struct os_mbuf *buf = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX);
+struct decrypt_ctx {
+	struct bt_mesh_app_crypto_ctx crypto;
+	struct os_mbuf *buf;
 	struct os_mbuf *sdu;
-	uint32_t seq = (seg->seq_auth & 0xffffff);
-	uint8_t *ad;
-	uint16_t i;
-	int err;
+	struct seg_rx *seg;
+};
 
-	BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr));
+static int sdu_try_decrypt(struct bt_mesh_net_rx *rx, const uint8_t key[16],
+			   void *cb_data)
+{
+	const struct decrypt_ctx *ctx = cb_data;
 
-	if (seg->len < 1 + APP_MIC_LEN(aszmic)) {
-		BT_ERR("Too short SDU + MIC");
-		return -EINVAL;
+	if (ctx->seg) {
+		seg_rx_assemble(ctx->seg, ctx->buf, ctx->crypto.aszmic);
 	}
 
+	os_mbuf_reset(ctx->sdu);
+
+	return bt_mesh_app_decrypt(key, &ctx->crypto, ctx->buf, ctx->sdu);
+}
+
+static int sdu_recv(struct bt_mesh_net_rx *rx, uint8_t hdr, uint8_t aszmic,
+		    struct os_mbuf *buf, struct os_mbuf *sdu,
+		    struct seg_rx *seg)
+{
+	struct decrypt_ctx ctx = {
+		.crypto = {
+			.dev_key = !AKF(&hdr),
+			.aszmic = aszmic,
+			.src = rx->ctx.addr,
+			.dst = rx->ctx.recv_dst,
+			.seq_num = rx->seq,
+			.iv_index = BT_MESH_NET_IVI_RX(rx),
+		},
+		.buf = buf,
+		.sdu = sdu,
+		.seg = seg,
+	};
+
+	BT_DBG("AKF %u AID 0x%02x", !ctx.crypto.dev_key, AID(&hdr));
+
 	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) {
 		BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend",
 		       rx->ctx.recv_dst);
@@ -874,76 +726,19 @@
 	}
 
 	if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) {
-		ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
-	} else {
-		ad = NULL;
+		ctx.crypto.ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
 	}
 
-	/* Decrypting in place to avoid creating two assembly buffers.
-	 * We'll reassemble the buffer from the segments before each decryption
-	 * attempt.
-	 */
-	if (!AKF(&hdr)) {
-		/* Attempt remote dev key first, as that is only available for
-		 * provisioner devices, which normally don't interact with nodes
-		 * that know their local dev key.
-		 */
-		seg_rx_assemble(seg, buf, aszmic);
-
-		net_buf_simple_init_with_data(sdu, buf->om_data,
-					      seg->len - APP_MIC_LEN(aszmic));
-		sdu->om_len = 0;
-
-		/* Check that the AppKey ID matches */
-		err = remote_devkey_decrypt(rx, seq, ad, aszmic, buf, sdu);
-		if (!err) {
-			rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE;
-			bt_mesh_model_recv(rx, sdu);
-			return 0;
-		}
-
-		seg_rx_assemble(seg, buf, aszmic);
-		net_buf_simple_init_with_data(sdu, buf->om_data,
-					      seg->len - APP_MIC_LEN(aszmic));
-		sdu->om_len = 0;
-
-		err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, aszmic, buf,
-					  sdu, ad, rx->ctx.addr,
-					  rx->ctx.recv_dst, seq,
-					  BT_MESH_NET_IVI_RX(rx));
-		if (err) {
-			BT_ERR("Unable to decrypt with local DevKey");
-			return err;
-		}
-
-		rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL;
-		bt_mesh_model_recv(rx, sdu);
+	rx->ctx.app_idx = bt_mesh_app_key_find(ctx.crypto.dev_key, AID(&hdr),
+					       rx, sdu_try_decrypt, &ctx);
+	if (rx->ctx.app_idx == BT_MESH_KEY_UNUSED) {
+		BT_DBG("No matching AppKey");
 		return 0;
 	}
 
-	for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
-		struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+	BT_DBG("Decrypted (AppIdx: 0x%03x)", rx->ctx.app_idx);
 
-		seg_rx_assemble(seg, buf, aszmic);
-		net_buf_simple_init_with_data(sdu, buf->om_data,
-					      seg->len - APP_MIC_LEN(aszmic));
-		sdu->om_len = 0;
-
-		err = app_key_decrypt(rx, &bt_mesh.app_keys[i], seq, ad, hdr,
-				      aszmic, buf, sdu);
-		if (err) {
-			continue;
-		}
-
-		rx->ctx.app_idx = key->app_idx;
-
-		bt_mesh_model_recv(rx, sdu);
-		return 0;
-	}
-
-	if (rx->local_match) {
-		BT_WARN("No matching AppKey");
-	}
+	bt_mesh_model_recv(rx, sdu);
 
     os_mbuf_free_chain(sdu);
     return 0;
@@ -1152,6 +947,7 @@
 static int trans_unseg(struct os_mbuf *buf, struct bt_mesh_net_rx *rx,
 		       uint64_t *seq_auth)
 {
+	struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_SDU_UNSEG_MAX);
 	uint8_t hdr;
 
 	BT_DBG("AFK %u AID 0x%02x", AKF(buf->om_data), AID(buf->om_data));
@@ -1171,14 +967,17 @@
 
 	if (rx->ctl) {
 		return ctl_recv(rx, hdr, buf, seq_auth);
-	} else {
-		/* SDUs must match a local element or an LPN of this Friend. */
-		if (!rx->local_match && !rx->friend_match) {
-			return 0;
-		}
-
-		return sdu_recv_unseg(rx, hdr, buf);
 	}
+
+	if (buf->om_len < 1 + APP_MIC_LEN(0)) {
+		BT_ERR("Too short SDU + MIC");
+		return -EINVAL;
+	}
+
+	/* Adjust the length to not contain the MIC at the end */
+	buf->om_len -= APP_MIC_LEN(0);
+
+	return sdu_recv(rx, hdr, 0, buf, sdu, NULL);
 }
 
 static inline int32_t ack_timeout(struct seg_rx *rx)
@@ -1209,7 +1008,7 @@
 int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, uint8_t ctl_op, void *data,
 		     size_t data_len, const struct bt_mesh_send_cb *cb, void *cb_data)
 {
-	struct os_mbuf *buf;
+	struct os_mbuf *buf = NET_BUF_SIMPLE(data_len);
 
 	net_buf_simple_init_with_data(buf, data, data_len);
 
@@ -1461,7 +1260,7 @@
 
 	BT_DBG("ASZMIC %u AKF %u AID 0x%02x", ASZMIC(hdr), AKF(hdr), AID(hdr));
 
-	net_buf_simple_pull(buf, 1);
+	net_buf_simple_pull_mem(buf, 1);
 
 	seq_zero = net_buf_simple_pull_be16(buf);
 	seg_o = (seq_zero & 0x03) << 3;
@@ -1646,12 +1445,31 @@
 	send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr,
 		 net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo);
 
+	/* Decrypt with seqAuth */
+	net_rx->seq = (rx->seq_auth & 0xffffff);
+
 	if (net_rx->ctl) {
 		struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_RX_CTL_MAX);
 		seg_rx_assemble(rx, sdu, 0U);
 		err = ctl_recv(net_rx, *hdr, sdu, seq_auth);
+	} else if (rx->len < 1 + APP_MIC_LEN(ASZMIC(hdr))) {
+		BT_ERR("Too short SDU + MIC");
+		err = -EINVAL;
 	} else {
-		err = sdu_recv_seg(rx, *hdr, ASZMIC(hdr), net_rx);
+		struct os_mbuf *seg_buf = NET_BUF_SIMPLE(BT_MESH_RX_SDU_MAX);
+		struct os_mbuf *sdu;
+
+		/* Decrypting in place to avoid creating two assembly buffers.
+		 * We'll reassemble the buffer from the segments before each
+		 * decryption attempt.
+		 */
+		net_buf_simple_init(seg_buf, 0);
+
+	    sdu = NET_BUF_SIMPLE(rx->len - APP_MIC_LEN(ASZMIC(hdr)));
+		net_buf_simple_init_with_data(
+			sdu, seg_buf->om_data, rx->len - APP_MIC_LEN(ASZMIC(hdr)));
+
+		err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), seg_buf, sdu, rx);
 	}
 
 	seg_rx_reset(rx, false);
@@ -1679,7 +1497,7 @@
 	       rx->friend_match);
 
 	/* Remove network headers */
-	net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
+	net_buf_simple_pull_mem(buf, BT_MESH_NET_HDR_LEN);
 
 	BT_DBG("Payload %s", bt_hex(buf->om_data, buf->om_len));
 
@@ -1702,7 +1520,7 @@
 	/* Save the app-level state so the buffer can later be placed in
 	 * the Friend Queue.
 	 */
-	net_buf_simple_save(buf, &state);
+	os_mbuf_save(buf, &state);
 
 	if (SEG(buf->om_data)) {
 		/* Segmented messages must match a local element or an
@@ -1735,7 +1553,7 @@
 		bt_mesh_lpn_msg_received(rx);
 	}
 
-	net_buf_simple_restore(buf, &state);
+	os_mbuf_restore(buf, &state);
 
 	if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->friend_match && !err) {
 		if (seq_auth == TRANS_SEQ_AUTH_NVAL) {
@@ -1843,49 +1661,3 @@
 	return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb),
 			cb, cb_data);
 }
-
-int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, uint16_t app_idx,
-			uint16_t addr, const uint8_t **key, uint8_t *aid)
-{
-	struct bt_mesh_app_key *app_key;
-
-	if (app_idx == BT_MESH_KEY_DEV_LOCAL ||
-	    (app_idx == BT_MESH_KEY_DEV_REMOTE &&
-	     bt_mesh_elem_find(addr) != NULL)) {
-		*aid = 0;
-		*key = bt_mesh.dev_key;
-		return 0;
-	} else if (app_idx == BT_MESH_KEY_DEV_REMOTE) {
-		if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) {
-			return -EINVAL;
-		}
-
-		struct bt_mesh_cdb_node *node = bt_mesh_cdb_node_get(addr);
-		if (!node) {
-			return -EINVAL;
-		}
-
-		*key = node->dev_key;
-		*aid = 0;
-		return 0;
-	}
-
-	if (!subnet) {
-		return -EINVAL;
-	}
-
-	app_key = bt_mesh_app_key_find(app_idx);
-	if (!app_key) {
-		return -ENOENT;
-	}
-
-	if (subnet->kr_phase == BT_MESH_KR_PHASE_2 && app_key->updated) {
-		*key = app_key->keys[1].val;
-		*aid = app_key->keys[1].id;
-	} else {
-		*key = app_key->keys[0].val;
-		*aid = app_key->keys[0].id;
-	}
-
-	return 0;
-}
diff --git a/nimble/host/mesh/src/transport.h b/nimble/host/mesh/src/transport.h
index 76af154..11ad701 100644
--- a/nimble/host/mesh/src/transport.h
+++ b/nimble/host/mesh/src/transport.h
@@ -84,8 +84,6 @@
 
 void bt_mesh_set_hb_sub_dst(uint16_t addr);
 
-struct bt_mesh_app_key *bt_mesh_app_key_find(uint16_t app_idx);
-
 bool bt_mesh_tx_in_progress(void);
 
 void bt_mesh_rx_reset(void);
@@ -102,6 +100,3 @@
 void bt_mesh_trans_init(void);
 
 int bt_mesh_heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data);
-
-int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, uint16_t app_idx,
-			uint16_t addr, const uint8_t **key, uint8_t *aid);
diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml
index d3b2cf0..446aee1 100644
--- a/nimble/host/mesh/syscfg.yml
+++ b/nimble/host/mesh/syscfg.yml
@@ -688,6 +688,15 @@
             Minimum level for the BLE Mesh Replay protection list log.
         value: 1
 
+    BLE_MESH_NET_KEYS_LOG_MOD:
+        description: >
+            Numeric module ID to use for BLE Mesh Replay protection list messages.
+        value: 23
+    BLE_MESH_NET_KEYS_LOG_LVL:
+        description: >
+            Minimum level for the BLE Mesh Replay protection list log.
+        value: 1
+
 syscfg.logs:
     BLE_MESH_LOG:
         module: MYNEWT_VAL(BLE_MESH_LOG_MOD)
@@ -745,6 +754,10 @@
         module: MYNEWT_VAL(BLE_MESH_TRANS_LOG_MOD)
         level: MYNEWT_VAL(BLE_MESH_TRANS_LOG_LVL)
 
+    BLE_MESH_NET_KEYS_LOG:
+        module: MYNEWT_VAL(BLE_MESH_NET_KEYS_LOG_MOD)
+        level: MYNEWT_VAL(BLE_MESH_NET_KEYS_LOG_LVL)
+
 syscfg.vals.BLE_MESH_SHELL:
     BLE_MESH_CFG_CLI: 1
     BLE_MESH_HEALTH_CLI: 1
diff --git a/porting/examples/linux_blemesh/include/logcfg/logcfg.h b/porting/examples/linux_blemesh/include/logcfg/logcfg.h
index 686ffcb..6eff5ef 100644
--- a/porting/examples/linux_blemesh/include/logcfg/logcfg.h
+++ b/porting/examples/linux_blemesh/include/logcfg/logcfg.h
@@ -113,6 +113,13 @@
 #define BLE_MESH_RPL_LOG_CRITICAL(...) MODLOG_CRITICAL(22, __VA_ARGS__)
 #define BLE_MESH_RPL_LOG_DISABLED(...) MODLOG_DISABLED(22, __VA_ARGS__)
 
+#define BLE_MESH_NET_KEYS_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_INFO(...) MODLOG_INFO(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_WARN(...) MODLOG_WARN(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_ERROR(...) MODLOG_ERROR(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_CRITICAL(...) MODLOG_CRITICAL(23, __VA_ARGS__)
+#define BLE_MESH_NET_KEYS_LOG_DISABLED(...) MODLOG_DISABLED(23, __VA_ARGS__)
+
 #define DFLT_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
 #define DFLT_LOG_INFO(...) MODLOG_INFO(0, __VA_ARGS__)
 #define DFLT_LOG_WARN(...) MODLOG_WARN(0, __VA_ARGS__)