refactor(crypto): rename noise implementation
What changed, and why it matters
This commit is a simple rename of the cryptographic module from 'noise' to 'noise_kk1' across file names, function names, constants, and build scripts. No security behavior changes are visible in the diff. It is a code-maintenance refactor, not a security fix.
No security action required. Treat as routine refactoring. If reviewing a larger series, verify that any actual security fixes are in adjacent commits, as this commit contains none.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change renames crypto/noise.c/h to crypto/noise_kk1.c/h and updates all identifiers (NOISE_ -> NOISE_KK1_, noise_ -> noise_kk1_). Build files and call sites in prodtest secure_channel and test_check.c are adjusted accordingly. The implementation logic, constants, and data structures remain identical except for the naming. No functional or security-relevant code changes are present.
Changed components
crypto/noise_kk1.ccrypto/noise_kk1.hcore/embed/projects/prodtest/cmd/secure_channel.ccore/embed/projects/prodtest/cmd/secure_channel.hcore/embed/projects/prodtest/cmd/prodtest_secrets.ccore/embed/projects/prodtest/cmd/prodtest_tropic.ccore/SConscript.prodtestcore/SConscript.prodtest_emucore/embed/rtl/build.rscrypto/Makefilecrypto/tests/test_check.cInspect captured patch +524 / −519
diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest
index 21c99e2e..5e862ba0 100644
--- a/core/SConscript.prodtest
+++ b/core/SConscript.prodtest
@@ -109,7 +109,7 @@ SOURCE_MOD_CRYPTO += [
'vendor/trezor-crypto/hmac.c',
'vendor/trezor-crypto/hmac_drbg.c',
'vendor/trezor-crypto/memzero.c',
- 'vendor/trezor-crypto/noise.c',
+ 'vendor/trezor-crypto/noise_kk1.c',
'vendor/trezor-crypto/nist256p1.c',
'vendor/trezor-crypto/rand.c',
'vendor/trezor-crypto/ripemd160.c',
diff --git a/core/SConscript.prodtest_emu b/core/SConscript.prodtest_emu
index ba049e93..d1b600b9 100644
--- a/core/SConscript.prodtest_emu
+++ b/core/SConscript.prodtest_emu
@@ -91,7 +91,7 @@ SOURCE_MOD_CRYPTO += [
'vendor/trezor-crypto/hmac.c',
'vendor/trezor-crypto/hmac_drbg.c',
'vendor/trezor-crypto/memzero.c',
- 'vendor/trezor-crypto/noise.c',
+ 'vendor/trezor-crypto/noise_kk1.c',
'vendor/trezor-crypto/nist256p1.c',
'vendor/trezor-crypto/rand.c',
'vendor/trezor-crypto/rand_insecure.c',
diff --git a/core/embed/projects/prodtest/cmd/prodtest_secrets.c b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
index 76b128c2..a2dd602d 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_secrets.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
@@ -197,7 +197,7 @@ static void prodtest_secrets_get_mcu_device_key(cli_t* cli) {
goto cleanup;
}
- uint8_t output[sizeof(mcu_public) + NOISE_TAG_SIZE] = {0};
+ uint8_t output[sizeof(mcu_public) + NOISE_KK1_TAG_SIZE] = {0};
if (!secure_channel_encrypt(mcu_public, sizeof(mcu_public), NULL, 0,
output)) {
// `secure_channel_handshake_2()` might not have been called
diff --git a/core/embed/projects/prodtest/cmd/prodtest_tropic.c b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
index 823331bb..6c25a350 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_tropic.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_tropic.c
@@ -563,7 +563,7 @@ static void prodtest_tropic_get_access_credential(cli_t* cli) {
goto cleanup;
}
- uint8_t output[sizeof(unprivileged_private) + NOISE_TAG_SIZE] = {0};
+ uint8_t output[sizeof(unprivileged_private) + NOISE_KK1_TAG_SIZE] = {0};
if (!secure_channel_encrypt((uint8_t*)unprivileged_private,
sizeof(unprivileged_private), tropic_public,
sizeof(curve25519_key), output)) {
@@ -592,7 +592,7 @@ static void prodtest_tropic_get_fido_masking_key(cli_t* cli) {
goto cleanup;
}
- uint8_t output[sizeof(fido_masking_key) + NOISE_TAG_SIZE] = {0};
+ uint8_t output[sizeof(fido_masking_key) + NOISE_KK1_TAG_SIZE] = {0};
if (!secure_channel_encrypt(fido_masking_key, sizeof(fido_masking_key), NULL,
0, output)) {
// `secure_channel_handshake_2()` might not have been called
diff --git a/core/embed/projects/prodtest/cmd/secure_channel.c b/core/embed/projects/prodtest/cmd/secure_channel.c
index bd516629..b26b4dcd 100644
--- a/core/embed/projects/prodtest/cmd/secure_channel.c
+++ b/core/embed/projects/prodtest/cmd/secure_channel.c
@@ -35,7 +35,7 @@ typedef enum {
} noise_state_t;
static noise_state_t noise_state = SECURE_CHANNEL_STATE_0;
-static noise_context_t noise_context = {0};
+static noise_kk1_context_t noise_context = {0};
static curve25519_key prodtest_private_key = {
0xc8, 0x56, 0x36, 0x89, 0xf5, 0xa6, 0x70, 0x66, 0x43, 0xeb, 0xe3,
@@ -56,8 +56,8 @@ static curve25519_key hsm_public_keys[] = {
};
bool secure_channel_handshake_1(uint8_t output[SECURE_CHANNEL_OUTPUT_SIZE]) {
- if (!noise_create_handshake_request(&noise_context,
- (noise_request_t*)output)) {
+ if (!noise_kk1_create_handshake_request(&noise_context,
+ (noise_kk1_request_t*)output)) {
return false;
}
@@ -72,10 +72,10 @@ bool secure_channel_handshake_2(
return false;
}
- if (!noise_handle_handshake_response_multiple_keys(
+ if (!noise_kk1_handle_handshake_response_multiple_keys(
&noise_context, prodtest_private_key, hsm_public_keys,
sizeof(hsm_public_keys) / sizeof(hsm_public_keys[0]),
- (const noise_response_t*)input)) {
+ (const noise_kk1_response_t*)input)) {
return false;
}
@@ -91,7 +91,7 @@ bool secure_channel_encrypt(const uint8_t* plaintext, size_t plaintext_length,
return false;
}
- return noise_send_message(&noise_context, associated_data,
- associated_data_length, plaintext, plaintext_length,
- ciphertext);
+ return noise_kk1_send_message(&noise_context, associated_data,
+ associated_data_length, plaintext,
+ plaintext_length, ciphertext);
}
diff --git a/core/embed/projects/prodtest/cmd/secure_channel.h b/core/embed/projects/prodtest/cmd/secure_channel.h
index e14a0902..6807a0e5 100644
--- a/core/embed/projects/prodtest/cmd/secure_channel.h
+++ b/core/embed/projects/prodtest/cmd/secure_channel.h
@@ -19,11 +19,11 @@
#pragma once
-#include "noise.h"
+#include "noise_kk1.h"
-#define SECURE_CHANNEL_INPUT_SIZE (sizeof(noise_response_t))
-#define SECURE_CHANNEL_OUTPUT_SIZE (sizeof(noise_request_t))
-#define SECURE_CHANNEL_TAG_SIZE (NOISE_TAG_SIZE)
+#define SECURE_CHANNEL_INPUT_SIZE (sizeof(noise_kk1_response_t))
+#define SECURE_CHANNEL_OUTPUT_SIZE (sizeof(noise_kk1_request_t))
+#define SECURE_CHANNEL_TAG_SIZE (NOISE_KK1_TAG_SIZE)
bool secure_channel_handshake_1(uint8_t output[SECURE_CHANNEL_OUTPUT_SIZE]);
bool secure_channel_handshake_2(const uint8_t input[SECURE_CHANNEL_INPUT_SIZE]);
diff --git a/core/embed/rtl/build.rs b/core/embed/rtl/build.rs
index 6299f66a..80bb62a8 100644
--- a/core/embed/rtl/build.rs
+++ b/core/embed/rtl/build.rs
@@ -205,7 +205,7 @@ fn add_crypto(lib: &mut xbuild::CLibrary) -> Result<()> {
}
if cfg!(feature = "noise") {
- lib.add_sources_in_dir_with_attrs(crypto_path, ["noise.c"], Some(crypto_attrs.clone()));
+ lib.add_sources_in_dir_with_attrs(crypto_path, ["noise_kk1.c"], Some(crypto_attrs.clone()));
}
if cfg!(feature = "secp256k1_zkp") {
diff --git a/crypto/Makefile b/crypto/Makefile
index c2257dd9..dd9e9976 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -112,7 +112,7 @@ SRCS += ed25519-donna/ed25519.c ed25519-donna/curve25519-donna-scalarmult-base.
SRCS += monero/base58.c
SRCS += monero/serialize.c
SRCS += monero/xmr.c
-SRCS += noise.c
+SRCS += noise_kk1.c
SRCS += blake256.c
SRCS += blake2b.c blake2s.c
SRCS += chacha_drbg.c
diff --git a/crypto/noise.c b/crypto/noise.c
deleted file mode 100644
index eff0e714..00000000
--- a/crypto/noise.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/**
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
- * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "noise.h"
-#include <string.h>
-
-#include "aes/aesgcm.h"
-#include "ed25519-donna/ed25519.h"
-#include "hmac.h"
-#include "memzero.h"
-#include "rand.h"
-#include "sha2.h"
-
-static uint8_t protocol_name[SHA256_DIGEST_LENGTH] = {
- 'N', 'o', 'i', 's', 'e', '_', 'K', 'K', '1', '_', '2',
- '5', '5', '1', '9', '_', 'A', 'E', 'S', 'G', 'C', 'M',
- '_', 'S', 'H', 'A', '2', '5', '6', 0x00, 0x00, 0x00};
-
-static bool encrypt(const uint8_t key[NOISE_KEY_SIZE],
- const uint8_t nonce[NOISE_NONCE_SIZE],
- const uint8_t *associated_data,
- size_t associated_data_length, const uint8_t *plaintext,
- size_t plaintext_length, uint8_t *ciphertext) {
- // ciphertext = AES-GCM-Encrypt(key, nonce, associated_data, plaintext)
- gcm_ctx ctx = {0};
- if (gcm_init_and_key(key, NOISE_KEY_SIZE, &ctx) != RETURN_GOOD) {
- return false;
- }
-
- memcpy(ciphertext, plaintext, plaintext_length);
-
- if (gcm_encrypt_message(nonce, NOISE_NONCE_SIZE, associated_data,
- associated_data_length, ciphertext, plaintext_length,
- ciphertext + plaintext_length, NOISE_TAG_SIZE,
- &ctx) != RETURN_GOOD) {
- memzero(&ctx, sizeof(ctx));
- memzero(ciphertext, plaintext_length);
- return false;
- }
- memzero(&ctx, sizeof(ctx));
-
- return true;
-}
-
-static bool decrypt(const uint8_t key[NOISE_KEY_SIZE],
- const uint8_t nonce[NOISE_NONCE_SIZE],
- const uint8_t *associated_data,
- size_t associated_data_length, const uint8_t *ciphertext,
- size_t ciphertext_length, uint8_t *plaintext) {
- // plaintext = AES-GCM-Decrypt(key, nonce, associated_data, ciphertext)
- if (ciphertext_length < NOISE_TAG_SIZE) {
- return false;
- }
- const size_t plaintext_length = ciphertext_length - NOISE_TAG_SIZE;
-
- gcm_ctx ctx = {0};
- if (gcm_init_and_key(key, NOISE_KEY_SIZE, &ctx) != RETURN_GOOD) {
- return false;
- }
-
- memcpy(plaintext, ciphertext, plaintext_length);
-
- if (gcm_decrypt_message(nonce, NOISE_NONCE_SIZE, associated_data,
- associated_data_length, plaintext, plaintext_length,
- ciphertext + plaintext_length, NOISE_TAG_SIZE,
- &ctx) != RETURN_GOOD) {
- memzero(&ctx, sizeof(ctx));
- memzero(plaintext, plaintext_length);
- return false;
- }
- memzero(&ctx, sizeof(ctx));
-
- return true;
-}
-
-static void mix_hash(uint8_t hash[SHA256_DIGEST_LENGTH], const uint8_t *data,
- size_t data_length) {
- // hash = SHA256(hash || data)
- SHA256_CTX sha256_ctx;
- sha256_Init(&sha256_ctx);
- sha256_Update(&sha256_ctx, hash, SHA256_DIGEST_LENGTH);
- sha256_Update(&sha256_ctx, data, data_length);
- sha256_Final(&sha256_ctx, hash);
-}
-
-static void hkdf(const uint8_t *salt, size_t salt_length, const uint8_t *key,
- size_t key_length, uint8_t output1[SHA256_DIGEST_LENGTH],
- uint8_t output2[SHA256_DIGEST_LENGTH]) {
- // output1 || output2 = HKDF(salt, key, output_length=2*SHA256_DIGEST_LENGTH)
- uint8_t prk[SHA256_DIGEST_LENGTH] = {0};
- hmac_sha256(salt, salt_length, key, key_length, prk);
-
- uint8_t message[SHA256_DIGEST_LENGTH + 1] = {0};
- message[0] = 1;
- hmac_sha256(prk, sizeof(prk), message, 1, output1);
-
- if (output2) {
- memcpy(message, output1, SHA256_DIGEST_LENGTH);
- message[SHA256_DIGEST_LENGTH] = 2;
- hmac_sha256(prk, sizeof(prk), message, SHA256_DIGEST_LENGTH + 1, output2);
- }
-
- memzero(message, sizeof(message));
- memzero(prk, sizeof(prk));
-}
-
-static void mix_key(uint8_t chaining_key[SHA256_DIGEST_LENGTH],
- curve25519_key input_key,
- uint8_t output_key[NOISE_KEY_SIZE]) {
- // chaining_key || output_key =
- // HKDF(salt=chaining_key, key=input_key, output_length=2*NOISE_KEY_SIZE)
- hkdf(chaining_key, SHA256_DIGEST_LENGTH, input_key, sizeof(curve25519_key),
- chaining_key, output_key);
- _Static_assert(NOISE_KEY_SIZE == SHA256_DIGEST_LENGTH,
- "output_key must be truncated to NOISE_KEY_SIZE");
-}
-
-void split(uint8_t chaining_key[SHA256_DIGEST_LENGTH],
- uint8_t output1[NOISE_KEY_SIZE], uint8_t output2[NOISE_KEY_SIZE]) {
- // output1 || output2 =
- // HKDF(salt=chaining_key, key=b"", output_length=2*NOISE_KEY_SIZE)
- hkdf(chaining_key, SHA256_DIGEST_LENGTH, NULL, 0, output1, output2);
- _Static_assert(NOISE_KEY_SIZE == SHA256_DIGEST_LENGTH,
- "output1 and output2 must be truncated to NOISE_KEY_SIZE");
-}
-
-static bool increase_nonce(uint8_t nonce[NOISE_NONCE_SIZE]) {
- // The first 4 bytes of the nonce are zeros
- // The last 8 bytes of the nonce are a big-endian encoded counter
- for (int i = NOISE_NONCE_SIZE - 1; i >= 4; i--) {
- nonce[i]++;
- if (nonce[i] != 0) {
- return true;
- }
- }
-
- // Nonce overflow
- return false;
-}
-
-bool noise_create_handshake_request(noise_context_t *ctx,
- noise_request_t *request) {
- memzero(ctx, sizeof(*ctx));
- ctx->initialized = false;
-
- random_buffer(ctx->initiator_ephemeral_private_key,
- sizeof(ctx->initiator_ephemeral_private_key));
- curve25519_scalarmult_basepoint(request->initiator_ephemeral_public_key,
- ctx->initiator_ephemeral_private_key);
- return true;
-}
-
-bool noise_handle_handshake_request(noise_context_t *ctx,
- const curve25519_key initiator_public_key,
- const curve25519_key responder_private_key,
- const noise_request_t *request,
- noise_response_t *response) {
- memzero(ctx, sizeof(*ctx));
-
- curve25519_key responder_public_key = {0};
- curve25519_scalarmult_basepoint(responder_public_key, responder_private_key);
-
- curve25519_key responder_ephemeral_private_key = {0};
- random_buffer(responder_ephemeral_private_key,
- sizeof(responder_ephemeral_private_key));
- curve25519_key responder_ephemeral_public_key = {0};
- curve25519_scalarmult_basepoint(responder_ephemeral_public_key,
- responder_ephemeral_private_key);
-
- uint8_t handshake_hash[SHA256_DIGEST_LENGTH] = {0};
- memcpy(handshake_hash, protocol_name, sizeof(protocol_name));
- mix_hash(handshake_hash, NULL, 0);
- mix_hash(handshake_hash, initiator_public_key, sizeof(curve25519_key));
- mix_hash(handshake_hash, responder_public_key, sizeof(curve25519_key));
- mix_hash(handshake_hash, request->initiator_ephemeral_public_key,
- sizeof(curve25519_key));
- mix_hash(handshake_hash, NULL, 0); // Empty message
- mix_hash(handshake_hash, responder_ephemeral_public_key,
- sizeof(curve25519_key));
-
- curve25519_key shared_secret = {0};
- uint8_t chaining_key[SHA256_DIGEST_LENGTH] = {0};
- uint8_t kauth[NOISE_KEY_SIZE] = {0};
- memcpy(chaining_key, protocol_name, sizeof(protocol_name));
- curve25519_scalarmult(shared_secret, responder_ephemeral_private_key,
- request->initiator_ephemeral_public_key);
- mix_key(chaining_key, shared_secret, NULL);
- curve25519_scalarmult(shared_secret, responder_ephemeral_private_key,
- initiator_public_key);
- memzero(responder_ephemeral_private_key,
- sizeof(responder_ephemeral_private_key));
- mix_key(chaining_key, shared_secret, NULL);
- curve25519_scalarmult(shared_secret, responder_private_key,
- request->initiator_ephemeral_public_key);
- mix_key(chaining_key, shared_secret, kauth);
- memzero(shared_secret, sizeof(shared_secret));
- split(chaining_key, ctx->decryption_key, ctx->encryption_key);
- memzero(chaining_key, sizeof(chaining_key));
-
- memcpy(response, responder_ephemeral_public_key, sizeof(curve25519_key));
-
- uint8_t zero_nonce[NOISE_NONCE_SIZE] = {0};
- encrypt(kauth, zero_nonce, handshake_hash, sizeof(handshake_hash), NULL, 0,
- response->tag);
- memzero(kauth, sizeof(kauth));
-
- // This is unnecessary, as the handshake hash is no longer used.
- // mix_hash(handshake_hash, response->tag, sizeof(response->tag));
-
- memset(ctx->encryption_nonce, 0, NOISE_NONCE_SIZE);
- memset(ctx->decryption_nonce, 0, NOISE_NONCE_SIZE);
-
- ctx->initialized = true;
-
- return true;
-}
-
-bool noise_handle_handshake_response(noise_context_t *ctx,
- const curve25519_key initiator_private_key,
- const curve25519_key responder_public_key,
- const noise_response_t *response) {
- curve25519_key initiator_public_key = {0};
- curve25519_scalarmult_basepoint(initiator_public_key, initiator_private_key);
-
- curve25519_key initiator_ephemeral_public_key = {0};
- curve25519_scalarmult_basepoint(initiator_ephemeral_public_key,
- ctx->initiator_ephemeral_private_key);
-
- uint8_t handshake_hash[SHA256_DIGEST_LENGTH] = {0};
- memcpy(handshake_hash, protocol_name, sizeof(protocol_name));
- mix_hash(handshake_hash, NULL, 0);
- mix_hash(handshake_hash, initiator_public_key, sizeof(curve25519_key));
- mix_hash(handshake_hash, responder_public_key, sizeof(curve25519_key));
- mix_hash(handshake_hash, initiator_ephemeral_public_key,
- sizeof(curve25519_key));
- mix_hash(handshake_hash, NULL, 0); // Empty message
- mix_hash(handshake_hash, response->responder_ephemeral_public_key,
- sizeof(curve25519_key));
-
- curve25519_key shared_secret = {0};
- uint8_t chaining_key[SHA256_DIGEST_LENGTH] = {0};
- uint8_t kauth[NOISE_KEY_SIZE] = {0};
- memcpy(chaining_key, protocol_name, sizeof(protocol_name));
- curve25519_scalarmult(shared_secret, ctx->initiator_ephemeral_private_key,
- response->responder_ephemeral_public_key);
- mix_key(chaining_key, shared_secret, NULL);
- curve25519_scalarmult(shared_secret, initiator_private_key,
- response->responder_ephemeral_public_key);
- mix_key(chaining_key, shared_secret, NULL);
- curve25519_scalarmult(shared_secret, ctx->initiator_ephemeral_private_key,
- responder_public_key);
- memzero(ctx->initiator_ephemeral_private_key,
- sizeof(ctx->initiator_ephemeral_private_key));
- mix_key(chaining_key, shared_secret, kauth);
- memzero(shared_secret, sizeof(shared_secret));
- split(chaining_key, ctx->encryption_key, ctx->decryption_key);
- memzero(chaining_key, sizeof(chaining_key));
-
- uint8_t zero_nonce[NOISE_NONCE_SIZE] = {0};
- if (!decrypt(kauth, zero_nonce, handshake_hash, sizeof(handshake_hash),
- response->tag, NOISE_TAG_SIZE, NULL)) {
- // Wrong tag
- memzero(kauth, sizeof(kauth));
- return false;
- }
- memzero(kauth, sizeof(kauth));
-
- // This is unnecessary, as the handshake hash is no longer used.
- // mix_hash(handshake_hash, response->tag, sizeof(response->tag));
-
- memset(ctx->encryption_nonce, 0, NOISE_NONCE_SIZE);
- memset(ctx->decryption_nonce, 0, NOISE_NONCE_SIZE);
-
- ctx->initialized = true;
-
- return true;
-}
-
-bool noise_send_message(noise_context_t *ctx, const uint8_t *associated_data,
- size_t associated_data_length, const uint8_t *plaintext,
- size_t plaintext_length, uint8_t *ciphertext) {
- if (!ctx->initialized) {
- return false;
- }
- if (!encrypt(ctx->encryption_key, ctx->encryption_nonce, associated_data,
- associated_data_length, plaintext, plaintext_length,
- ciphertext)) {
- return false;
- }
- if (!increase_nonce(ctx->encryption_nonce)) {
- // Nonce overflow
- memzero(ctx, sizeof(*ctx));
- ctx->initialized = false;
- return false;
- }
-
- return true;
-}
-
-bool noise_receive_message(noise_context_t *ctx, const uint8_t *associated_data,
- size_t associated_data_length,
- const uint8_t *ciphertext, size_t ciphertext_length,
- uint8_t *plaintext) {
- if (!ctx->initialized) {
- return false;
- }
- if (!decrypt(ctx->decryption_key, ctx->decryption_nonce, associated_data,
- associated_data_length, ciphertext, ciphertext_length,
- plaintext)) {
- // Wrong tag
- return false;
- }
- if (!increase_nonce(ctx->decryption_nonce)) {
- // Nonce overflow
- memzero(ctx, sizeof(*ctx));
- ctx->initialized = false;
- return false;
- }
- return true;
-}
-
-bool noise_handle_handshake_response_multiple_keys(
- noise_context_t *ctx, const curve25519_key initiator_private_key,
- const curve25519_key *responder_public_keys,
- size_t responder_public_keys_count, const noise_response_t *response) {
- curve25519_key ephemeral_key_backup = {0};
- memcpy(ephemeral_key_backup, ctx->initiator_ephemeral_private_key,
- sizeof(ephemeral_key_backup));
- for (size_t i = 0; i < responder_public_keys_count; i++) {
- memcpy(ctx->initiator_ephemeral_private_key, ephemeral_key_backup,
- sizeof(ephemeral_key_backup));
- if (noise_handle_handshake_response(ctx, initiator_private_key,
- responder_public_keys[i], response)) {
- memzero(ephemeral_key_backup, sizeof(ephemeral_key_backup));
- return true;
- }
- }
- return false;
-}
diff --git a/crypto/noise.h b/crypto/noise.h
deleted file mode 100644
index d3d66f76..00000000
--- a/crypto/noise.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
- * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef __NOISE_H__
-#define __NOISE_H__
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "ed25519-donna/ed25519.h"
-
-// Noise protocol using KK1 handshake pattern and X25519, AES-GCM and SHA256.
-// The handshake messages and the prologue are empty.
-
-#define NOISE_KEY_SIZE 32
-#define NOISE_NONCE_SIZE 12
-#define NOISE_TAG_SIZE 16
-
-typedef struct {
- curve25519_key initiator_ephemeral_private_key; // This is used only by the
- // initiator during handshake
- uint8_t encryption_nonce[NOISE_NONCE_SIZE];
- uint8_t decryption_nonce[NOISE_NONCE_SIZE];
- // There is a time-memory trade-off between storing encryption/decryption keys
- // and storing encryption/decryption contexts, we choose to optimize for
- // memory usage by storing the keys
- uint8_t encryption_key[NOISE_KEY_SIZE];
- uint8_t decryption_key[NOISE_KEY_SIZE];
- bool initialized;
-} noise_context_t;
-
-typedef struct {
- curve25519_key initiator_ephemeral_public_key;
-} noise_request_t;
-
-typedef struct {
- curve25519_key responder_ephemeral_public_key;
- uint8_t tag[NOISE_TAG_SIZE];
-} noise_response_t;
-
-// This is called by the initiator to initialize the context and create the
-// handshake request
-bool noise_create_handshake_request(noise_context_t* ctx,
- noise_request_t* request);
-
-// This is called by the responder to initialize the context, handle the
-// handshake request and create the handshake response
-bool noise_handle_handshake_request(noise_context_t* ctx,
- const curve25519_key initiator_public_key,
- const curve25519_key responder_private_key,
- const noise_request_t* request,
- noise_response_t* response);
-
-// This is called by the initiator to handle the handshake response
-bool noise_handle_handshake_response(noise_context_t* ctx,
- const curve25519_key initiator_private_key,
- const curve25519_key responder_public_key,
- const noise_response_t* response);
-
-// This is called by the initiator to handle the handshake response
-// This is a wrapper above noise_handle_handshake_response that allows to pass
-// multiple responder public keys, the first key that succeeds in paring is
-// used
-bool noise_handle_handshake_response_multiple_keys(
- noise_context_t* ctx, const curve25519_key initiator_private_key,
- const curve25519_key* responder_public_keys,
- size_t responder_public_keys_count, const noise_response_t* response);
-
-// This is called by both the initiator and responder to send a message
-// len(ciphertext) == plaintext_length + NOISE_TAG_SIZE
-// The official Noise specification requires the associated_data to be empty
-bool noise_send_message(noise_context_t* ctx, const uint8_t* associated_data,
- size_t associated_data_length, const uint8_t* plaintext,
- size_t plaintext_length, uint8_t* ciphertext);
-
-// This is called by both the initiator and responder to receive a message
-// len(plaintext) == ciphertext_length - NOISE_TAG_SIZE
-// The official Noise specification requires the associated_data to be empty
-bool noise_receive_message(noise_context_t* ctx, const uint8_t* associated_data,
- size_t associated_data_length,
- const uint8_t* ciphertext, size_t ciphertext_length,
- uint8_t* plaintext);
-
-#endif // __NOISE_H__
diff --git a/crypto/noise_kk1.c b/crypto/noise_kk1.c
new file mode 100644
index 00000000..c8191656
--- /dev/null
+++ b/crypto/noise_kk1.c
@@ -0,0 +1,359 @@
+/**
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "noise_kk1.h"
+#include <string.h>
+
+#include "aes/aesgcm.h"
+#include "ed25519-donna/ed25519.h"
+#include "hmac.h"
+#include "memzero.h"
+#include "rand.h"
+#include "sha2.h"
+
+static uint8_t protocol_name[SHA256_DIGEST_LENGTH] = {
+ 'N', 'o', 'i', 's', 'e', '_', 'K', 'K', '1', '_', '2',
+ '5', '5', '1', '9', '_', 'A', 'E', 'S', 'G', 'C', 'M',
+ '_', 'S', 'H', 'A', '2', '5', '6', 0x00, 0x00, 0x00};
+
+static bool encrypt(const uint8_t key[NOISE_KK1_KEY_SIZE],
+ const uint8_t nonce[NOISE_KK1_NONCE_SIZE],
+ const uint8_t *associated_data,
+ size_t associated_data_length, const uint8_t *plaintext,
+ size_t plaintext_length, uint8_t *ciphertext) {
+ // ciphertext = AES-GCM-Encrypt(key, nonce, associated_data, plaintext)
+ gcm_ctx ctx = {0};
+ if (gcm_init_and_key(key, NOISE_KK1_KEY_SIZE, &ctx) != RETURN_GOOD) {
+ return false;
+ }
+
+ memcpy(ciphertext, plaintext, plaintext_length);
+
+ if (gcm_encrypt_message(nonce, NOISE_KK1_NONCE_SIZE, associated_data,
+ associated_data_length, ciphertext, plaintext_length,
+ ciphertext + plaintext_length, NOISE_KK1_TAG_SIZE,
+ &ctx) != RETURN_GOOD) {
+ memzero(&ctx, sizeof(ctx));
+ memzero(ciphertext, plaintext_length);
+ return false;
+ }
+ memzero(&ctx, sizeof(ctx));
+
+ return true;
+}
+
+static bool decrypt(const uint8_t key[NOISE_KK1_KEY_SIZE],
+ const uint8_t nonce[NOISE_KK1_NONCE_SIZE],
+ const uint8_t *associated_data,
+ size_t associated_data_length, const uint8_t *ciphertext,
+ size_t ciphertext_length, uint8_t *plaintext) {
+ // plaintext = AES-GCM-Decrypt(key, nonce, associated_data, ciphertext)
+ if (ciphertext_length < NOISE_KK1_TAG_SIZE) {
+ return false;
+ }
+ const size_t plaintext_length = ciphertext_length - NOISE_KK1_TAG_SIZE;
+
+ gcm_ctx ctx = {0};
+ if (gcm_init_and_key(key, NOISE_KK1_KEY_SIZE, &ctx) != RETURN_GOOD) {
+ return false;
+ }
+
+ memcpy(plaintext, ciphertext, plaintext_length);
+
+ if (gcm_decrypt_message(nonce, NOISE_KK1_NONCE_SIZE, associated_data,
+ associated_data_length, plaintext, plaintext_length,
+ ciphertext + plaintext_length, NOISE_KK1_TAG_SIZE,
+ &ctx) != RETURN_GOOD) {
+ memzero(&ctx, sizeof(ctx));
+ memzero(plaintext, plaintext_length);
+ return false;
+ }
+ memzero(&ctx, sizeof(ctx));
+
+ return true;
+}
+
+static void mix_hash(uint8_t hash[SHA256_DIGEST_LENGTH], const uint8_t *data,
+ size_t data_length) {
+ // hash = SHA256(hash || data)
+ SHA256_CTX sha256_ctx;
+ sha256_Init(&sha256_ctx);
+ sha256_Update(&sha256_ctx, hash, SHA256_DIGEST_LENGTH);
+ sha256_Update(&sha256_ctx, data, data_length);
+ sha256_Final(&sha256_ctx, hash);
+}
+
+static void hkdf(const uint8_t *salt, size_t salt_length, const uint8_t *key,
+ size_t key_length, uint8_t output1[SHA256_DIGEST_LENGTH],
+ uint8_t output2[SHA256_DIGEST_LENGTH]) {
+ // output1 || output2 = HKDF(salt, key, output_length=2*SHA256_DIGEST_LENGTH)
+ uint8_t prk[SHA256_DIGEST_LENGTH] = {0};
+ hmac_sha256(salt, salt_length, key, key_length, prk);
+
+ uint8_t message[SHA256_DIGEST_LENGTH + 1] = {0};
+ message[0] = 1;
+ hmac_sha256(prk, sizeof(prk), message, 1, output1);
+
+ if (output2) {
+ memcpy(message, output1, SHA256_DIGEST_LENGTH);
+ message[SHA256_DIGEST_LENGTH] = 2;
+ hmac_sha256(prk, sizeof(prk), message, SHA256_DIGEST_LENGTH + 1, output2);
+ }
+
+ memzero(message, sizeof(message));
+ memzero(prk, sizeof(prk));
+}
+
+static void mix_key(uint8_t chaining_key[SHA256_DIGEST_LENGTH],
+ curve25519_key input_key,
+ uint8_t output_key[NOISE_KK1_KEY_SIZE]) {
+ // chaining_key || output_key =
+ // HKDF(salt=chaining_key, key=input_key,
+ // output_length=2*NOISE_KK1_KEY_SIZE)
+ hkdf(chaining_key, SHA256_DIGEST_LENGTH, input_key, sizeof(curve25519_key),
+ chaining_key, output_key);
+ _Static_assert(NOISE_KK1_KEY_SIZE == SHA256_DIGEST_LENGTH,
+ "output_key must be truncated to NOISE_KK1_KEY_SIZE");
+}
+
+void split(uint8_t chaining_key[SHA256_DIGEST_LENGTH],
+ uint8_t output1[NOISE_KEY_SIZE], uint8_t output2[NOISE_KEY_SIZE]) {
+ // output1 || output2 =
+ // HKDF(salt=chaining_key, key=b"", output_length=2*NOISE_KK1_KEY_SIZE)
+ hkdf(chaining_key, SHA256_DIGEST_LENGTH, NULL, 0, output1, output2);
+ _Static_assert(NOISE_KK1_KEY_SIZE == SHA256_DIGEST_LENGTH,
+ "output1 and output2 must be truncated to NOISE_KK1_KEY_SIZE");
+}
+
+static bool increase_nonce(uint8_t nonce[NOISE_KK1_NONCE_SIZE]) {
+ // The first 4 bytes of the nonce are zeros
+ // The last 8 bytes of the nonce are a big-endian encoded counter
+ for (int i = NOISE_KK1_NONCE_SIZE - 1; i >= 4; i--) {
+ nonce[i]++;
+ if (nonce[i] != 0) {
+ return true;
+ }
+ }
+
+ // Nonce overflow
+ return false;
+}
+
+bool noise_kk1_create_handshake_request(noise_kk1_context_t *ctx,
+ noise_kk1_request_t *request) {
+ memzero(ctx, sizeof(*ctx));
+ ctx->initialized = false;
+
+ random_buffer(ctx->initiator_ephemeral_private_key,
+ sizeof(ctx->initiator_ephemeral_private_key));
+ curve25519_scalarmult_basepoint(request->initiator_ephemeral_public_key,
+ ctx->initiator_ephemeral_private_key);
+ return true;
+}
+
+bool noise_kk1_handle_handshake_request(
+ noise_kk1_context_t *ctx, const curve25519_key initiator_public_key,
+ const curve25519_key responder_private_key,
+ const noise_kk1_request_t *request, noise_kk1_response_t *response) {
+ memzero(ctx, sizeof(*ctx));
+
+ curve25519_key responder_public_key = {0};
+ curve25519_scalarmult_basepoint(responder_public_key, responder_private_key);
+
+ curve25519_key responder_ephemeral_private_key = {0};
+ random_buffer(responder_ephemeral_private_key,
+ sizeof(responder_ephemeral_private_key));
+ curve25519_key responder_ephemeral_public_key = {0};
+ curve25519_scalarmult_basepoint(responder_ephemeral_public_key,
+ responder_ephemeral_private_key);
+
+ uint8_t handshake_hash[SHA256_DIGEST_LENGTH] = {0};
+ memcpy(handshake_hash, protocol_name, sizeof(protocol_name));
+ mix_hash(handshake_hash, NULL, 0);
+ mix_hash(handshake_hash, initiator_public_key, sizeof(curve25519_key));
+ mix_hash(handshake_hash, responder_public_key, sizeof(curve25519_key));
+ mix_hash(handshake_hash, request->initiator_ephemeral_public_key,
+ sizeof(curve25519_key));
+ mix_hash(handshake_hash, NULL, 0); // Empty message
+ mix_hash(handshake_hash, responder_ephemeral_public_key,
+ sizeof(curve25519_key));
+
+ curve25519_key shared_secret = {0};
+ uint8_t chaining_key[SHA256_DIGEST_LENGTH] = {0};
+ uint8_t kauth[NOISE_KK1_KEY_SIZE] = {0};
+ memcpy(chaining_key, protocol_name, sizeof(protocol_name));
+ curve25519_scalarmult(shared_secret, responder_ephemeral_private_key,
+ request->initiator_ephemeral_public_key);
+ mix_key(chaining_key, shared_secret, NULL);
+ curve25519_scalarmult(shared_secret, responder_ephemeral_private_key,
+ initiator_public_key);
+ memzero(responder_ephemeral_private_key,
+ sizeof(responder_ephemeral_private_key));
+ mix_key(chaining_key, shared_secret, NULL);
+ curve25519_scalarmult(shared_secret, responder_private_key,
+ request->initiator_ephemeral_public_key);
+ mix_key(chaining_key, shared_secret, kauth);
+ memzero(shared_secret, sizeof(shared_secret));
+ split(chaining_key, ctx->decryption_key, ctx->encryption_key);
+ memzero(chaining_key, sizeof(chaining_key));
+
+ memcpy(response, responder_ephemeral_public_key, sizeof(curve25519_key));
+
+ uint8_t zero_nonce[NOISE_KK1_NONCE_SIZE] = {0};
+ encrypt(kauth, zero_nonce, handshake_hash, sizeof(handshake_hash), NULL, 0,
+ response->tag);
+ memzero(kauth, sizeof(kauth));
+
+ // This is unnecessary, as the handshake hash is no longer used.
+ // mix_hash(handshake_hash, response->tag, sizeof(response->tag));
+
+ memset(ctx->encryption_nonce, 0, NOISE_KK1_NONCE_SIZE);
+ memset(ctx->decryption_nonce, 0, NOISE_KK1_NONCE_SIZE);
+
+ ctx->initialized = true;
+
+ return true;
+}
+
+bool noise_kk1_handle_handshake_response(
+ noise_kk1_context_t *ctx, const curve25519_key initiator_private_key,
+ const curve25519_key responder_public_key,
+ const noise_kk1_response_t *response) {
+ curve25519_key initiator_public_key = {0};
+ curve25519_scalarmult_basepoint(initiator_public_key, initiator_private_key);
+
+ curve25519_key initiator_ephemeral_public_key = {0};
+ curve25519_scalarmult_basepoint(initiator_ephemeral_public_key,
+ ctx->initiator_ephemeral_private_key);
+
+ uint8_t handshake_hash[SHA256_DIGEST_LENGTH] = {0};
+ memcpy(handshake_hash, protocol_name, sizeof(protocol_name));
+ mix_hash(handshake_hash, NULL, 0);
+ mix_hash(handshake_hash, initiator_public_key, sizeof(curve25519_key));
+ mix_hash(handshake_hash, responder_public_key, sizeof(curve25519_key));
+ mix_hash(handshake_hash, initiator_ephemeral_public_key,
+ sizeof(curve25519_key));
+ mix_hash(handshake_hash, NULL, 0); // Empty message
+ mix_hash(handshake_hash, response->responder_ephemeral_public_key,
+ sizeof(curve25519_key));
+
+ curve25519_key shared_secret = {0};
+ uint8_t chaining_key[SHA256_DIGEST_LENGTH] = {0};
+ uint8_t kauth[NOISE_KK1_KEY_SIZE] = {0};
+ memcpy(chaining_key, protocol_name, sizeof(protocol_name));
+ curve25519_scalarmult(shared_secret, ctx->initiator_ephemeral_private_key,
+ response->responder_ephemeral_public_key);
+ mix_key(chaining_key, shared_secret, NULL);
+ curve25519_scalarmult(shared_secret, initiator_private_key,
+ response->responder_ephemeral_public_key);
+ mix_key(chaining_key, shared_secret, NULL);
+ curve25519_scalarmult(shared_secret, ctx->initiator_ephemeral_private_key,
+ responder_public_key);
+ memzero(ctx->initiator_ephemeral_private_key,
+ sizeof(ctx->initiator_ephemeral_private_key));
+ mix_key(chaining_key, shared_secret, kauth);
+ memzero(shared_secret, sizeof(shared_secret));
+ split(chaining_key, ctx->encryption_key, ctx->decryption_key);
+ memzero(chaining_key, sizeof(chaining_key));
+
+ uint8_t zero_nonce[NOISE_KK1_NONCE_SIZE] = {0};
+ if (!decrypt(kauth, zero_nonce, handshake_hash, sizeof(handshake_hash),
+ response->tag, NOISE_KK1_TAG_SIZE, NULL)) {
+ // Wrong tag
+ memzero(kauth, sizeof(kauth));
+ return false;
+ }
+ memzero(kauth, sizeof(kauth));
+
+ // This is unnecessary, as the handshake hash is no longer used.
+ // mix_hash(handshake_hash, response->tag, sizeof(response->tag));
+
+ memset(ctx->encryption_nonce, 0, NOISE_KK1_NONCE_SIZE);
+ memset(ctx->decryption_nonce, 0, NOISE_KK1_NONCE_SIZE);
+
+ ctx->initialized = true;
+
+ return true;
+}
+
+bool noise_kk1_send_message(noise_kk1_context_t *ctx,
+ const uint8_t *associated_data,
+ size_t associated_data_length,
+ const uint8_t *plaintext, size_t plaintext_length,
+ uint8_t *ciphertext) {
+ if (!ctx->initialized) {
+ return false;
+ }
+ if (!encrypt(ctx->encryption_key, ctx->encryption_nonce, associated_data,
+ associated_data_length, plaintext, plaintext_length,
+ ciphertext)) {
+ return false;
+ }
+ if (!increase_nonce(ctx->encryption_nonce)) {
+ // Nonce overflow
+ memzero(ctx, sizeof(*ctx));
+ ctx->initialized = false;
+ return false;
+ }
+
+ return true;
+}
+
+bool noise_kk1_receive_message(noise_kk1_context_t *ctx,
+ const uint8_t *associated_data,
+ size_t associated_data_length,
+ const uint8_t *ciphertext,
+ size_t ciphertext_length, uint8_t *plaintext) {
+ if (!ctx->initialized) {
+ return false;
+ }
+ if (!decrypt(ctx->decryption_key, ctx->decryption_nonce, associated_data,
+ associated_data_length, ciphertext, ciphertext_length,
+ plaintext)) {
+ // Wrong tag
+ return false;
+ }
+ if (!increase_nonce(ctx->decryption_nonce)) {
+ // Nonce overflow
+ memzero(ctx, sizeof(*ctx));
+ ctx->initialized = false;
+ return false;
+ }
+ return true;
+}
+
+bool noise_kk1_handle_handshake_response_multiple_keys(
+ noise_kk1_context_t *ctx, const curve25519_key initiator_private_key,
+ const curve25519_key *responder_public_keys,
+ size_t responder_public_keys_count, const noise_kk1_response_t *response) {
+ curve25519_key ephemeral_key_backup = {0};
+ memcpy(ephemeral_key_backup, ctx->initiator_ephemeral_private_key,
+ sizeof(ephemeral_key_backup));
+ for (size_t i = 0; i < responder_public_keys_count; i++) {
+ memcpy(ctx->initiator_ephemeral_private_key, ephemeral_key_backup,
+ sizeof(ephemeral_key_backup));
+ if (noise_kk1_handle_handshake_response(
+ ctx, initiator_private_key, responder_public_keys[i], response)) {
+ memzero(ephemeral_key_backup, sizeof(ephemeral_key_backup));
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/crypto/noise_kk1.h b/crypto/noise_kk1.h
new file mode 100644
index 00000000..f3e3332b
--- /dev/null
+++ b/crypto/noise_kk1.h
@@ -0,0 +1,104 @@
+/**
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
+ * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NOISE_KK1_H__
+#define __NOISE_KK1_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "ed25519-donna/ed25519.h"
+
+// Noise protocol using KK1 handshake pattern and X25519, AES-GCM and SHA256.
+// The handshake messages and the prologue are empty.
+
+#define NOISE_KK1_KEY_SIZE 32
+#define NOISE_KK1_NONCE_SIZE 12
+#define NOISE_KK1_TAG_SIZE 16
+
+typedef struct {
+ curve25519_key initiator_ephemeral_private_key; // This is used only by the
+ // initiator during handshake
+ uint8_t encryption_nonce[NOISE_KK1_NONCE_SIZE];
+ uint8_t decryption_nonce[NOISE_KK1_NONCE_SIZE];
+ // There is a time-memory trade-off between storing encryption/decryption keys
+ // and storing encryption/decryption contexts, we choose to optimize for
+ // memory usage by storing the keys
+ uint8_t encryption_key[NOISE_KK1_KEY_SIZE];
+ uint8_t decryption_key[NOISE_KK1_KEY_SIZE];
+ bool initialized;
+} noise_kk1_context_t;
+
+typedef struct {
+ curve25519_key initiator_ephemeral_public_key;
+} noise_kk1_request_t;
+
+typedef struct {
+ curve25519_key responder_ephemeral_public_key;
+ uint8_t tag[NOISE_KK1_TAG_SIZE];
+} noise_kk1_response_t;
+
+// This is called by the initiator to initialize the context and create the
+// handshake request
+bool noise_kk1_create_handshake_request(noise_kk1_context_t* ctx,
+ noise_kk1_request_t* request);
+
+// This is called by the responder to initialize the context, handle the
+// handshake request and create the handshake response
+bool noise_kk1_handle_handshake_request(
+ noise_kk1_context_t* ctx, const curve25519_key initiator_public_key,
+ const curve25519_key responder_private_key,
+ const noise_kk1_request_t* request, noise_kk1_response_t* response);
+
+// This is called by the initiator to handle the handshake response
+bool noise_kk1_handle_handshake_response(
+ noise_kk1_context_t* ctx, const curve25519_key initiator_private_key,
+ const curve25519_key responder_public_key,
+ const noise_kk1_response_t* response);
+
+// This is called by the initiator to handle the handshake response
+// This is a wrapper above noise_kk1_handle_handshake_response that allows to
+// pass multiple responder public keys, the first key that succeeds in paring is
+// used
+bool noise_kk1_handle_handshake_response_multiple_keys(
+ noise_kk1_context_t* ctx, const curve25519_key initiator_private_key,
+ const curve25519_key* responder_public_keys,
+ size_t responder_public_keys_count, const noise_kk1_response_t* response);
+
+// This is called by both the initiator and responder to send a message
+// len(ciphertext) == plaintext_length + NOISE_KK1_TAG_SIZE
+// The official Noise specification requires the associated_data to be empty
+bool noise_kk1_send_message(noise_kk1_context_t* ctx,
+ const uint8_t* associated_data,
+ size_t associated_data_length,
+ const uint8_t* plaintext, size_t plaintext_length,
+ uint8_t* ciphertext);
+
+// This is called by both the initiator and responder to receive a message
+// len(plaintext) == ciphertext_length - NOISE_KK1_TAG_SIZE
+// The official Noise specification requires the associated_data to be empty
+bool noise_kk1_receive_message(noise_kk1_context_t* ctx,
+ const uint8_t* associated_data,
+ size_t associated_data_length,
+ const uint8_t* ciphertext,
+ size_t ciphertext_length, uint8_t* plaintext);
+
+#endif // __NOISE_H__
diff --git a/crypto/tests/test_check.c b/crypto/tests/test_check.c
index 9b9e0ddc..5d77b3ed 100644
--- a/crypto/tests/test_check.c
+++ b/crypto/tests/test_check.c
@@ -69,7 +69,7 @@
#include "monero/monero.h"
#include "nem.h"
#include "nist256p1.h"
-#include "noise.h"
+#include "noise_kk1.h"
#include "pbkdf2.h"
#include "rand.h"
#include "rc4.h"
@@ -11502,7 +11502,7 @@ START_TEST(test_elligator2) {
}
END_TEST
-START_TEST(test_noise) {
+START_TEST(test_noise_kk1) {
// Inject the seed to the random number generator to make the test
// deterministic
random_reseed(2748932008);
@@ -11516,11 +11516,11 @@ START_TEST(test_noise) {
curve25519_scalarmult_basepoint(initiator_public_key, initiator_private_key);
curve25519_scalarmult_basepoint(responder_public_key, responder_private_key);
- noise_context_t initiator_context = {0};
- noise_context_t responder_context = {0};
+ noise_kk1_context_t initiator_context = {0};
+ noise_kk1_context_t responder_context = {0};
- noise_request_t request = {0};
- noise_response_t response = {0};
+ noise_kk1_request_t request = {0};
+ noise_kk1_response_t response = {0};
uint8_t message1[] = "message1";
uint8_t associated_data1[] = "associated_data1";
@@ -11547,91 +11547,91 @@ START_TEST(test_noise) {
"5e21772c915f1bbfeff75c87c7c2a1589dcb5fe791656c9332";
uint8_t plaintext1[sizeof(message1)] = {0};
- uint8_t ciphertext1[sizeof(plaintext1) + NOISE_TAG_SIZE];
+ uint8_t ciphertext1[sizeof(plaintext1) + NOISE_KK1_TAG_SIZE];
uint8_t plaintext2[sizeof(message2)] = {0};
- uint8_t ciphertext2[sizeof(plaintext2) + NOISE_TAG_SIZE] = {0};
+ uint8_t ciphertext2[sizeof(plaintext2) + NOISE_KK1_TAG_SIZE] = {0};
uint8_t plaintext3[sizeof(message3)] = {0};
- uint8_t ciphertext3[sizeof(plaintext3) + NOISE_TAG_SIZE] = {0};
+ uint8_t ciphertext3[sizeof(plaintext3) + NOISE_KK1_TAG_SIZE] = {0};
uint8_t plaintext4[sizeof(message4)] = {0};
- uint8_t ciphertext4[sizeof(plaintext4) + NOISE_TAG_SIZE] = {0};
+ uint8_t ciphertext4[sizeof(plaintext4) + NOISE_KK1_TAG_SIZE] = {0};
bool ret = false;
// Initiator sends request
- ret = noise_create_handshake_request(&initiator_context, &request);
+ ret = noise_kk1_create_handshake_request(&initiator_context, &request);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(&request, fromhex(expected_request_hex), sizeof(request));
// Responder receives request and sends response
- ret = noise_handle_handshake_request(&responder_context, initiator_public_key,
- responder_private_key, &request,
- &response);
+ ret = noise_kk1_handle_handshake_request(
+ &responder_context, initiator_public_key, responder_private_key, &request,
+ &response);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(&response, fromhex(expected_response_hex), sizeof(response));
// Initiator receives response
- ret =
- noise_handle_handshake_response(&initiator_context, initiator_private_key,
- responder_public_key, &response);
+ ret = noise_kk1_handle_handshake_response(&initiator_context,
+ initiator_private_key,
+ responder_public_key, &response);
ck_assert_int_eq(ret, true);
// Initiator sends message1
- ret = noise_send_message(&initiator_context, associated_data1,
- sizeof(associated_data1), message1, sizeof(message1),
- ciphertext1);
+ ret = noise_kk1_send_message(&initiator_context, associated_data1,
+ sizeof(associated_data1), message1,
+ sizeof(message1), ciphertext1);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(ciphertext1, fromhex(expected_message1_hex),
sizeof(ciphertext1));
// Initiator sends message2
- ret = noise_send_message(&initiator_context, associated_data2,
- sizeof(associated_data2), message2, sizeof(message2),
- ciphertext2);
+ ret = noise_kk1_send_message(&initiator_context, associated_data2,
+ sizeof(associated_data2), message2,
+ sizeof(message2), ciphertext2);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(ciphertext2, fromhex(expected_message2_hex),
sizeof(ciphertext2));
// Responder sends message3
- ret = noise_send_message(&responder_context, associated_data3,
- sizeof(associated_data3), message3, sizeof(message3),
- ciphertext3);
+ ret = noise_kk1_send_message(&responder_context, associated_data3,
+ sizeof(associated_data3), message3,
+ sizeof(message3), ciphertext3);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(ciphertext3, fromhex(expected_message3_hex),
sizeof(ciphertext3));
// Responder sends message4
- ret = noise_send_message(&responder_context, associated_data4,
- sizeof(associated_data4), message4, sizeof(message4),
- ciphertext4);
+ ret = noise_kk1_send_message(&responder_context, associated_data4,
+ sizeof(associated_data4), message4,
+ sizeof(message4), ciphertext4);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(ciphertext4, fromhex(expected_message4_hex),
sizeof(ciphertext4));
// Responder receives message1
- ret = noise_receive_message(&responder_context, associated_data1,
- sizeof(associated_data1), ciphertext1,
- sizeof(ciphertext1), plaintext1);
+ ret = noise_kk1_receive_message(&responder_context, associated_data1,
+ sizeof(associated_data1), ciphertext1,
+ sizeof(ciphertext1), plaintext1);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(plaintext1, message1, sizeof(message1));
// Responder receives message2
- ret = noise_receive_message(&responder_context, associated_data2,
- sizeof(associated_data2), ciphertext2,
- sizeof(ciphertext2), plaintext2);
+ ret = noise_kk1_receive_message(&responder_context, associated_data2,
+ sizeof(associated_data2), ciphertext2,
+ sizeof(ciphertext2), plaintext2);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(plaintext2, message2, sizeof(message2));
// Initiator receives message3
- ret = noise_receive_message(&initiator_context, associated_data3,
- sizeof(associated_data3), ciphertext3,
- sizeof(ciphertext3), plaintext3);
+ ret = noise_kk1_receive_message(&initiator_context, associated_data3,
+ sizeof(associated_data3), ciphertext3,
+ sizeof(ciphertext3), plaintext3);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(plaintext3, message3, sizeof(message3));
// Initiator receives message4
- ret = noise_receive_message(&initiator_context, associated_data4,
- sizeof(associated_data4), ciphertext4,
- sizeof(ciphertext4), plaintext4);
+ ret = noise_kk1_receive_message(&initiator_context, associated_data4,
+ sizeof(associated_data4), ciphertext4,
+ sizeof(ciphertext4), plaintext4);
ck_assert_int_eq(ret, true);
ck_assert_mem_eq(plaintext4, message4, sizeof(message4));
}
@@ -11995,7 +11995,7 @@ Suite *test_suite(void) {
suite_add_tcase(s, tc);
tc = tcase_create("noise");
- tcase_add_test(tc, test_noise);
+ tcase_add_test(tc, test_noise_kk1);
suite_add_tcase(s, tc);
#if USE_CARDANO
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.