diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 92a58b2f2..7c15e9bc8 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -3744,6 +3744,14 @@ struct wpa_driver_ops {
        int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);

        /**
+        * set_receive_lowest_pn - Set receive lowest pn
+        * @priv: Private driver interface data
+        * @sa: secure association
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+       /**
         * create_receive_sc - create secure channel for receiving
         * @priv: Private driver interface data
         * @sc: secure channel
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index e89b3ba14..ee0f64de3 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -670,6 +670,48 @@ static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)


 /**
+ * macsec_drv_set_receive_lowest_pn - Set receive lowest pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+       struct macsec_drv_data *drv = priv;
+       struct macsec_genl_ctx *ctx = &drv->ctx;
+       struct nl_msg *msg;
+       struct nlattr *nest;
+       int ret = -1;
+
+       wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
+
+       msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
+       if (!msg)
+               return ret;
+
+       nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+       if (!nest)
+               goto nla_put_failure;
+
+       NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+       NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+       nla_nest_end(msg, nest);
+
+       ret = nl_send_recv(ctx->sk, msg);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR,
+                          DRV_PREFIX "failed to communicate: %d (%s)",
+                          ret, nl_geterror(-ret));
+       }
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+/**
  * macsec_drv_get_transmit_next_pn - Get transmit next PN
  * @priv: Private driver interface data
  * @sa: secure association
@@ -1293,6 +1335,7 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
        .set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
        .enable_controlled_port = macsec_drv_enable_controlled_port,
        .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+       .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
        .get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
        .set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
        .create_receive_sc = macsec_drv_create_receive_sc,
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index ba2636ad6..4ac4fdc15 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -1144,27 +1144,37 @@ ieee802_1x_mka_get_sak_use_length(


 /**
- *
+ * ieee802_1x_mka_get_lpn
  */
 static u32
 ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
                       struct ieee802_1x_mka_ki *ki)
 {
-       struct receive_sa *rxsa;
-       struct receive_sc *rxsc;
+       struct transmit_sa *txsa;
        u32 lpn = 0;

-       dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
-               dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
-               {
-                       if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
-                               secy_get_receive_lowest_pn(principal->kay,
-                                                          rxsa);
-
-                               lpn = lpn > rxsa->lowest_pn ?
-                                       lpn : rxsa->lowest_pn;
-                               break;
-                       }
+       dl_list_for_each(txsa, &principal->txsc->sa_list,
+                        struct transmit_sa, list) {
+               if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
+                       /* Per IEEE802.1X-2010 Clase 9, "Eacy SecY uses MKA to
+                        * communicate the lowest PN used for transmission with
+                        * the SAK within the last two seconds".  Achive this
+                        * 2 second delay by setting the lpn using the transmit
+                        * next PN (i.e., txsa->next_pn) that was read last
+                        * time here (i.e., mka_hello_time 2 seconds ago).
+                        * The lowest acceptable PN is the same as the last
+                        * transmitted PN, which is one less than the next
+                        * transmit PN.
+                        *
+                        * NOTE: this method only works if mka_hello_time is 2s.
+                        */
+                       lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
+
+                       /* Now read the current transmit next PN for use next
+                        * time through. */
+                       secy_get_transmit_next_pn(principal->kay,
+                                                 txsa);
+                       break;
                }
        }

@@ -1265,7 +1275,8 @@ ieee802_1x_mka_decode_sak_use_body(
        struct ieee802_1x_mka_hdr *hdr;
        struct ieee802_1x_mka_sak_use_body *body;
        struct ieee802_1x_kay_peer *peer;
-       struct transmit_sa *txsa;
+       struct receive_sc *rxsc;
+       struct receive_sa *rxsa;
        struct data_key *sa_key = NULL;
        size_t body_len;
        struct ieee802_1x_mka_ki ki;
@@ -1385,25 +1396,34 @@ ieee802_1x_mka_decode_sak_use_body(
        }

        found = FALSE;
-       dl_list_for_each(txsa, &participant->txsc->sa_list,
-                        struct transmit_sa, list) {
-               if (sa_key != NULL && txsa->pkey == sa_key) {
-                       found = TRUE;
-                       break;
+       dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc, list) {
+               dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
+                       if (sa_key != NULL && rxsa->pkey == sa_key) {
+                               found = TRUE;
+                               break;
+                       }
                }
        }
        if (!found) {
-               wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
+               wpa_printf(MSG_WARNING, "KaY: Can't find rxsa");
                return -1;
        }

-       /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
-        * npn is larger than txsa's npn, set it to txsa.
-        */
-       secy_get_transmit_next_pn(kay, txsa);
-       if (lpn > txsa->next_pn) {
-               secy_set_transmit_next_pn(kay, txsa);
-               wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
+       if (body->delay_protect) {
+               secy_get_receive_lowest_pn(participant->kay, rxsa);
+               if (lpn > rxsa->lowest_pn) {
+                       /* Delay protect window (communicated via MKA) is
+                        * tighter than SecY's current replay protect window,
+                        * so tell SecY the new (and higher) lpn. */
+                       rxsa->lowest_pn = lpn;
+                       secy_set_receive_lowest_pn(participant->kay, rxsa);
+                       wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn);
+               }
+               /* FIXME: Delay protection for olpn not implemented.
+                * Note that Old Key is only active for MKA_SAK_RETIRE_TIME
+                * (3 seconds) and delay protection does allow PN's within
+                * a 2 seconds window, so olpn would be a lot of work for
+                * just 1 second's worth of protection. */
        }

        return 0;
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 7031c1a83..01af578fc 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -150,6 +150,7 @@ struct ieee802_1x_kay_ctx {
        int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
        int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
        int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+       int (*set_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
        int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
                                 enum validate_frames vf,
                                 enum confidentiality_offset co);
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
index ab5339bb2..4e5379ff7 100644
--- a/src/pae/ieee802_1x_secy_ops.c
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -216,6 +216,27 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
 }


+int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
+                              struct receive_sa *rxsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->set_receive_lowest_pn) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy set_receive_lowest_pn operation not supported");
+               return -1;
+       }
+
+       return ops->set_receive_lowest_pn(ops->ctx, rxsa);
+}
+
+
 int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
 {
        struct ieee802_1x_kay_ctx *ops;
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
index 9fb29c3dd..2d112ba7c 100644
--- a/src/pae/ieee802_1x_secy_ops.h
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -36,6 +36,8 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
                              struct transmit_sa *txsa);
 int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
                              struct transmit_sa *txsa);
+int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
+                              struct receive_sa *txsa);
 int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 078de23f7..92a0a8fda 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -796,6 +796,14 @@ static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
        return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa);
 }

+static inline int wpa_drv_set_receive_lowest_pn(struct wpa_supplicant *wpa_s,
+                                               struct receive_sa *sa)
+{
+       if (!wpa_s->driver->set_receive_lowest_pn)
+               return -1;
+       return wpa_s->driver->set_receive_lowest_pn(wpa_s->drv_priv, sa);
+}
+
 static inline int
 wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc,
                          unsigned int conf_offset, int validation)
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index 11708b8a6..5235af77a 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -92,6 +92,12 @@ static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
 }


+static int wpas_set_receive_lowest_pn(void *wpa_s, struct receive_sa *sa)
+{
+       return wpa_drv_set_receive_lowest_pn(wpa_s, sa);
+}
+
+
 static unsigned int conf_offset_val(enum confidentiality_offset co)
 {
        switch (co) {
@@ -219,6 +225,7 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
        kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
        kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
+       kay_ctx->set_receive_lowest_pn = wpas_set_receive_lowest_pn;
        kay_ctx->create_receive_sc = wpas_create_receive_sc;
        kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
        kay_ctx->create_receive_sa = wpas_create_receive_sa;
