From 7aa3f2283a7ee3ec53a655244ed6342418a2e5d9 Mon Sep 17 00:00:00 2001
From: huitema <huitema@huitema.net>
Date: Wed, 30 May 2018 21:55:17 -0700
Subject: [PATCH] Unified function for header parse and packet decrypt, plus
 unit test.

---
 UnitTest1/unittest1.cpp            |   8 +
 picoquic/packet.c                  |  89 ++++++++++
 picoquic/picoquic.h                |   1 +
 picoquic/picoquic_internal.h       |  17 ++
 picoquic/sender.c                  |  56 +++---
 picoquic/tls_api.c                 |  22 +++
 picoquic/tls_api.h                 |   3 +
 picoquic_t/picoquic_t.c            |   3 +-
 picoquictest/cleartext_aead_test.c |   3 +-
 picoquictest/parseheadertest.c     | 264 ++++++++++++++++++++++++++++-
 picoquictest/picoquictest.h        |   1 +
 11 files changed, 442 insertions(+), 25 deletions(-)

diff --git a/UnitTest1/unittest1.cpp b/UnitTest1/unittest1.cpp
index 67bec4fd..08c802f1 100644
--- a/UnitTest1/unittest1.cpp
+++ b/UnitTest1/unittest1.cpp
@@ -533,5 +533,13 @@ namespace UnitTest1
 
             Assert::AreEqual(ret, 0);
         }
+
+        TEST_METHOD(test_packet_enc_dec)
+        {
+            int ret = packet_enc_dec_test();
+
+            Assert::AreEqual(ret, 0);
+        }
+
     };
 }
diff --git a/picoquic/packet.c b/picoquic/packet.c
index 688f6f50..22e81a28 100644
--- a/picoquic/packet.c
+++ b/picoquic/packet.c
@@ -404,6 +404,95 @@ size_t picoquic_decrypt_cleartext(picoquic_cnx_t* cnx,
     return decoded_length;
 }
 
+int picoquic_parse_header_and_decrypt(
+    picoquic_quic_t* quic,
+    uint8_t* bytes,
+    uint32_t length,
+    uint32_t packet_length,
+    struct sockaddr* addr_from,
+    uint64_t current_time,
+    picoquic_packet_header* ph,
+    picoquic_cnx_t** pcnx,
+    uint32_t * consumed,
+    int receiving)
+{
+    /* Parse the clear text header. Ret == 0 means an incorrect packet that could not be parsed */
+    int already_received = 0;
+    size_t decoded_length = 0;
+    int ret = picoquic_parse_packet_header(quic, bytes, length, addr_from, ph, pcnx, receiving);
+    int cmp_reset_secret = 0;
+
+    if (ret == 0) {
+        length = ph->offset + ph->payload_length;
+        *consumed = length;
+
+        if (*pcnx == NULL && ph->ptype == picoquic_packet_client_initial) {
+            /* Create a connection context if the CI is acceptable */
+            if (packet_length < PICOQUIC_ENFORCED_INITIAL_MTU) {
+                /* Unexpected packet. Reject, drop and log. */
+                ret = PICOQUIC_ERROR_INITIAL_TOO_SHORT;
+            }
+            else {
+                /* if listening is OK, listen */
+                *pcnx = picoquic_create_cnx(quic, ph->dest_cnx_id, ph->srce_cnx_id, addr_from, current_time, ph->vn, NULL, NULL, 0);
+            }
+        }
+
+        if (*pcnx != NULL) {
+            /* Decrypt the sequence number if needed, and then decrypt the packet */
+            switch (ph->ptype) {
+            case picoquic_packet_version_negotiation:
+                /* Packet is not encrypted */
+                break;
+            case picoquic_packet_client_initial:
+                decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
+                    (*pcnx)->pn_dec_cleartext, (*pcnx)->aead_decrypt_cleartext_ctx, &already_received);
+                break;
+            case picoquic_packet_server_stateless:
+                decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
+                    (*pcnx)->pn_dec_cleartext, (*pcnx)->aead_decrypt_cleartext_ctx, &already_received);
+                break;
+            case picoquic_packet_handshake:
+                decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph,
+                    (*pcnx)->pn_dec_cleartext, (*pcnx)->aead_decrypt_cleartext_ctx, &already_received);
+                break;
+            case picoquic_packet_0rtt_protected:
+                decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph, (*pcnx)->pn_enc_0rtt,
+                    (*pcnx)->aead_0rtt_decrypt_ctx, &already_received);
+                break;
+            case picoquic_packet_1rtt_protected_phi0:
+            case picoquic_packet_1rtt_protected_phi1:
+                /* TODO : roll key based on PHI */
+                /* Check the possible reset before performing in place AEAD decrypt */
+                cmp_reset_secret = memcmp(bytes + length - PICOQUIC_RESET_SECRET_SIZE,
+                    (*pcnx)->reset_secret, PICOQUIC_RESET_SECRET_SIZE);
+                /* AEAD Decrypt, in place */
+                decoded_length = picoquic_decrypt_packet(*pcnx, bytes, length, ph, (*pcnx)->pn_dec,
+                    (*pcnx)->aead_decrypt_ctx, &already_received);
+                break;
+            default:
+                /* Packet type error. Log and ignore */
+                ret = PICOQUIC_ERROR_DETECTED;
+                break;
+            }
+
+            /* TODO: consider the error "too soon" */
+            if (decoded_length > (length - ph->offset)) {
+                if (cmp_reset_secret == 0) {
+                    ret = PICOQUIC_ERROR_STATELESS_RESET;
+                } else {
+                    ret = PICOQUIC_ERROR_AEAD_CHECK;
+                }
+            } else if (already_received != 0) {
+                ret = PICOQUIC_ERROR_DUPLICATE;
+            } else {
+                ph->payload_length = (uint16_t)decoded_length;
+            }
+        }
+    }
+
+    return ret;
+}
 /*
  * Processing of a version renegotiation packet.
  *
diff --git a/picoquic/picoquic.h b/picoquic/picoquic.h
index 89d27ef2..07bc895e 100644
--- a/picoquic/picoquic.h
+++ b/picoquic/picoquic.h
@@ -69,6 +69,7 @@ extern "C" {
 #define PICOQUIC_ERROR_UNEXPECTED_ERROR (PICOQUIC_ERROR_CLASS + 27)
 #define PICOQUIC_ERROR_TLS_SERVER_CON_WITHOUT_CERT (PICOQUIC_ERROR_CLASS + 28)
 #define PICOQUIC_ERROR_NO_SUCH_FILE (PICOQUIC_ERROR_CLASS + 29)
+#define PICOQUIC_ERROR_STATELESS_RESET (PICOQUIC_ERROR_CLASS + 30)
 
 /*
  * Protocol errors defined in the QUIC spec
diff --git a/picoquic/picoquic_internal.h b/picoquic/picoquic_internal.h
index 7a736657..7369ab15 100644
--- a/picoquic/picoquic_internal.h
+++ b/picoquic/picoquic_internal.h
@@ -585,6 +585,23 @@ uint32_t picoquic_protect_packet(picoquic_cnx_t* cnx,
     uint8_t* send_buffer,
     void * aead_context, void* pn_enc);
 
+void picoquic_finalize_and_protect_packet(picoquic_cnx_t *cnx, picoquic_packet * packet, int ret,
+    uint32_t length, uint32_t header_length, uint32_t checksum_overhead,
+    size_t * send_length, uint8_t * send_buffer, picoquic_path_t * path_x,
+    uint64_t current_time);
+
+int picoquic_parse_header_and_decrypt(
+    picoquic_quic_t* quic,
+    uint8_t* bytes,
+    uint32_t length,
+    uint32_t packet_length,
+    struct sockaddr* addr_from,
+    uint64_t current_time,
+    picoquic_packet_header* ph,
+    picoquic_cnx_t** pcnx,
+    uint32_t * consumed,
+    int receiving);
+
 /* handling of ACK logic */
 int picoquic_is_ack_needed(picoquic_cnx_t* cnx, uint64_t current_time);
 
diff --git a/picoquic/sender.c b/picoquic/sender.c
index 034855c5..3484a00d 100644
--- a/picoquic/sender.c
+++ b/picoquic/sender.c
@@ -468,37 +468,51 @@ void picoquic_queue_for_retransmit(picoquic_cnx_t* cnx, picoquic_path_t * path_x
  */
 
 void picoquic_finalize_and_protect_packet(picoquic_cnx_t *cnx, picoquic_packet * packet, int ret, 
-    int is_cleartext_mode, uint32_t length, uint32_t header_length, uint32_t checksum_overhead,
+    uint32_t length, uint32_t header_length, uint32_t checksum_overhead,
     size_t * send_length, uint8_t * send_buffer, picoquic_path_t * path_x,
     uint64_t current_time)
 {
     if (ret == 0 && length > 0) {
         packet->length = length;
         cnx->send_sequence++;
-        /* Make sure that the payload length is encoded in the header */
-        picoquic_update_payload_length(packet->bytes, header_length, length + checksum_overhead);
 
-        if (is_cleartext_mode == 1) {
-            /* AEAD Encrypt, to the send buffer */
+        switch (packet->ptype) {
+
+        case picoquic_packet_version_negotiation:
+            /* Packet is not encrypted */
+            break;
+        case picoquic_packet_client_initial:
+        case picoquic_packet_server_stateless:
+        case picoquic_packet_handshake:
             length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
-                length, header_length, 
+                length, header_length,
                 send_buffer, cnx->aead_encrypt_cleartext_ctx, cnx->pn_enc_cleartext);
-        } else if (is_cleartext_mode == 0) {
-            /* AEAD Encrypt, to the send buffer */
-            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
-                length, header_length, 
-                send_buffer, cnx->aead_encrypt_ctx, cnx->pn_enc);
-        } else if (is_cleartext_mode == -1) {
-            /* AEAD Encrypt, to the send buffer */
+            break;
+        case picoquic_packet_0rtt_protected:
             length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
-                length, header_length, 
+                length, header_length,
                 send_buffer, cnx->aead_0rtt_encrypt_ctx, cnx->pn_enc_0rtt);
+            break;
+        case picoquic_packet_1rtt_protected_phi0:
+        case picoquic_packet_1rtt_protected_phi1:
+            length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number,
+                length, header_length,
+                send_buffer, cnx->aead_encrypt_ctx, cnx->pn_enc);
+            break;
+        default:
+            /* Packet type error. Do nothing at all. */
+            length = 0;
+            break;
         }
 
-        packet->checksum_overhead = checksum_overhead;
         *send_length = length;
 
-        picoquic_queue_for_retransmit(cnx, path_x, packet, length, current_time);
+        if (length > 0) {
+            packet->checksum_overhead = checksum_overhead;
+            picoquic_queue_for_retransmit(cnx, path_x, packet, length, current_time);
+        } else {
+            *send_length = 0;
+        }
     }
     else {
         *send_length = 0;
@@ -1080,7 +1094,7 @@ int picoquic_prepare_packet_0rtt(picoquic_cnx_t* cnx, picoquic_path_t * path_x,
     }
 
     picoquic_finalize_and_protect_packet(cnx, packet,
-        ret, -1, length, header_length, checksum_overhead,
+        ret, length, header_length, checksum_overhead,
         send_length, send_buffer, path_x, current_time);
 
     if (length > 0) {
@@ -1244,7 +1258,7 @@ int picoquic_prepare_packet_client_init(picoquic_cnx_t* cnx, picoquic_path_t * p
         ret = picoquic_prepare_packet_0rtt(cnx, path_x, packet, current_time, send_buffer, send_length);
     } else {
         picoquic_finalize_and_protect_packet(cnx, packet,
-            ret, is_cleartext_mode, length, header_length, checksum_overhead,
+            ret, length, header_length, checksum_overhead,
             send_length, send_buffer, path_x, current_time);
 
         if (cnx->cnx_state != picoquic_state_draining) {
@@ -1365,7 +1379,7 @@ int picoquic_prepare_packet_server_init(picoquic_cnx_t* cnx, picoquic_path_t * p
     }
 
     picoquic_finalize_and_protect_packet(cnx, packet,
-        ret, is_cleartext_mode, length, header_length, checksum_overhead,
+        ret, length, header_length, checksum_overhead,
         send_length, send_buffer, path_x, current_time);
 
     picoquic_cnx_set_next_wake_time(cnx, current_time);
@@ -1558,7 +1572,7 @@ int picoquic_prepare_packet_closing(picoquic_cnx_t* cnx, picoquic_path_t * path_
     }
 
     picoquic_finalize_and_protect_packet(cnx, packet,
-        ret, is_cleartext_mode, length, header_length, checksum_overhead,
+        ret, length, header_length, checksum_overhead,
         send_length, send_buffer, path_x, current_time);
 
     return ret;
@@ -1750,7 +1764,7 @@ int picoquic_prepare_packet_ready(picoquic_cnx_t* cnx, picoquic_path_t * path_x,
     }
 
     picoquic_finalize_and_protect_packet(cnx, packet,
-        ret, is_cleartext_mode, length, header_length, checksum_overhead,
+        ret, length, header_length, checksum_overhead,
         send_length, send_buffer, path_x, current_time);
 
     picoquic_cnx_set_next_wake_time(cnx, current_time);
diff --git a/picoquic/tls_api.c b/picoquic/tls_api.c
index d8b0857d..91784c89 100644
--- a/picoquic/tls_api.c
+++ b/picoquic/tls_api.c
@@ -1048,6 +1048,17 @@ void * picoquic_pn_enc_create(
     return (void *)pn_enc;
 }
 
+void * picoquic_pn_enc_create_for_test(const uint8_t * secret)
+{
+    void * ret = NULL;
+    ptls_hash_algorithm_t* algo = &ptls_openssl_sha256;
+    ptls_aead_algorithm_t* aead = &ptls_openssl_aes128gcm;
+    
+    ret = picoquic_pn_enc_create(aead, algo, (uint8_t *) secret, PICOQUIC_QUIC_BASE_LABEL);
+
+    return ret;
+}
+
 void picoquic_pn_encrypt(void *pn_enc, void * iv, void *output, const void *input, size_t len)
 {
     ptls_cipher_init((ptls_cipher_context_t *) pn_enc, iv);
@@ -1147,6 +1158,17 @@ uint32_t picoquic_aead_get_checksum_length(void* aead_context)
     return ((uint32_t)((ptls_aead_context_t*)aead_context)->algo->tag_size);
 }
 
+/* Setting of encryption contexts for test */
+void * picoquic_setup_test_aead_context(int is_encrypt, const uint8_t * secret)
+{
+    void * ret = NULL;
+    ptls_hash_algorithm_t* algo = &ptls_openssl_sha256;
+    ptls_aead_algorithm_t* aead = &ptls_openssl_aes128gcm;
+
+    ret = (void *)picoquic_aead_new(aead, algo, is_encrypt, secret, PICOQUIC_QUIC_BASE_LABEL);
+    return ret;
+}
+
 /* Computation of the encryption and decryption methods for 0-RTT data */
 
 int picoquic_setup_0RTT_aead_contexts(picoquic_cnx_t* cnx, int is_server)
diff --git a/picoquic/tls_api.h b/picoquic/tls_api.h
index 18f86261..1cc65d16 100644
--- a/picoquic/tls_api.h
+++ b/picoquic/tls_api.h
@@ -67,6 +67,9 @@ void picoquic_pn_encrypt(void *pn_enc, void * iv, void *output, const void *inpu
 
 void picoquic_pn_enc_free(void * pn_enc);
 
+void * picoquic_setup_test_aead_context(int is_encrypt, const uint8_t * secret);
+void * picoquic_pn_enc_create_for_test(const uint8_t * secret);
+
 int picoquic_compare_cleartext_aead_contexts(picoquic_cnx_t* cnx1, picoquic_cnx_t* cnx2);
 
 int picoquic_create_cnxid_reset_secret(picoquic_quic_t* quic, picoquic_connection_id_t cnx_id,
diff --git a/picoquic_t/picoquic_t.c b/picoquic_t/picoquic_t.c
index 03b4ad42..14599a9f 100644
--- a/picoquic_t/picoquic_t.c
+++ b/picoquic_t/picoquic_t.c
@@ -105,7 +105,8 @@ static const picoquic_test_def_t test_table[] = {
     { "nat_rebinding", nat_rebinding_test },
     { "nat_rebinding_loss", nat_rebinding_loss_test },
     { "spin_bit", spin_bit_test},
-    { "client_error", client_error_test }
+    { "client_error", client_error_test },
+    { "packet_enc_dec", packet_enc_dec_test}
 };
 
 static size_t const 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 87d361d2..a068808b 100644
--- a/picoquictest/cleartext_aead_test.c
+++ b/picoquictest/cleartext_aead_test.c
@@ -514,5 +514,4 @@ int cleartext_pn_enc_test()
     }
 
     return ret;
-}
-
+}
\ No newline at end of file
diff --git a/picoquictest/parseheadertest.c b/picoquictest/parseheadertest.c
index 8799c5b6..f13579a7 100644
--- a/picoquictest/parseheadertest.c
+++ b/picoquictest/parseheadertest.c
@@ -19,9 +19,10 @@
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#include "../picoquic/picoquic_internal.h"
 #include <stdlib.h>
 #include <string.h>
+#include "../picoquic/tls_api.h"
+#include "../picoquic/picoquic_internal.h"
 
 /* test vectors and corresponding structure */
 #define TEST_CNXID_LEN_BYTE 0x51
@@ -383,3 +384,264 @@ int parseheadertest()
 
     return ret;
 }
+
+
+/* Test a range of variations of packet encryption and decryption */
+int test_packet_decrypt_one(
+    picoquic_quic_t* q_server,
+    uint8_t * send_buffer,
+    size_t send_length,
+    size_t packet_length,
+    struct sockaddr * addr_from,
+    picoquic_cnx_t* cnx_target,
+    picoquic_packet_header * expected_ph,
+    int expected_return
+)
+{
+    int ret = 0;
+    int decoding_return;
+    uint64_t current_time = 0;
+    picoquic_packet_header received_ph;
+    picoquic_cnx_t* server_cnx = NULL;
+    uint32_t consumed = 0;
+
+    /* Decrypt the packet */
+    decoding_return = picoquic_parse_header_and_decrypt(q_server,
+        send_buffer, send_length, packet_length,
+        addr_from,
+        current_time, &received_ph, &server_cnx,
+        &consumed, 1);
+
+    /* verify that decryption matches original value */
+    if (decoding_return != expected_return) {
+        DBG_PRINTF("Return %x instead of %x.\n", decoding_return, expected_return);
+        ret = -1;
+    } else if (cnx_target != NULL && server_cnx != cnx_target) {
+        DBG_PRINTF("%s", "Could not retrieve the connection\n");
+        ret = -1;
+    }
+    else if (received_ph.ptype != expected_ph->ptype) {
+        DBG_PRINTF("PTYPE %x instead of %x.\n", received_ph.ptype, expected_ph->ptype);
+        ret = -1;
+    }
+    else if (received_ph.offset != expected_ph->offset) {
+        DBG_PRINTF("Offset %x instead of %x.\n", received_ph.offset, expected_ph->offset);
+        ret = -1;
+    }
+    else if (received_ph.vn != expected_ph->vn) {
+        DBG_PRINTF("Version %x instead of %x.\n", received_ph.vn, expected_ph->vn);
+        ret = -1;
+    }
+    else if (received_ph.pn64 != expected_ph->pn64) {
+        DBG_PRINTF("PN64 %llx instead of %llx.\n", (unsigned long long)received_ph.pn64, (unsigned long long)expected_ph->pn64);
+        ret = -1;
+    }
+    else if (received_ph.payload_length != expected_ph->payload_length) {
+        DBG_PRINTF("Payload length %x instead of %x.\n", received_ph.payload_length, expected_ph->payload_length);
+        ret = -1;
+    }
+    else if (picoquic_compare_connection_id(&received_ph.dest_cnx_id, &expected_ph->dest_cnx_id) != 0) {
+        DBG_PRINTF("%s", "Dest CNXID does not match.\n");
+        ret = -1;
+    }
+    else if (picoquic_compare_connection_id(&received_ph.srce_cnx_id, &expected_ph->srce_cnx_id) != 0) {
+        DBG_PRINTF("%s", "Srce CNXID does not match.\n");
+        ret = -1;
+    }
+
+    return ret;
+}
+
+int test_packet_encrypt_one(
+    struct sockaddr * addr_from,
+    picoquic_cnx_t* cnx_client,
+    picoquic_quic_t* q_server,
+    picoquic_cnx_t* server_cnx,
+    picoquic_packet_type_enum ptype,
+    uint32_t length
+)
+{
+    int ret = 0;
+    uint32_t header_length = 0;
+    uint32_t checksum_overhead = 0;
+    size_t send_length = 0;
+    uint8_t send_buffer[PICOQUIC_MAX_PACKET_SIZE];
+    picoquic_path_t * path_x = cnx_client->path[0];
+    uint64_t current_time = 0;
+    uint32_t consumed = 0;
+    picoquic_packet_header expected_header;
+    picoquic_packet * packet = (picoquic_packet *) malloc(sizeof(picoquic_packet));
+
+    if (packet == NULL) {
+        DBG_PRINTF("%s", "Out of memory\n");
+        ret = -1;
+    }
+    else {
+        memset(packet, 0, sizeof(picoquic_packet));
+        memset(packet->bytes, 0xbb, length);
+        header_length = picoquic_predict_packet_header_length(cnx_client, ptype);
+        packet->ptype = ptype;
+        packet->offset = header_length;
+        packet->length = length;
+        packet->sequence_number = cnx_client->send_sequence;
+        packet->send_path = cnx_client->path[0];
+
+        /* Create a packet with specified parameters */
+        picoquic_finalize_and_protect_packet(cnx_client, packet,
+            ret, length, header_length, checksum_overhead,
+            &send_length, send_buffer, path_x, current_time);
+
+        expected_header.ptype = packet->ptype;
+        expected_header.offset = packet->offset;
+        expected_header.pn64 = packet->sequence_number;
+        expected_header.vn = picoquic_supported_versions[cnx_client->version_index].version;
+        expected_header.payload_length = packet->length - packet->offset;
+
+        if (packet->ptype == picoquic_packet_0rtt_protected ||
+            packet->ptype == picoquic_packet_client_initial) {
+            expected_header.dest_cnx_id = cnx_client->initial_cnxid;
+        }
+        else {
+            expected_header.dest_cnx_id = cnx_client->remote_cnxid;
+        }
+
+        if (packet->ptype == picoquic_packet_1rtt_protected_phi0 ||
+            packet->ptype == picoquic_packet_1rtt_protected_phi0) {
+            expected_header.vn = 0;
+            expected_header.srce_cnx_id = picoquic_null_connection_id;
+        }
+        else {
+            expected_header.vn = picoquic_supported_versions[cnx_client->version_index].version;
+            expected_header.srce_cnx_id = cnx_client->local_cnxid;
+        }
+
+        /* Decrypt the packet */
+        ret = test_packet_decrypt_one(q_server,
+            send_buffer, send_length, send_length,
+            addr_from, server_cnx, &expected_header, 0);
+    }
+    return ret;
+}
+
+static const uint8_t test_0rtt_secret[] = {
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+    0, 1
+};
+
+static const uint8_t test_1rtt_secret[] = {
+    0, 1,
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+};
+
+static uint8_t const addr1[4] = { 10, 0, 0, 1 };
+static uint8_t const addr2[4] = { 10, 0, 0, 2 };
+
+int packet_enc_dec_test()
+{
+    int ret = 0;
+    struct sockaddr_in test_addr_c;
+    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) {
+        DBG_PRINTF("%s", "Could not create Quic contexts.\n");
+        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, picoquic_null_connection_id, picoquic_null_connection_id,
+            (struct sockaddr*)&test_addr_c, 0, 0, NULL, NULL, 1);
+        if (cnx_client == NULL) {
+            DBG_PRINTF("%s", "Could not create client connection context.\n");
+            ret = -1;
+        }
+        else {
+            ret = picoquic_start_client_cnx(cnx_client);
+        }
+    }
+
+    /* Test with a series of packets */
+    /* First, client initial */
+    if (ret == 0) {
+        ret = test_packet_encrypt_one(
+            (struct sockaddr *) &test_addr_c,
+            cnx_client, qserver, NULL, picoquic_packet_client_initial, 1256);
+    }
+    /* If that work, update the connection context */
+    if (ret == 0) {
+        cnx_server = qserver->cnx_list;
+        if (cnx_server == NULL) {
+            DBG_PRINTF("%s", "Did not create the server connection context.\n");
+            ret = -1;
+        } else {
+            /* Set the remote context ID for the client */
+            cnx_client->remote_cnxid = cnx_server->local_cnxid;
+        }
+    }
+
+    /* Try handshake packet from client */
+    if (ret == 0) {
+        ret = test_packet_encrypt_one(
+            (struct sockaddr *) &test_addr_c,
+            cnx_client, qserver, cnx_server, picoquic_packet_handshake, 1256);
+    }
+
+    /* Now try a zero RTT packet */
+    if (ret == 0) {
+        cnx_client->aead_0rtt_encrypt_ctx = picoquic_setup_test_aead_context(1, test_0rtt_secret);
+        cnx_server->aead_0rtt_decrypt_ctx = picoquic_setup_test_aead_context(0, test_0rtt_secret);
+        cnx_client->pn_enc_0rtt = picoquic_pn_enc_create_for_test(test_0rtt_secret);
+        cnx_server->pn_enc_0rtt = picoquic_pn_enc_create_for_test(test_0rtt_secret);
+
+        ret = test_packet_encrypt_one(
+            (struct sockaddr *) &test_addr_c,
+            cnx_client, qserver, cnx_server, picoquic_packet_0rtt_protected, 256);
+    }
+
+    /* And try a 1 RTT packet */
+    if (ret == 0) {
+        cnx_client->aead_encrypt_ctx = picoquic_setup_test_aead_context(1, test_1rtt_secret);
+        cnx_server->aead_decrypt_ctx = picoquic_setup_test_aead_context(0, test_1rtt_secret);
+        cnx_client->pn_enc = picoquic_pn_enc_create_for_test(test_1rtt_secret);
+        cnx_server->pn_dec = picoquic_pn_enc_create_for_test(test_1rtt_secret);
+
+        ret = test_packet_encrypt_one(
+            (struct sockaddr *) &test_addr_c,
+            cnx_client, qserver, cnx_server, picoquic_packet_1rtt_protected_phi0, 1024);
+    }
+
+    if (cnx_client != NULL) {
+        picoquic_delete_cnx(cnx_client);
+    }
+
+    if (qclient != NULL) {
+        picoquic_free(qclient);
+    }
+
+    if (qserver != NULL) {
+        picoquic_free(qserver);
+    }
+
+    return ret;
+}
\ No newline at end of file
diff --git a/picoquictest/picoquictest.h b/picoquictest/picoquictest.h
index 3a283584..8233626a 100644
--- a/picoquictest/picoquictest.h
+++ b/picoquictest/picoquictest.h
@@ -100,6 +100,7 @@ int nat_rebinding_test();
 int nat_rebinding_loss_test();
 int spin_bit_test();
 int client_error_test();
+int packet_enc_dec_test();
 
 #ifdef __cplusplus
 }
-- 
GitLab