From 4f3bbfa21cdc5cd3cc937e2e1405a289c9005edb Mon Sep 17 00:00:00 2001
From: huitema <huitema@huitema.net>
Date: Sun, 8 Jul 2018 22:07:53 -0700
Subject: [PATCH] Implement the retry token, and fix a number of issues in the
 parsing of headers and packets.

---
 UnitTest1/unittest1.cpp        |  76 ++++---
 picoquic/frames.c              |   4 +-
 picoquic/packet.c              | 388 +++++++++++++++++----------------
 picoquic/picoquic_internal.h   |  19 +-
 picoquic/quicctx.c             |  67 +++---
 picoquic/sender.c              | 256 ++++++++--------------
 picoquic_t/picoquic_t.c        |  10 +-
 picoquictest/parseheadertest.c |  21 +-
 picoquictest/tls_api_test.c    |  80 ++++---
 9 files changed, 452 insertions(+), 469 deletions(-)

diff --git a/UnitTest1/unittest1.cpp b/UnitTest1/unittest1.cpp
index fd36b540..e4ac1509 100644
--- a/UnitTest1/unittest1.cpp
+++ b/UnitTest1/unittest1.cpp
@@ -30,12 +30,19 @@ namespace UnitTest1
 	TEST_CLASS(UnitTest1)
 	{
 	public:
-		    TEST_METHOD(test_picohash)
-		    {
+	    TEST_METHOD(test_picohash)
+	    {
             int ret = picohash_test();
 
             Assert::AreEqual(ret, 0);
-		    }
+	    }
+
+        TEST_METHOD(splay)
+        {
+            int ret = splay_test();
+
+            Assert::AreEqual(ret, 0);
+        }
 
         TEST_METHOD(test_cnxcreation)
         {
@@ -72,23 +79,23 @@ namespace UnitTest1
             Assert::AreEqual(ret, 0);
         }
 
-        TEST_METHOD(test_sack)
+        TEST_METHOD(test_float16)
         {
-            int ret = sacktest();
+            int ret = float16test();
 
             Assert::AreEqual(ret, 0);
         }
 
-        TEST_METHOD(test_float16)
+        TEST_METHOD(test_varints)
         {
-            int ret = float16test();
+            int ret = varint_test();
 
             Assert::AreEqual(ret, 0);
         }
 
-        TEST_METHOD(test_varints)
+        TEST_METHOD(test_sack)
         {
-            int ret = varint_test();
+            int ret = sacktest();
 
             Assert::AreEqual(ret, 0);
         }
@@ -106,6 +113,13 @@ namespace UnitTest1
 
             Assert::AreEqual(ret, 0);
         }
+
+        TEST_METHOD(test_logger)
+        {
+            int ret = logger_test();
+
+            Assert::AreEqual(ret, 0);
+        }
         
         TEST_METHOD(test_TlsStreamFrame)
         {
@@ -146,6 +160,20 @@ namespace UnitTest1
         {
             int ret = sim_link_test();
 
+            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);
         }
 
@@ -352,13 +380,6 @@ namespace UnitTest1
           Assert::AreEqual(ret, 0);
         }
 
-        TEST_METHOD(test_logger)
-        {
-            int ret = logger_test();
-
-            Assert::AreEqual(ret, 0);
-        }
-
         TEST_METHOD(test_sockets)
         {
             int ret = socket_test();
@@ -429,20 +450,6 @@ 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);
-        }
-
         TEST_METHOD(test_tls_zero_share)
         {
             int ret = tls_zero_share_test();
@@ -582,14 +589,5 @@ namespace UnitTest1
 
             Assert::AreEqual(ret, 0);
         }
-
-
-        TEST_METHOD(splay)
-        {
-            int ret = splay_test();
-
-            Assert::AreEqual(ret, 0);
-        }
-        
     };
 }
diff --git a/picoquic/frames.c b/picoquic/frames.c
index bc73918b..1e6f7a57 100644
--- a/picoquic/frames.c
+++ b/picoquic/frames.c
@@ -2090,7 +2090,7 @@ int picoquic_decode_frames(picoquic_cnx_t* cnx, uint8_t* bytes,
             }
 
             bytes = picoquic_decode_stream_frame(cnx, bytes, bytes_max, current_time);
-            pkt_ctx->ack_needed = 1;
+            ack_needed = 1;
 
         } else if (first_byte == picoquic_frame_type_ack) {
             bytes = picoquic_decode_ack_frame(cnx, bytes, bytes_max, current_time, epoch);
@@ -2187,7 +2187,7 @@ int picoquic_decode_frames(picoquic_cnx_t* cnx, uint8_t* bytes,
         pkt_ctx->ack_needed = 1;
     }
 
-    return bytes != NULL ? 0 : 1;
+    return bytes != NULL ? 0 : PICOQUIC_ERROR_DETECTED;
 }
 
 /*
diff --git a/picoquic/packet.c b/picoquic/packet.c
index d2ebb488..beb7db15 100644
--- a/picoquic/packet.c
+++ b/picoquic/packet.c
@@ -108,9 +108,6 @@ int picoquic_parse_packet_header(
                         length - ph->offset, &payload_length);
 
                     ph->version_index = picoquic_get_version_index(ph->vn);
-                    if ((picoquic_supported_versions[ph->version_index].version_flags&picoquic_version_use_pn_encryption) == 0) {
-                        pn_length_clear = 4;
-                    }
 
                     if (var_length <= 0 || ph->offset + var_length + pn_length_clear + payload_length > length ||
                         ph->version_index < 0) {
@@ -122,12 +119,6 @@ int picoquic_parse_packet_header(
                         ph->offset += var_length;
                         ph->pn_offset = ph->offset;
 
-                        if ((picoquic_supported_versions[ph->version_index].version_flags&picoquic_version_use_pn_encryption) == 0) {
-                            ph->pn = PICOPARSE_32(bytes + ph->offset);
-                            ph->pnmask = 0xFFFFFFFF00000000ull;
-                            ph->offset += 4;
-                        }
-
                         /* Retrieve the connection context */
                         if (*pcnx == NULL) {
                             *pcnx = picoquic_cnx_by_id(quic, ph->dest_cnx_id);
@@ -265,10 +256,9 @@ int picoquic_parse_packet_header(
                  else {
                      ph->ptype = picoquic_packet_1rtt_protected_phi1;
                  }
-
-		 ph->has_spin_bit = 1;
+                 ph->has_spin_bit = 1;
                  ph->spin = (bytes[0] >> 2) & 1;
-		 ph->spin_vec = bytes[0] & 0x03 ;
+                 ph->spin_vec = bytes[0] & 0x03 ;
 
                  ph->pn_offset = ph->offset;
                  ph->pn = 0;
@@ -293,24 +283,6 @@ int picoquic_parse_packet_header(
     return ret;
 }
 
-/* Check whether a packet was sent in clear text */
-int picoquic_is_packet_encrypted(picoquic_packet_type_enum ptype)
-{
-    int ret = 0;
-    switch (ptype) {
-    case picoquic_packet_0rtt_protected:
-    case picoquic_packet_1rtt_protected_phi0:
-    case picoquic_packet_1rtt_protected_phi1:
-        ret = 1;
-        break;
-    default:
-        ret = 0;
-        break;
-    }
-
-    return ret;
-}
-
 /* The packet number logic */
 uint64_t picoquic_get_packet_number64(uint64_t highest, uint64_t mask, uint32_t pn)
 {
@@ -338,123 +310,121 @@ uint64_t picoquic_get_packet_number64(uint64_t highest, uint64_t mask, uint32_t
 }
 
 /*
+ * Decrypt the incoming packet.
  * Apply packet number decryption. This may require updating the 
  * sequence number and the offset 
  */
 size_t  picoquic_decrypt_packet(picoquic_cnx_t* cnx,
-    uint8_t* bytes, size_t length, picoquic_packet_header* ph, 
+    uint8_t* bytes, size_t packet_length, picoquic_packet_header* ph, 
     void * pn_enc, void* aead_context, int * already_received)
 {
-    /*
-     * If needed, decrypt the packet number, in place.
-     */
-    size_t decoded = length + 32;
+    size_t decoded = packet_length + 32; /* by conventions, values larger than input indicate error */
+    size_t length = ph->offset + ph->payload_length; /* this may change after decrypting the PN */
 
     if (already_received != NULL) {
         *already_received = 0;
     }
-
-    if ((picoquic_supported_versions[cnx->version_index].version_flags&picoquic_version_use_pn_encryption) != 0)
+    
+    if (pn_enc != NULL)
     {
-        if (pn_enc != NULL)
-        {
-            /* The header length is not yet known, will only be known after the sequence number is decrypted */
-            size_t encrypted_length = 4;
-            size_t sample_offset = ph->pn_offset + encrypted_length;
-            size_t aead_checksum_length = picoquic_aead_get_checksum_length(aead_context);
-            uint8_t decoded_pn_bytes[4];
+        /* The header length is not yet known, will only be known after the sequence number is decrypted */
+        size_t encrypted_length = 4;
+        size_t sample_offset = ph->pn_offset + encrypted_length;
+        size_t aead_checksum_length = picoquic_aead_get_checksum_length(aead_context);
+        uint8_t decoded_pn_bytes[4];
 
-            if (sample_offset + aead_checksum_length > length)
-            {
-                sample_offset = length - aead_checksum_length;
-                if (ph->pn_offset < sample_offset) {
-                    encrypted_length = sample_offset - ph->pn_offset;
-                }
-                else {
-                    encrypted_length = 0;
-                }
+        if (sample_offset + aead_checksum_length > length)
+        {
+            sample_offset = length - aead_checksum_length;
+            if (ph->pn_offset < sample_offset) {
+                encrypted_length = sample_offset - ph->pn_offset;
             }
-            if (encrypted_length > 0)
-            {
-                if (picoquic_supported_versions[ph->version_index].version_header_encoding == picoquic_version_header_11) {
-                    /* Decode */
-                    picoquic_pn_encrypt(pn_enc, bytes + sample_offset, decoded_pn_bytes, bytes + ph->pn_offset, encrypted_length);
-                    /* Packet encoding is varint, specialized for sequence number */
-                    switch (bytes[0] & 0x03) {
-                    case 0x00:/* single byte encoding */
-                        ph->pn = decoded_pn_bytes[0];
-                        ph->pnmask = 0xFFFFFFFFFFFFFF00ull;
-                        ph->offset = ph->pn_offset + 1;
-                        ph->payload_length -= 1;
-                        break;
-                    case 0x01: /* two byte encoding */
-                        ph->pn = PICOPARSE_16(decoded_pn_bytes);
-                        ph->pnmask = 0xFFFFFFFFFFFF0000ull;
-                        ph->offset = ph->pn_offset + 2;
-                        ph->payload_length -= 2;
-                        break;
-                    case 0x02:
-                        ph->pn = PICOPARSE_32(decoded_pn_bytes);
-                        ph->pnmask = 0xFFFFFFFF00000000ull;
-                        ph->offset = ph->pn_offset + 4;
-                        ph->payload_length -= 4;
-                        break;
-                    default:
-                        /* Invalid packet format. Avoid crash! */
-                        ph->pn = 0xFFFFFFFF;
-                        ph->pnmask = 0xFFFFFFFF00000000ull;
-                        ph->offset = ph->pn_offset;
-                        break;
-                    }
-                }
-                else {
-                    /* Decode */
-                    picoquic_pn_encrypt(pn_enc, bytes + sample_offset, decoded_pn_bytes, bytes + ph->pn_offset, encrypted_length);
-                    /* Packet encoding is varint, specialized for sequence number */
-                    switch (decoded_pn_bytes[0] & 0xC0) {
-                    case 0x00:
-                    case 0x40: /* single byte encoding */
-                        ph->pn = decoded_pn_bytes[0] & 0x7F;
-                        ph->pnmask = 0xFFFFFFFFFFFFFF80ull;
-                        ph->offset = ph->pn_offset + 1;
-                        ph->payload_length -= 1;
-                        break;
-                    case 0x80: /* two byte encoding */
-                        ph->pn = (PICOPARSE_16(decoded_pn_bytes)) & 0x3FFF;
-                        ph->pnmask = 0xFFFFFFFFFFFFC000ull;
-                        ph->offset = ph->pn_offset + 2;
-                        ph->payload_length -= 2;
-                        break;
-                    case 0xC0:
-                        ph->pn = (PICOPARSE_32(decoded_pn_bytes)) & 0x3FFFFFFF;
-                        ph->pnmask = 0xFFFFFFFFC0000000ull;
-                        ph->offset = ph->pn_offset + 4;
-                        ph->payload_length -= 4;
-                        break;
-                    }
+            else {
+                encrypted_length = 0;
+            }
+        }
+        if (encrypted_length > 0)
+        {
+            if (picoquic_supported_versions[ph->version_index].version_header_encoding == picoquic_version_header_11) {
+                /* Decode */
+                picoquic_pn_encrypt(pn_enc, bytes + sample_offset, decoded_pn_bytes, bytes + ph->pn_offset, encrypted_length);
+                /* Packet encoding is varint, specialized for sequence number */
+                switch (bytes[0] & 0x03) {
+                case 0x00:/* single byte encoding */
+                    ph->pn = decoded_pn_bytes[0];
+                    ph->pnmask = 0xFFFFFFFFFFFFFF00ull;
+                    ph->offset = ph->pn_offset + 1;
+                    ph->payload_length -= 1;
+                    break;
+                case 0x01: /* two byte encoding */
+                    ph->pn = PICOPARSE_16(decoded_pn_bytes);
+                    ph->pnmask = 0xFFFFFFFFFFFF0000ull;
+                    ph->offset = ph->pn_offset + 2;
+                    ph->payload_length -= 2;
+                    break;
+                case 0x02:
+                    ph->pn = PICOPARSE_32(decoded_pn_bytes);
+                    ph->pnmask = 0xFFFFFFFF00000000ull;
+                    ph->offset = ph->pn_offset + 4;
+                    ph->payload_length -= 4;
+                    break;
+                default:
+                    /* Invalid packet format. Avoid crash! */
+                    ph->pn = 0xFFFFFFFF;
+                    ph->pnmask = 0xFFFFFFFF00000000ull;
+                    ph->offset = ph->pn_offset;
+                    break;
                 }
-                if (ph->offset > ph->pn_offset) {
-                    memcpy(bytes + ph->pn_offset, decoded_pn_bytes, ph->offset - ph->pn_offset);
+            }
+            else {
+                /* Decode */
+                picoquic_pn_encrypt(pn_enc, bytes + sample_offset, decoded_pn_bytes, bytes + ph->pn_offset, encrypted_length);
+                /* Packet encoding is varint, specialized for sequence number */
+                switch (decoded_pn_bytes[0] & 0xC0) {
+                case 0x00:
+                case 0x40: /* single byte encoding */
+                    ph->pn = decoded_pn_bytes[0] & 0x7F;
+                    ph->pnmask = 0xFFFFFFFFFFFFFF80ull;
+                    ph->offset = ph->pn_offset + 1;
+                    ph->payload_length -= 1;
+                    break;
+                case 0x80: /* two byte encoding */
+                    ph->pn = (PICOPARSE_16(decoded_pn_bytes)) & 0x3FFF;
+                    ph->pnmask = 0xFFFFFFFFFFFFC000ull;
+                    ph->offset = ph->pn_offset + 2;
+                    ph->payload_length -= 2;
+                    break;
+                case 0xC0:
+                    ph->pn = (PICOPARSE_32(decoded_pn_bytes)) & 0x3FFFFFFF;
+                    ph->pnmask = 0xFFFFFFFFC0000000ull;
+                    ph->offset = ph->pn_offset + 4;
+                    ph->payload_length -= 4;
+                    break;
                 }
-            } else {
-                /* Invalid packet format. Avoid crash! */
-                ph->pn = 0xFFFFFFFF;
-                ph->pnmask = 0xFFFFFFFF00000000ull;
-                ph->offset = ph->pn_offset;
-
-                DBG_PRINTF("Invalid packet format, type: %d, epoch: %d, pc: %d, pn: %d\n",
-                    ph->ptype, ph->epoch, ph->pc, (int) ph->pn);
             }
-        } else {
-            /* The pn_enc algorithm was not initialized. Avoid crash! */
+            if (ph->offset > ph->pn_offset) {
+                memcpy(bytes + ph->pn_offset, decoded_pn_bytes, ph->offset - ph->pn_offset);
+            }
+        }
+        else {
+            /* Invalid packet format. Avoid crash! */
             ph->pn = 0xFFFFFFFF;
             ph->pnmask = 0xFFFFFFFF00000000ull;
             ph->offset = ph->pn_offset;
 
-            DBG_PRINTF("PN dec not ready, type: %d, epoch: %d, pc: %d, pn: %d\n",
+            DBG_PRINTF("Invalid packet format, type: %d, epoch: %d, pc: %d, pn: %d\n",
                 ph->ptype, ph->epoch, ph->pc, (int)ph->pn);
         }
     }
+    else {
+        /* The pn_enc algorithm was not initialized. Avoid crash! */
+        ph->pn = 0xFFFFFFFF;
+        ph->pnmask = 0xFFFFFFFF00000000ull;
+        ph->offset = ph->pn_offset;
+
+        DBG_PRINTF("PN dec not ready, type: %d, epoch: %d, pc: %d, pn: %d\n",
+            ph->ptype, ph->epoch, ph->pc, (int)ph->pn);
+    }
 
     /* Build a packet number to 64 bits */
     ph->pn64 = picoquic_get_packet_number64(
@@ -466,10 +436,32 @@ size_t  picoquic_decrypt_packet(picoquic_cnx_t* cnx,
         /* Set error type: already received */
         *already_received = 1;
     } else {
-        /* Attempt to decrypt the packet */
-        decoded = picoquic_aead_decrypt_generic(bytes + ph->offset,
-            bytes + ph->offset, length - ph->offset, ph->pn64, bytes, ph->offset, aead_context);
+        /* special case of the initial packets. They contain a retry token between the header
+         * and the encrypted payload */
+
+        if (ph->ptype == picoquic_packet_initial) {
+            uint64_t tok_len = 0;
+            size_t l_tok_len = picoquic_varint_decode(bytes + ph->offset, length - ph->offset, &tok_len);
+
+            if (l_tok_len == 0) {
+                /* packet is malformed */
+            }
+            else {
+                ph->token_length = (uint32_t)tok_len;
+                ph->token_offset = ph->offset + 1;
+                ph->offset += 1 + (size_t)tok_len;
+
+                if (ph->offset  + ph->payload_length > packet_length) {
+                    ph->offset = length;
+                    ph->token_offset = 0;
+                    ph->token_length = 0;
+                } 
+            }
+        }
     }
+    
+    decoded = picoquic_aead_decrypt_generic(bytes + ph->offset,
+                bytes + ph->offset, ph->payload_length, ph->pn64, bytes, ph->offset, aead_context);
 
     return decoded;
 }
@@ -494,6 +486,7 @@ int picoquic_parse_header_and_decrypt(
     int new_ctx_created = 0;
 
     if (ret == 0) {
+        /* TODO: clarify length, payload length, packet length -- special case of initial packet */
         length = ph->offset + ph->payload_length;
         *consumed = length;
 
@@ -510,6 +503,8 @@ int picoquic_parse_header_and_decrypt(
             }
         }
 
+        /* TODO: replace switch by reference to epoch */
+
         if (*pcnx != NULL) {
             if (receiving) {
                 switch (ph->ptype) {
@@ -517,14 +512,14 @@ int picoquic_parse_header_and_decrypt(
                     /* Packet is not encrypted */
                     break;
                 case picoquic_packet_initial:
-                    decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
+                    decoded_length = picoquic_decrypt_packet(*pcnx, bytes, packet_length, ph,
                         (*pcnx)->crypto_context[0].pn_dec, 
                         (*pcnx)->crypto_context[0].aead_decrypt, &already_received);
+                    length = ph->offset + ph->payload_length;
+                    *consumed = length;
                     break;
                 case picoquic_packet_retry:
-                    decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
-                        (*pcnx)->crypto_context[0].pn_dec,
-                        (*pcnx)->crypto_context[0].aead_decrypt, &already_received);
+                    /* packet is not encrypted */
                     break;
                 case picoquic_packet_handshake:
                     decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
@@ -560,12 +555,12 @@ int picoquic_parse_header_and_decrypt(
                     /* Packet is not encrypted */
                     break;
                 case picoquic_packet_initial:
-                    decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
+                    decoded_length = picoquic_decrypt_packet(*pcnx, bytes, packet_length, ph,
                         (*pcnx)->crypto_context[0].pn_enc, (*pcnx)->crypto_context[0].aead_de_encrypt, NULL);
+                    length = ph->offset + packet_length;
                     break;
                 case picoquic_packet_retry:
-                    decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
-                        (*pcnx)->crypto_context[0].pn_enc, (*pcnx)->crypto_context[0].aead_de_encrypt, NULL);
+                    /* packet is not encrypted */
                     break;
                 case picoquic_packet_handshake:
                     decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
@@ -613,6 +608,7 @@ int picoquic_parse_header_and_decrypt(
 
     return ret;
 }
+
 /*
  * Processing of a version renegotiation packet.
  *
@@ -781,9 +777,7 @@ void picoquic_queue_stateless_reset(picoquic_cnx_t* cnx,
         if (picoquic_prepare_crypto_hs_frame(cnx, 0 /* assume stateless reply in initial state */,
             bytes + byte_index, PICOQUIC_MAX_PACKET_SIZE - byte_index - checksum_length, &data_bytes)
             == 0) {
-            uint64_t sequence_number =
-                ((picoquic_supported_versions[cnx->version_index].version_flags&picoquic_version_use_pn_encryption) != 0)
-                ? 0 : ph->pn;
+            uint64_t sequence_number = 0;
 
             byte_index += (uint32_t)data_bytes;
             /* AEAD Encrypt, to the send buffer */
@@ -820,9 +814,17 @@ int picoquic_incoming_initial(
     uint64_t current_time)
 {
     int ret = 0;
+    size_t l_tok_len = 0;
+    uint64_t tok_len = 0;
+    size_t extra_offset = 0;
+
+    /* TODO: if there is a problem with the retry token, schedule a retry packet */
 
-    ret = picoquic_decode_frames(cnx,
-        bytes + ph->offset, ph->payload_length, ph->epoch, current_time);
+    /* decode the incoming frames */
+    if (ret == 0) {
+        ret = picoquic_decode_frames(cnx,
+            bytes + ph->offset + extra_offset, ph->payload_length - extra_offset, ph->epoch, current_time);
+    }
 
     /* processing of client initial packet */
     if (ret == 0) {
@@ -830,6 +832,7 @@ int picoquic_incoming_initial(
         /* TODO: find path to send data produced by TLS. */
         ret = picoquic_tls_stream_process(cnx);
 
+        /* TODO: remove the stateless reset code, not needed with retry token */
         if (cnx->cnx_state == picoquic_state_server_send_hrr) {
             picoquic_queue_stateless_reset(cnx, ph, addr_from, addr_to, if_index_to, current_time);
             cnx->cnx_state = picoquic_state_disconnected;
@@ -853,7 +856,7 @@ int picoquic_incoming_initial(
 }
 
 /*
- * Processing of a server stateless packet.
+ * Processing of a server retry
  *
  * The packet number and connection ID fields echo the corresponding fields from the 
  * triggering client packet. This allows a client to verify that the server received its packet.
@@ -862,74 +865,79 @@ int picoquic_incoming_initial(
  * Receiving another Client Initial packet implicitly acknowledges a Server Stateless Retry packet.
  *
  * After receiving a Server Stateless Retry packet, the client uses a new Client Initial packet 
- * containing the next cryptographic handshake message. The client retains the state of its 
- * cryptographic handshake, but discards all transport state. In effect, the next cryptographic
- * handshake message is sent on a new connection. The new Client Initial packet is sent in a 
- * packet with a newly randomized packet number and starting at a stream offset of 0.
- *
- * Continuing the cryptographic handshake is necessary to ensure that an attacker cannot force
- * a downgrade of any cryptographic parameters. In addition to continuing the cryptographic 
- * handshake, the client MUST remember the results of any version negotiation that occurred 
- * (see Section 7.1). The client MAY also retain any observed RTT or congestion state that it 
- * has accumulated for the flow, but other transport state MUST be discarded.
+ * containing the next token. In effect, the next cryptographic
+ * handshake message is sent on a new connection. 
  */
 
-int picoquic_incoming_server_stateless(
+int picoquic_incoming_retry(
     picoquic_cnx_t* cnx,
     uint8_t* bytes,
     picoquic_packet_header* ph,
     uint64_t current_time)
 {
-    picoquic_packet_context_enum pc = ph->pc;
     int ret = 0;
+    size_t token_length = 0;
+    uint8_t * token = NULL;
 
     if (cnx->cnx_state != picoquic_state_client_init_sent && cnx->cnx_state != picoquic_state_client_init_resent) {
         ret = PICOQUIC_ERROR_UNEXPECTED_PACKET;
-    }
-    else {
+    } else {
         /* Verify that the header is a proper echo of what was sent */
-        if (ph->vn != picoquic_supported_versions[cnx->version_index].version || 
-            ((picoquic_supported_versions[cnx->version_index].version_flags&picoquic_version_use_pn_encryption)  == 0 && (
-            (cnx->pkt_ctx[pc].retransmit_newest == NULL || ph->pn64 > cnx->pkt_ctx[pc].retransmit_newest->sequence_number) ||
-            (cnx->pkt_ctx[pc].retransmit_oldest == NULL || ph->pn64 < cnx->pkt_ctx[pc].retransmit_oldest->sequence_number)))) {
+        if (ph->vn != picoquic_supported_versions[cnx->version_index].version) {
             /* Packet that do not match the "echo" checks should be logged and ignored */
             ret = PICOQUIC_ERROR_UNEXPECTED_PACKET;
-        }
-
-        if ((picoquic_supported_versions[cnx->version_index].version_flags&picoquic_version_use_pn_encryption) != 0 &&
-            ph->pn64 != 0) {
+        } else if (ph->pn64 != 0) {
             /* after draft-12, PN is required to be 0 */
             ret = PICOQUIC_ERROR_UNEXPECTED_PACKET;
         }
     }
 
     if (ret == 0) {
-        /* Accept the incoming frames */
-        ret = picoquic_decode_frames(cnx,
-            bytes + ph->offset, ph->payload_length, ph->epoch, current_time);
+        /* Parse the retry frame */
+        size_t byte_index = ph->offset;
+
+        uint8_t odcil = bytes[byte_index++];
+
+        if (odcil < 8 || odcil != cnx->initial_cnxid.id_len || odcil + 1 > ph->payload_length ||
+            memcmp(cnx->initial_cnxid.id, &bytes[byte_index], odcil) != 0) {
+            /* malformed ODCIL, or does not match initial cid; ignore */
+            ret = PICOQUIC_ERROR_UNEXPECTED_PACKET;
+        } else {
+            byte_index += odcil;
+            token_length = ph->offset + ph->payload_length - byte_index;
+
+            if (token_length > 0) {
+                token = malloc(token_length);
+                if (token == NULL) {
+                    ret = PICOQUIC_ERROR_MEMORY;
+                } else {
+                    memcpy(token, &bytes[byte_index], odcil);
+                }
+            }
+        }
     }
 
-    /* processing of the TLS message */
     if (ret == 0) {
-        /* set the state to HRR received, will trigger behavior when processing stream zero */
-        cnx->cnx_state = picoquic_state_client_hrr_received;
-        /* Remove the resume ticket if any */
-        picoquic_tlscontext_remove_ticket(cnx);
-        /* submit the embedded message (presumably HRR) to stream zero */
-        ret = picoquic_tls_stream_process(cnx);
-        if (ret == 0)
-        {
-            /* reset the initial CNX_ID to the version sent by the server */
-            cnx->initial_cnxid = ph->srce_cnx_id;
+        /* reset the initial CNX_ID to the version sent by the server */
+        cnx->initial_cnxid = ph->srce_cnx_id;
 
-            /* reset the encryption contexts */
-            for (int i = 0; i < 4; i++) {
-                picoquic_crypto_context_free(&cnx->crypto_context[i]);
-            }
+        /* keep a copy of the retry token */
+        if (cnx->retry_token != NULL) {
+            free(cnx->retry_token);
+        }
+        cnx->retry_token = token;
+        cnx->retry_token_length = token_length;
+
+        /* reset the initial stream, keep a copy of the 0-RTT packets sent. */
+        picoquic_reset_packet_context(cnx, picoquic_packet_context_initial);
 
-            /* Reinit the clear text AEAD */
-            ret = picoquic_setup_initial_traffic_keys(cnx);
+        /* reset the encryption contexts */
+        for (int i = 0; i < 4; i++) {
+            picoquic_crypto_context_free(&cnx->crypto_context[i]);
         }
+
+        /* Reinit the clear text AEAD */
+        ret = picoquic_setup_initial_traffic_keys(cnx);
     }
     if (ret == 0) {
         /* Mark the packet as not required for ack */
@@ -1248,7 +1256,8 @@ int picoquic_incoming_segment(
                 break;
             case picoquic_packet_initial:
                 /* Initial packet: either crypto handshakes or acks. */
-                if (picoquic_compare_connection_id(&ph.dest_cnx_id, &cnx->initial_cnxid) == 0) {
+                if (picoquic_compare_connection_id(&ph.dest_cnx_id, &cnx->initial_cnxid) == 0 ||
+                    picoquic_compare_connection_id(&ph.dest_cnx_id, &cnx->local_cnxid) == 0) {
                     if (cnx->client_mode == 0) {
                         /* TODO: finish processing initial connection packet */
                         ret = picoquic_incoming_initial(cnx, bytes,
@@ -1265,7 +1274,7 @@ int picoquic_incoming_segment(
                 break;
             case picoquic_packet_retry:
                 /* TODO: server retry is completely revised in the new version. */
-                ret = picoquic_incoming_server_stateless(cnx, bytes, &ph, current_time);
+                ret = picoquic_incoming_retry(cnx, bytes, &ph, current_time);
                 break;
             case picoquic_packet_handshake:
                 if (cnx->client_mode)
@@ -1311,7 +1320,7 @@ int picoquic_incoming_segment(
         if (cnx != NULL) {
             cnx->pkt_ctx[ph.pc].ack_needed = 1;
         }
-        ret = 0;
+        ret = -1;
     } else if (ret == PICOQUIC_ERROR_AEAD_CHECK || ret == PICOQUIC_ERROR_INITIAL_TOO_SHORT ||
         ret == PICOQUIC_ERROR_UNEXPECTED_PACKET || ret == PICOQUIC_ERROR_FNV1A_CHECK || 
         ret == PICOQUIC_ERROR_CNXID_CHECK || ret == PICOQUIC_ERROR_HRR || ret == PICOQUIC_ERROR_DETECTED ||
@@ -1321,17 +1330,17 @@ int picoquic_incoming_segment(
         DBG_PRINTF("Packet (%d) dropped, t: %d, e: %d, pc: %d, pn: %d, l: %d, ret : %x\n",
             (cnx == NULL) ? -1 : cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn, 
             length, ret);
-        ret = 0;
-    } else if (ret == 1)
-    {
+        ret = -1;
+    } else if (ret == 1) {
         /* wonder what happened ! */
         DBG_PRINTF("Packet (%d) get ret=1, t: %d, e: %d, pc: %d, pn: %d, l: %d\n",
             (cnx == NULL) ? -1 : cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn, length);
-        ret = 0;
+        ret = -1;
     }
     else if (ret != 0) {
         DBG_PRINTF("Packet (%d) error, t: %d, e: %d, pc: %d, pn: %d, l: %d, ret : %x\n",
             (cnx == NULL) ? -1 : cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn, length, ret);
+        ret = -1;
     }
 
     return ret;
@@ -1359,6 +1368,7 @@ int picoquic_incoming_packet(
         if (ret == 0) {
             consumed_index += consumed;
         } else {
+            ret = 0;
             break;
         }
     }
diff --git a/picoquic/picoquic_internal.h b/picoquic/picoquic_internal.h
index 034ad344..845e6195 100644
--- a/picoquic/picoquic_internal.h
+++ b/picoquic/picoquic_internal.h
@@ -113,8 +113,7 @@ typedef enum {
  */
 
 typedef enum {
-    picoquic_version_no_flag = 0,
-    picoquic_version_use_pn_encryption = 1
+    picoquic_version_no_flag = 0
 } picoquic_version_feature_flags;
 
 /*
@@ -424,7 +423,6 @@ typedef struct st_picoquic_cnx_t {
     int version_index;
 
     /* Series of flags showing the state or choices of the connection */
-    unsigned int use_pn_encryption : 1;
     unsigned int is_0RTT_accepted : 1; /* whether 0-RTT is accepted */
     unsigned int remote_parameters_received : 1; /* whether remote parameters where received */
     unsigned int current_spin : 1; /* Current value of the spin bit */             
@@ -461,6 +459,9 @@ typedef struct st_picoquic_cnx_t {
     uint16_t remote_application_error;
     uint16_t remote_error;
     uint16_t remote_crypto_error;
+    uint32_t retry_token_length;
+    uint8_t * retry_token;
+
 
     /* Next time sending data is expected */
     uint64_t next_wake_time;
@@ -537,6 +538,10 @@ void picoquic_dequeue_retransmit_packet(picoquic_cnx_t* cnx, picoquic_packet* p,
 /* Reset connection after receiving version negotiation */
 int picoquic_reset_cnx_version(picoquic_cnx_t* cnx, uint8_t* bytes, size_t length, uint64_t current_time);
 
+/* Reset packet context */
+void picoquic_reset_packet_context(picoquic_cnx_t* cnx,
+    picoquic_packet_context_enum pc);
+
 /* Notify error on connection */
 int picoquic_connection_error(picoquic_cnx_t* cnx, uint16_t local_error);
 
@@ -598,6 +603,8 @@ typedef struct _picoquic_packet_header {
     unsigned int spin : 1;
     unsigned int spin_vec : 2;
     unsigned int has_spin_bit : 1;
+    uint32_t token_length;
+    uint32_t token_offset;
 } picoquic_packet_header;
 
 int picoquic_parse_packet_header(
@@ -614,7 +621,8 @@ uint32_t picoquic_create_packet_header(
     picoquic_packet_type_enum packet_type,
     uint64_t sequence_number,
     uint8_t* bytes,
-    uint32_t * pn_offset);
+    uint32_t * pn_offset,
+    uint32_t * pn_length);
 
 uint32_t  picoquic_predict_packet_header_length(
     picoquic_cnx_t* cnx,
@@ -745,9 +753,6 @@ int picoquic_prepare_transport_extensions(picoquic_cnx_t* cnx, int extension_mod
 int picoquic_receive_transport_extensions(picoquic_cnx_t* cnx, int extension_mode,
     uint8_t* bytes, size_t bytes_max, size_t* consumed);
 
-/* Check whether a packet was sent in clear text */
-int picoquic_is_packet_encrypted(picoquic_packet_type_enum ptype);
-
 /* Queue stateless reset */
 void picoquic_queue_stateless_reset(picoquic_cnx_t* cnx,
     picoquic_packet_header* ph, struct sockaddr* addr_from,
diff --git a/picoquic/quicctx.c b/picoquic/quicctx.c
index 8dacc9c5..f1ac0db6 100644
--- a/picoquic/quicctx.c
+++ b/picoquic/quicctx.c
@@ -139,11 +139,11 @@ static uint8_t picoquic_cleartext_draft_10_salt[] = {
 
 /* Support for draft 13! */
 const picoquic_version_parameters_t picoquic_supported_versions[] = {
-    { PICOQUIC_INTERNAL_TEST_VERSION_1, picoquic_version_use_pn_encryption,
+    { PICOQUIC_INTERNAL_TEST_VERSION_1, 0,
         picoquic_version_header_12,
         sizeof(picoquic_cleartext_internal_test_1_salt),
         picoquic_cleartext_internal_test_1_salt },
-    { PICOQUIC_SEVENTH_INTEROP_VERSION, picoquic_version_use_pn_encryption,
+    { PICOQUIC_SEVENTH_INTEROP_VERSION, 0,
         picoquic_version_header_12,
         sizeof(picoquic_cleartext_draft_10_salt),
         picoquic_cleartext_draft_10_salt }
@@ -1161,6 +1161,41 @@ void picoquic_dequeue_retransmit_packet(picoquic_cnx_t* cnx, picoquic_packet* p,
     }
 }
 
+void picoquic_reset_packet_context(picoquic_cnx_t* cnx,
+    picoquic_packet_context_enum pc)
+{
+    /* TODO: special case for 0-RTT packets! */
+    picoquic_packet_context_t * pkt_ctx = &cnx->pkt_ctx[pc];
+
+    while (pkt_ctx->retransmit_newest != NULL) {
+        picoquic_dequeue_retransmit_packet(cnx, pkt_ctx->retransmit_newest, 1);
+    }
+    
+    while (pkt_ctx->retransmitted_newest != NULL) {
+        picoquic_packet* p = pkt_ctx->retransmitted_newest;
+        pkt_ctx->retransmitted_newest = p->next_packet;
+        free(p);
+    }
+
+    pkt_ctx->retransmitted_oldest = NULL;
+
+#if 0
+    /* BUG #225: this crashes in some tests not related to this PR. */
+    /* Reset the sack lists*/
+    while (pkt_ctx->first_sack_item.next_sack != NULL) {
+        picoquic_sack_item_t * next = pkt_ctx->first_sack_item.next_sack;
+        cnx->pkt_ctx->first_sack_item.next_sack = next->next_sack;
+        free(next);
+    }
+#else
+    /* BUG: see above */
+    pkt_ctx->first_sack_item.next_sack = NULL;
+#endif
+
+    pkt_ctx->first_sack_item.start_of_sack_range = (uint64_t)((int64_t)-1);
+    pkt_ctx->first_sack_item.end_of_sack_range = 0;
+}
+
 /*
 * Reset the version to a new supported value.
 *
@@ -1195,25 +1230,13 @@ int picoquic_reset_cnx_version(picoquic_cnx_t* cnx, uint8_t* bytes, size_t lengt
                     cnx->version_index = (int)i;
                     cnx->cnx_state = picoquic_state_client_renegotiate;
 
-                    /* TODO: Reset the clear text context */
-
                     /* Delete the packets queued for retransmission */
                     for (picoquic_packet_context_enum pc = 0;
                         pc < picoquic_nb_packet_context; pc++) {
-                        while (cnx->pkt_ctx[pc].retransmit_newest != NULL) {
-                            picoquic_dequeue_retransmit_packet(cnx, cnx->pkt_ctx[pc].retransmit_newest, 1);
-                        }
-                        /* Reset the sack lists*/
-                        while (cnx->pkt_ctx[pc].first_sack_item.next_sack != NULL) {
-                            picoquic_sack_item_t * next = cnx->pkt_ctx[pc].first_sack_item.next_sack;
-                            cnx->pkt_ctx[pc].first_sack_item.next_sack = next->next_sack;
-                            free(next);
-                        }
-                        cnx->pkt_ctx[pc].first_sack_item.start_of_sack_range = (uint64_t)((int64_t)-1);
-                        cnx->pkt_ctx[pc].first_sack_item.end_of_sack_range = 0;
+                        picoquic_reset_packet_context(cnx, pc);
                     }
 
-                    /* Reset the streams */
+                    /* Reset the crypto stream */
                     picoquic_clear_stream(&cnx->tls_stream);
                     cnx->tls_stream.consumed_offset = 0;
                     cnx->tls_stream.stream_flags = 0;
@@ -1308,18 +1331,10 @@ void picoquic_delete_cnx(picoquic_cnx_t* cnx)
         for (int i = 0; i < 4; i++) {
             picoquic_crypto_context_free(&cnx->crypto_context[i]);
         }
+
         for (picoquic_packet_context_enum pc = 0;
             pc < picoquic_nb_packet_context; pc++) {
-            while (cnx->pkt_ctx[pc].retransmit_newest != NULL) {
-                picoquic_dequeue_retransmit_packet(cnx, cnx->pkt_ctx[pc].retransmit_newest, 1);
-            }
-
-            while (cnx->pkt_ctx[pc].retransmitted_newest != NULL) {
-                picoquic_packet* p = cnx->pkt_ctx[pc].retransmitted_newest;
-                cnx->pkt_ctx[pc].retransmitted_newest = p->next_packet;
-                free(p);
-            }
-            cnx->pkt_ctx[pc].retransmitted_oldest = NULL;
+            picoquic_reset_packet_context(cnx, pc);
         }
 
         while ((misc_frame = cnx->first_misc_frame) != NULL) {
diff --git a/picoquic/sender.c b/picoquic/sender.c
index d984879e..61cc3802 100644
--- a/picoquic/sender.c
+++ b/picoquic/sender.c
@@ -193,79 +193,6 @@ void picoquic_update_payload_length(
     }
 }
 
-uint32_t picoquic_create_packet_header_11(
-    picoquic_cnx_t* cnx,
-    picoquic_packet_type_enum packet_type,
-    picoquic_connection_id_t dest_cnx_id,
-    picoquic_connection_id_t srce_cnx_id,
-    uint64_t sequence_number,
-    uint8_t* bytes,
-    uint32_t * pn_offset)
-{
-    uint32_t length = 0;
-
-    /* Prepare the packet header */
-    if (packet_type == picoquic_packet_1rtt_protected_phi0 || packet_type == picoquic_packet_1rtt_protected_phi1) {
-        /* Create a short packet -- using 32 bit sequence numbers for now */
-        uint8_t K = (packet_type == picoquic_packet_1rtt_protected_phi0) ? 0 : 0x40;
-        const uint8_t C = 0x32;
-        uint8_t spin_bit = (uint8_t)((cnx->current_spin) << 2);
-
-        length = 0;
-        bytes[length++] = (K | C | spin_bit);
-        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id);
-
-        *pn_offset = length;
-        picoformat_32(&bytes[length], (uint32_t)sequence_number);
-        length += 4;
-    }
-    else {
-        /* Create a long packet */
-
-        switch (packet_type) {
-        case picoquic_packet_initial:
-            bytes[0] = 0xFF;
-            break;
-        case picoquic_packet_retry:
-            bytes[0] = 0xFE;
-            break;
-        case picoquic_packet_handshake:
-            bytes[0] = 0xFD;
-            break;
-        case picoquic_packet_0rtt_protected:
-            bytes[0] = 0xFC;
-            break;
-        default:
-            bytes[0] = 0x80;
-            break;
-        }
-        length = 1;
-        if ((cnx->cnx_state == picoquic_state_client_init || cnx->cnx_state == picoquic_state_client_init_sent) && packet_type == picoquic_packet_initial) {
-            picoformat_32(&bytes[length], cnx->proposed_version);
-        }
-        else {
-            picoformat_32(&bytes[length],
-                picoquic_supported_versions[cnx->version_index].version);
-        }
-        length += 4;
-
-        bytes[length++] = picoquic_create_packet_header_cnxid_lengths(dest_cnx_id.id_len, srce_cnx_id.id_len);
-
-        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id);
-        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, srce_cnx_id);
-
-        /* Reserve two bytes for payload length */
-        bytes[length++] = 0;
-        bytes[length++] = 0;
-        /* Encode the length */
-        *pn_offset = length;
-        picoformat_32(&bytes[length], (uint32_t)sequence_number);
-        length += 4;
-    }
-
-    return length;
-}
-
 uint32_t picoquic_predict_packet_header_length_11(
     picoquic_packet_type_enum packet_type,
     picoquic_connection_id_t dest_cnx_id,
@@ -286,45 +213,47 @@ uint32_t picoquic_predict_packet_header_length_11(
     return length;
 }
 
-
-uint32_t picoquic_create_packet_header_12(
+uint32_t picoquic_create_packet_header(
     picoquic_cnx_t* cnx,
     picoquic_packet_type_enum packet_type,
-    picoquic_connection_id_t dest_cnx_id,
-    picoquic_connection_id_t srce_cnx_id,
     uint64_t sequence_number,
     uint8_t* bytes,
-    uint32_t * pn_offset)
+    uint32_t * pn_offset,
+    uint32_t * pn_length)
 {
     uint32_t length = 0;
+    picoquic_connection_id_t dest_cnx_id =
+        ((packet_type == picoquic_packet_initial && cnx->client_mode) ||
+            packet_type == picoquic_packet_0rtt_protected) ?
+        cnx->initial_cnxid : cnx->remote_cnxid;
 
     /* Prepare the packet header */
     if (packet_type == picoquic_packet_1rtt_protected_phi0 || packet_type == picoquic_packet_1rtt_protected_phi1) {
         /* Create a short packet -- using 32 bit sequence numbers for now */
         uint8_t K = (packet_type == picoquic_packet_1rtt_protected_phi0) ? 0 : 0x40;
         const uint8_t C = 0x30;
-	uint8_t spin_vec = (uint8_t) (cnx->spin_vec );
-    uint8_t spin_bit = (uint8_t)((cnx->current_spin) << 2);
-
-	if (!cnx->spin_edge) spin_vec = 0;
-	else {
-		cnx->spin_edge = 0;
-		uint64_t dt = picoquic_get_quic_time(cnx->quic) - cnx->spin_last_trigger;
-		if (dt > PICOQUIC_SPIN_VEC_LATE) { // DELAYED
-			spin_vec = 1;
-			// fprintf(stderr, "Delayed Outgoing Spin=%d DT=%ld\n", cnx->current_spin, dt);
-		}
-	}
-
-	length = 0;
+        uint8_t spin_vec = (uint8_t)(cnx->spin_vec);
+        uint8_t spin_bit = (uint8_t)((cnx->current_spin) << 2);
+
+        if (!cnx->spin_edge) spin_vec = 0;
+        else {
+            cnx->spin_edge = 0;
+            uint64_t dt = picoquic_get_quic_time(cnx->quic) - cnx->spin_last_trigger;
+            if (dt > PICOQUIC_SPIN_VEC_LATE) { // DELAYED
+                spin_vec = 1;
+                // fprintf(stderr, "Delayed Outgoing Spin=%d DT=%ld\n", cnx->current_spin, dt);
+            }
+        }
+
+        length = 0;
         bytes[length++] = (K | C | spin_bit | spin_vec);
         length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id);
 
         *pn_offset = length;
+        *pn_length = 4;
         picoquic_headint_encode_32(&bytes[length], sequence_number);
         length += 4;
-    }
-    else {
+    } else {
         /* Create a long packet */
 
         switch (packet_type) {
@@ -354,70 +283,70 @@ uint32_t picoquic_create_packet_header_12(
         }
         length += 4;
 
-        bytes[length++] = picoquic_create_packet_header_cnxid_lengths(dest_cnx_id.id_len, srce_cnx_id.id_len);
+        bytes[length++] = picoquic_create_packet_header_cnxid_lengths(dest_cnx_id.id_len, cnx->local_cnxid.id_len);
 
         length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id);
-        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, srce_cnx_id);
+        length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, cnx->local_cnxid);
 
         /* Reserve two bytes for payload length */
         bytes[length++] = 0;
         bytes[length++] = 0;
-        /* Encode the length */
+        /* Encode the sequence number */
         *pn_offset = length;
+        *pn_length = 4;
         picoquic_headint_encode_32(&bytes[length], sequence_number);
         length += 4;
+        /* Special case of packet initial */
+        if (packet_type == picoquic_packet_initial) {
+            length += picoquic_varint_encode(&bytes[length], 16, cnx->retry_token_length);
+            if (cnx->retry_token_length > 0) {
+                memcpy(&bytes[length], cnx->retry_token, cnx->retry_token_length);
+            }
+        }
     }
 
     return length;
 }
 
-
 uint32_t picoquic_predict_packet_header_length(
     picoquic_cnx_t* cnx,
     picoquic_packet_type_enum packet_type)
 {
     uint32_t header_length = 0;
-    switch (picoquic_supported_versions[cnx->version_index].version_header_encoding) {
-    case picoquic_version_header_11:
-    case picoquic_version_header_12:
-        header_length = picoquic_predict_packet_header_length_11(packet_type,
-            (packet_type == picoquic_packet_initial ||
-                packet_type == picoquic_packet_0rtt_protected) ?
-            cnx->initial_cnxid : cnx->remote_cnxid,
-            cnx->local_cnxid);
-        break;
-    default:
-        break;
+
+    if (packet_type == picoquic_packet_1rtt_protected_phi0 || 
+        packet_type == picoquic_packet_1rtt_protected_phi1) {
+        /* Compute length of a short packet header */
+
+        header_length = 1 + cnx->remote_cnxid.id_len + 4;
     }
-    return header_length;
-}
+    else {
+        /* Compute length of a long packet header */
+        header_length = 1 + /* version */ 4 + /* cnx_id prefix */ 1;
 
-uint32_t picoquic_create_packet_header(
-    picoquic_cnx_t* cnx,
-    picoquic_packet_type_enum packet_type,
-    uint64_t sequence_number,
-    uint8_t* bytes,
-    uint32_t * pn_offset)
-{
-    uint32_t header_length = 0;
-    switch (picoquic_supported_versions[cnx->version_index].version_header_encoding) {
-    case picoquic_version_header_11:
-        header_length = picoquic_create_packet_header_11(cnx, packet_type, 
-            (packet_type==picoquic_packet_initial ||
-                packet_type == picoquic_packet_0rtt_protected)?
-            cnx->initial_cnxid:cnx->remote_cnxid,
-            cnx->local_cnxid, sequence_number, bytes, pn_offset);
-        break;
-    case picoquic_version_header_12:
-        header_length = picoquic_create_packet_header_12(cnx, packet_type,
-            (packet_type == picoquic_packet_initial ||
-                packet_type == picoquic_packet_0rtt_protected) ?
-            cnx->initial_cnxid : cnx->remote_cnxid,
-            cnx->local_cnxid, sequence_number, bytes, pn_offset);
-        break;
-    default:
-        break;
+        /* add dest-id length */
+        if ((packet_type == picoquic_packet_initial && cnx->client_mode) ||
+            packet_type == picoquic_packet_0rtt_protected) {
+            header_length += cnx->initial_cnxid.id_len;
+        }
+        else {
+            header_length += cnx->remote_cnxid.id_len;
+        }
+
+        /* add srce-id length */
+        header_length += cnx->local_cnxid.id_len;
+
+        /* add length of payload length and packet number */
+        header_length += 2 + 4;
+
+        /* add length of tokens for initial packets */
+        if (packet_type == picoquic_packet_initial) {
+            uint8_t useless[16];
+            header_length += picoquic_varint_encode(useless, 16, cnx->retry_token_length);
+            header_length += cnx->retry_token_length;
+        }
     }
+
     return header_length;
 }
 
@@ -447,20 +376,17 @@ uint32_t picoquic_protect_packet(picoquic_cnx_t* cnx,
 {
     uint32_t send_length;
     uint32_t h_length;
-    uint32_t pnum_offset = 0;
+    uint32_t pn_offset = 0;
+    size_t sample_offset = 0;
+    size_t pn_length = 0;
     size_t aead_checksum_length = picoquic_aead_get_checksum_length(aead_context);
 
     /* Create the packet header just before encrypting the content */
     h_length = picoquic_create_packet_header(cnx, ptype,
-        sequence_number, send_buffer, &pnum_offset);
+        sequence_number, send_buffer, &pn_offset, &pn_length);
     /* Make sure that the payload length is encoded in the header */
-    if (picoquic_supported_versions[cnx->version_index].version_flags&picoquic_version_use_pn_encryption)
-    {
-        /* If using encryption, the "payload" length also includes the encrypted packet length */
-        picoquic_update_payload_length(send_buffer, pnum_offset, pnum_offset, length + aead_checksum_length);
-    } else {
-        picoquic_update_payload_length(send_buffer, pnum_offset, h_length, length + aead_checksum_length);
-    }
+    /* Using encryption, the "payload" length also includes the encrypted packet length */
+    picoquic_update_payload_length(send_buffer, pn_offset, h_length - pn_length, length + aead_checksum_length);
 
     send_length = (uint32_t)picoquic_aead_encrypt_generic(send_buffer + /* header_length */ h_length,
         bytes + header_length, length - header_length,
@@ -468,24 +394,18 @@ uint32_t picoquic_protect_packet(picoquic_cnx_t* cnx,
 
     send_length += /* header_length */ h_length;
 
-    /* Next, encrypt the PN, if needed */
+    /* Next, encrypt the PN -- The sample is located after the pn_offset */
+    sample_offset = /* header_length */ pn_offset + 4;
 
-    if (picoquic_supported_versions[cnx->version_index].version_flags&picoquic_version_use_pn_encryption)
+    if (sample_offset + aead_checksum_length > send_length)
     {
-        /* The sample is located at the offset */
-        size_t sample_offset = /* header_length */ h_length;
-        size_t pn_length = h_length - pnum_offset;
-
-        if (sample_offset + aead_checksum_length > send_length)
-        {
-            sample_offset = length - aead_checksum_length;
-        }
-        if (pnum_offset < sample_offset)
-        {
-            /* Encode */
-            picoquic_pn_encrypt(pn_enc, send_buffer + sample_offset, send_buffer + /* pn_offset */ pnum_offset, 
-                send_buffer + /* pn_offset */ pnum_offset, pn_length);
-        }
+        sample_offset = length - aead_checksum_length;
+    }
+    if (pn_offset < sample_offset)
+    {
+        /* Encode */
+        picoquic_pn_encrypt(pn_enc, send_buffer + sample_offset, send_buffer + /* pn_offset */ pn_offset, 
+            send_buffer + /* pn_offset */ pn_offset, pn_length);
     }
 
     return send_length;
@@ -747,12 +667,6 @@ int picoquic_retransmit_needed(picoquic_cnx_t* cnx,
 
                 *header_length = length;
 
-                if (p->ptype < picoquic_packet_1rtt_protected_phi0) {
-                    DBG_PRINTF("Retransmit packet type %d, pc=%d, seq = %llx, is_client = %d\n",
-                        p->ptype, p->pc,
-                        (unsigned long long)p->sequence_number, cnx->client_mode);
-                }
-
                 if (p->ptype == picoquic_packet_1rtt_protected_phi0 || p->ptype == picoquic_packet_1rtt_protected_phi1 || p->ptype == picoquic_packet_0rtt_protected) {
                     *is_cleartext_mode = 0;
                 } else {
@@ -830,6 +744,12 @@ int picoquic_retransmit_needed(picoquic_cnx_t* cnx,
                     }
 
                     if (should_retransmit != 0) {
+                        if (p->ptype < picoquic_packet_1rtt_protected_phi0) {
+                            DBG_PRINTF("Retransmit packet type %d, pc=%d, seq = %llx, is_client = %d\n",
+                                p->ptype, p->pc,
+                                (unsigned long long)p->sequence_number, cnx->client_mode);
+                        }
+
                         /* special case for the client initial */
                         if (p->ptype == picoquic_packet_initial && cnx->client_mode != 0) {
                             while (length < (path_x->send_mtu - checksum_length)) {
@@ -1632,7 +1552,7 @@ int picoquic_prepare_packet_server_init(picoquic_cnx_t* cnx, picoquic_path_t * p
             packet->checksum_overhead = checksum_overhead;
         }
         else {
-            /* when in a clear text mode, only send packets if there is
+            /* when in a handshake mode, only send packets if there is
             * actually something to send, or resend */
             length = 0;
             packet->length = 0;
diff --git a/picoquic_t/picoquic_t.c b/picoquic_t/picoquic_t.c
index dae9fbe9..f5c96f7d 100644
--- a/picoquic_t/picoquic_t.c
+++ b/picoquic_t/picoquic_t.c
@@ -42,6 +42,7 @@ typedef enum {
 
 static const picoquic_test_def_t test_table[] = {
     { "picohash", picohash_test },
+    { "splay", splay_test },
     { "cnxcreation", cnxcreation_test },
     { "parseheader", parseheadertest },
     { "pn2pn64", pn2pn64test },
@@ -49,15 +50,16 @@ static const picoquic_test_def_t test_table[] = {
     { "fnv1a", fnv1atest },
     { "float16", float16test },
     { "varint", varint_test },
+    { "sack", sacktest },
     { "skip_frames", skip_frame_test },
+    { "parse_frames", parse_frame_test },
+    { "logger", logger_test },
     { "TlsStreamFrame", TlsStreamFrameTest },
     { "StreamZeroFrame", StreamZeroFrameTest },
-    { "sack", sacktest },
     { "sendack", sendacktest },
     { "ackrange", ackrange_test },
     { "ack_of_ack", ack_of_ack_test },
     { "sim_link", sim_link_test },
-    { "logger", logger_test },
     { "tls_api", tls_api_test },
     { "silence_test", tls_api_silence_test },
     { "tls_api_version_negotiation", tls_api_version_negotiation_test },
@@ -118,9 +120,7 @@ static const picoquic_test_def_t test_table[] = {
     { "pn_vector", cleartext_pn_vector_test },
     { "zero_rtt_spurious", zero_rtt_spurious_test },
     { "zero_rtt_retry", zero_rtt_retry_test },
-    { "parse_frames", parse_frame_test },
-    { "stress", stress_test },
-    { "splay", splay_test }
+    { "stress", stress_test }
 };
 
 static size_t const nb_tests = sizeof(test_table) / sizeof(picoquic_test_def_t);
diff --git a/picoquictest/parseheadertest.c b/picoquictest/parseheadertest.c
index f99711f8..ea9a3e1b 100644
--- a/picoquictest/parseheadertest.c
+++ b/picoquictest/parseheadertest.c
@@ -76,6 +76,8 @@ static picoquic_packet_header hinitial10 = {
     picoquic_packet_context_initial,
     0,
     0,
+    0,
+    0,
     0
 };
 
@@ -105,6 +107,8 @@ static picoquic_packet_header hinitial10_l = {
     picoquic_packet_context_initial,
     0,
     0,
+    0,
+    0,
     0
 };
 
@@ -144,6 +148,8 @@ static picoquic_packet_header hvnego10 = {
     picoquic_packet_context_initial,
     0,
     0,
+    0,
+    0,
     0
 };
 
@@ -173,6 +179,8 @@ static picoquic_packet_header hhandshake = {
     picoquic_packet_context_handshake,
     0,
     0,
+    0,
+    0,
     0
 };
 
@@ -198,7 +206,9 @@ static picoquic_packet_header hphi0_c_32 = {
     picoquic_packet_context_application,
     0,
     0,
-    1
+    1,
+    0,
+    0
 };
 
 static uint8_t packet_short_phi0_c_32_spin[] = {
@@ -223,7 +233,9 @@ static picoquic_packet_header hphi0_c_32_spin = {
     picoquic_packet_context_application,
     1,
     0,
-    1
+    1,
+    0,
+    0
 };
 
 static uint8_t packet_short_phi0_noc_32[] = {
@@ -247,6 +259,8 @@ static picoquic_packet_header hphi0_noc_32 = {
     picoquic_packet_context_application,
     0,
     0,
+    0,
+    0,
     0
 };
 
@@ -350,6 +364,7 @@ int parseheadertest()
     for (size_t i = 0; ret == 0 && i < nb_test_entries; i++) {
         uint32_t header_length;
         uint32_t pn_offset;
+        uint32_t pn_length;
 
         if (test_entries[i].decode_test_only) {
             continue;
@@ -359,7 +374,7 @@ int parseheadertest()
         memset(packet, 0xcc, sizeof(packet));
         /* Prepare the header inside the packet */
         header_length = picoquic_create_packet_header(cnx_10, test_entries[i].ph->ptype,
-            test_entries[i].ph->pn, packet, &pn_offset);
+            test_entries[i].ph->pn, packet, &pn_offset, &pn_length);
         picoquic_update_payload_length(packet, pn_offset, pn_offset, pn_offset +
             test_entries[i].ph->payload_length);
         
diff --git a/picoquictest/tls_api_test.c b/picoquictest/tls_api_test.c
index c5dcbf32..6c67fbc7 100644
--- a/picoquictest/tls_api_test.c
+++ b/picoquictest/tls_api_test.c
@@ -897,13 +897,53 @@ static int tls_api_data_sending_loop(picoquic_test_tls_api_ctx_t* test_ctx,
     return ret; /* end of sending loop */
 }
 
+
+static int wait_application_pn_enc_ready(picoquic_test_tls_api_ctx_t* test_ctx,
+    uint64_t * simulated_time)
+{
+    int ret = 0;
+    uint64_t time_out = *simulated_time + 4000000;
+    int nb_trials = 0;
+    int nb_inactive = 0;
+
+    while (*simulated_time < time_out &&
+        test_ctx->cnx_client->cnx_state == picoquic_state_client_ready &&
+        test_ctx->cnx_server->cnx_state == picoquic_state_server_ready &&
+        test_ctx->cnx_server->crypto_context[3].aead_decrypt == NULL &&
+        nb_trials < 1024 &&
+        nb_inactive < 64 &&
+        ret == 0) {
+        int was_active = 0;
+        nb_trials++;
+
+        ret = tls_api_one_sim_round(test_ctx, simulated_time, &was_active);
+
+        if (was_active) {
+            nb_inactive = 0;
+        }
+        else {
+            nb_inactive++;
+        }
+    }
+
+    if (test_ctx->cnx_server->crypto_context[3].aead_decrypt == NULL) {
+        DBG_PRINTF("Could not obtain the 1-RTT decryption key, state = %d\n",
+            test_ctx->cnx_server->cnx_state);
+        ret = -1;
+    }
+
+    return ret;
+}
+
 static int tls_api_attempt_to_close(
     picoquic_test_tls_api_ctx_t* test_ctx, uint64_t* simulated_time)
 {
     int ret = 0;
     int nb_rounds = 0;
 
-    ret = picoquic_close(test_ctx->cnx_client, 0);
+    if (ret == 0) {
+        ret = picoquic_close(test_ctx->cnx_client, 0);
+    }
 
     if (ret == 0) {
         /* packet from client to server */
@@ -1267,6 +1307,10 @@ int tls_api_server_reset_test()
         ret = tls_api_connection_loop(test_ctx, &loss_mask, 0, &simulated_time);
     }
 
+    if (ret == 0) {
+        ret = wait_application_pn_enc_ready(test_ctx, &simulated_time);
+    }
+
     /* verify that client and server have the same reset secret */
     if (ret == 0 && memcmp(test_ctx->cnx_client->reset_secret, test_ctx->cnx_server->reset_secret, PICOQUIC_RESET_SECRET_SIZE) != 0) {
         ret = -1;
@@ -2474,35 +2518,7 @@ int pn_enc_1rtt_test()
     }
 
     if (ret == 0) {
-        uint64_t time_out = simulated_time + 4000000;
-        int nb_trials = 0;
-        int nb_inactive = 0;
-
-        while (simulated_time <time_out &&
-            test_ctx->cnx_client->cnx_state == picoquic_state_client_ready &&
-            test_ctx->cnx_server->cnx_state == picoquic_state_server_ready &&
-            test_ctx->cnx_server->crypto_context[3].aead_decrypt == NULL &&
-            nb_trials < 1024 &&
-            nb_inactive < 64 &&
-            ret == 0) {
-            int was_active = 0;
-            nb_trials++;
-
-            ret = tls_api_one_sim_round(test_ctx, &simulated_time, &was_active);
-
-            if (was_active) {
-                nb_inactive = 0;
-            }
-            else {
-                nb_inactive++;
-            }
-        }
-
-        if (test_ctx->cnx_server->crypto_context[3].aead_decrypt == NULL) {
-            DBG_PRINTF("Could not obtain the 1-RTT decryption key, state = %d\n",
-                test_ctx->cnx_server->cnx_state);
-            ret = -1;
-        }
+        ret = wait_application_pn_enc_ready(test_ctx, &simulated_time);
     }
 
     if (ret == 0)
@@ -3288,6 +3304,10 @@ int client_error_test()
         ret = tls_api_connection_loop(test_ctx, &loss_mask, 0, &simulated_time);
     }
 
+    if (ret == 0){
+        ret = wait_application_pn_enc_ready(test_ctx, &simulated_time);
+    }
+
     if (ret == 0) {
         ret = tls_api_attempt_to_close(test_ctx, &simulated_time);
     }
-- 
GitLab