mirror of
https://github.com/UberGuidoZ/Flipper.git
synced 2026-09-18 20:01:29 +00:00
Reorg, added more sources
This commit is contained in:
@@ -0,0 +1,733 @@
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../list/list.h"
|
||||
#include "../../types/common.h"
|
||||
#include "../../types/token_info.h"
|
||||
#include "migrations/config_migration_v1_to_v2.h"
|
||||
|
||||
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator")
|
||||
#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
|
||||
#define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup"
|
||||
#define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp"
|
||||
#define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig"
|
||||
#define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf"
|
||||
|
||||
static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
|
||||
switch(token_info->algo) {
|
||||
case SHA1:
|
||||
return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME;
|
||||
case SHA256:
|
||||
return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME;
|
||||
case SHA512:
|
||||
return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) {
|
||||
if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) {
|
||||
token_info->algo = SHA1;
|
||||
} else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) {
|
||||
token_info->algo = SHA256;
|
||||
} else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) {
|
||||
token_info->algo = SHA512;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Opens storage record
|
||||
* @return Storage record
|
||||
*/
|
||||
static Storage* totp_open_storage() {
|
||||
return furi_record_open(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Closes storage record
|
||||
*/
|
||||
static void totp_close_storage() {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Closes config file
|
||||
* @param file config file reference
|
||||
*/
|
||||
static void totp_close_config_file(FlipperFormat* file) {
|
||||
if(file == NULL) return;
|
||||
flipper_format_file_close(file);
|
||||
flipper_format_free(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Opens or creates TOTP application standard config file
|
||||
* @param storage storage record to use
|
||||
* @param[out] file opened config file
|
||||
* @return Config file open result
|
||||
*/
|
||||
static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperFormat** file) {
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Config file %s found", CONFIG_FILE_PATH);
|
||||
if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH);
|
||||
totp_close_config_file(fff_data_file);
|
||||
return TotpConfigFileOpenError;
|
||||
}
|
||||
} else if(storage_common_stat(storage, CONFIG_FILE_PATH_PREVIOUS, NULL) == FSE_OK) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Old config file %s found", CONFIG_FILE_PATH_PREVIOUS);
|
||||
if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
|
||||
FURI_LOG_D(
|
||||
LOGGING_TAG,
|
||||
"Directory %s doesn't exist. Will create new.",
|
||||
CONFIG_FILE_DIRECTORY_PATH);
|
||||
if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
|
||||
totp_close_config_file(fff_data_file);
|
||||
return TotpConfigFileOpenError;
|
||||
}
|
||||
}
|
||||
if(storage_common_rename(storage, CONFIG_FILE_PATH_PREVIOUS, CONFIG_FILE_PATH) != FSE_OK) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Error moving config to %s", CONFIG_FILE_PATH);
|
||||
totp_close_config_file(fff_data_file);
|
||||
return TotpConfigFileOpenError;
|
||||
}
|
||||
FURI_LOG_I(LOGGING_TAG, "Applied config file path migration");
|
||||
return totp_open_config_file(storage, file);
|
||||
} else {
|
||||
FURI_LOG_D(LOGGING_TAG, "Config file %s is not found. Will create new.", CONFIG_FILE_PATH);
|
||||
if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
|
||||
FURI_LOG_D(
|
||||
LOGGING_TAG,
|
||||
"Directory %s doesn't exist. Will create new.",
|
||||
CONFIG_FILE_DIRECTORY_PATH);
|
||||
if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
|
||||
return TotpConfigFileOpenError;
|
||||
}
|
||||
}
|
||||
|
||||
if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) {
|
||||
totp_close_config_file(fff_data_file);
|
||||
FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH);
|
||||
return TotpConfigFileOpenError;
|
||||
}
|
||||
|
||||
flipper_format_write_header_cstr(
|
||||
fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION);
|
||||
float tmp_tz = 0;
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
flipper_format_write_comment_cstr(
|
||||
fff_data_file,
|
||||
"Timezone offset in hours. Important note: do not put '+' sign for positive values");
|
||||
flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1);
|
||||
|
||||
uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
flipper_format_write_comment_cstr(
|
||||
fff_data_file,
|
||||
"How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)");
|
||||
flipper_format_write_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE BEGIN ===");
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
flipper_format_write_comment_cstr(
|
||||
fff_data_file, "# Token name which will be visible in the UI.");
|
||||
furi_string_printf(temp_str, "%s: Sample token name", TOTP_CONFIG_KEY_TOKEN_NAME);
|
||||
flipper_format_write_comment(fff_data_file, temp_str);
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
|
||||
flipper_format_write_comment_cstr(
|
||||
fff_data_file,
|
||||
"# Plain token secret without spaces, dashes and etc, just pure alpha-numeric characters. Important note: plain token will be encrypted and replaced by TOTP app");
|
||||
furi_string_printf(temp_str, "%s: plaintokensecret", TOTP_CONFIG_KEY_TOKEN_SECRET);
|
||||
flipper_format_write_comment(fff_data_file, temp_str);
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
|
||||
furi_string_printf(
|
||||
temp_str,
|
||||
" # Token hashing algorithm to use during code generation. Supported options are %s, %s and %s. If you are not use which one to use - use %s",
|
||||
TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME,
|
||||
TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME,
|
||||
TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME,
|
||||
TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME);
|
||||
flipper_format_write_comment(fff_data_file, temp_str);
|
||||
furi_string_printf(
|
||||
temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME);
|
||||
flipper_format_write_comment(fff_data_file, temp_str);
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
|
||||
flipper_format_write_comment_cstr(
|
||||
fff_data_file,
|
||||
"# How many digits there should be in generated code. Available options are 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6");
|
||||
furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS);
|
||||
flipper_format_write_comment(fff_data_file, temp_str);
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
|
||||
flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ===");
|
||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||
|
||||
furi_string_free(temp_str);
|
||||
if(!flipper_format_rewind(fff_data_file)) {
|
||||
totp_close_config_file(fff_data_file);
|
||||
FURI_LOG_E(LOGGING_TAG, "Rewind error");
|
||||
return TotpConfigFileOpenError;
|
||||
}
|
||||
}
|
||||
|
||||
*file = fff_data_file;
|
||||
return TotpConfigFileOpenSuccess;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult
|
||||
totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) {
|
||||
TotpConfigFileUpdateResult update_result;
|
||||
do {
|
||||
if(!flipper_format_seek_to_end(file)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
bool token_is_valid = token_info->token != NULL && token_info->token_length > 0;
|
||||
if(!token_is_valid &&
|
||||
!flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!")) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!token_is_valid && !flipper_format_write_comment_cstr(file, "!!! WARNING END !!!")) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_string_cstr(
|
||||
file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info))) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t tmp_uint32 = token_info->digits;
|
||||
if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
update_result = TotpConfigFileUpdateSuccess;
|
||||
} while(false);
|
||||
|
||||
return update_result;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) {
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* file;
|
||||
TotpConfigFileUpdateResult update_result;
|
||||
|
||||
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
|
||||
do {
|
||||
if(totp_config_file_save_new_token_i(file, token_info) !=
|
||||
TotpConfigFileUpdateSuccess) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
update_result = TotpConfigFileUpdateSuccess;
|
||||
} while(false);
|
||||
|
||||
totp_close_config_file(file);
|
||||
} else {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
}
|
||||
|
||||
totp_close_storage();
|
||||
return update_result;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset) {
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* file;
|
||||
TotpConfigFileUpdateResult update_result;
|
||||
|
||||
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
|
||||
do {
|
||||
if(!flipper_format_insert_or_update_float(
|
||||
file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
update_result = TotpConfigFileUpdateSuccess;
|
||||
} while(false);
|
||||
|
||||
totp_close_config_file(file);
|
||||
} else {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
}
|
||||
|
||||
totp_close_storage();
|
||||
return update_result;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult
|
||||
totp_config_file_update_notification_method(NotificationMethod new_notification_method) {
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* file;
|
||||
TotpConfigFileUpdateResult update_result;
|
||||
|
||||
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
|
||||
do {
|
||||
uint32_t tmp_uint32 = new_notification_method;
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
update_result = TotpConfigFileUpdateSuccess;
|
||||
} while(false);
|
||||
|
||||
totp_close_config_file(file);
|
||||
} else {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
}
|
||||
|
||||
totp_close_storage();
|
||||
return update_result;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) {
|
||||
Storage* cfg_storage = totp_open_storage();
|
||||
FlipperFormat* file;
|
||||
TotpConfigFileUpdateResult update_result;
|
||||
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
|
||||
do {
|
||||
if(!flipper_format_insert_or_update_float(
|
||||
file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
uint32_t tmp_uint32 = plugin_state->notification_method;
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
update_result = TotpConfigFileUpdateSuccess;
|
||||
} while(false);
|
||||
|
||||
totp_close_config_file(file);
|
||||
} else {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
}
|
||||
|
||||
totp_close_storage();
|
||||
return update_result;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
TotpConfigFileUpdateResult result = TotpConfigFileUpdateSuccess;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(fff_data_file, CONFIG_FILE_TEMP_PATH)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_header_cstr(
|
||||
fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
fff_data_file,
|
||||
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
|
||||
plugin_state->crypto_verify_data,
|
||||
plugin_state->crypto_verify_data_length)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_float(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_bool(
|
||||
fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
uint32_t tmp_uint32 = plugin_state->notification_method;
|
||||
if(!flipper_format_write_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
bool tokens_written = true;
|
||||
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
|
||||
const TokenInfo* token_info = node->data;
|
||||
tokens_written = tokens_written &&
|
||||
totp_config_file_save_new_token_i(fff_data_file, token_info) ==
|
||||
TotpConfigFileUpdateSuccess;
|
||||
});
|
||||
|
||||
if(!tokens_written) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
totp_close_config_file(fff_data_file);
|
||||
|
||||
if(result == TotpConfigFileUpdateSuccess) {
|
||||
if(storage_file_exists(storage, CONFIG_FILE_ORIG_PATH)) {
|
||||
storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH);
|
||||
}
|
||||
|
||||
if(storage_common_rename(storage, CONFIG_FILE_PATH, CONFIG_FILE_ORIG_PATH) != FSE_OK) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
} else if(storage_common_rename(storage, CONFIG_FILE_TEMP_PATH, CONFIG_FILE_PATH) != FSE_OK) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
} else if(!storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH)) {
|
||||
result = TotpConfigFileUpdateError;
|
||||
}
|
||||
}
|
||||
|
||||
totp_close_storage();
|
||||
return result;
|
||||
}
|
||||
|
||||
TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file;
|
||||
|
||||
TotpConfigFileOpenResult result;
|
||||
if((result = totp_open_config_file(storage, &fff_data_file)) != TotpConfigFileOpenSuccess) {
|
||||
totp_close_storage();
|
||||
return result;
|
||||
}
|
||||
|
||||
plugin_state->timezone_offset = 0;
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
uint32_t file_version;
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(file_version < CONFIG_FILE_ACTUAL_VERSION) {
|
||||
FURI_LOG_I(
|
||||
LOGGING_TAG,
|
||||
"Obsolete config file version detected. Current version: %" PRIu32
|
||||
"; Actual version: %" PRId16,
|
||||
file_version,
|
||||
CONFIG_FILE_ACTUAL_VERSION);
|
||||
totp_close_config_file(fff_data_file);
|
||||
|
||||
if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) {
|
||||
storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH);
|
||||
}
|
||||
|
||||
if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) {
|
||||
FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH);
|
||||
if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
|
||||
FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage);
|
||||
if(!flipper_format_file_open_existing(
|
||||
fff_backup_data_file, CONFIG_FILE_BACKUP_PATH)) {
|
||||
flipper_format_file_close(fff_backup_data_file);
|
||||
flipper_format_free(fff_backup_data_file);
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(file_version == 1) {
|
||||
if(totp_config_migrate_v1_to_v2(fff_data_file, fff_backup_data_file)) {
|
||||
FURI_LOG_I(LOGGING_TAG, "Applied migration from v1 to v2");
|
||||
} else {
|
||||
FURI_LOG_W(
|
||||
LOGGING_TAG, "An error occurred during migration from v1 to v2");
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_file_close(fff_backup_data_file);
|
||||
flipper_format_free(fff_backup_data_file);
|
||||
flipper_format_rewind(fff_data_file);
|
||||
} else {
|
||||
FURI_LOG_E(
|
||||
LOGGING_TAG,
|
||||
"An error occurred during taking backup of %s into %s before migration",
|
||||
CONFIG_FILE_PATH,
|
||||
CONFIG_FILE_BACKUP_PATH);
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!flipper_format_read_hex(
|
||||
fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Missing base IV");
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(fff_data_file)) {
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t crypto_size;
|
||||
if(flipper_format_get_value_count(
|
||||
fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) &&
|
||||
crypto_size > 0) {
|
||||
plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size);
|
||||
furi_check(plugin_state->crypto_verify_data != NULL);
|
||||
plugin_state->crypto_verify_data_length = crypto_size;
|
||||
if(!flipper_format_read_hex(
|
||||
fff_data_file,
|
||||
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
|
||||
plugin_state->crypto_verify_data,
|
||||
crypto_size)) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token");
|
||||
free(plugin_state->crypto_verify_data);
|
||||
plugin_state->crypto_verify_data = NULL;
|
||||
plugin_state->crypto_verify_data_length = 0;
|
||||
}
|
||||
} else {
|
||||
plugin_state->crypto_verify_data = NULL;
|
||||
plugin_state->crypto_verify_data_length = 0;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(fff_data_file)) {
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_float(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
|
||||
plugin_state->timezone_offset = 0;
|
||||
FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0");
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(fff_data_file)) {
|
||||
result = TotpConfigFileOpenError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
|
||||
plugin_state->pin_set = true;
|
||||
}
|
||||
|
||||
flipper_format_rewind(fff_data_file);
|
||||
|
||||
uint32_t tmp_uint32;
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
|
||||
tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
|
||||
}
|
||||
|
||||
plugin_state->notification_method = tmp_uint32;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
totp_close_config_file(fff_data_file);
|
||||
totp_close_storage();
|
||||
return result;
|
||||
}
|
||||
|
||||
TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* fff_data_file;
|
||||
if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
|
||||
totp_close_storage();
|
||||
return TokenLoadingResultError;
|
||||
}
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t temp_data32;
|
||||
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
|
||||
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
|
||||
totp_close_storage();
|
||||
furi_string_free(temp_str);
|
||||
return TokenLoadingResultError;
|
||||
}
|
||||
|
||||
TokenLoadingResult result = TokenLoadingResultSuccess;
|
||||
uint16_t index = 0;
|
||||
bool has_any_plain_secret = false;
|
||||
|
||||
while(true) {
|
||||
if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
|
||||
break;
|
||||
}
|
||||
|
||||
TokenInfo* tokenInfo = token_info_alloc();
|
||||
|
||||
size_t temp_cstr_len = furi_string_size(temp_str);
|
||||
tokenInfo->name = malloc(temp_cstr_len + 1);
|
||||
furi_check(tokenInfo->name != NULL);
|
||||
strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1);
|
||||
|
||||
uint32_t secret_bytes_count;
|
||||
if(!flipper_format_get_value_count(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) {
|
||||
secret_bytes_count = 0;
|
||||
}
|
||||
|
||||
if(secret_bytes_count == 1) { // Plain secret key
|
||||
if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) {
|
||||
if(token_info_set_secret(
|
||||
tokenInfo,
|
||||
furi_string_get_cstr(temp_str),
|
||||
furi_string_size(temp_str),
|
||||
&plugin_state->iv[0])) {
|
||||
FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
|
||||
} else {
|
||||
tokenInfo->token = NULL;
|
||||
tokenInfo->token_length = 0;
|
||||
FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name);
|
||||
result = TokenLoadingResultWarning;
|
||||
}
|
||||
} else {
|
||||
tokenInfo->token = NULL;
|
||||
tokenInfo->token_length = 0;
|
||||
result = TokenLoadingResultWarning;
|
||||
}
|
||||
|
||||
has_any_plain_secret = true;
|
||||
} else { // encrypted
|
||||
tokenInfo->token_length = secret_bytes_count;
|
||||
if(secret_bytes_count > 0) {
|
||||
tokenInfo->token = malloc(tokenInfo->token_length);
|
||||
furi_check(tokenInfo->token != NULL);
|
||||
if(!flipper_format_read_hex(
|
||||
fff_data_file,
|
||||
TOTP_CONFIG_KEY_TOKEN_SECRET,
|
||||
tokenInfo->token,
|
||||
tokenInfo->token_length)) {
|
||||
free(tokenInfo->token);
|
||||
tokenInfo->token = NULL;
|
||||
tokenInfo->token_length = 0;
|
||||
result = TokenLoadingResultWarning;
|
||||
}
|
||||
} else {
|
||||
tokenInfo->token = NULL;
|
||||
result = TokenLoadingResultWarning;
|
||||
}
|
||||
}
|
||||
|
||||
if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) {
|
||||
token_info_set_algo_from_str(tokenInfo, temp_str);
|
||||
} else {
|
||||
tokenInfo->algo = SHA1;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) ||
|
||||
!token_info_set_digits_from_int(tokenInfo, temp_data32)) {
|
||||
tokenInfo->digits = TOTP_6_DIGITS;
|
||||
}
|
||||
|
||||
FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name);
|
||||
|
||||
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
plugin_state->tokens_count = index;
|
||||
plugin_state->token_list_loaded = true;
|
||||
|
||||
FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
totp_close_config_file(fff_data_file);
|
||||
totp_close_storage();
|
||||
|
||||
if(has_any_plain_secret) {
|
||||
totp_full_save_config_file(plugin_state);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TotpConfigFileUpdateResult
|
||||
totp_config_file_update_crypto_signatures(const PluginState* plugin_state) {
|
||||
Storage* storage = totp_open_storage();
|
||||
FlipperFormat* config_file;
|
||||
TotpConfigFileUpdateResult update_result;
|
||||
if(totp_open_config_file(storage, &config_file) == TotpConfigFileOpenSuccess) {
|
||||
do {
|
||||
if(!flipper_format_insert_or_update_hex(
|
||||
config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_insert_or_update_hex(
|
||||
config_file,
|
||||
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
|
||||
plugin_state->crypto_verify_data,
|
||||
plugin_state->crypto_verify_data_length)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
break;
|
||||
}
|
||||
|
||||
update_result = TotpConfigFileUpdateSuccess;
|
||||
} while(false);
|
||||
|
||||
totp_close_config_file(config_file);
|
||||
} else {
|
||||
update_result = TotpConfigFileUpdateError;
|
||||
}
|
||||
|
||||
totp_close_storage();
|
||||
return update_result;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <furi.h>
|
||||
#include "../../types/plugin_state.h"
|
||||
#include "../../types/token_info.h"
|
||||
#include "constants.h"
|
||||
|
||||
typedef uint8_t TokenLoadingResult;
|
||||
typedef uint8_t TotpConfigFileOpenResult;
|
||||
typedef uint8_t TotpConfigFileUpdateResult;
|
||||
|
||||
/**
|
||||
* @brief Token loading results
|
||||
*/
|
||||
enum TokenLoadingResults {
|
||||
/**
|
||||
* @brief All the tokens loaded successfully
|
||||
*/
|
||||
TokenLoadingResultSuccess,
|
||||
|
||||
/**
|
||||
* @brief All the tokens loaded, but there are some warnings
|
||||
*/
|
||||
TokenLoadingResultWarning,
|
||||
|
||||
/**
|
||||
* @brief Tokens not loaded because of error(s)
|
||||
*/
|
||||
TokenLoadingResultError
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Config file opening result
|
||||
*/
|
||||
enum TotpConfigFileOpenResults {
|
||||
/**
|
||||
* @brief Config file opened successfully
|
||||
*/
|
||||
TotpConfigFileOpenSuccess = 0,
|
||||
|
||||
/**
|
||||
* @brief An error has occurred during opening config file
|
||||
*/
|
||||
TotpConfigFileOpenError = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Config file updating result
|
||||
*/
|
||||
enum TotpConfigFileUpdateResults {
|
||||
/**
|
||||
* @brief Config file updated successfully
|
||||
*/
|
||||
TotpConfigFileUpdateSuccess,
|
||||
|
||||
/**
|
||||
* @brief An error has occurred during updating config file
|
||||
*/
|
||||
TotpConfigFileUpdateError
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Saves all the settings and tokens to an application config file
|
||||
* @param plugin_state application state
|
||||
* @return Config file update result
|
||||
*/
|
||||
TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Loads basic information from an application config file into application state without loading all the tokens
|
||||
* @param plugin_state application state
|
||||
* @return Config file open result
|
||||
*/
|
||||
TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Loads tokens from an application config file into application state
|
||||
* @param plugin_state application state
|
||||
* @return Results of the loading
|
||||
*/
|
||||
TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Add new token to the end of the application config file
|
||||
* @param token_info token information to be saved
|
||||
* @return Config file update result
|
||||
*/
|
||||
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info);
|
||||
|
||||
/**
|
||||
* @brief Updates timezone offset in an application config file
|
||||
* @param new_timezone_offset new timezone offset to be set
|
||||
* @return Config file update result
|
||||
*/
|
||||
TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset);
|
||||
|
||||
/**
|
||||
* @brief Updates notification method in an application config file
|
||||
* @param new_notification_method new notification method to be set
|
||||
* @return Config file update result
|
||||
*/
|
||||
TotpConfigFileUpdateResult
|
||||
totp_config_file_update_notification_method(NotificationMethod new_notification_method);
|
||||
|
||||
/**
|
||||
* @brief Updates application user settings
|
||||
* @param plugin_state application state
|
||||
* @return Config file update result
|
||||
*/
|
||||
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state);
|
||||
|
||||
/**
|
||||
* @brief Updates crypto signatures information
|
||||
* @param plugin_state application state
|
||||
* @return Config file update result
|
||||
*/
|
||||
TotpConfigFileUpdateResult
|
||||
totp_config_file_update_crypto_signatures(const PluginState* plugin_state);
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#define CONFIG_FILE_HEADER "Flipper TOTP plugin config file"
|
||||
#define CONFIG_FILE_ACTUAL_VERSION 2
|
||||
|
||||
#define TOTP_CONFIG_KEY_TIMEZONE "Timezone"
|
||||
#define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName"
|
||||
#define TOTP_CONFIG_KEY_TOKEN_SECRET "TokenSecret"
|
||||
#define TOTP_CONFIG_KEY_TOKEN_ALGO "TokenAlgo"
|
||||
#define TOTP_CONFIG_KEY_TOKEN_DIGITS "TokenDigits"
|
||||
#define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto"
|
||||
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
|
||||
#define TOTP_CONFIG_KEY_PINSET "PinIsSet"
|
||||
#define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod"
|
||||
|
||||
#define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1"
|
||||
#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"
|
||||
#define TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME "sha512"
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#include "config_migration_v1_to_v2.h"
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include "../constants.h"
|
||||
|
||||
#define NEW_VERSION 2
|
||||
|
||||
bool totp_config_migrate_v1_to_v2(
|
||||
FlipperFormat* fff_data_file,
|
||||
FlipperFormat* fff_backup_data_file) {
|
||||
flipper_format_write_header_cstr(fff_data_file, CONFIG_FILE_HEADER, NEW_VERSION);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str)) {
|
||||
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str);
|
||||
}
|
||||
|
||||
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str)) {
|
||||
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, temp_str);
|
||||
}
|
||||
|
||||
if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str)) {
|
||||
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, temp_str);
|
||||
}
|
||||
|
||||
while(true) {
|
||||
if(!flipper_format_read_string(
|
||||
fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) {
|
||||
break;
|
||||
}
|
||||
|
||||
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str);
|
||||
|
||||
flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str);
|
||||
flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str);
|
||||
|
||||
flipper_format_write_string_cstr(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME);
|
||||
uint32_t default_digits = 6;
|
||||
flipper_format_write_uint32(
|
||||
fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1);
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
return true;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
bool totp_config_migrate_v1_to_v2(
|
||||
FlipperFormat* fff_data_file,
|
||||
FlipperFormat* fff_backup_data_file);
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define CONVERT_DIGIT_TO_CHAR(digit) ((digit) + '0')
|
||||
#define CONVERT_CHAR_TO_DIGIT(ch) ((ch) - '0')
|
||||
@@ -0,0 +1,133 @@
|
||||
#include "crypto.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../config/config.h"
|
||||
#include "../../types/common.h"
|
||||
#include "memset_s.h"
|
||||
|
||||
#define CRYPTO_KEY_SLOT 2
|
||||
#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass"
|
||||
#define CRYPTO_VERIFY_KEY_LENGTH 16
|
||||
#define CRYPTO_ALIGNMENT_FACTOR 16
|
||||
|
||||
uint8_t* totp_crypto_encrypt(
|
||||
const uint8_t* plain_data,
|
||||
const size_t plain_data_length,
|
||||
const uint8_t* iv,
|
||||
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_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
|
||||
furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length);
|
||||
furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
|
||||
|
||||
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_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv);
|
||||
furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length);
|
||||
furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
|
||||
}
|
||||
|
||||
return encrypted_data;
|
||||
}
|
||||
|
||||
uint8_t* totp_crypto_decrypt(
|
||||
const uint8_t* encrypted_data,
|
||||
const size_t encrypted_data_length,
|
||||
const uint8_t* iv,
|
||||
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_decrypt(encrypted_data, decrypted_data, encrypted_data_length);
|
||||
furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT);
|
||||
return decrypted_data;
|
||||
}
|
||||
|
||||
bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
|
||||
if(plugin_state->crypto_verify_data == NULL) {
|
||||
FURI_LOG_D(LOGGING_TAG, "Generating new IV");
|
||||
furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE);
|
||||
}
|
||||
|
||||
memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE);
|
||||
if(pin != NULL && pin_length > 0) {
|
||||
uint8_t max_i;
|
||||
if(pin_length > TOTP_IV_SIZE) {
|
||||
max_i = TOTP_IV_SIZE;
|
||||
} else {
|
||||
max_i = pin_length;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < max_i; i++) {
|
||||
plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1));
|
||||
}
|
||||
} else {
|
||||
uint8_t max_i;
|
||||
size_t uid_size = furi_hal_version_uid_size();
|
||||
if(uid_size > TOTP_IV_SIZE) {
|
||||
max_i = TOTP_IV_SIZE;
|
||||
} else {
|
||||
max_i = uid_size;
|
||||
}
|
||||
|
||||
const uint8_t* uid = furi_hal_version_uid();
|
||||
for(uint8_t i = 0; i < max_i; i++) {
|
||||
plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
if(plugin_state->crypto_verify_data == NULL) {
|
||||
FURI_LOG_D(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;
|
||||
|
||||
plugin_state->crypto_verify_data = totp_crypto_encrypt(
|
||||
(uint8_t*)CRYPTO_VERIFY_KEY,
|
||||
CRYPTO_VERIFY_KEY_LENGTH,
|
||||
&plugin_state->iv[0],
|
||||
&plugin_state->crypto_verify_data_length);
|
||||
|
||||
plugin_state->pin_set = pin != NULL && pin_length > 0;
|
||||
|
||||
result = totp_config_file_update_crypto_signatures(plugin_state) ==
|
||||
TotpConfigFileUpdateSuccess;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool totp_crypto_verify_key(const PluginState* plugin_state) {
|
||||
size_t decrypted_key_length;
|
||||
const uint8_t* decrypted_key = totp_crypto_decrypt(
|
||||
plugin_state->crypto_verify_data,
|
||||
plugin_state->crypto_verify_data_length,
|
||||
&plugin_state->iv[0],
|
||||
&decrypted_key_length);
|
||||
|
||||
bool key_valid = true;
|
||||
for(uint8_t i = 0; i < CRYPTO_VERIFY_KEY_LENGTH && key_valid; i++) {
|
||||
if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false;
|
||||
}
|
||||
|
||||
return key_valid;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../types/plugin_state.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 iv initialization vector (IV) to be used to encrypt plain data
|
||||
* @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,
|
||||
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[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,
|
||||
size_t* decrypted_data_length);
|
||||
|
||||
/**
|
||||
* @brief Seed initialization vector (IV) using user's PIN
|
||||
* @param plugin_state application state
|
||||
* @param pin user's PIN
|
||||
* @param pin_length user's PIN length
|
||||
* @return \c true on success; \c false otherwise
|
||||
*/
|
||||
bool totp_crypto_seed_iv(PluginState* plugin_state, 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
|
||||
* @return \c true if cryptographic information is valid; \c false otherwise
|
||||
*/
|
||||
bool totp_crypto_verify_key(const PluginState* plugin_state);
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "byteswap.h"
|
||||
|
||||
uint32_t swap_uint32(uint32_t val) {
|
||||
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
|
||||
return (val << 16) | (val >> 16);
|
||||
}
|
||||
|
||||
uint64_t swap_uint64(uint64_t val) {
|
||||
val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);
|
||||
val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);
|
||||
return (val << 32) | (val >> 32);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Swap bytes in 32-bit value
|
||||
* @param val value to swap bytes in
|
||||
* @return Value with bytes swapped
|
||||
*/
|
||||
uint32_t swap_uint32(uint32_t val);
|
||||
|
||||
/**
|
||||
* @brief Swap bytes in 64-bit value
|
||||
* @param val value to swap bytes in
|
||||
* @return Value with bytes swapped
|
||||
*/
|
||||
uint64_t swap_uint64(uint64_t val);
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <string.h>
|
||||
#include "sha256.h"
|
||||
#include "memxor.h"
|
||||
|
||||
#define IPAD 0x36
|
||||
#define OPAD 0x5c
|
||||
|
||||
/* Concatenate two preprocessor tokens. */
|
||||
#define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix
|
||||
#define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix)
|
||||
|
||||
#if GL_HMAC_NAME == 5
|
||||
#define HMAC_ALG md5
|
||||
#else
|
||||
#define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME)
|
||||
#endif
|
||||
|
||||
#define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx)
|
||||
#define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG)
|
||||
#define GL_HMAC_FN_INIT _GLHMAC_CONCAT(HMAC_ALG, _init_ctx)
|
||||
#define GL_HMAC_FN_BLOC _GLHMAC_CONCAT(HMAC_ALG, _process_block)
|
||||
#define GL_HMAC_FN_PROC _GLHMAC_CONCAT(HMAC_ALG, _process_bytes)
|
||||
#define GL_HMAC_FN_FINI _GLHMAC_CONCAT(HMAC_ALG, _finish_ctx)
|
||||
|
||||
static void
|
||||
hmac_hash(const void* key, size_t keylen, const void* in, size_t inlen, int pad, void* resbuf) {
|
||||
struct GL_HMAC_CTX hmac_ctx;
|
||||
char block[GL_HMAC_BLOCKSIZE];
|
||||
|
||||
memset(block, pad, sizeof block);
|
||||
memxor(block, key, keylen);
|
||||
|
||||
GL_HMAC_FN_INIT(&hmac_ctx);
|
||||
GL_HMAC_FN_BLOC(block, sizeof block, &hmac_ctx);
|
||||
GL_HMAC_FN_PROC(in, inlen, &hmac_ctx);
|
||||
GL_HMAC_FN_FINI(&hmac_ctx, resbuf);
|
||||
}
|
||||
|
||||
int GL_HMAC_FN(const void* key, size_t keylen, const void* in, size_t inlen, void* resbuf) {
|
||||
char optkeybuf[GL_HMAC_HASHSIZE];
|
||||
char innerhash[GL_HMAC_HASHSIZE];
|
||||
|
||||
/* Ensure key size is <= block size. */
|
||||
if(keylen > GL_HMAC_BLOCKSIZE) {
|
||||
struct GL_HMAC_CTX keyhash;
|
||||
|
||||
GL_HMAC_FN_INIT(&keyhash);
|
||||
GL_HMAC_FN_PROC(key, keylen, &keyhash);
|
||||
GL_HMAC_FN_FINI(&keyhash, optkeybuf);
|
||||
|
||||
key = optkeybuf;
|
||||
/* zero padding of the key to the block size
|
||||
is implicit in the memxor. */
|
||||
keylen = sizeof optkeybuf;
|
||||
}
|
||||
|
||||
/* Compute INNERHASH from KEY and IN. */
|
||||
hmac_hash(key, keylen, in, inlen, IPAD, innerhash);
|
||||
|
||||
/* Compute result from KEY and INNERHASH. */
|
||||
hmac_hash(key, keylen, innerhash, sizeof innerhash, OPAD, resbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* hmac-sha1.c -- hashed message authentication codes
|
||||
Copyright (C) 2018-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "hmac_sha1.h"
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
#define GL_HMAC_NAME 1
|
||||
#define GL_HMAC_BLOCKSIZE 64
|
||||
#define GL_HMAC_HASHSIZE 20
|
||||
#include "hmac_common.h"
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define HMAC_SHA1_RESULT_SIZE 20
|
||||
|
||||
/* Compute Hashed Message Authentication Code with SHA-1, over BUFFER
|
||||
data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the
|
||||
output to pre-allocated 20 byte minimum RESBUF buffer. Return 0 on
|
||||
success. */
|
||||
int hmac_sha1(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf);
|
||||
@@ -0,0 +1,23 @@
|
||||
/* hmac-sha256.c -- hashed message authentication codes
|
||||
Copyright (C) 2018-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "hmac_sha256.h"
|
||||
|
||||
#define GL_HMAC_NAME 256
|
||||
#define GL_HMAC_BLOCKSIZE 64
|
||||
#define GL_HMAC_HASHSIZE 32
|
||||
|
||||
#include "hmac_common.h"
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define HMAC_SHA256_RESULT_SIZE 32
|
||||
|
||||
/* Compute Hashed Message Authentication Code with SHA-256, over BUFFER
|
||||
data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the
|
||||
output to pre-allocated 32 byte minimum RESBUF buffer. Return 0 on
|
||||
success. */
|
||||
int hmac_sha256(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf);
|
||||
@@ -0,0 +1,24 @@
|
||||
/* hmac-sha512.c -- hashed message authentication codes
|
||||
Copyright (C) 2018-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "hmac_sha512.h"
|
||||
|
||||
#include "sha512.h"
|
||||
|
||||
#define GL_HMAC_NAME 512
|
||||
#define GL_HMAC_BLOCKSIZE 128
|
||||
#define GL_HMAC_HASHSIZE 64
|
||||
#include "hmac_common.h"
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define HMAC_SHA512_RESULT_SIZE 64
|
||||
|
||||
/* Compute Hashed Message Authentication Code with SHA-512, over BUFFER
|
||||
data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the
|
||||
output to pre-allocated 64 byte minimum RESBUF buffer. Return 0 on
|
||||
success. */
|
||||
int hmac_sha512(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf);
|
||||
@@ -0,0 +1,30 @@
|
||||
/* memxor.c -- perform binary exclusive OR operation of two memory blocks.
|
||||
Copyright (C) 2005, 2006 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
/* Written by Simon Josefsson. The interface was inspired by memxor
|
||||
in Niels Möller's Nettle. */
|
||||
|
||||
#include "memxor.h"
|
||||
|
||||
void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n) {
|
||||
char const* s = (char const*)src;
|
||||
char* d = (char*)dest;
|
||||
|
||||
for(; n > 0; n--) *d++ ^= *s++;
|
||||
|
||||
return dest;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* memxor.h -- perform binary exclusive OR operation on memory blocks.
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
/* Written by Simon Josefsson. The interface was inspired by memxor
|
||||
in Niels Möller's Nettle. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Compute binary exclusive OR of memory areas DEST and SRC, putting
|
||||
the result in DEST, of length N bytes. Returns a pointer to
|
||||
DEST. */
|
||||
void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n);
|
||||
@@ -0,0 +1,269 @@
|
||||
/* sha1.c - Functions to compute SHA1 message digest of files or
|
||||
memory blocks according to the NIST specification FIPS-180-1.
|
||||
|
||||
Copyright (C) 2000-2001, 2003-2006, 2008-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Scott G. Miller
|
||||
Credits:
|
||||
Robert Klep <robert@ilse.nl> -- Expansion function fix
|
||||
*/
|
||||
|
||||
/* Specification. */
|
||||
#include "sha1.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define SWAP(n) (n)
|
||||
#else
|
||||
#include "byteswap.h"
|
||||
#define SWAP(n) swap_uint32(n)
|
||||
#endif
|
||||
|
||||
/* This array contains the bytes used to pad the buffer to the next
|
||||
64-byte boundary. (RFC 1321, 3.1: Step 1) */
|
||||
static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */};
|
||||
|
||||
/* Take a pointer to a 160 bit block of data (five 32 bit ints) and
|
||||
initialize it to the start constants of the SHA1 algorithm. This
|
||||
must be called before using hash in the call to sha1_hash. */
|
||||
void sha1_init_ctx(struct sha1_ctx* ctx) {
|
||||
ctx->A = 0x67452301;
|
||||
ctx->B = 0xefcdab89;
|
||||
ctx->C = 0x98badcfe;
|
||||
ctx->D = 0x10325476;
|
||||
ctx->E = 0xc3d2e1f0;
|
||||
|
||||
ctx->total[0] = ctx->total[1] = 0;
|
||||
ctx->buflen = 0;
|
||||
}
|
||||
|
||||
/* Copy the 4 byte value from v into the memory location pointed to by *cp,
|
||||
If your architecture allows unaligned access this is equivalent to
|
||||
* (uint32_t *) cp = v */
|
||||
static void set_uint32(char* cp, uint32_t v) {
|
||||
memcpy(cp, &v, sizeof v);
|
||||
}
|
||||
|
||||
/* Put result from CTX in first 20 bytes following RESBUF. The result
|
||||
must be in little endian byte order. */
|
||||
void* sha1_read_ctx(const struct sha1_ctx* ctx, void* resbuf) {
|
||||
char* r = resbuf;
|
||||
set_uint32(r + 0 * sizeof ctx->A, SWAP(ctx->A));
|
||||
set_uint32(r + 1 * sizeof ctx->B, SWAP(ctx->B));
|
||||
set_uint32(r + 2 * sizeof ctx->C, SWAP(ctx->C));
|
||||
set_uint32(r + 3 * sizeof ctx->D, SWAP(ctx->D));
|
||||
set_uint32(r + 4 * sizeof ctx->E, SWAP(ctx->E));
|
||||
|
||||
return resbuf;
|
||||
}
|
||||
|
||||
/* Process the remaining bytes in the internal buffer and the usual
|
||||
prolog according to the standard and write the result to RESBUF. */
|
||||
void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) {
|
||||
/* Take yet unprocessed bytes into account. */
|
||||
uint32_t bytes = ctx->buflen;
|
||||
size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
|
||||
|
||||
/* Now count remaining bytes. */
|
||||
ctx->total[0] += bytes;
|
||||
if(ctx->total[0] < bytes) ++ctx->total[1];
|
||||
|
||||
/* Put the 64-bit file length in *bits* at the end of the buffer. */
|
||||
ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29));
|
||||
ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3);
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
|
||||
|
||||
/* Process last bytes. */
|
||||
sha1_process_block(ctx->buffer, size * 4, ctx);
|
||||
|
||||
return sha1_read_ctx(ctx, resbuf);
|
||||
}
|
||||
|
||||
/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The
|
||||
result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest. */
|
||||
void* sha1_buffer(const char* buffer, size_t len, void* resblock) {
|
||||
struct sha1_ctx ctx;
|
||||
|
||||
/* Initialize the computation context. */
|
||||
sha1_init_ctx(&ctx);
|
||||
|
||||
/* Process whole buffer but last len % 64 bytes. */
|
||||
sha1_process_bytes(buffer, len, &ctx);
|
||||
|
||||
/* Put result in desired memory area. */
|
||||
return sha1_finish_ctx(&ctx, resblock);
|
||||
}
|
||||
|
||||
void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx) {
|
||||
/* When we already have some bits in our internal buffer concatenate
|
||||
both inputs first. */
|
||||
if(ctx->buflen != 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
size_t add = 128 - left_over > len ? len : 128 - left_over;
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[left_over], buffer, add);
|
||||
ctx->buflen += add;
|
||||
|
||||
if(ctx->buflen > 64) {
|
||||
sha1_process_block(ctx->buffer, ctx->buflen & ~63, ctx);
|
||||
|
||||
ctx->buflen &= 63;
|
||||
/* The regions in the following copy operation cannot overlap,
|
||||
because ctx->buflen < 64 ≤ (left_over + add) & ~63. */
|
||||
memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen);
|
||||
}
|
||||
|
||||
buffer = (const char*)buffer + add;
|
||||
len -= add;
|
||||
}
|
||||
|
||||
/* Process available complete blocks. */
|
||||
if(len >= 64) {
|
||||
#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned)
|
||||
#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0)
|
||||
if(UNALIGNED_P(buffer))
|
||||
while(len > 64) {
|
||||
sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086
|
||||
buffer = (const char*)buffer + 64;
|
||||
len -= 64;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
sha1_process_block(buffer, len & ~63, ctx);
|
||||
buffer = (const char*)buffer + (len & ~63);
|
||||
len &= 63;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move remaining bytes in internal buffer. */
|
||||
if(len > 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[left_over], buffer, len);
|
||||
left_over += len;
|
||||
if(left_over >= 64) {
|
||||
sha1_process_block(ctx->buffer, 64, ctx);
|
||||
left_over -= 64;
|
||||
/* The regions in the following copy operation cannot overlap,
|
||||
because left_over ≤ 64. */
|
||||
memcpy(ctx->buffer, &ctx->buffer[16], left_over);
|
||||
}
|
||||
ctx->buflen = left_over;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Code below is the primary difference between md5.c and sha1.c --- */
|
||||
|
||||
/* SHA1 round constants */
|
||||
#define K1 0x5a827999
|
||||
#define K2 0x6ed9eba1
|
||||
#define K3 0x8f1bbcdc
|
||||
#define K4 0xca62c1d6
|
||||
|
||||
/* Round functions. Note that F2 is the same as F4. */
|
||||
#define F1(B, C, D) (D ^ (B & (C ^ D)))
|
||||
#define F2(B, C, D) (B ^ C ^ D)
|
||||
#define F3(B, C, D) ((B & C) | (D & (B | C)))
|
||||
#define F4(B, C, D) (B ^ C ^ D)
|
||||
|
||||
/* Process LEN bytes of BUFFER, accumulating context into CTX.
|
||||
It is assumed that LEN % 64 == 0.
|
||||
Most of this code comes from GnuPG's cipher/sha1.c. */
|
||||
|
||||
void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) {
|
||||
const uint32_t* words = buffer;
|
||||
size_t nwords = len / sizeof(uint32_t);
|
||||
const uint32_t* endp = words + nwords;
|
||||
uint32_t x[16];
|
||||
uint32_t a = ctx->A;
|
||||
uint32_t b = ctx->B;
|
||||
uint32_t c = ctx->C;
|
||||
uint32_t d = ctx->D;
|
||||
uint32_t e = ctx->E;
|
||||
uint32_t lolen = len;
|
||||
|
||||
/* First increment the byte count. RFC 1321 specifies the possible
|
||||
length of the file up to 2^64 bits. Here we only compute the
|
||||
number of bytes. Do a double word increment. */
|
||||
ctx->total[0] += lolen;
|
||||
ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen);
|
||||
|
||||
#define rol(x, n) (((x) << (n)) | ((uint32_t)(x) >> (32 - (n))))
|
||||
|
||||
#define M(I) \
|
||||
(tm = x[I & 0x0f] ^ x[(I - 14) & 0x0f] ^ x[(I - 8) & 0x0f] ^ x[(I - 3) & 0x0f], \
|
||||
(x[I & 0x0f] = rol(tm, 1)))
|
||||
|
||||
#define R(A, B, C, D, E, F, K, M) \
|
||||
do { \
|
||||
E += rol(A, 5) + F(B, C, D) + K + M; \
|
||||
B = rol(B, 30); \
|
||||
} while(0)
|
||||
|
||||
while(words < endp) {
|
||||
uint32_t tm;
|
||||
int t;
|
||||
for(t = 0; t < 16; t++) {
|
||||
x[t] = SWAP(*words);
|
||||
words++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 80; i++) {
|
||||
uint32_t xx = i < 16 ? x[i] : M(i);
|
||||
uint32_t ki = i / 20;
|
||||
switch(ki) {
|
||||
case 0:
|
||||
R(a, b, c, d, e, F1, K1, xx);
|
||||
break;
|
||||
case 1:
|
||||
R(a, b, c, d, e, F2, K2, xx);
|
||||
break;
|
||||
case 2:
|
||||
R(a, b, c, d, e, F3, K3, xx);
|
||||
break;
|
||||
default:
|
||||
R(a, b, c, d, e, F4, K4, xx);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t tt = a;
|
||||
a = e;
|
||||
e = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b = tt;
|
||||
}
|
||||
|
||||
a = ctx->A += a;
|
||||
b = ctx->B += b;
|
||||
c = ctx->C += c;
|
||||
d = ctx->D += d;
|
||||
e = ctx->E += e;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hey Emacs!
|
||||
* Local Variables:
|
||||
* coding: utf-8
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Declarations of functions and data types used for SHA1 sum
|
||||
library functions.
|
||||
Copyright (C) 2000-2001, 2003, 2005-2006, 2008-2022 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
/* Structure to save state of computation between the single steps. */
|
||||
struct sha1_ctx {
|
||||
uint32_t A;
|
||||
uint32_t B;
|
||||
uint32_t C;
|
||||
uint32_t D;
|
||||
uint32_t E;
|
||||
|
||||
uint32_t total[2];
|
||||
uint32_t buflen; /* ≥ 0, ≤ 128 */
|
||||
uint32_t buffer[32]; /* 128 bytes; the first buflen bytes are in use */
|
||||
};
|
||||
|
||||
/* Initialize structure containing state of computation. */
|
||||
extern void sha1_init_ctx(struct sha1_ctx* ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is necessary that LEN is a multiple of 64!!! */
|
||||
extern void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is NOT required that LEN is a multiple of 64. */
|
||||
extern void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx);
|
||||
|
||||
/* Process the remaining bytes in the buffer and put result from CTX
|
||||
in first 20 bytes following RESBUF. The result is always in little
|
||||
endian byte order, so that a byte-wise output yields to the wanted
|
||||
ASCII representation of the message digest. */
|
||||
extern void* sha1_finish_ctx(struct sha1_ctx* ctx, void* restrict resbuf);
|
||||
|
||||
/* Put result from CTX in first 20 bytes following RESBUF. The result is
|
||||
always in little endian byte order, so that a byte-wise output yields
|
||||
to the wanted ASCII representation of the message digest. */
|
||||
extern void* sha1_read_ctx(const struct sha1_ctx* ctx, void* restrict resbuf);
|
||||
|
||||
/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The
|
||||
result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest. */
|
||||
extern void* sha1_buffer(const char* buffer, size_t len, void* restrict resblock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hey Emacs!
|
||||
* Local Variables:
|
||||
* coding: utf-8
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,286 @@
|
||||
/* sha256.c - Functions to compute SHA256 message digest of files or
|
||||
memory blocks according to the NIST specification FIPS-180-2.
|
||||
|
||||
Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by David Madore, considerably copypasting from
|
||||
Scott G. Miller's sha1.c
|
||||
*/
|
||||
|
||||
/* Specification. */
|
||||
#include "sha256.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define SWAP(n) (n)
|
||||
#else
|
||||
#include "byteswap.h"
|
||||
#define SWAP(n) swap_uint32(n)
|
||||
#endif
|
||||
|
||||
/* This array contains the bytes used to pad the buffer to the next
|
||||
64-byte boundary. */
|
||||
static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */};
|
||||
|
||||
/*
|
||||
Takes a pointer to a 256 bit block of data (eight 32 bit ints) and
|
||||
initializes it to the start constants of the SHA256 algorithm. This
|
||||
must be called before using hash in the call to sha256_hash
|
||||
*/
|
||||
void sha256_init_ctx(struct sha256_ctx* ctx) {
|
||||
ctx->state[0] = 0x6a09e667UL;
|
||||
ctx->state[1] = 0xbb67ae85UL;
|
||||
ctx->state[2] = 0x3c6ef372UL;
|
||||
ctx->state[3] = 0xa54ff53aUL;
|
||||
ctx->state[4] = 0x510e527fUL;
|
||||
ctx->state[5] = 0x9b05688cUL;
|
||||
ctx->state[6] = 0x1f83d9abUL;
|
||||
ctx->state[7] = 0x5be0cd19UL;
|
||||
|
||||
ctx->total[0] = ctx->total[1] = 0;
|
||||
ctx->buflen = 0;
|
||||
}
|
||||
|
||||
/* Copy the value from v into the memory location pointed to by *CP,
|
||||
If your architecture allows unaligned access, this is equivalent to
|
||||
* (__typeof__ (v) *) cp = v */
|
||||
static void set_uint32(char* cp, uint32_t v) {
|
||||
memcpy(cp, &v, sizeof v);
|
||||
}
|
||||
|
||||
/* Put result from CTX in first 32 bytes following RESBUF.
|
||||
The result must be in little endian byte order. */
|
||||
void* sha256_read_ctx(const struct sha256_ctx* ctx, void* resbuf) {
|
||||
int i;
|
||||
char* r = resbuf;
|
||||
|
||||
for(i = 0; i < 8; i++) set_uint32(r + i * sizeof ctx->state[0], SWAP(ctx->state[i]));
|
||||
|
||||
return resbuf;
|
||||
}
|
||||
|
||||
/* Process the remaining bytes in the internal buffer and the usual
|
||||
prolog according to the standard and write the result to RESBUF. */
|
||||
static void sha256_conclude_ctx(struct sha256_ctx* ctx) {
|
||||
/* Take yet unprocessed bytes into account. */
|
||||
size_t bytes = ctx->buflen;
|
||||
size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
|
||||
|
||||
/* Now count remaining bytes. */
|
||||
ctx->total[0] += bytes;
|
||||
if(ctx->total[0] < bytes) ++ctx->total[1];
|
||||
|
||||
/* Put the 64-bit file length in *bits* at the end of the buffer.
|
||||
Use set_uint32 rather than a simple assignment, to avoid risk of
|
||||
unaligned access. */
|
||||
set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
|
||||
set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3));
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
|
||||
|
||||
/* Process last bytes. */
|
||||
sha256_process_block(ctx->buffer, size * 4, ctx);
|
||||
}
|
||||
|
||||
void* sha256_finish_ctx(struct sha256_ctx* ctx, void* resbuf) {
|
||||
sha256_conclude_ctx(ctx);
|
||||
return sha256_read_ctx(ctx, resbuf);
|
||||
}
|
||||
|
||||
/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The
|
||||
result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest. */
|
||||
void* sha256_buffer(const char* buffer, size_t len, void* resblock) {
|
||||
struct sha256_ctx ctx;
|
||||
|
||||
/* Initialize the computation context. */
|
||||
sha256_init_ctx(&ctx);
|
||||
|
||||
/* Process whole buffer but last len % 64 bytes. */
|
||||
sha256_process_bytes(buffer, len, &ctx);
|
||||
|
||||
/* Put result in desired memory area. */
|
||||
return sha256_finish_ctx(&ctx, resblock);
|
||||
}
|
||||
|
||||
void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx) {
|
||||
/* When we already have some bits in our internal buffer concatenate
|
||||
both inputs first. */
|
||||
if(ctx->buflen != 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
size_t add = 128 - left_over > len ? len : 128 - left_over;
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[left_over], buffer, add);
|
||||
ctx->buflen += add;
|
||||
|
||||
if(ctx->buflen > 64) {
|
||||
sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx);
|
||||
|
||||
ctx->buflen &= 63;
|
||||
/* The regions in the following copy operation cannot overlap,
|
||||
because ctx->buflen < 64 ≤ (left_over + add) & ~63. */
|
||||
memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen);
|
||||
}
|
||||
|
||||
buffer = (const char*)buffer + add;
|
||||
len -= add;
|
||||
}
|
||||
|
||||
/* Process available complete blocks. */
|
||||
if(len >= 64) {
|
||||
#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned)
|
||||
#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0)
|
||||
if(UNALIGNED_P(buffer))
|
||||
while(len > 64) {
|
||||
sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086
|
||||
buffer = (const char*)buffer + 64;
|
||||
len -= 64;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
sha256_process_block(buffer, len & ~63, ctx);
|
||||
buffer = (const char*)buffer + (len & ~63);
|
||||
len &= 63;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move remaining bytes in internal buffer. */
|
||||
if(len > 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[left_over], buffer, len);
|
||||
left_over += len;
|
||||
if(left_over >= 64) {
|
||||
sha256_process_block(ctx->buffer, 64, ctx);
|
||||
left_over -= 64;
|
||||
/* The regions in the following copy operation cannot overlap,
|
||||
because left_over ≤ 64. */
|
||||
memcpy(ctx->buffer, &ctx->buffer[16], left_over);
|
||||
}
|
||||
ctx->buflen = left_over;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Code below is the primary difference between sha1.c and sha256.c --- */
|
||||
|
||||
/* SHA256 round constants */
|
||||
#define K(I) sha256_round_constants[I]
|
||||
static const uint32_t sha256_round_constants[64] = {
|
||||
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL,
|
||||
0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
|
||||
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL,
|
||||
0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
|
||||
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
|
||||
0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
|
||||
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL,
|
||||
0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
|
||||
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL,
|
||||
0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
|
||||
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL,
|
||||
};
|
||||
|
||||
/* Round functions. */
|
||||
#define F2(A, B, C) ((A & B) | (C & (A | B)))
|
||||
#define F1(E, F, G) (G ^ (E & (F ^ G)))
|
||||
|
||||
/* Process LEN bytes of BUFFER, accumulating context into CTX.
|
||||
It is assumed that LEN % 64 == 0.
|
||||
Most of this code comes from GnuPG's cipher/sha1.c. */
|
||||
|
||||
void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx) {
|
||||
const uint32_t* words = buffer;
|
||||
size_t nwords = len / sizeof(uint32_t);
|
||||
const uint32_t* endp = words + nwords;
|
||||
uint32_t x[16];
|
||||
uint32_t a = ctx->state[0];
|
||||
uint32_t b = ctx->state[1];
|
||||
uint32_t c = ctx->state[2];
|
||||
uint32_t d = ctx->state[3];
|
||||
uint32_t e = ctx->state[4];
|
||||
uint32_t f = ctx->state[5];
|
||||
uint32_t g = ctx->state[6];
|
||||
uint32_t h = ctx->state[7];
|
||||
uint32_t lolen = len;
|
||||
|
||||
/* First increment the byte count. FIPS PUB 180-2 specifies the possible
|
||||
length of the file up to 2^64 bits. Here we only compute the
|
||||
number of bytes. Do a double word increment. */
|
||||
ctx->total[0] += lolen;
|
||||
ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen);
|
||||
|
||||
#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
#define S0(x) (rol(x, 25) ^ rol(x, 14) ^ (x >> 3))
|
||||
#define S1(x) (rol(x, 15) ^ rol(x, 13) ^ (x >> 10))
|
||||
#define SS0(x) (rol(x, 30) ^ rol(x, 19) ^ rol(x, 10))
|
||||
#define SS1(x) (rol(x, 26) ^ rol(x, 21) ^ rol(x, 7))
|
||||
|
||||
#define M(I) \
|
||||
(tm = S1(x[(I - 2) & 0x0f]) + x[(I - 7) & 0x0f] + S0(x[(I - 15) & 0x0f]) + x[I & 0x0f], \
|
||||
x[I & 0x0f] = tm)
|
||||
|
||||
#define R(A, B, C, D, E, F, G, H, K, M) \
|
||||
do { \
|
||||
t0 = SS0(A) + F2(A, B, C); \
|
||||
t1 = H + SS1(E) + F1(E, F, G) + K + M; \
|
||||
D += t1; \
|
||||
H = t0 + t1; \
|
||||
} while(0)
|
||||
|
||||
while(words < endp) {
|
||||
uint32_t tm;
|
||||
uint32_t t0, t1;
|
||||
int t;
|
||||
/* FIXME: see sha1.c for a better implementation. */
|
||||
for(t = 0; t < 16; t++) {
|
||||
x[t] = SWAP(*words);
|
||||
words++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 64; i++) {
|
||||
uint32_t xx = i < 16 ? x[i] : M(i);
|
||||
R(a, b, c, d, e, f, g, h, K(i), xx);
|
||||
uint32_t tt = a;
|
||||
a = h;
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b = tt;
|
||||
}
|
||||
|
||||
a = ctx->state[0] += a;
|
||||
b = ctx->state[1] += b;
|
||||
c = ctx->state[2] += c;
|
||||
d = ctx->state[3] += d;
|
||||
e = ctx->state[4] += e;
|
||||
f = ctx->state[5] += f;
|
||||
g = ctx->state[6] += g;
|
||||
h = ctx->state[7] += h;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hey Emacs!
|
||||
* Local Variables:
|
||||
* coding: utf-8
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,79 @@
|
||||
/* Declarations of functions and data types used for SHA256 sum
|
||||
library functions.
|
||||
Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { SHA256_DIGEST_SIZE = 256 / 8 };
|
||||
|
||||
/* Structure to save state of computation between the single steps. */
|
||||
struct sha256_ctx {
|
||||
uint32_t state[8];
|
||||
|
||||
uint32_t total[2];
|
||||
size_t buflen; /* ≥ 0, ≤ 128 */
|
||||
uint32_t buffer[32]; /* 128 bytes; the first buflen bytes are in use */
|
||||
};
|
||||
|
||||
/* Initialize structure containing state of computation. */
|
||||
extern void sha256_init_ctx(struct sha256_ctx* ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is necessary that LEN is a multiple of 64!!! */
|
||||
extern void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is NOT required that LEN is a multiple of 64. */
|
||||
extern void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx);
|
||||
|
||||
/* Process the remaining bytes in the buffer and put result from CTX
|
||||
in first 32 (28) bytes following RESBUF. The result is always in little
|
||||
endian byte order, so that a byte-wise output yields to the wanted
|
||||
ASCII representation of the message digest. */
|
||||
extern void* sha256_finish_ctx(struct sha256_ctx* ctx, void* restrict resbuf);
|
||||
|
||||
/* Put result from CTX in first 32 (28) bytes following RESBUF. The result is
|
||||
always in little endian byte order, so that a byte-wise output yields
|
||||
to the wanted ASCII representation of the message digest. */
|
||||
extern void* sha256_read_ctx(const struct sha256_ctx* ctx, void* restrict resbuf);
|
||||
|
||||
/* Compute SHA256 message digest for LEN bytes beginning at BUFFER.
|
||||
The result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest. */
|
||||
extern void* sha256_buffer(const char* buffer, size_t len, void* restrict resblock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hey Emacs!
|
||||
* Local Variables:
|
||||
* coding: utf-8
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,315 @@
|
||||
/* sha512.c - Functions to compute SHA512 message digest of files or
|
||||
memory blocks according to the NIST specification FIPS-180-2.
|
||||
|
||||
Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by David Madore, considerably copypasting from
|
||||
Scott G. Miller's sha1.c
|
||||
*/
|
||||
|
||||
/* Specification. */
|
||||
#include "sha512.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define SWAP(n) (n)
|
||||
#else
|
||||
#include "byteswap.h"
|
||||
#define SWAP(n) swap_uint64(n)
|
||||
#endif
|
||||
|
||||
/* This array contains the bytes used to pad the buffer to the next
|
||||
128-byte boundary. */
|
||||
static const unsigned char fillbuf[128] = {0x80, 0 /* , 0, 0, ... */};
|
||||
|
||||
/*
|
||||
Takes a pointer to a 512 bit block of data (eight 64 bit ints) and
|
||||
initializes it to the start constants of the SHA512 algorithm. This
|
||||
must be called before using hash in the call to sha512_hash
|
||||
*/
|
||||
void sha512_init_ctx(struct sha512_ctx* ctx) {
|
||||
ctx->state[0] = u64hilo(0x6a09e667, 0xf3bcc908);
|
||||
ctx->state[1] = u64hilo(0xbb67ae85, 0x84caa73b);
|
||||
ctx->state[2] = u64hilo(0x3c6ef372, 0xfe94f82b);
|
||||
ctx->state[3] = u64hilo(0xa54ff53a, 0x5f1d36f1);
|
||||
ctx->state[4] = u64hilo(0x510e527f, 0xade682d1);
|
||||
ctx->state[5] = u64hilo(0x9b05688c, 0x2b3e6c1f);
|
||||
ctx->state[6] = u64hilo(0x1f83d9ab, 0xfb41bd6b);
|
||||
ctx->state[7] = u64hilo(0x5be0cd19, 0x137e2179);
|
||||
|
||||
ctx->total[0] = ctx->total[1] = u64lo(0);
|
||||
ctx->buflen = 0;
|
||||
}
|
||||
|
||||
/* Copy the value from V into the memory location pointed to by *CP,
|
||||
If your architecture allows unaligned access, this is equivalent to
|
||||
* (__typeof__ (v) *) cp = v */
|
||||
static void set_uint64(char* cp, u64 v) {
|
||||
memcpy(cp, &v, sizeof v);
|
||||
}
|
||||
|
||||
/* Put result from CTX in first 64 bytes following RESBUF.
|
||||
The result must be in little endian byte order. */
|
||||
void* sha512_read_ctx(const struct sha512_ctx* ctx, void* resbuf) {
|
||||
int i;
|
||||
char* r = resbuf;
|
||||
|
||||
for(i = 0; i < 8; i++) set_uint64(r + i * sizeof ctx->state[0], SWAP(ctx->state[i]));
|
||||
|
||||
return resbuf;
|
||||
}
|
||||
|
||||
/* Process the remaining bytes in the internal buffer and the usual
|
||||
prolog according to the standard and write the result to RESBUF. */
|
||||
static void sha512_conclude_ctx(struct sha512_ctx* ctx) {
|
||||
/* Take yet unprocessed bytes into account. */
|
||||
size_t bytes = ctx->buflen;
|
||||
size_t size = (bytes < 112) ? 128 / 8 : 128 * 2 / 8;
|
||||
|
||||
/* Now count remaining bytes. */
|
||||
ctx->total[0] = u64plus(ctx->total[0], u64lo(bytes));
|
||||
if(u64lt(ctx->total[0], u64lo(bytes))) ctx->total[1] = u64plus(ctx->total[1], u64lo(1));
|
||||
|
||||
/* Put the 128-bit file length in *bits* at the end of the buffer.
|
||||
Use set_uint64 rather than a simple assignment, to avoid risk of
|
||||
unaligned access. */
|
||||
set_uint64(
|
||||
(char*)&ctx->buffer[size - 2],
|
||||
SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61))));
|
||||
set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3)));
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes);
|
||||
|
||||
/* Process last bytes. */
|
||||
sha512_process_block(ctx->buffer, size * 8, ctx);
|
||||
}
|
||||
|
||||
void* sha512_finish_ctx(struct sha512_ctx* ctx, void* resbuf) {
|
||||
sha512_conclude_ctx(ctx);
|
||||
return sha512_read_ctx(ctx, resbuf);
|
||||
}
|
||||
|
||||
/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The
|
||||
result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest. */
|
||||
void* sha512_buffer(const char* buffer, size_t len, void* resblock) {
|
||||
struct sha512_ctx ctx;
|
||||
|
||||
/* Initialize the computation context. */
|
||||
sha512_init_ctx(&ctx);
|
||||
|
||||
/* Process whole buffer but last len % 128 bytes. */
|
||||
sha512_process_bytes(buffer, len, &ctx);
|
||||
|
||||
/* Put result in desired memory area. */
|
||||
return sha512_finish_ctx(&ctx, resblock);
|
||||
}
|
||||
|
||||
void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx) {
|
||||
/* When we already have some bits in our internal buffer concatenate
|
||||
both inputs first. */
|
||||
if(ctx->buflen != 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
size_t add = 256 - left_over > len ? len : 256 - left_over;
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[left_over], buffer, add);
|
||||
ctx->buflen += add;
|
||||
|
||||
if(ctx->buflen > 128) {
|
||||
sha512_process_block(ctx->buffer, ctx->buflen & ~127, ctx);
|
||||
|
||||
ctx->buflen &= 127;
|
||||
/* The regions in the following copy operation cannot overlap,
|
||||
because ctx->buflen < 128 ≤ (left_over + add) & ~127. */
|
||||
memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~127], ctx->buflen);
|
||||
}
|
||||
|
||||
buffer = (const char*)buffer + add;
|
||||
len -= add;
|
||||
}
|
||||
|
||||
/* Process available complete blocks. */
|
||||
if(len >= 128) {
|
||||
#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned)
|
||||
#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(u64) != 0)
|
||||
if(UNALIGNED_P(buffer))
|
||||
while(len > 128) {
|
||||
sha512_process_block(memcpy(ctx->buffer, buffer, 128), 128, ctx); //-V1086
|
||||
buffer = (const char*)buffer + 128;
|
||||
len -= 128;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
sha512_process_block(buffer, len & ~127, ctx);
|
||||
buffer = (const char*)buffer + (len & ~127);
|
||||
len &= 127;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move remaining bytes in internal buffer. */
|
||||
if(len > 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
|
||||
memcpy(&((char*)ctx->buffer)[left_over], buffer, len);
|
||||
left_over += len;
|
||||
if(left_over >= 128) {
|
||||
sha512_process_block(ctx->buffer, 128, ctx);
|
||||
left_over -= 128;
|
||||
/* The regions in the following copy operation cannot overlap,
|
||||
because left_over ≤ 128. */
|
||||
memcpy(ctx->buffer, &ctx->buffer[16], left_over);
|
||||
}
|
||||
ctx->buflen = left_over;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Code below is the primary difference between sha1.c and sha512.c --- */
|
||||
|
||||
/* SHA512 round constants */
|
||||
#define K(I) sha512_round_constants[I]
|
||||
static u64 const sha512_round_constants[80] = {
|
||||
u64init(0x428a2f98, 0xd728ae22), u64init(0x71374491, 0x23ef65cd),
|
||||
u64init(0xb5c0fbcf, 0xec4d3b2f), u64init(0xe9b5dba5, 0x8189dbbc),
|
||||
u64init(0x3956c25b, 0xf348b538), u64init(0x59f111f1, 0xb605d019),
|
||||
u64init(0x923f82a4, 0xaf194f9b), u64init(0xab1c5ed5, 0xda6d8118),
|
||||
u64init(0xd807aa98, 0xa3030242), u64init(0x12835b01, 0x45706fbe),
|
||||
u64init(0x243185be, 0x4ee4b28c), u64init(0x550c7dc3, 0xd5ffb4e2),
|
||||
u64init(0x72be5d74, 0xf27b896f), u64init(0x80deb1fe, 0x3b1696b1),
|
||||
u64init(0x9bdc06a7, 0x25c71235), u64init(0xc19bf174, 0xcf692694),
|
||||
u64init(0xe49b69c1, 0x9ef14ad2), u64init(0xefbe4786, 0x384f25e3),
|
||||
u64init(0x0fc19dc6, 0x8b8cd5b5), u64init(0x240ca1cc, 0x77ac9c65),
|
||||
u64init(0x2de92c6f, 0x592b0275), u64init(0x4a7484aa, 0x6ea6e483),
|
||||
u64init(0x5cb0a9dc, 0xbd41fbd4), u64init(0x76f988da, 0x831153b5),
|
||||
u64init(0x983e5152, 0xee66dfab), u64init(0xa831c66d, 0x2db43210),
|
||||
u64init(0xb00327c8, 0x98fb213f), u64init(0xbf597fc7, 0xbeef0ee4),
|
||||
u64init(0xc6e00bf3, 0x3da88fc2), u64init(0xd5a79147, 0x930aa725),
|
||||
u64init(0x06ca6351, 0xe003826f), u64init(0x14292967, 0x0a0e6e70),
|
||||
u64init(0x27b70a85, 0x46d22ffc), u64init(0x2e1b2138, 0x5c26c926),
|
||||
u64init(0x4d2c6dfc, 0x5ac42aed), u64init(0x53380d13, 0x9d95b3df),
|
||||
u64init(0x650a7354, 0x8baf63de), u64init(0x766a0abb, 0x3c77b2a8),
|
||||
u64init(0x81c2c92e, 0x47edaee6), u64init(0x92722c85, 0x1482353b),
|
||||
u64init(0xa2bfe8a1, 0x4cf10364), u64init(0xa81a664b, 0xbc423001),
|
||||
u64init(0xc24b8b70, 0xd0f89791), u64init(0xc76c51a3, 0x0654be30),
|
||||
u64init(0xd192e819, 0xd6ef5218), u64init(0xd6990624, 0x5565a910),
|
||||
u64init(0xf40e3585, 0x5771202a), u64init(0x106aa070, 0x32bbd1b8),
|
||||
u64init(0x19a4c116, 0xb8d2d0c8), u64init(0x1e376c08, 0x5141ab53),
|
||||
u64init(0x2748774c, 0xdf8eeb99), u64init(0x34b0bcb5, 0xe19b48a8),
|
||||
u64init(0x391c0cb3, 0xc5c95a63), u64init(0x4ed8aa4a, 0xe3418acb),
|
||||
u64init(0x5b9cca4f, 0x7763e373), u64init(0x682e6ff3, 0xd6b2b8a3),
|
||||
u64init(0x748f82ee, 0x5defb2fc), u64init(0x78a5636f, 0x43172f60),
|
||||
u64init(0x84c87814, 0xa1f0ab72), u64init(0x8cc70208, 0x1a6439ec),
|
||||
u64init(0x90befffa, 0x23631e28), u64init(0xa4506ceb, 0xde82bde9),
|
||||
u64init(0xbef9a3f7, 0xb2c67915), u64init(0xc67178f2, 0xe372532b),
|
||||
u64init(0xca273ece, 0xea26619c), u64init(0xd186b8c7, 0x21c0c207),
|
||||
u64init(0xeada7dd6, 0xcde0eb1e), u64init(0xf57d4f7f, 0xee6ed178),
|
||||
u64init(0x06f067aa, 0x72176fba), u64init(0x0a637dc5, 0xa2c898a6),
|
||||
u64init(0x113f9804, 0xbef90dae), u64init(0x1b710b35, 0x131c471b),
|
||||
u64init(0x28db77f5, 0x23047d84), u64init(0x32caab7b, 0x40c72493),
|
||||
u64init(0x3c9ebe0a, 0x15c9bebc), u64init(0x431d67c4, 0x9c100d4c),
|
||||
u64init(0x4cc5d4be, 0xcb3e42b6), u64init(0x597f299c, 0xfc657e2a),
|
||||
u64init(0x5fcb6fab, 0x3ad6faec), u64init(0x6c44198c, 0x4a475817),
|
||||
};
|
||||
|
||||
/* Round functions. */
|
||||
#define F2(A, B, C) u64or(u64and(A, B), u64and(C, u64or(A, B)))
|
||||
#define F1(E, F, G) u64xor(G, u64and(E, u64xor(F, G)))
|
||||
|
||||
/* Process LEN bytes of BUFFER, accumulating context into CTX.
|
||||
It is assumed that LEN % 128 == 0.
|
||||
Most of this code comes from GnuPG's cipher/sha1.c. */
|
||||
|
||||
void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx) {
|
||||
u64 const* words = buffer;
|
||||
u64 const* endp = words + len / sizeof(u64);
|
||||
u64 x[16];
|
||||
u64 a = ctx->state[0];
|
||||
u64 b = ctx->state[1];
|
||||
u64 c = ctx->state[2];
|
||||
u64 d = ctx->state[3];
|
||||
u64 e = ctx->state[4];
|
||||
u64 f = ctx->state[5];
|
||||
u64 g = ctx->state[6];
|
||||
u64 h = ctx->state[7];
|
||||
u64 lolen = u64size(len);
|
||||
|
||||
/* First increment the byte count. FIPS PUB 180-2 specifies the possible
|
||||
length of the file up to 2^128 bits. Here we only compute the
|
||||
number of bytes. Do a double word increment. */
|
||||
ctx->total[0] = u64plus(ctx->total[0], lolen);
|
||||
ctx->total[1] = u64plus(
|
||||
ctx->total[1], u64plus(u64size(len >> 31 >> 31 >> 2), u64lo(u64lt(ctx->total[0], lolen))));
|
||||
|
||||
#define S0(x) u64xor(u64rol(x, 63), u64xor(u64rol(x, 56), u64shr(x, 7)))
|
||||
#define S1(x) u64xor(u64rol(x, 45), u64xor(u64rol(x, 3), u64shr(x, 6)))
|
||||
#define SS0(x) u64xor(u64rol(x, 36), u64xor(u64rol(x, 30), u64rol(x, 25)))
|
||||
#define SS1(x) u64xor(u64rol(x, 50), u64xor(u64rol(x, 46), u64rol(x, 23)))
|
||||
|
||||
#define M(I) \
|
||||
(x[(I)&15] = u64plus( \
|
||||
x[(I)&15], \
|
||||
u64plus(S1(x[((I)-2) & 15]), u64plus(x[((I)-7) & 15], S0(x[((I)-15) & 15])))))
|
||||
|
||||
#define R(A, B, C, D, E, F, G, H, K, M) \
|
||||
do { \
|
||||
u64 t0 = u64plus(SS0(A), F2(A, B, C)); \
|
||||
u64 t1 = u64plus(H, u64plus(SS1(E), u64plus(F1(E, F, G), u64plus(K, M)))); \
|
||||
D = u64plus(D, t1); \
|
||||
H = u64plus(t0, t1); \
|
||||
} while(0)
|
||||
|
||||
while(words < endp) {
|
||||
int t;
|
||||
/* FIXME: see sha1.c for a better implementation. */
|
||||
for(t = 0; t < 16; t++) {
|
||||
x[t] = SWAP(*words);
|
||||
words++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 80; i++) {
|
||||
u64 xx = i < 16 ? x[i] : M(i);
|
||||
R(a, b, c, d, e, f, g, h, K(i), xx);
|
||||
u64 tt = a;
|
||||
a = h;
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b = tt;
|
||||
}
|
||||
|
||||
a = ctx->state[0] = u64plus(ctx->state[0], a);
|
||||
b = ctx->state[1] = u64plus(ctx->state[1], b);
|
||||
c = ctx->state[2] = u64plus(ctx->state[2], c);
|
||||
d = ctx->state[3] = u64plus(ctx->state[3], d);
|
||||
e = ctx->state[4] = u64plus(ctx->state[4], e);
|
||||
f = ctx->state[5] = u64plus(ctx->state[5], f);
|
||||
g = ctx->state[6] = u64plus(ctx->state[6], g);
|
||||
h = ctx->state[7] = u64plus(ctx->state[7], h);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hey Emacs!
|
||||
* Local Variables:
|
||||
* coding: utf-8
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,82 @@
|
||||
/* Declarations of functions and data types used for SHA512 and SHA384 sum
|
||||
library functions.
|
||||
Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include "u64.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { SHA512_DIGEST_SIZE = 512 / 8 };
|
||||
|
||||
/* Structure to save state of computation between the single steps. */
|
||||
struct sha512_ctx {
|
||||
u64 state[8];
|
||||
|
||||
u64 total[2];
|
||||
size_t buflen; /* ≥ 0, ≤ 256 */
|
||||
u64 buffer[32]; /* 256 bytes; the first buflen bytes are in use */
|
||||
};
|
||||
|
||||
/* Initialize structure containing state of computation. */
|
||||
extern void sha512_init_ctx(struct sha512_ctx* ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is necessary that LEN is a multiple of 128!!! */
|
||||
extern void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is NOT required that LEN is a multiple of 128. */
|
||||
extern void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx);
|
||||
|
||||
/* Process the remaining bytes in the buffer and put result from CTX
|
||||
in first 64 (48) bytes following RESBUF. The result is always in little
|
||||
endian byte order, so that a byte-wise output yields to the wanted
|
||||
ASCII representation of the message digest. */
|
||||
extern void* sha512_finish_ctx(struct sha512_ctx* ctx, void* restrict resbuf);
|
||||
|
||||
/* Put result from CTX in first 64 (48) bytes following RESBUF. The result is
|
||||
always in little endian byte order, so that a byte-wise output yields
|
||||
to the wanted ASCII representation of the message digest.
|
||||
|
||||
IMPORTANT: On some systems it is required that RESBUF is correctly
|
||||
aligned for a 32 bits value. */
|
||||
extern void* sha512_read_ctx(const struct sha512_ctx* ctx, void* restrict resbuf);
|
||||
|
||||
/* Compute SHA512 message digest for LEN bytes beginning at BUFFER.
|
||||
The result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest. */
|
||||
extern void* sha512_buffer(const char* buffer, size_t len, void* restrict resblock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hey Emacs!
|
||||
* Local Variables:
|
||||
* coding: utf-8
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,44 @@
|
||||
/* uint64_t-like operations that work even on hosts lacking uint64_t
|
||||
|
||||
Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This file is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _GL_U64_INLINE
|
||||
#define _GL_U64_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
/* Return X rotated left by N bits, where 0 < N < 64. */
|
||||
#define u64rol(x, n) u64or(u64shl(x, n), u64shr(x, 64 - (n)))
|
||||
|
||||
/* Native implementations are trivial. See below for comments on what
|
||||
these operations do. */
|
||||
typedef uint64_t u64;
|
||||
#define u64hilo(hi, lo) ((u64)(((u64)(hi) << 32) + (lo)))
|
||||
#define u64init(hi, lo) u64hilo(hi, lo)
|
||||
#define u64lo(x) ((u64)(x))
|
||||
#define u64size(x) u64lo(x)
|
||||
#define u64lt(x, y) ((x) < (y))
|
||||
#define u64and(x, y) ((x) & (y))
|
||||
#define u64or(x, y) ((x) | (y))
|
||||
#define u64xor(x, y) ((x) ^ (y))
|
||||
#define u64plus(x, y) ((x) + (y))
|
||||
#define u64shl(x, n) ((x) << (n))
|
||||
#define u64shr(x, n) ((x) >> (n))
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "totp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "../hmac/hmac_sha1.h"
|
||||
#include "../hmac/hmac_sha256.h"
|
||||
#include "../hmac/hmac_sha512.h"
|
||||
#include "../hmac/byteswap.h"
|
||||
#include "../../lib/timezone_utils/timezone_utils.h"
|
||||
|
||||
#define HMAC_MAX_SIZE 64
|
||||
|
||||
/**
|
||||
* @brief Generates the timeblock for a time in seconds.
|
||||
* Timeblocks are the amount of intervals in a given time. For example,
|
||||
* if 1,000,000 seconds has passed for 30 second intervals, you would get
|
||||
* 33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds.
|
||||
* @param interval in seconds
|
||||
* @param for_time a time in seconds to get the current timeblocks
|
||||
* @return Timeblock given \p for_time using \p interval
|
||||
*/
|
||||
uint64_t totp_timecode(uint8_t interval, uint64_t for_time) {
|
||||
return for_time / interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates an OTP (One Time Password)
|
||||
* @param algo hashing algorithm to be used
|
||||
* @param digits desired TOTP code length
|
||||
* @param plain_secret plain token secret
|
||||
* @param plain_secret_length plain token secret length
|
||||
* @param input input data for OTP code generation
|
||||
* @return OTP code if code was successfully generated; 0 otherwise
|
||||
*/
|
||||
uint32_t otp_generate(
|
||||
TOTP_ALGO algo,
|
||||
uint8_t digits,
|
||||
const uint8_t* plain_secret,
|
||||
size_t plain_secret_length,
|
||||
uint64_t input) {
|
||||
uint8_t hmac[HMAC_MAX_SIZE] = {0};
|
||||
|
||||
uint64_t input_swapped = swap_uint64(input);
|
||||
|
||||
int hmac_len =
|
||||
(*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, &hmac[0]);
|
||||
if(hmac_len == 0) {
|
||||
return OTP_ERROR;
|
||||
}
|
||||
|
||||
uint64_t offset = (hmac[hmac_len - 1] & 0xF);
|
||||
uint64_t i_code =
|
||||
((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 |
|
||||
(hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
|
||||
i_code %= (uint64_t)pow(10, digits);
|
||||
|
||||
return i_code;
|
||||
}
|
||||
|
||||
uint32_t totp_at(
|
||||
TOTP_ALGO algo,
|
||||
uint8_t digits,
|
||||
const uint8_t* plain_secret,
|
||||
size_t plain_secret_length,
|
||||
uint64_t for_time,
|
||||
float timezone,
|
||||
uint8_t interval) {
|
||||
uint64_t for_time_adjusted =
|
||||
timezone_offset_apply(for_time, timezone_offset_from_hours(timezone));
|
||||
return otp_generate(
|
||||
algo,
|
||||
digits,
|
||||
plain_secret,
|
||||
plain_secret_length,
|
||||
totp_timecode(interval, for_time_adjusted));
|
||||
}
|
||||
|
||||
static int totp_algo_sha1(
|
||||
const uint8_t* key,
|
||||
size_t key_length,
|
||||
const uint8_t* input,
|
||||
size_t input_length,
|
||||
uint8_t* output) {
|
||||
hmac_sha1(key, key_length, input, input_length, output);
|
||||
return HMAC_SHA1_RESULT_SIZE;
|
||||
}
|
||||
|
||||
static int totp_algo_sha256(
|
||||
const uint8_t* key,
|
||||
size_t key_length,
|
||||
const uint8_t* input,
|
||||
size_t input_length,
|
||||
uint8_t* output) {
|
||||
hmac_sha256(key, key_length, input, input_length, output);
|
||||
return HMAC_SHA256_RESULT_SIZE;
|
||||
}
|
||||
|
||||
static int totp_algo_sha512(
|
||||
const uint8_t* key,
|
||||
size_t key_length,
|
||||
const uint8_t* input,
|
||||
size_t input_length,
|
||||
uint8_t* output) {
|
||||
hmac_sha512(key, key_length, input, input_length, output);
|
||||
return HMAC_SHA512_RESULT_SIZE;
|
||||
}
|
||||
|
||||
const TOTP_ALGO TOTP_ALGO_SHA1 = (TOTP_ALGO)(&totp_algo_sha1);
|
||||
const TOTP_ALGO TOTP_ALGO_SHA256 = (TOTP_ALGO)(&totp_algo_sha256);
|
||||
const TOTP_ALGO TOTP_ALGO_SHA512 = (TOTP_ALGO)(&totp_algo_sha512);
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define OTP_ERROR (0)
|
||||
|
||||
/**
|
||||
* @brief Must compute HMAC using passed arguments, output as char array through output.
|
||||
* \p key is secret key buffer.
|
||||
* \p key_length is secret key buffer length.
|
||||
* \p input is input buffer.
|
||||
* \p input_length is input buffer length.
|
||||
* \p output is an output buffer of the resulting HMAC operation.
|
||||
* Must return 0 if error, or the length in bytes of the HMAC operation.
|
||||
*/
|
||||
typedef int (*TOTP_ALGO)(
|
||||
const uint8_t* key,
|
||||
size_t key_length,
|
||||
const uint8_t* input,
|
||||
size_t input_length,
|
||||
uint8_t* output);
|
||||
|
||||
/**
|
||||
* @brief Computes HMAC using SHA1
|
||||
*/
|
||||
extern const TOTP_ALGO TOTP_ALGO_SHA1;
|
||||
|
||||
/**
|
||||
* @brief Computes HMAC using SHA256
|
||||
*/
|
||||
extern const TOTP_ALGO TOTP_ALGO_SHA256;
|
||||
|
||||
/**
|
||||
* @brief Computes HMAC using SHA512
|
||||
*/
|
||||
extern const TOTP_ALGO TOTP_ALGO_SHA512;
|
||||
|
||||
/**
|
||||
* @brief Generates a OTP key using the totp algorithm.
|
||||
* @param algo hashing algorithm to be used
|
||||
* @param digits desired TOTP code length
|
||||
* @param plain_secret plain token secret
|
||||
* @param plain_secret_length plain token secret length
|
||||
* @param for_time the time the generated key will be created for
|
||||
* @param timezone UTC timezone adjustment for the generated key
|
||||
* @param interval token lifetime in seconds
|
||||
* @return TOTP code if code was successfully generated; 0 otherwise
|
||||
*/
|
||||
uint32_t totp_at(
|
||||
TOTP_ALGO algo,
|
||||
uint8_t digits,
|
||||
const uint8_t* plain_secret,
|
||||
size_t plain_secret_length,
|
||||
uint64_t for_time,
|
||||
float timezone,
|
||||
uint8_t interval);
|
||||
Reference in New Issue
Block a user