diff --git a/UnitTest1/unittest1.cpp b/UnitTest1/unittest1.cpp
index 3c36e276ef9bf67a8ee19767473a2a0f7f81af5d..23711c84ec0aa2267c0369b3306f63056620b3d4 100644
--- a/UnitTest1/unittest1.cpp
+++ b/UnitTest1/unittest1.cpp
@@ -400,5 +400,19 @@ namespace UnitTest1
 
             Assert::AreEqual(ret, 0);
         }
+
+        TEST_METHOD(test_cleartext_pn_enc)
+        {
+            int ret = cleartext_pn_enc_test();
+
+            Assert::AreEqual(ret, 0);
+        }
+
+        TEST_METHOD(test_pn_enc_1rtt)
+        {
+            int ret = pn_enc_1rtt_test();
+
+            Assert::AreEqual(ret, 0);
+        }
 	};
 }
\ No newline at end of file
diff --git a/picoquic/picoquic_internal.h b/picoquic/picoquic_internal.h
index 73a275facfa095cfbb2fc8b0fd44984c1c408061..b9ce7856cef1f0162c5b7ee454b688b5239cb1cd 100644
--- a/picoquic/picoquic_internal.h
+++ b/picoquic/picoquic_internal.h
@@ -358,11 +358,16 @@ typedef struct st_picoquic_cnx_t {
     void* aead_encrypt_cleartext_ctx;
     void* aead_decrypt_cleartext_ctx;
     void* aead_de_encrypt_cleartext_ctx; /* used by logging functions to see what is sent. */
+    void* pn_enc_cleartext; /* Used for PN encryption of clear text packets */
+    void* pn_dec_cleartext; /* Used for PN decryption of clear text packets */
     void* aead_encrypt_ctx;
     void* aead_decrypt_ctx;
     void* aead_de_encrypt_ctx; /* used by logging functions to see what is sent. */
+    void* pn_enc; /* Used for PN encryption of 1 RTT packets */
+    void* pn_dec; /* Used for PN decryption of 1 RTT packets */
     void* aead_0rtt_encrypt_ctx; /* setup on client if 0-RTT is possible */
     void* aead_0rtt_decrypt_ctx; /* setup on server if 0-RTT is possible, also used on client for logging */
+    void* pn_enc_0rtt; /* Used for PN encryption or decryption of 0-RTT packets */
 
     /* Receive state */
     picoquic_sack_item_t first_sack_item;
diff --git a/picoquic/quicctx.c b/picoquic/quicctx.c
index d29fa7c5f8cb8650c85756407dac7cca17abbebb..5d167f62a8adb7472ea7ad8e99c2d543e32e5453 100644
--- a/picoquic/quicctx.c
+++ b/picoquic/quicctx.c
@@ -916,6 +916,24 @@ int picoquic_reset_cnx_version(picoquic_cnx_t* cnx, uint8_t* bytes, size_t lengt
                         cnx->aead_0rtt_encrypt_ctx = NULL;
                     }
 
+                    if (cnx->pn_enc_cleartext != NULL)
+                    {
+                        picoquic_pn_enc_free(cnx->pn_enc_cleartext);
+                        cnx->pn_enc_cleartext = NULL;
+                    }
+
+                    if (cnx->pn_dec_cleartext != NULL)
+                    {
+                        picoquic_pn_enc_free(cnx->pn_dec_cleartext);
+                        cnx->pn_dec_cleartext = NULL;
+                    }
+
+                    if (cnx->pn_enc_0rtt != NULL)
+                    {
+                        picoquic_pn_enc_free(cnx->pn_enc_0rtt);
+                        cnx->pn_enc_0rtt = NULL;
+                    }
+
                     if (ret == 0) {
                         ret = picoquic_setup_cleartext_aead_contexts(cnx);
                     }
@@ -1027,6 +1045,48 @@ void picoquic_delete_cnx(picoquic_cnx_t* cnx)
             cnx->aead_encrypt_ctx = NULL;
         }
 
+        if (cnx->aead_0rtt_decrypt_ctx != NULL) {
+            picoquic_aead_free(cnx->aead_0rtt_decrypt_ctx);
+            cnx->aead_0rtt_decrypt_ctx = NULL;
+        }
+
+        if (cnx->aead_0rtt_encrypt_ctx != NULL) {
+            picoquic_aead_free(cnx->aead_0rtt_encrypt_ctx);
+            cnx->aead_0rtt_encrypt_ctx = NULL;
+        }
+
+        if (cnx->pn_enc != NULL)
+        {
+            picoquic_pn_enc_free(cnx->pn_enc);
+            cnx->pn_enc = NULL;
+        }
+
+        if (cnx->pn_dec != NULL)
+        {
+            picoquic_pn_enc_free(cnx->pn_dec);
+            cnx->pn_dec = NULL;
+        }
+
+        if (cnx->pn_enc_cleartext != NULL)
+        {
+            picoquic_pn_enc_free(cnx->pn_enc_cleartext);
+            cnx->pn_enc_cleartext = NULL;
+        }
+
+        if (cnx->pn_dec_cleartext != NULL)
+        {
+            picoquic_pn_enc_free(cnx->pn_dec_cleartext);
+            cnx->pn_dec_cleartext = NULL;
+        }
+
+        if (cnx->pn_enc_0rtt != NULL)
+        {
+            picoquic_pn_enc_free(cnx->pn_enc_0rtt);
+            cnx->pn_enc_0rtt = NULL;
+        }
+
+
+
         while (cnx->retransmit_newest != NULL) {
             picoquic_dequeue_retransmit_packet(cnx, cnx->retransmit_newest, 1);
         }
diff --git a/picoquic/tls_api.c b/picoquic/tls_api.c
index 640638dbd032bf71c74448c3cfeb766571ac3dc2..84015de63481c45897b710cbaa06f09cfb6200d3 100644
--- a/picoquic/tls_api.c
+++ b/picoquic/tls_api.c
@@ -753,6 +753,39 @@ int picoquic_initialize_stream_zero(picoquic_cnx_t* cnx)
     return ret;
 }
 
+/*
+ * Packet number encryption and decryption utilities
+ */
+
+void picoquic_pn_enc_free(void * pn_enc)
+{
+    ptls_cipher_free((ptls_cipher_context_t*) pn_enc);
+}
+
+void * picoquic_pn_enc_create(
+    ptls_aead_algorithm_t* aead, ptls_hash_algorithm_t* hash, uint8_t * secret, const char* base_label)
+{
+    int ret = 0;
+    uint8_t key[256]; 
+    ptls_cipher_context_t *pn_enc = ptls_cipher_new(aead->ctr_cipher, 1, key);
+
+    /*
+     * Derive the key by extending the secret for PN encryption 
+     */
+    ret = ptls_hkdf_expand_label(
+        hash, key, aead->key_size, ptls_iovec_init(secret, hash->digest_size), "pn", ptls_iovec_init(NULL, 0), base_label);
+
+    /*
+     * Create the context. This is always an encryptng context, because of the stream cipher mode.
+     */
+    if (ret == 0)
+    {
+        pn_enc = ptls_cipher_new(aead->ctr_cipher, 1, key);
+    }
+
+    return (void *)pn_enc;
+}
+
 /*
 
 Using function ptls_aead_new(cipher->aead, cipher->hash, is_enc, pp->secret);
@@ -809,6 +842,7 @@ int picoquic_setup_0RTT_aead_contexts(picoquic_cnx_t* cnx, int is_server)
         if (ret == 0) {
             cnx->aead_0rtt_decrypt_ctx = (void*)
                 ptls_aead_new(cipher->aead, cipher->hash, 0, secret, NULL);
+            cnx->pn_enc_0rtt = picoquic_pn_enc_create(cipher->aead, cipher->hash, secret, NULL);
         }
     }
 
@@ -836,6 +870,7 @@ int picoquic_setup_1RTT_aead_contexts(picoquic_cnx_t* cnx, int is_server)
         if (ret == 0) {
             cnx->aead_encrypt_ctx = (void*)
                 ptls_aead_new(cipher->aead, cipher->hash, 1, secret, NULL);
+            cnx->pn_enc = picoquic_pn_enc_create(cipher->aead, cipher->hash, secret, NULL);
 
             if (cnx->aead_encrypt_ctx == NULL) {
                 ret = PICOQUIC_ERROR_MEMORY;
@@ -854,6 +889,7 @@ int picoquic_setup_1RTT_aead_contexts(picoquic_cnx_t* cnx, int is_server)
 
         if (ret == 0) {
             cnx->aead_decrypt_ctx = (void*)ptls_aead_new(cipher->aead, cipher->hash, 0, secret, NULL);
+            cnx->pn_dec = picoquic_pn_enc_create(cipher->aead, cipher->hash, secret, NULL);
 
             if (cnx->aead_decrypt_ctx == NULL) {
                 ret = -1;
@@ -1117,6 +1153,9 @@ int picoquic_setup_cleartext_aead_contexts(picoquic_cnx_t* cnx)
                 ptls_aead_new(aead, algo, 0, secret2, NULL);
             cnx->aead_de_encrypt_cleartext_ctx = (void*)
                 ptls_aead_new(aead, algo, 0, secret1, NULL);
+
+            cnx->pn_enc_cleartext = picoquic_pn_enc_create(aead, algo, secret1, NULL);
+            cnx->pn_dec_cleartext = picoquic_pn_enc_create(aead, algo, secret2, NULL);
         }
     }
 
diff --git a/picoquic/tls_api.h b/picoquic/tls_api.h
index 42c143c92979111519c550aeb1f80ffc349eefe8..8f2b0152eef4dd967dc7c79975add862a50f0fad 100644
--- a/picoquic/tls_api.h
+++ b/picoquic/tls_api.h
@@ -77,6 +77,8 @@ size_t picoquic_aead_cleartext_de_encrypt(picoquic_cnx_t* cnx, uint8_t* output,
 
 void picoquic_aead_free(void* aead_context);
 
+void picoquic_pn_enc_free(void * pn_enc);
+
 int picoquic_create_cnxid_reset_secret(picoquic_quic_t* quic, uint64_t cnx_id,
     uint8_t reset_secret[PICOQUIC_RESET_SECRET_SIZE]);
 
diff --git a/picoquic_t/picoquic_t.c b/picoquic_t/picoquic_t.c
index d77b16cc8e1be876453b42d6cf120dc99382bfac..1e68bdf1ff0ec44e9c1715fad1aa682785e70f63 100644
--- a/picoquic_t/picoquic_t.c
+++ b/picoquic_t/picoquic_t.c
@@ -83,7 +83,9 @@ static picoquic_test_def_t test_table[] = {
     { "mtu_discovery", mtu_discovery_test },
     { "spurious_retransmit", spurious_retransmit_test },
     { "wrong_keyshare", wrong_keyshare_test },
-    { "pn_ctr", pn_ctr_test}
+    { "pn_ctr", pn_ctr_test},
+    { "cleartext_pn_enc", cleartext_pn_enc_test},
+    { "pn_enc_1rtt", pn_enc_1rtt_test }
 };
 
 static size_t nb_tests = sizeof(test_table) / sizeof(picoquic_test_def_t);
diff --git a/picoquictest/cleartext_aead_test.c b/picoquictest/cleartext_aead_test.c
index 4b9aab26b228f352e5643f0cf46ea2e81489d4f9..988060acccf932aa9877df329b0de9e4d1ff1ba3 100644
--- a/picoquictest/cleartext_aead_test.c
+++ b/picoquictest/cleartext_aead_test.c
@@ -270,3 +270,124 @@ int pn_ctr_test()
 
     return ret;
 }
+
+/*
+* Test that the generated encryption and decryption produce
+* the same results.
+*/
+
+int test_one_pn_enc_pair(uint8_t * seqnum, size_t seqnum_len, void * pn_enc, void * pn_dec, uint8_t * sample)
+{
+    int ret = 0;
+    uint8_t encoded[32];
+    uint8_t decoded[32];
+
+    ptls_cipher_init((ptls_cipher_context_t *)pn_enc, sample);
+    ptls_cipher_encrypt((ptls_cipher_context_t *)pn_enc, encoded, seqnum, seqnum_len);
+
+    ptls_cipher_init((ptls_cipher_context_t *)pn_dec, sample);
+    ptls_cipher_encrypt((ptls_cipher_context_t *)pn_dec, decoded, encoded, seqnum_len);
+
+    if (memcmp(seqnum, decoded, seqnum_len) != 0)
+    {
+        ret = -1;
+    }
+
+    return ret;
+}
+
+/*
+ * Test that the key generated for cleartext PN encryption on
+ * client and server produce the correct results.
+ */
+
+int cleartext_pn_enc_test()
+{
+    int ret = 0;
+    struct sockaddr_in test_addr_c, test_addr_s;
+    picoquic_cnx_t* cnx_client = NULL;
+    picoquic_cnx_t* cnx_server = NULL;
+    picoquic_quic_t* qclient = picoquic_create(8, NULL, NULL, NULL, NULL, NULL,
+        NULL, NULL, NULL, 0, NULL, NULL, NULL, 0);
+    picoquic_quic_t* qserver = picoquic_create(8,
+#ifdef _WINDOWS
+#ifdef _WINDOWS64
+        "..\\..\\certs\\cert.pem", "..\\..\\certs\\key.pem",
+#else
+        "..\\certs\\cert.pem", "..\\certs\\key.pem",
+#endif
+#else
+        "certs/cert.pem", "certs/key.pem",
+#endif
+        "test", NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, 0);
+    if (qclient == NULL || qserver == NULL) {
+        ret = -1;
+    }
+
+    if (ret == 0) {
+        memset(&test_addr_c, 0, sizeof(struct sockaddr_in));
+        test_addr_c.sin_family = AF_INET;
+        memcpy(&test_addr_c.sin_addr, addr1, 4);
+        test_addr_c.sin_port = 12345;
+
+        cnx_client = picoquic_create_cnx(qclient, 0,
+            (struct sockaddr*)&test_addr_c, 0, 0, NULL, NULL, 1);
+        if (cnx_client == NULL) {
+            ret = -1;
+        }
+    }
+
+    if (ret == 0) {
+
+        memset(&test_addr_s, 0, sizeof(struct sockaddr_in));
+        test_addr_s.sin_family = AF_INET;
+        memcpy(&test_addr_s.sin_addr, addr2, 4);
+        test_addr_s.sin_port = 4433;
+
+        cnx_server = picoquic_create_cnx(qserver, cnx_client->initial_cnxid,
+            (struct sockaddr*)&test_addr_s, 0,
+            cnx_client->proposed_version, NULL, NULL, 0);
+
+        if (cnx_server == NULL) {
+            ret = -1;
+        }
+    }
+
+    /* Try to encrypt a sequence number */
+    if (ret == 0) {
+        uint8_t seq_num_1[4] = { 0xde, 0xad, 0xbe, 0xef };
+        uint8_t sample_1[16] = {
+            0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+            0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a };
+        uint8_t seq_num_2[4] = { 0xba, 0xba, 0xc0, 0x0l };
+        uint8_t sample_2[16] = {
+            0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+            0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96};
+
+        ret = test_one_pn_enc_pair(seq_num_1, 4, cnx_client->pn_enc_cleartext, cnx_server->pn_dec_cleartext, sample_1);
+
+        if (ret == 0)
+        {
+            ret = test_one_pn_enc_pair(seq_num_2, 4, cnx_server->pn_enc_cleartext, cnx_client->pn_dec_cleartext, sample_2);
+        }
+    }
+
+    if (cnx_client != NULL) {
+        picoquic_delete_cnx(cnx_client);
+    }
+
+    if (cnx_server != NULL) {
+        picoquic_delete_cnx(cnx_server);
+    }
+
+    if (qclient != NULL) {
+        picoquic_free(qclient);
+    }
+
+    if (qserver != NULL) {
+        picoquic_free(qserver);
+    }
+
+    return ret;
+}
+
diff --git a/picoquictest/picoquictest.h b/picoquictest/picoquictest.h
index 437eb5eb3c82187dddb3e8812146757a85a125bf..ca0937909fa9d48640a3eeb2f1cc1a04269105e6 100644
--- a/picoquictest/picoquictest.h
+++ b/picoquictest/picoquictest.h
@@ -81,6 +81,8 @@ int mtu_discovery_test();
 int spurious_retransmit_test();
 int wrong_keyshare_test();
 int pn_ctr_test();
+int cleartext_pn_enc_test();
+int pn_enc_1rtt_test();
 
 #ifdef __cplusplus
 }
diff --git a/picoquictest/picoquictest_internal.h b/picoquictest/picoquictest_internal.h
index 515b1dd42650dc45e91b3cf3522accdb75059e6f..2e5d85f0215c0da6dda42b2865408ed229ba1f98 100644
--- a/picoquictest/picoquictest_internal.h
+++ b/picoquictest/picoquictest_internal.h
@@ -73,6 +73,9 @@ picoquictest_sim_packet_t* picoquictest_sim_link_dequeue(picoquictest_sim_link_t
 
 void picoquictest_sim_link_submit(picoquictest_sim_link_t* link, picoquictest_sim_packet_t* packet,
     uint64_t current_time);
+
+int test_one_pn_enc_pair(uint8_t * seqnum, size_t seqnum_len, void * pn_enc, void * pn_dec, uint8_t * sample);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/picoquictest/tls_api_test.c b/picoquictest/tls_api_test.c
index 3c62cd0234105ca40c84e114c67f3cdcbbdc487b..fdd78da112ad24fc8a7fbc70a43d649a8a156880 100644
--- a/picoquictest/tls_api_test.c
+++ b/picoquictest/tls_api_test.c
@@ -1942,3 +1942,53 @@ int wrong_keyshare_test()
 
     return ret;
 }
+
+/*
+* Set up a connection, and verify
+* that the key generated for PN encryption on
+* client and server produce the correct results.
+*/
+
+int pn_enc_1rtt_test()
+{
+    uint64_t loss_mask = 0;
+    uint64_t simulated_time = 0;
+    picoquic_test_tls_api_ctx_t* test_ctx = NULL;
+    int ret = tls_api_init_ctx(&test_ctx, 0, NULL, NULL, &simulated_time, NULL);
+
+    if (ret == 0) {
+        ret = tls_api_connection_loop(test_ctx, &loss_mask, 0, &simulated_time);
+    }
+
+    if (ret == 0)
+    {
+        /* Try to encrypt a sequence number */
+        if (ret == 0) {
+            uint8_t seq_num_1[4] = { 0xde, 0xad, 0xbe, 0xef };
+            uint8_t sample_1[16] = {
+                0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+                0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a };
+            uint8_t seq_num_2[4] = { 0xba, 0xba, 0xc0, 0x0l };
+            uint8_t sample_2[16] = {
+                0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+                0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96 };
+
+            for (int i = 1; i < 4; i *= 2)
+            {
+                ret = test_one_pn_enc_pair(seq_num_1, 4, test_ctx->cnx_client->pn_enc, test_ctx->cnx_server->pn_dec, sample_1);
+
+                if (ret == 0)
+                {
+                    ret = test_one_pn_enc_pair(seq_num_2, 4, test_ctx->cnx_server->pn_enc, test_ctx->cnx_client->pn_dec, sample_2);
+                }
+            }
+        }
+    }
+
+    if (test_ctx != NULL) {
+        tls_api_delete_ctx(test_ctx);
+        test_ctx = NULL;
+    }
+
+    return ret;
+}
\ No newline at end of file