MX
2023-08-05 13:57:55 +03:00
parent 74ffb02b56
commit afa7bd7f79
57 changed files with 1521 additions and 577 deletions

View File

@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
typedef uint8_t CryptoSeedIVResult;
enum CryptoSeedIVResults {
/**
* @brief IV seeding operation failed
*/
CryptoSeedIVResultFailed = 0b00,
/**
* @brief IV seeding operation succeeded
*/
CryptoSeedIVResultFlagSuccess = 0b01,
/**
* @brief As a part of IV seeding operation new crypto verify data has been generated
*/
CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10
};

View File

@@ -0,0 +1,11 @@
#pragma once
#define CRYPTO_IV_LENGTH (16)
// According to this explanation: https://github.com/flipperdevices/flipperzero-firmware/issues/2885#issuecomment-1646664666
// disabling usage of any key which is "the same across all devices"
#define ACCEPTABLE_CRYPTO_KEY_SLOT_START (12)
#define ACCEPTABLE_CRYPTO_KEY_SLOT_END (100)
#define DEFAULT_CRYPTO_KEY_SLOT ACCEPTABLE_CRYPTO_KEY_SLOT_START
#define CRYPTO_LATEST_VERSION (2)

View File

@@ -0,0 +1,78 @@
#include "crypto_facade.h"
#include <furi_hal_crypto.h>
#include <furi/core/check.h>
#include "crypto_v1.h"
#include "crypto_v2.h"
#include "constants.h"
bool totp_crypto_check_key_slot(uint8_t key_slot) {
uint8_t empty_iv[CRYPTO_IV_LENGTH] = {0};
if(key_slot < ACCEPTABLE_CRYPTO_KEY_SLOT_START || key_slot > ACCEPTABLE_CRYPTO_KEY_SLOT_END) {
return false;
}
return furi_hal_crypto_verify_key(key_slot) &&
furi_hal_crypto_store_load_key(key_slot, empty_iv) &&
furi_hal_crypto_store_unload_key(key_slot);
}
uint8_t* totp_crypto_encrypt(
const uint8_t* plain_data,
const size_t plain_data_length,
const CryptoSettings* crypto_settings,
size_t* encrypted_data_length) {
if(crypto_settings->crypto_version == 1) {
return totp_crypto_encrypt_v1(
plain_data, plain_data_length, crypto_settings, encrypted_data_length);
}
if(crypto_settings->crypto_version == 2) {
return totp_crypto_encrypt_v2(
plain_data, plain_data_length, crypto_settings, encrypted_data_length);
}
furi_crash("Unsupported crypto version");
}
uint8_t* totp_crypto_decrypt(
const uint8_t* encrypted_data,
const size_t encrypted_data_length,
const CryptoSettings* crypto_settings,
size_t* decrypted_data_length) {
if(crypto_settings->crypto_version == 1) {
return totp_crypto_decrypt_v1(
encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length);
}
if(crypto_settings->crypto_version == 2) {
return totp_crypto_decrypt_v2(
encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length);
}
furi_crash("Unsupported crypto version");
}
CryptoSeedIVResult
totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length) {
if(crypto_settings->crypto_version == 1) {
return totp_crypto_seed_iv_v1(crypto_settings, pin, pin_length);
}
if(crypto_settings->crypto_version == 2) {
return totp_crypto_seed_iv_v2(crypto_settings, pin, pin_length);
}
furi_crash("Unsupported crypto version");
}
bool totp_crypto_verify_key(const CryptoSettings* crypto_settings) {
if(crypto_settings->crypto_version == 1) {
return totp_crypto_verify_key_v1(crypto_settings);
}
if(crypto_settings->crypto_version == 2) {
return totp_crypto_verify_key_v2(crypto_settings);
}
furi_crash("Unsupported crypto version");
}

View File

@@ -1,68 +1,59 @@
#pragma once
#include "../../types/plugin_state.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "../../types/crypto_settings.h"
#include "common_types.h"
typedef uint8_t CryptoSeedIVResult;
enum CryptoSeedIVResults {
/**
* @brief IV seeding operation failed
*/
CryptoSeedIVResultFailed = 0b00,
/**
* @brief IV seeding operation succeeded
*/
CryptoSeedIVResultFlagSuccess = 0b01,
/**
* @brief As a part of IV seeding operation new crypto verify data has been generated
*/
CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10
};
/**
* @brief Checks whether key slot can be used for encryption purposes
* @param key_slot key slot index
* @return \c true if key slot can be used for encryption; \c false otherwise
*/
bool totp_crypto_check_key_slot(uint8_t key_slot);
/**
* @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
* @param plain_data plain data to be encrypted
* @param plain_data_length plain data length
* @param iv initialization vector (IV) to be used to encrypt plain data
* @param crypto_settings crypto settings
* @param[out] encrypted_data_length encrypted data length
* @return Encrypted data
*/
uint8_t* totp_crypto_encrypt(
const uint8_t* plain_data,
const size_t plain_data_length,
const uint8_t* iv,
const CryptoSettings* crypto_settings,
size_t* encrypted_data_length);
/**
* @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV)
* @param encrypted_data encrypted data to be decrypted
* @param encrypted_data_length encrypted data length
* @param iv initialization vector (IV) to be used to encrypt plain data
* @param crypto_settings crypto settings
* @param[out] decrypted_data_length decrypted data length
* @return Decrypted data
*/
uint8_t* totp_crypto_decrypt(
const uint8_t* encrypted_data,
const size_t encrypted_data_length,
const uint8_t* iv,
const CryptoSettings* crypto_settings,
size_t* decrypted_data_length);
/**
* @brief Seed initialization vector (IV) using user's PIN
* @param plugin_state application state
* @param crypto_settings crypto settings
* @param pin user's PIN
* @param pin_length user's PIN length
* @return Results of seeding IV
*/
CryptoSeedIVResult
totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length);
/**
* @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
* @param plugin_state application state
* @param crypto_settings crypto settings
* @return \c true if cryptographic information is valid; \c false otherwise
*/
bool totp_crypto_verify_key(const PluginState* plugin_state);
bool totp_crypto_verify_key(const CryptoSettings* crypto_settings);

View File

@@ -1,4 +1,6 @@
#include "crypto.h"
#include "crypto_v1.h"
#include <stdlib.h>
#include <furi.h>
#include <furi_hal_crypto.h>
#include <furi_hal_random.h>
#include <furi_hal_version.h>
@@ -8,13 +10,14 @@
#define CRYPTO_KEY_SLOT (2)
#define CRYPTO_VERIFY_KEY_LENGTH (16)
#define CRYPTO_ALIGNMENT_FACTOR (16)
#define TOTP_IV_SIZE (16)
static const char* CRYPTO_VERIFY_KEY = "FFF_Crypto_pass";
uint8_t* totp_crypto_encrypt(
uint8_t* totp_crypto_encrypt_v1(
const uint8_t* plain_data,
const size_t plain_data_length,
const uint8_t* iv,
const CryptoSettings* crypto_settings,
size_t* encrypted_data_length) {
uint8_t* encrypted_data;
size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR;
@@ -29,7 +32,7 @@ uint8_t* totp_crypto_encrypt(
furi_check(encrypted_data != NULL);
*encrypted_data_length = plain_data_aligned_length;
furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv);
furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length);
furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
@@ -40,7 +43,7 @@ uint8_t* totp_crypto_encrypt(
furi_check(encrypted_data != NULL);
*encrypted_data_length = plain_data_length;
furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv);
furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length);
furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
}
@@ -48,29 +51,31 @@ uint8_t* totp_crypto_encrypt(
return encrypted_data;
}
uint8_t* totp_crypto_decrypt(
uint8_t* totp_crypto_decrypt_v1(
const uint8_t* encrypted_data,
const size_t encrypted_data_length,
const uint8_t* iv,
const CryptoSettings* crypto_settings,
size_t* decrypted_data_length) {
*decrypted_data_length = encrypted_data_length;
uint8_t* decrypted_data = malloc(*decrypted_data_length);
furi_check(decrypted_data != NULL);
furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv);
furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length);
furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
return decrypted_data;
}
CryptoSeedIVResult
totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
CryptoSeedIVResult totp_crypto_seed_iv_v1(
CryptoSettings* crypto_settings,
const uint8_t* pin,
uint8_t pin_length) {
CryptoSeedIVResult result;
if(plugin_state->crypto_verify_data == NULL) {
if(crypto_settings->crypto_verify_data == NULL) {
FURI_LOG_I(LOGGING_TAG, "Generating new IV");
furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE);
furi_hal_random_fill_buf(&crypto_settings->base_iv[0], TOTP_IV_SIZE);
}
memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE);
memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], TOTP_IV_SIZE);
if(pin != NULL && pin_length > 0) {
uint8_t max_i;
if(pin_length > TOTP_IV_SIZE) {
@@ -80,7 +85,7 @@ CryptoSeedIVResult
}
for(uint8_t i = 0; i < max_i; i++) {
plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1));
crypto_settings->iv[i] = crypto_settings->iv[i] ^ (uint8_t)(pin[i] * (i + 1));
}
} else {
uint8_t max_i;
@@ -93,24 +98,24 @@ CryptoSeedIVResult
const uint8_t* uid = (const uint8_t*)UID64_BASE; //-V566
for(uint8_t i = 0; i < max_i; i++) {
plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i];
crypto_settings->iv[i] = crypto_settings->iv[i] ^ uid[i];
}
}
result = CryptoSeedIVResultFlagSuccess;
if(plugin_state->crypto_verify_data == NULL) {
if(crypto_settings->crypto_verify_data == NULL) {
FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data");
plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
furi_check(plugin_state->crypto_verify_data != NULL);
plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH;
crypto_settings->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
furi_check(crypto_settings->crypto_verify_data != NULL);
crypto_settings->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH;
plugin_state->crypto_verify_data = totp_crypto_encrypt(
crypto_settings->crypto_verify_data = totp_crypto_encrypt_v1(
(const uint8_t*)CRYPTO_VERIFY_KEY,
CRYPTO_VERIFY_KEY_LENGTH,
&plugin_state->iv[0],
&plugin_state->crypto_verify_data_length);
crypto_settings,
&crypto_settings->crypto_verify_data_length);
plugin_state->pin_set = pin != NULL && pin_length > 0;
crypto_settings->pin_required = pin != NULL && pin_length > 0;
result |= CryptoSeedIVResultFlagNewCryptoVerifyData;
}
@@ -118,12 +123,12 @@ CryptoSeedIVResult
return result;
}
bool totp_crypto_verify_key(const PluginState* plugin_state) {
bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings) {
size_t decrypted_key_length;
uint8_t* decrypted_key = totp_crypto_decrypt(
plugin_state->crypto_verify_data,
plugin_state->crypto_verify_data_length,
&plugin_state->iv[0],
uint8_t* decrypted_key = totp_crypto_decrypt_v1(
crypto_settings->crypto_verify_data,
crypto_settings->crypto_verify_data_length,
crypto_settings,
&decrypted_key_length);
bool key_valid = true;

View File

@@ -0,0 +1,52 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "../../types/crypto_settings.h"
#include "common_types.h"
/**
* @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
* @param plain_data plain data to be encrypted
* @param plain_data_length plain data length
* @param crypto_settings crypto settings
* @param[out] encrypted_data_length encrypted data length
* @return Encrypted data
*/
uint8_t* totp_crypto_encrypt_v1(
const uint8_t* plain_data,
const size_t plain_data_length,
const CryptoSettings* crypto_settings,
size_t* encrypted_data_length);
/**
* @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV)
* @param encrypted_data encrypted data to be decrypted
* @param encrypted_data_length encrypted data length
* @param crypto_settings crypto settings
* @param[out] decrypted_data_length decrypted data length
* @return Decrypted data
*/
uint8_t* totp_crypto_decrypt_v1(
const uint8_t* encrypted_data,
const size_t encrypted_data_length,
const CryptoSettings* crypto_settings,
size_t* decrypted_data_length);
/**
* @brief Seed initialization vector (IV) using user's PIN
* @param crypto_settings crypto settings
* @param pin user's PIN
* @param pin_length user's PIN length
* @return Results of seeding IV
*/
CryptoSeedIVResult
totp_crypto_seed_iv_v1(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length);
/**
* @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
* @param crypto_settings crypto settings
* @return \c true if cryptographic information is valid; \c false otherwise
*/
bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings);

View File

@@ -0,0 +1,184 @@
#include "crypto_v2.h"
#include <stdlib.h>
#include <furi.h>
#include <furi_hal_crypto.h>
#include <furi_hal_random.h>
#include <furi_hal_version.h>
#include "../../types/common.h"
#include "../hmac/hmac_sha512.h"
#include "memset_s.h"
#include "constants.h"
#define CRYPTO_ALIGNMENT_FACTOR (16)
static const uint8_t* get_device_uid() {
return (const uint8_t*)UID64_BASE; //-V566
}
static uint8_t get_device_uid_length() {
return furi_hal_version_uid_size();
}
static const uint8_t* get_crypto_verify_key() {
return get_device_uid();
}
static uint8_t get_crypto_verify_key_length() {
return get_device_uid_length();
}
uint8_t* totp_crypto_encrypt_v2(
const uint8_t* plain_data,
const size_t plain_data_length,
const CryptoSettings* crypto_settings,
size_t* encrypted_data_length) {
uint8_t* encrypted_data;
size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR;
if(remain) {
size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR;
uint8_t* plain_data_aligned = malloc(plain_data_aligned_length);
furi_check(plain_data_aligned != NULL);
memset(plain_data_aligned, 0, plain_data_aligned_length);
memcpy(plain_data_aligned, plain_data, plain_data_length);
encrypted_data = malloc(plain_data_aligned_length);
furi_check(encrypted_data != NULL);
*encrypted_data_length = plain_data_aligned_length;
furi_check(
furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv),
"Encryption failed: store_load_key");
furi_check(
furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length),
"Encryption failed: encrypt");
furi_check(
furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot),
"Encryption failed: store_unload_key");
memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length);
free(plain_data_aligned);
} else {
encrypted_data = malloc(plain_data_length);
furi_check(encrypted_data != NULL);
*encrypted_data_length = plain_data_length;
furi_check(
furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv),
"Encryption failed: store_load_key");
furi_check(
furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length),
"Encryption failed: encrypt");
furi_check(
furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot),
"Encryption failed: store_unload_key");
}
return encrypted_data;
}
uint8_t* totp_crypto_decrypt_v2(
const uint8_t* encrypted_data,
const size_t encrypted_data_length,
const CryptoSettings* crypto_settings,
size_t* decrypted_data_length) {
*decrypted_data_length = encrypted_data_length;
uint8_t* decrypted_data = malloc(*decrypted_data_length);
furi_check(decrypted_data != NULL);
furi_check(
furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv),
"Decryption failed: store_load_key");
furi_check(
furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length),
"Decryption failed: decrypt");
furi_check(
furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot),
"Decryption failed: store_unload_key");
return decrypted_data;
}
CryptoSeedIVResult totp_crypto_seed_iv_v2(
CryptoSettings* crypto_settings,
const uint8_t* pin,
uint8_t pin_length) {
CryptoSeedIVResult result;
if(crypto_settings->crypto_verify_data == NULL) {
FURI_LOG_I(LOGGING_TAG, "Generating new IV");
furi_hal_random_fill_buf(&crypto_settings->base_iv[0], CRYPTO_IV_LENGTH);
}
memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH);
const uint8_t* device_uid = get_device_uid();
uint8_t device_uid_length = get_device_uid_length();
uint8_t hmac_key_length = device_uid_length;
if(pin != NULL && pin_length > 0) {
hmac_key_length += pin_length;
}
uint8_t* hmac_key = malloc(hmac_key_length);
furi_check(hmac_key != NULL);
memcpy(hmac_key, device_uid, device_uid_length);
if(pin != NULL && pin_length > 0) {
memcpy(hmac_key + device_uid_length, pin, pin_length);
}
uint8_t hmac[HMAC_SHA512_RESULT_SIZE] = {0};
int hmac_result_code = hmac_sha512(
hmac_key, hmac_key_length, &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH, &hmac[0]);
memset_s(hmac_key, hmac_key_length, 0, hmac_key_length);
free(hmac_key);
if(hmac_result_code == 0) {
uint8_t offset =
hmac[HMAC_SHA512_RESULT_SIZE - 1] % (HMAC_SHA512_RESULT_SIZE - CRYPTO_IV_LENGTH - 1);
memcpy(&crypto_settings->iv[0], &hmac[offset], CRYPTO_IV_LENGTH);
result = CryptoSeedIVResultFlagSuccess;
if(crypto_settings->crypto_verify_data == NULL) {
const uint8_t* crypto_vkey = get_crypto_verify_key();
uint8_t crypto_vkey_length = get_crypto_verify_key_length();
FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data");
crypto_settings->crypto_verify_data = malloc(crypto_vkey_length);
furi_check(crypto_settings->crypto_verify_data != NULL);
crypto_settings->crypto_verify_data_length = crypto_vkey_length;
crypto_settings->crypto_verify_data = totp_crypto_encrypt_v2(
crypto_vkey,
crypto_vkey_length,
crypto_settings,
&crypto_settings->crypto_verify_data_length);
crypto_settings->pin_required = pin != NULL && pin_length > 0;
result |= CryptoSeedIVResultFlagNewCryptoVerifyData;
}
} else {
result = CryptoSeedIVResultFailed;
}
return result;
}
bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings) {
size_t decrypted_key_length;
uint8_t* decrypted_key = totp_crypto_decrypt_v2(
crypto_settings->crypto_verify_data,
crypto_settings->crypto_verify_data_length,
crypto_settings,
&decrypted_key_length);
const uint8_t* crypto_vkey = get_crypto_verify_key();
uint8_t crypto_vkey_length = get_crypto_verify_key_length();
bool key_valid = true;
for(uint8_t i = 0; i < crypto_vkey_length && key_valid; i++) {
if(decrypted_key[i] != crypto_vkey[i]) key_valid = false;
}
free(decrypted_key);
return key_valid;
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "../../types/crypto_settings.h"
#include "common_types.h"
/**
* @brief Encrypts plain data using built-in certificate and given initialization vector (IV)
* @param plain_data plain data to be encrypted
* @param plain_data_length plain data length
* @param crypto_settings crypto settings
* @param[out] encrypted_data_length encrypted data length
* @return Encrypted data
*/
uint8_t* totp_crypto_encrypt_v2(
const uint8_t* plain_data,
const size_t plain_data_length,
const CryptoSettings* crypto_settings,
size_t* encrypted_data_length);
/**
* @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV)
* @param encrypted_data encrypted data to be decrypted
* @param encrypted_data_length encrypted data length
* @param crypto_settings crypto settings
* @param[out] decrypted_data_length decrypted data length
* @return Decrypted data
*/
uint8_t* totp_crypto_decrypt_v2(
const uint8_t* encrypted_data,
const size_t encrypted_data_length,
const CryptoSettings* crypto_settings,
size_t* decrypted_data_length);
/**
* @brief Seed initialization vector (IV) using user's PIN
* @param crypto_settings crypto settings
* @param pin user's PIN
* @param pin_length user's PIN length
* @return Results of seeding IV
*/
CryptoSeedIVResult
totp_crypto_seed_iv_v2(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length);
/**
* @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
* @param crypto_settings crypto settings
* @return \c true if cryptographic information is valid; \c false otherwise
*/
bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings);