diff --git a/include/noise/protocol/cipherstate.h b/include/noise/protocol/cipherstate.h index 0b64973a..10c3ddcb 100644 --- a/include/noise/protocol/cipherstate.h +++ b/include/noise/protocol/cipherstate.h @@ -51,6 +51,7 @@ int noise_cipherstate_decrypt(NoiseCipherState *state, NoiseBuffer *buffer); int noise_cipherstate_set_nonce(NoiseCipherState *state, uint64_t nonce); int noise_cipherstate_get_max_key_length(void); int noise_cipherstate_get_max_mac_length(void); +int noise_cipherstate_rekey(NoiseCipherState* state); #ifdef __cplusplus }; diff --git a/src/protocol/cipherstate.c b/src/protocol/cipherstate.c index 2f484388..08dcf6f1 100644 --- a/src/protocol/cipherstate.c +++ b/src/protocol/cipherstate.c @@ -554,4 +554,35 @@ int noise_cipherstate_get_max_mac_length(void) return NOISE_MAX_MAC_LEN; } +/** + * \brief Rekeys the cipherstate as described in Noise protocol spec section 4.2. + */ +int noise_cipherstate_rekey(NoiseCipherState* state) +{ + int err; + uint64_t n; + uint8_t new_key[NOISE_MAX_KEY_LEN + NOISE_MAX_MAC_LEN]; + if (!state) + return NOISE_ERROR_INVALID_STATE; + if (!state->has_key) + return NOISE_ERROR_INVALID_STATE; + + memset(new_key, 0, sizeof(new_key)); + + /* we call encrypt with max nonce, then reset to the current value */ + n = state->n; + state->n = 0xFFFFFFFFFFFFFFFFULL; + + /* Encrypt the plaintext and authenticate it */ + err = (*(state->encrypt))(state, NULL, 0, new_key, state->key_len); + state->n = n; + + if (err != NOISE_ERROR_NONE) + return err; + + (*(state->init_key))(state, new_key); + + return NOISE_ERROR_NONE; +} + /**@}*/ diff --git a/tests/unit/test-cipherstate.c b/tests/unit/test-cipherstate.c index 34abcc84..4d4e829c 100644 --- a/tests/unit/test-cipherstate.c +++ b/tests/unit/test-cipherstate.c @@ -136,6 +136,10 @@ static void check_cipher(int id, size_t key_len, size_t mac_len, to encrypt one more block, and then the next request will be rejected */ compare(noise_cipherstate_set_nonce(state, 0xFFFFFFFFFFFFFFFEULL), NOISE_ERROR_NONE); + /* Rekey, which should not affect the nonce and should still allow us to + encrypt one more block. */ + compare(noise_cipherstate_rekey(state), + NOISE_ERROR_NONE); noise_buffer_set_inout(mbuf, buffer, pt_len, sizeof(buffer)); compare(noise_cipherstate_encrypt_with_ad(state, a, ad_len, &mbuf), NOISE_ERROR_NONE);