Просмотр исходного кода

Add simple replay protection

WARNING: This commit will break interoperability with previous versions
of the app!
twisted_pear 2 лет назад
Родитель
Сommit
72d2a6e05b

+ 101 - 20
crypto_wrapper.c

@@ -1,4 +1,6 @@
 #include <furi_hal.h>
+#include <lib/mlib/m-dict.h>
+#include <toolbox/sha256.h>
 
 #ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
 #include "crypto/gcm.h"
@@ -6,13 +8,26 @@
 
 #include "crypto_wrapper.h"
 
+DICT_DEF2(ESubGhzChatReplayDict, uint64_t, uint32_t)
+
 struct ESugGhzChatCryptoCtx {
 	uint8_t key[KEY_BITS / 8];
 #ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
 	gcm_context gcm_ctx;
 #endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
+	ESubGhzChatReplayDict_t replay_dict;
+	uint64_t run_id;
+	uint32_t counter;
 };
 
+struct ESubGhzChatCryptoMsg {
+	uint64_t run_id;
+	uint32_t counter;
+	uint8_t iv[IV_BYTES];
+	uint8_t tag[TAG_BYTES];
+	uint8_t data[0];
+} __attribute__ ((packed));
+
 void crypto_init(void)
 {
 #ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL
@@ -33,6 +48,9 @@ ESubGhzChatCryptoCtx *crypto_ctx_alloc(void)
 
 	if (ret != NULL) {
 		memset(ret, 0, sizeof(ESubGhzChatCryptoCtx));
+		ESubGhzChatReplayDict_init(ret->replay_dict);
+		ret->run_id = 0;
+		ret->counter = 1;
 	}
 
 	return ret;
@@ -41,16 +59,42 @@ ESubGhzChatCryptoCtx *crypto_ctx_alloc(void)
 void crypto_ctx_free(ESubGhzChatCryptoCtx *ctx)
 {
 	crypto_ctx_clear(ctx);
+	ESubGhzChatReplayDict_clear(ctx->replay_dict);
 	free(ctx);
 }
 
 void crypto_ctx_clear(ESubGhzChatCryptoCtx *ctx)
 {
 	crypto_explicit_bzero(ctx, sizeof(ESubGhzChatCryptoCtx));
+	ESubGhzChatReplayDict_reset(ctx->replay_dict);
+	ctx->run_id = 0;
+	ctx->counter = 1;
+}
+
+static uint64_t crypto_calc_run_id(FuriString *flipper_name, uint32_t tick)
+{
+	const char *fn = furi_string_get_cstr(flipper_name);
+	size_t fn_len = strlen(fn);
+
+	uint8_t h_in[fn_len + sizeof(uint32_t)];
+	memcpy(h_in, fn, fn_len);
+	memcpy(h_in + fn_len, &tick, sizeof(uint32_t));
+
+	uint8_t h_out[256];
+	sha256(h_in, fn_len + sizeof(uint32_t), h_out);
+
+	uint64_t run_id;
+	memcpy(&run_id, h_out, sizeof(uint64_t));
+
+	return run_id;
 }
 
-bool crypto_ctx_set_key(ESubGhzChatCryptoCtx *ctx, const uint8_t *key)
+bool crypto_ctx_set_key(ESubGhzChatCryptoCtx *ctx, const uint8_t *key,
+		FuriString *flipper_name, uint32_t tick)
 {
+	ctx->run_id = crypto_calc_run_id(flipper_name, tick);
+	ctx->counter = 1;
+
 	memcpy(ctx->key, key, KEY_BITS / 8);
 #ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
 	return true;
@@ -71,37 +115,74 @@ bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx *ctx, uint8_t *in, size_t in_len,
 		return false;
 	}
 
+	struct ESubGhzChatCryptoMsg *msg = (struct ESubGhzChatCryptoMsg *) in;
+
+	// check if message is stale, if yes, discard
+	uint32_t *counter = ESubGhzChatReplayDict_get(ctx->replay_dict,
+			msg->run_id);
+	if (counter != NULL) {
+		if (*counter >= __ntohl(msg->counter)) {
+			return false;
+		}
+	}
+
+	// decrypt and auth message
 #ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
-	return (furi_hal_crypto_gcm_decrypt_and_verify(ctx->key,
-			in, NULL, 0,
-			in + IV_BYTES, out,
+	bool ret = (furi_hal_crypto_gcm_decrypt_and_verify(ctx->key,
+			msg->iv,
+			(uint8_t *) msg, RUN_ID_BYTES + COUNTER_BYTES,
+			msg->data, out,
 			in_len - MSG_OVERHEAD,
-			in + in_len - TAG_BYTES) == FuriHalCryptoGCMStateOk);
+			msg->tag) == FuriHalCryptoGCMStateOk);
 #else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
-	return (gcm_auth_decrypt(&(ctx->gcm_ctx),
-			in, IV_BYTES,
-			NULL, 0,
-			in + IV_BYTES, out, in_len - MSG_OVERHEAD,
-			in + in_len - TAG_BYTES, TAG_BYTES) == 0);
+	bool ret = (gcm_auth_decrypt(&(ctx->gcm_ctx),
+			msg->iv, IV_BYTES,
+			(uint8_t *) msg, RUN_ID_BYTES + COUNTER_BYTES,
+			msg->data, out,
+			in_len - MSG_OVERHEAD,
+			msg->tag, TAG_BYTES) == 0);
 #endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
+
+	// if auth was successful update replay dict
+	if (ret) {
+		ESubGhzChatReplayDict_set_at(ctx->replay_dict, msg->run_id,
+				__ntohl(msg->counter));
+	}
+
+	return ret;
 }
 
 bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx *ctx, uint8_t *in, size_t in_len,
 		uint8_t *out)
 {
-	furi_hal_random_fill_buf(out, IV_BYTES);
+	struct ESubGhzChatCryptoMsg *msg = (struct ESubGhzChatCryptoMsg *) out;
 
+	// fill message header
+	msg->run_id = ctx->run_id;
+	msg->counter = __htonl(ctx->counter);
+	furi_hal_random_fill_buf(msg->iv, IV_BYTES);
+
+	// encrypt message and store tag in header
 #ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL
-	return (furi_hal_crypto_gcm_encrypt_and_tag(ctx->key,
-			out, NULL, 0,
-			in, out + IV_BYTES,
+	bool ret = (furi_hal_crypto_gcm_encrypt_and_tag(ctx->key,
+			msg->iv,
+			(uint8_t *) msg, RUN_ID_BYTES + COUNTER_BYTES,
+			in, msg->data,
 			in_len,
-			out + IV_BYTES + in_len) == FuriHalCryptoGCMStateOk);
+			msg->tag) == FuriHalCryptoGCMStateOk);
 #else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
-	return (gcm_crypt_and_tag(&(ctx->gcm_ctx), ENCRYPT,
-			out, IV_BYTES,
-			NULL, 0,
-			in, out + IV_BYTES, in_len,
-			out + IV_BYTES + in_len, TAG_BYTES) == 0);
+	bool ret = (gcm_crypt_and_tag(&(ctx->gcm_ctx), ENCRYPT,
+			msg->iv, IV_BYTES,
+			(uint8_t *) msg, RUN_ID_BYTES + COUNTER_BYTES,
+			in, msg->data,
+			in_len,
+			msg->tag, TAG_BYTES) == 0);
 #endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */
+
+	// increase internal counter
+	if (ret) {
+		ctx->counter++;
+	}
+
+	return ret;
 }

+ 5 - 2
crypto_wrapper.h

@@ -4,11 +4,13 @@
 extern "C" {
 #endif
 
+#define RUN_ID_BYTES (sizeof(uint64_t))
+#define COUNTER_BYTES (sizeof(uint32_t))
 #define KEY_BITS 256
 #define IV_BYTES 12
 #define TAG_BYTES 16
 
-#define MSG_OVERHEAD (IV_BYTES + TAG_BYTES)
+#define MSG_OVERHEAD (RUN_ID_BYTES + COUNTER_BYTES + IV_BYTES + TAG_BYTES)
 
 typedef struct ESugGhzChatCryptoCtx ESubGhzChatCryptoCtx;
 
@@ -22,7 +24,8 @@ void crypto_ctx_free(ESubGhzChatCryptoCtx *ctx);
 
 void crypto_ctx_clear(ESubGhzChatCryptoCtx *ctx);
 
-bool crypto_ctx_set_key(ESubGhzChatCryptoCtx *ctx, const uint8_t *key);
+bool crypto_ctx_set_key(ESubGhzChatCryptoCtx *ctx, const uint8_t *key,
+		FuriString *flipper_name, uint32_t tick);
 void crypto_ctx_get_key(ESubGhzChatCryptoCtx *ctx, uint8_t *key);
 
 bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx *ctx, uint8_t *in, size_t in_len,

+ 2 - 1
scenes/esubghz_chat_hex_key_input.c

@@ -9,7 +9,8 @@ static void hex_key_input_cb(void* context)
 
 	/* initiate the crypto context */
 	bool ret = crypto_ctx_set_key(state->crypto_ctx,
-			state->hex_key_input_store);
+			state->hex_key_input_store, state->name_prefix,
+			furi_get_tick());
 
 	/* cleanup */
 	crypto_explicit_bzero(state->hex_key_input_store,

+ 2 - 1
scenes/esubghz_chat_key_menu.c

@@ -39,7 +39,8 @@ static void key_menu_cb(void* context, uint32_t index)
 		furi_hal_random_fill_buf(key, KEY_BITS / 8);
 
 		/* initiate the crypto context */
-		bool ret = crypto_ctx_set_key(state->crypto_ctx, key);
+		bool ret = crypto_ctx_set_key(state->crypto_ctx, key,
+				state->name_prefix, furi_get_tick());
 
 		/* cleanup */
 		crypto_explicit_bzero(key, sizeof(key));

+ 2 - 1
scenes/esubghz_chat_key_read_popup.c

@@ -48,7 +48,8 @@ static bool key_read_popup_handle_key_read(ESubGhzChatState *state)
 
 	/* initiate the crypto context */
 	bool ret = crypto_ctx_set_key(state->crypto_ctx,
-			dev_data->mf_ul_data.data);
+			dev_data->mf_ul_data.data, state->name_prefix,
+			furi_get_tick());
 
 	/* cleanup */
 	crypto_explicit_bzero(dev_data->mf_ul_data.data, KEY_BITS / 8);

+ 2 - 1
scenes/esubghz_chat_pass_input.c

@@ -38,7 +38,8 @@ static bool pass_input_validator(const char *text, FuriString *error,
 	sha256((unsigned char *) text, strlen(text), key);
 
 	/* initiate the crypto context */
-	bool ret = crypto_ctx_set_key(state->crypto_ctx, key);
+	bool ret = crypto_ctx_set_key(state->crypto_ctx, key,
+			state->name_prefix, furi_get_tick());
 
 	/* cleanup */
 	crypto_explicit_bzero(key, sizeof(key));