From 5b8e3526b10047e0cc51a1db62d082807adfe2ed Mon Sep 17 00:00:00 2001 From: huitema <huitema@huitema.net> Date: Sat, 3 Feb 2018 15:53:51 -1000 Subject: [PATCH] Generation of PN encoding contexts, and corresponding unit tests. --- UnitTest1/unittest1.cpp | 14 ++++ picoquic/picoquic_internal.h | 5 ++ picoquic/quicctx.c | 60 +++++++++++++ picoquic/tls_api.c | 39 +++++++++ picoquic/tls_api.h | 2 + picoquic_t/picoquic_t.c | 4 +- picoquictest/cleartext_aead_test.c | 121 +++++++++++++++++++++++++++ picoquictest/picoquictest.h | 2 + picoquictest/picoquictest_internal.h | 3 + picoquictest/tls_api_test.c | 50 +++++++++++ 10 files changed, 299 insertions(+), 1 deletion(-) diff --git a/UnitTest1/unittest1.cpp b/UnitTest1/unittest1.cpp index 3c36e276..23711c84 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 73a275fa..b9ce7856 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 d29fa7c5..5d167f62 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 640638db..84015de6 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 42c143c9..8f2b0152 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 d77b16cc..1e68bdf1 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 4b9aab26..988060ac 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 437eb5eb..ca093790 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 515b1dd4..2e5d85f0 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 3c62cd02..fdd78da1 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 -- GitLab