diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d1987c7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + + ] +} \ No newline at end of file diff --git a/README.md b/README.md index b40dc67..a6a36bd 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,18 @@ The flipper stores your usernames and password and can write them on your PC act - The flipper shows a lists of website names - If OK is clicked, the flipper will remove the selected line from the credentials file -### Storage -Credentials are stored as ```service,username,password``` in a .txt file. -```\``` and ```,``` needs to be escaped as ```\\``` and ```\,``` if you handle the file manually. +### Storage (updated) +Credentials are stored encrypted as hex (AES-128-CBC via FlipCrypt). +Before encryption we concatenate `service`, `username`, and `password` with a safe non‑printable delimiter (0x1E), +so commas/symbols inside fields do not need escaping. + +### USB and BLE HID +- USB: temporarily switches to USB HID, types the data, then returns to the default Flipper USB profile. +- BLE: types over BLE HID (advertises as "Control ") and works on iOS/Android/PC. + +### Keyboard layout +Keyboard layout files (`.kl`) from BadUSB are supported and loaded after passcode unlock based on +`/ext/apps_data/PasswordManager/config.conf`. ## Add new password @@ -28,10 +37,29 @@ Credentials are stored as ```service,username,password``` in a .txt file. ![usage](img/login.gif) -## Show passwords - -![usage](img/passwords.gif) - ## Delete passwords ![usage](img/delete.gif) + +## BLE +Typing over BLE HID (iOS/Android/PC). Advertises as "Control ". +LED blinking blue = discovery +LED solid blue = paired + +![usage](img/ble.gif) + +## Passcode +Initial passcode setup and unlock flow (layout loads after unlock). + +![usage](img/pcode.gif) + +## Options / Layout +Select keyboard layout (.kl). BT indicator shows when BLE is active. + +![usage](img/keyboard.gif) + +## Credits +- Momentum firmware (Firmware used for development and testing) +- FlipCrypt (tiny-AES-c) for AES-128-CBC encryption library +- Rrycbarm for the original Password Manager +- TOTP app for inspiration on passcode flow, BLE integration, and USB fixes \ No newline at end of file diff --git a/application.fam b/application.fam index 0b19bd3..20df2b2 100644 --- a/application.fam +++ b/application.fam @@ -3,10 +3,21 @@ App( name="Password Manager", apptype=FlipperAppType.EXTERNAL, entry_point="password_manager_app", - requires=["gui", "usb_hid"], - stack_size=2 * 1024, + requires=["gui", "usb_hid", "bt"], + fap_libs=["ble_profile"], + stack_size=4 * 1024, fap_category="Tools", fap_icon="key.png", fap_description="This app stores your usernames and password and can write them on your PC acting as a keyboard", - fap_version=(1, 2) + fap_version=(1, 3), + fap_private_libs=[ + Lib( + name="FlipCrypt", + sources=[ + "ciphers/aes.c", + "hashes/sha2.c", + ], + cincludes=["lib/FlipCrypt/ciphers", "lib/FlipCrypt/hashes"], + ), + ], ) \ No newline at end of file diff --git a/badusb/badusb.c b/badusb/badusb.c index aba9760..215fcc3 100644 --- a/badusb/badusb.c +++ b/badusb/badusb.c @@ -1,4 +1,104 @@ #include "badusb.h" +#include + +// Layout system based on official BadUSB implementation +static uint16_t keyboard_layout[128]; +static bool layout_loaded = false; + +// Initialize with default US layout (same as official BadUSB) +static void set_default_keyboard_layout(void) { + // Clear layout + memset(keyboard_layout, HID_KEYBOARD_NONE, sizeof(keyboard_layout)); + + // Copy default ASCII map (this would normally come from hid_asciimap) + // For now, use basic US layout mappings for common characters + keyboard_layout['a'] = HID_KEYBOARD_A; + keyboard_layout['b'] = HID_KEYBOARD_B; + keyboard_layout['c'] = HID_KEYBOARD_C; + keyboard_layout['d'] = HID_KEYBOARD_D; + keyboard_layout['e'] = HID_KEYBOARD_E; + keyboard_layout['f'] = HID_KEYBOARD_F; + keyboard_layout['g'] = HID_KEYBOARD_G; + keyboard_layout['h'] = HID_KEYBOARD_H; + keyboard_layout['i'] = HID_KEYBOARD_I; + keyboard_layout['j'] = HID_KEYBOARD_J; + keyboard_layout['k'] = HID_KEYBOARD_K; + keyboard_layout['l'] = HID_KEYBOARD_L; + keyboard_layout['m'] = HID_KEYBOARD_M; + keyboard_layout['n'] = HID_KEYBOARD_N; + keyboard_layout['o'] = HID_KEYBOARD_O; + keyboard_layout['p'] = HID_KEYBOARD_P; + keyboard_layout['q'] = HID_KEYBOARD_Q; + keyboard_layout['r'] = HID_KEYBOARD_R; + keyboard_layout['s'] = HID_KEYBOARD_S; + keyboard_layout['t'] = HID_KEYBOARD_T; + keyboard_layout['u'] = HID_KEYBOARD_U; + keyboard_layout['v'] = HID_KEYBOARD_V; + keyboard_layout['w'] = HID_KEYBOARD_W; + keyboard_layout['x'] = HID_KEYBOARD_X; + keyboard_layout['y'] = HID_KEYBOARD_Y; + keyboard_layout['z'] = HID_KEYBOARD_Z; + + // Numbers + keyboard_layout['1'] = HID_KEYBOARD_1; + keyboard_layout['2'] = HID_KEYBOARD_2; + keyboard_layout['3'] = HID_KEYBOARD_3; + keyboard_layout['4'] = HID_KEYBOARD_4; + keyboard_layout['5'] = HID_KEYBOARD_5; + keyboard_layout['6'] = HID_KEYBOARD_6; + keyboard_layout['7'] = HID_KEYBOARD_7; + keyboard_layout['8'] = HID_KEYBOARD_8; + keyboard_layout['9'] = HID_KEYBOARD_9; + keyboard_layout['0'] = HID_KEYBOARD_0; + + // Common symbols (US layout) + keyboard_layout[' '] = HID_KEYBOARD_SPACEBAR; + keyboard_layout['\n'] = HID_KEYBOARD_RETURN; + keyboard_layout['\r'] = HID_KEYBOARD_RETURN; + keyboard_layout['\t'] = HID_KEYBOARD_TAB; + + layout_loaded = true; +} + +// Load keyboard layout from .kl file (same as official BadUSB) +void load_keyboard_layout(const char* layout_path) { + if(!layout_path || strlen(layout_path) == 0) { + set_default_keyboard_layout(); + return; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* layout_file = storage_file_alloc(storage); + + if(storage_file_open(layout_file, layout_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(keyboard_layout, layout, sizeof(layout)); + layout_loaded = true; + } else { + set_default_keyboard_layout(); + } + storage_file_close(layout_file); + } else { + set_default_keyboard_layout(); + } + + storage_file_free(layout_file); + furi_record_close(RECORD_STORAGE); +} + +// Convert ASCII to keycode using loaded layout (same as BADUSB_ASCII_TO_KEY) +uint16_t ascii_to_key(char c) { + if(!layout_loaded) { + set_default_keyboard_layout(); + } + + uint8_t ascii_code = (uint8_t)c; + if(ascii_code < 128) { + return keyboard_layout[ascii_code]; + } + return HID_KEYBOARD_NONE; +} void initialize_hid(void) { // Stop other USB modes that might be active @@ -11,16 +111,50 @@ void initialize_hid(void) { furi_delay_ms(100); } +void deinitialize_hid(void) { + // Release all keys before switching modes + release_all_keys(); + + // Stop HID mode and unlock USB + furi_hal_usb_unlock(); + + // Give system time to enumerate device + furi_delay_ms(100); +} + +void deinitialize_hid_with_restore(FuriHalUsbInterface* previous_config) { + // Release all keys before switching modes + release_all_keys(); + + // Small delay to ensure all keys are released + furi_delay_ms(25); + + // Restore previous USB configuration if provided + if(previous_config) { + furi_hal_usb_set_config(previous_config, NULL); + } else { + // Fallback to unlock if no previous config + furi_hal_usb_unlock(); + } + + // Give system time to enumerate device + furi_delay_ms(100); +} + // Release all keys (important after sending key combinations) void release_all_keys(void) { furi_hal_hid_kb_release_all(); } -// Type a string +// Type a string using loaded keyboard layout void type_string(const char* str) { for(size_t i = 0; i < strlen(str); i++) { - furi_hal_hid_kb_press(HID_ASCII_TO_KEY(str[i])); - furi_hal_hid_kb_release(HID_ASCII_TO_KEY(str[i])); + uint16_t keycode = ascii_to_key(str[i]); + if(keycode != HID_KEYBOARD_NONE) { + furi_hal_hid_kb_press(keycode); + furi_delay_ms(10); + furi_hal_hid_kb_release(keycode); + } furi_delay_ms(10); // Small delay between keypresses } } diff --git a/badusb/badusb.h b/badusb/badusb.h index 7b48a07..f4d6e42 100644 --- a/badusb/badusb.h +++ b/badusb/badusb.h @@ -5,6 +5,10 @@ #include void initialize_hid(void); +void deinitialize_hid(void); +void deinitialize_hid_with_restore(FuriHalUsbInterface* previous_config); void release_all_keys(void); void type_string(const char* str); -void press_key(uint8_t key); \ No newline at end of file +void press_key(uint8_t key); +void load_keyboard_layout(const char* layout_path); +uint16_t ascii_to_key(char c); \ No newline at end of file diff --git a/ble/pm_ble_hid_ext_profile.c b/ble/pm_ble_hid_ext_profile.c new file mode 100644 index 0000000..40336b9 --- /dev/null +++ b/ble/pm_ble_hid_ext_profile.c @@ -0,0 +1,35 @@ +#include "pm_ble_hid_ext_profile.h" + +static FuriHalBleProfileBase* pm_ble_profile_hid_ext_start(FuriHalBleProfileParams profile_params) { + UNUSED(profile_params); + return ble_profile_hid->start(NULL); +} + +static void pm_ble_profile_hid_ext_stop(FuriHalBleProfileBase* profile) { + ble_profile_hid->stop(profile); +} + +static void pm_ble_profile_hid_ext_get_config(GapConfig* config, FuriHalBleProfileParams profile_params) { + furi_check(config); + furi_check(profile_params); + PmBleProfileHidExtParams* params = profile_params; + + // Base HID config + ble_profile_hid->get_gap_config(config, NULL); + + // Name already set by HID base: "Control " + + // Ensure iOS-friendly settings + config->bonding_mode = params->bonding; // true + config->pairing_method = params->pairing; // GapPairingPinCodeVerifyYesNo +} + +static const FuriHalBleProfileTemplate pm_profile_callbacks = { + .start = pm_ble_profile_hid_ext_start, + .stop = pm_ble_profile_hid_ext_stop, + .get_gap_config = pm_ble_profile_hid_ext_get_config, +}; + +const FuriHalBleProfileTemplate* pm_ble_profile_hid_ext = &pm_profile_callbacks; + + diff --git a/ble/pm_ble_hid_ext_profile.h b/ble/pm_ble_hid_ext_profile.h new file mode 100644 index 0000000..4eda4b9 --- /dev/null +++ b/ble/pm_ble_hid_ext_profile.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef struct { + bool bonding; + GapPairing pairing; +} PmBleProfileHidExtParams; + +extern const FuriHalBleProfileTemplate* pm_ble_profile_hid_ext; + + diff --git a/dist/debug/password_manager_d.elf b/dist/debug/password_manager_d.elf deleted file mode 100644 index 003222f..0000000 Binary files a/dist/debug/password_manager_d.elf and /dev/null differ diff --git a/dist/password_manager.fap b/dist/password_manager.fap deleted file mode 100644 index 4372c78..0000000 Binary files a/dist/password_manager.fap and /dev/null differ diff --git a/img/01-menu.png b/img/01-menu.png deleted file mode 100644 index f97ea8f..0000000 Binary files a/img/01-menu.png and /dev/null differ diff --git a/img/02-saved.png b/img/02-saved.png deleted file mode 100644 index e5e1ba1..0000000 Binary files a/img/02-saved.png and /dev/null differ diff --git a/img/03-insert.png b/img/03-insert.png deleted file mode 100644 index 3ed8728..0000000 Binary files a/img/03-insert.png and /dev/null differ diff --git a/img/04-insert.png b/img/04-insert.png deleted file mode 100644 index 898c28e..0000000 Binary files a/img/04-insert.png and /dev/null differ diff --git a/img/05-insert.png b/img/05-insert.png deleted file mode 100644 index 99f4c19..0000000 Binary files a/img/05-insert.png and /dev/null differ diff --git a/img/06-delete.png b/img/06-delete.png deleted file mode 100644 index 31879d8..0000000 Binary files a/img/06-delete.png and /dev/null differ diff --git a/img/07-delete.png b/img/07-delete.png deleted file mode 100644 index 7abf0eb..0000000 Binary files a/img/07-delete.png and /dev/null differ diff --git a/img/add.gif b/img/add.gif index 4a6d399..3a83c86 100644 Binary files a/img/add.gif and b/img/add.gif differ diff --git a/img/ble.gif b/img/ble.gif new file mode 100644 index 0000000..b6da51b Binary files /dev/null and b/img/ble.gif differ diff --git a/img/delete.gif b/img/delete.gif index 7c41ac7..e6d1089 100644 Binary files a/img/delete.gif and b/img/delete.gif differ diff --git a/img/passwords.gif b/img/keyboard.gif similarity index 50% rename from img/passwords.gif rename to img/keyboard.gif index 967bb4b..fcfb59c 100644 Binary files a/img/passwords.gif and b/img/keyboard.gif differ diff --git a/img/login.gif b/img/login.gif index 824536f..88509ba 100644 Binary files a/img/login.gif and b/img/login.gif differ diff --git a/img/pcode.gif b/img/pcode.gif new file mode 100644 index 0000000..8d4cfc0 Binary files /dev/null and b/img/pcode.gif differ diff --git a/lib/FlipCrypt/LICENSE b/lib/FlipCrypt/LICENSE new file mode 100644 index 0000000..e72bfdd --- /dev/null +++ b/lib/FlipCrypt/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/lib/FlipCrypt/README.md b/lib/FlipCrypt/README.md new file mode 100644 index 0000000..519ed35 --- /dev/null +++ b/lib/FlipCrypt/README.md @@ -0,0 +1,66 @@ +# FlipCrypt + +FlipCrypt is a Flipper Zero app that provides a collection of classic cipher algorithms, cryptographic hash functions, and some text encoding methods to explore and learn about. You can emulate the result using NFC, generate a QR code, or save it to a .txt file on the Flipper. + +## Ciphers +- AES-128 +- Affine +- Atbash +- Baconian +- Beaufort +- Caesar +- Playfair +- Polybius Square +- Rail Fence +- RC4 +- ROT-13 +- Scytale +- Vigenère + +## Hashing Algorithms +- BLAKE-2s +- FNV-1a +- MD2 +- MD5 +- MurmurHash3 +- SHA-1 +- SHA-224 +- SHA-256 +- SHA-384 +- SHA-512 +- SipHash +- XXHash64 + +## Other +- Base32 +- Base58 +- Base64 + +## Usage +Navigate through the app's menu on your Flipper Zero to: +- Choose a cipher to encrypt or decrypt text. +- Select a hashing algorithm to generate a hash from input data. +- Select an encoding method to encode / decode inputs. + +Once on the output screen, you can choose to save the output to a file located at ext/flip_crypt_saved/, emulate it using NFC (NTAG215) or generate and display a QR code of the data (if the output is not too long). Not all three options will be available on every output screen due to memory limitations. + +Warning - Being connected to qFlipper does make a decent amount of QR generations run out of memory and crash that work when standalone. + +## Licensing +v0.1 is licensed under the MIT license. +v0.2 and later is licensed under the GNU General Public License. + +v0.1 can still be accessed through the Releases page. + +Any licenses for the cipher / hash function implementations I included is at the top of their respective code files. + +## Future Ideas +- Add chain encoding / hashing +- Auto-detect cipher decoding +- Add hash bruteforcing +- Add Polybius square key support +- Option to upload .txt file / read NFC as input instead of always typing. + +## +Feel free to leave a Github issue / PR with a feature you'd like to see. +## diff --git a/lib/FlipCrypt/application.fam b/lib/FlipCrypt/application.fam new file mode 100644 index 0000000..953cd1b --- /dev/null +++ b/lib/FlipCrypt/application.fam @@ -0,0 +1,14 @@ +App( + appid="flip_crypt", + name="FlipCrypt", + apptype=FlipperAppType.EXTERNAL, + entry_point="flip_crypt_app", + stack_size=4 * 1024, + fap_category="Tools", + fap_version="0.6", + fap_icon="icon.png", + fap_icon_assets="assets", + fap_description="Encrypt, decrypt, and hash text using a wide variety of classic and modern crypto tools.", + fap_author="Tyl3rA", + fap_weburl="https://github.com/Tyl3rA/FlipCrypt", +) diff --git a/lib/FlipCrypt/assets/DolphinSaved_92x58.png b/lib/FlipCrypt/assets/DolphinSaved_92x58.png new file mode 100644 index 0000000..4d8195b Binary files /dev/null and b/lib/FlipCrypt/assets/DolphinSaved_92x58.png differ diff --git a/lib/FlipCrypt/assets/NFC_dolphin_emulation_51x64.png b/lib/FlipCrypt/assets/NFC_dolphin_emulation_51x64.png new file mode 100644 index 0000000..ef57f73 Binary files /dev/null and b/lib/FlipCrypt/assets/NFC_dolphin_emulation_51x64.png differ diff --git a/lib/FlipCrypt/ciphers/aes.c b/lib/FlipCrypt/ciphers/aes.c new file mode 100644 index 0000000..2a2ae3c --- /dev/null +++ b/lib/FlipCrypt/ciphers/aes.c @@ -0,0 +1,614 @@ +// https://github.com/kokke/tiny-AES-c/tree/master?tab=readme-ov-file + +// This is free and unencumbered software released into the public domain. + +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +// For more information, please refer to + +/* + +This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. +Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. + +The implementation is verified against the test vectors in: + National Institute of Standards and Technology Special Publication 800-38A 2001 ED + +ECB-AES128 +---------- + + plain-text: + 6bc1bee22e409f96e93d7e117393172a + ae2d8a571e03ac9c9eb76fac45af8e51 + 30c81c46a35ce411e5fbc1191a0a52ef + f69f2445df4f9b17ad2b417be66c3710 + + key: + 2b7e151628aed2a6abf7158809cf4f3c + + resulting cipher + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 + + +NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) + You should pad the end of the string with zeros if this is not the case. + For AES192/256 the key size is proportionally larger. + +*/ + + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ +#include // CBC mode, for memset +#include +#include "aes.h" + +/*****************************************************************************/ +/* Defines: */ +/*****************************************************************************/ +// The number of columns comprising a state in AES. This is a constant in AES. Value=4 +#define Nb 4 + +#if defined(AES256) && (AES256 == 1) + #define Nk 8 + #define Nr 14 +#elif defined(AES192) && (AES192 == 1) + #define Nk 6 + #define Nr 12 +#else + #define Nk 4 // The number of 32 bit words in a key. + #define Nr 10 // The number of rounds in AES Cipher. +#endif + +// jcallan@github points out that declaring Multiply as a function +// reduces code size considerably with the Keil ARM compiler. +// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 +#ifndef MULTIPLY_AS_A_FUNCTION + #define MULTIPLY_AS_A_FUNCTION 0 +#endif + +// These were my own additions +void aes_bytes_to_hex(const uint8_t* in, size_t len, char* out) { + const char hex_chars[] = "0123456789ABCDEF"; + for(size_t i = 0; i < len; i++) { + out[i * 2] = hex_chars[(in[i] >> 4) & 0x0F]; + out[i * 2 + 1] = hex_chars[in[i] & 0x0F]; + } + out[len * 2] = '\0'; +} + +void aes_hex_to_bytes(const char* hex_str, uint8_t* byte_array, size_t max_len) { + for(size_t i = 0; i < max_len / 2; ++i) { + sscanf(&hex_str[i * 2], "%2hhx", &byte_array[i]); + } +} + + +/*****************************************************************************/ +/* Private variables: */ +/*****************************************************************************/ +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; + + + +// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM +// The numbers below can be computed dynamically trading ROM for RAM - +// This can be useful in (embedded) bootloader applications, where ROM is often limited. +static const uint8_t sbox[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static const uint8_t rsbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; +#endif + +// The round constant word array, Rcon[i], contains the values given by +// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +static const uint8_t Rcon[11] = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +/* + * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), + * that you can remove most of the elements in the Rcon array, because they are unused. + * + * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon + * + * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), + * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." + */ + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +/* +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} +*/ +#define getSBoxValue(num) (sbox[(num)]) + +// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. +static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) +{ + unsigned i, j, k; + uint8_t tempa[4]; // Used for the column/row operations + + // The first round key is the key itself. + for (i = 0; i < Nk; ++i) + { + RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; + RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; + RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; + RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + } + + // All other round keys are found from the previous round keys. + for (i = Nk; i < Nb * (Nr + 1); ++i) + { + { + k = (i - 1) * 4; + tempa[0]=RoundKey[k + 0]; + tempa[1]=RoundKey[k + 1]; + tempa[2]=RoundKey[k + 2]; + tempa[3]=RoundKey[k + 3]; + + } + + if (i % Nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + const uint8_t u8tmp = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = u8tmp; + } + + // SubWord() is a function that takes a four-byte input word and + // applies the S-box to each of the four bytes to produce an output word. + + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + + tempa[0] = tempa[0] ^ Rcon[i/Nk]; + } +#if defined(AES256) && (AES256 == 1) + if (i % Nk == 4) + { + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + } +#endif + j = i * 4; k=(i - Nk) * 4; + RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; + RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; + RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; + RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; + } +} + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) +{ + KeyExpansion(ctx->RoundKey, key); +} +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) +{ + KeyExpansion(ctx->RoundKey, key); + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) +{ + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +#endif + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) +{ + uint8_t i,j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; + } + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void SubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxValue((*state)[j][i]); + } + } +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +static void ShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to left + temp = (*state)[0][1]; + (*state)[0][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[3][1]; + (*state)[3][1] = temp; + + // Rotate second row 2 columns to left + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to left + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[3][3]; + (*state)[3][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[1][3]; + (*state)[1][3] = temp; +} + +static uint8_t xtime(uint8_t x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +} + +// MixColumns function mixes the columns of the state matrix +static void MixColumns(state_t* state) +{ + uint8_t i; + uint8_t Tmp, Tm, t; + for (i = 0; i < 4; ++i) + { + t = (*state)[i][0]; + Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; + Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; + Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; + Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; + Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +#if MULTIPLY_AS_A_FUNCTION +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y>>1 & 1) * xtime(x)) ^ + ((y>>2 & 1) * xtime(xtime(x))) ^ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ + } +#else +#define Multiply(x, y) \ + ( ((y & 1) * x) ^ \ + ((y>>1 & 1) * xtime(x)) ^ \ + ((y>>2 & 1) * xtime(xtime(x))) ^ \ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ + +#endif + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +/* +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} +*/ +#define getSBoxInvert(num) (rsbox[(num)]) + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +static void InvMixColumns(state_t* state) +{ + int i; + uint8_t a, b, c, d; + for (i = 0; i < 4; ++i) + { + a = (*state)[i][0]; + b = (*state)[i][1]; + c = (*state)[i][2]; + d = (*state)[i][3]; + + (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void InvSubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxInvert((*state)[j][i]); + } + } +} + +static void InvShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to right + temp = (*state)[3][1]; + (*state)[3][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[0][1]; + (*state)[0][1] = temp; + + // Rotate second row 2 columns to right + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to right + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[1][3]; + (*state)[1][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[3][3]; + (*state)[3][3] = temp; +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without MixColumns() + for (round = 1; ; ++round) + { + SubBytes(state); + ShiftRows(state); + if (round == Nr) { + break; + } + MixColumns(state); + AddRoundKey(round, state, RoundKey); + } + // Add round key to last round + AddRoundKey(Nr, state, RoundKey); +} + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static void InvCipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(Nr, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without InvMixColumn() + for (round = (Nr - 1); ; --round) + { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, RoundKey); + if (round == 0) { + break; + } + InvMixColumns(state); + } + +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && (ECB == 1) + + +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call encrypts the PlainText with the Key using AES algorithm. + Cipher((state_t*)buf, ctx->RoundKey); +} + +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call decrypts the PlainText with the Key using AES algorithm. + InvCipher((state_t*)buf, ctx->RoundKey); +} + + +#endif // #if defined(ECB) && (ECB == 1) + + + + + +#if defined(CBC) && (CBC == 1) + + +static void XorWithIv(uint8_t* buf, const uint8_t* Iv) +{ + uint8_t i; + for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size + { + buf[i] ^= Iv[i]; + } +} + +void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length) +{ + size_t i; + uint8_t *Iv = ctx->Iv; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + XorWithIv(buf, Iv); + Cipher((state_t*)buf, ctx->RoundKey); + Iv = buf; + buf += AES_BLOCKLEN; + } + /* store Iv in ctx for next call */ + memcpy(ctx->Iv, Iv, AES_BLOCKLEN); +} + +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) +{ + size_t i; + uint8_t storeNextIv[AES_BLOCKLEN]; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + memcpy(storeNextIv, buf, AES_BLOCKLEN); + InvCipher((state_t*)buf, ctx->RoundKey); + XorWithIv(buf, ctx->Iv); + memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); + buf += AES_BLOCKLEN; + } + +} + +#endif // #if defined(CBC) && (CBC == 1) + + + +#if defined(CTR) && (CTR == 1) + +/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) +{ + uint8_t buffer[AES_BLOCKLEN]; + + size_t i; + int bi; + for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) + { + if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ + { + + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); + Cipher((state_t*)buffer,ctx->RoundKey); + + /* Increment Iv and handle overflow */ + for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) + { + /* inc will overflow */ + if (ctx->Iv[bi] == 255) + { + ctx->Iv[bi] = 0; + continue; + } + ctx->Iv[bi] += 1; + break; + } + bi = 0; + } + + buf[i] = (buf[i] ^ buffer[bi]); + } +} + +#endif // #if defined(CTR) && (CTR == 1) + diff --git a/lib/FlipCrypt/ciphers/aes.h b/lib/FlipCrypt/ciphers/aes.h new file mode 100644 index 0000000..68dc88a --- /dev/null +++ b/lib/FlipCrypt/ciphers/aes.h @@ -0,0 +1,94 @@ +#ifndef _AES_H_ +#define _AES_H_ + +#include +#include + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES encryption in CBC-mode of operation. +// CTR enables encryption in counter-mode. +// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. + +// The #ifndef-guard allows it to be configured before #include'ing or at compile time. +#ifndef CBC + #define CBC 1 +#endif + +#ifndef ECB + #define ECB 1 +#endif + +#ifndef CTR + #define CTR 1 +#endif + + +#define AES128 1 +//#define AES192 1 +//#define AES256 1 + +#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only + +#if defined(AES256) && (AES256 == 1) + #define AES_KEYLEN 32 + #define AES_keyExpSize 240 +#elif defined(AES192) && (AES192 == 1) + #define AES_KEYLEN 24 + #define AES_keyExpSize 208 +#else + #define AES_KEYLEN 16 // Key length in bytes + #define AES_keyExpSize 176 +#endif + +void aes_bytes_to_hex(const uint8_t* in, size_t len, char* out); +void aes_hex_to_bytes(const char* hex_str, uint8_t* byte_array, size_t max_len); + +struct AES_ctx +{ + uint8_t RoundKey[AES_keyExpSize]; +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; +#endif +}; + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(ECB) && (ECB == 1) +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB +// NB: ECB is considered insecure for most uses +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); + +#endif // #if defined(ECB) && (ECB == !) + + +#if defined(CBC) && (CBC == 1) +// buffer size MUST be mutile of AES_BLOCKLEN; +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); + +#endif // #if defined(CBC) && (CBC == 1) + + +#if defined(CTR) && (CTR == 1) + +// Same function for encrypting as for decrypting. +// IV is incremented for every block, and used after encryption as XOR-compliment for output +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); + +#endif // #if defined(CTR) && (CTR == 1) + + +#endif // _AES_H_ diff --git a/lib/FlipCrypt/ciphers/aes128.c b/lib/FlipCrypt/ciphers/aes128.c new file mode 100644 index 0000000..098f51f --- /dev/null +++ b/lib/FlipCrypt/ciphers/aes128.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include + +#include "aes128.h" + +#define Nb 4 // number of columns (32-bit words) comprising the State +#define Nk 4 // key length (in 32-bit words) +#define Nr 10 // number of rounds + +typedef uint8_t state_t[4][4]; + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +static const uint8_t Rcon[11] = { + 0x00, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, + 0x80, 0x1B, 0x36 +}; + +// Multiply by 2 in GF(2^8) +uint8_t xtime(uint8_t x) { + return (x << 1) ^ ((x >> 7) * 0x1b); +} + +void KeyExpansion(const uint8_t* key, uint8_t* roundKeys) { + int i; + uint8_t temp[4]; + + memcpy(roundKeys, key, 16); + + for (i = 4; i < 44; ++i) { + memcpy(temp, &roundKeys[4 * (i - 1)], 4); + + if (i % 4 == 0) { + // Rotate + uint8_t t = temp[0]; + temp[0] = sbox[temp[1]]; + temp[1] = sbox[temp[2]]; + temp[2] = sbox[temp[3]]; + temp[3] = sbox[t]; + temp[0] ^= Rcon[i / 4]; + } + + for (int j = 0; j < 4; ++j) { + roundKeys[4 * i + j] = roundKeys[4 * (i - 4) + j] ^ temp[j]; + } + } +} + +void AddRoundKey(state_t* state, const uint8_t* roundKey) { + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + (*state)[j][i] ^= roundKey[i * 4 + j]; +} + +void SubBytes(state_t* state) { + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + (*state)[i][j] = sbox[(*state)[i][j]]; +} + +void ShiftRows(state_t* state) { + uint8_t tmp; + + tmp = (*state)[1][0]; + (*state)[1][0] = (*state)[1][1]; + (*state)[1][1] = (*state)[1][2]; + (*state)[1][2] = (*state)[1][3]; + (*state)[1][3] = tmp; + + tmp = (*state)[2][0]; + uint8_t tmp2 = (*state)[2][1]; + (*state)[2][0] = (*state)[2][2]; + (*state)[2][1] = (*state)[2][3]; + (*state)[2][2] = tmp; + (*state)[2][3] = tmp2; + + tmp = (*state)[3][3]; + (*state)[3][3] = (*state)[3][2]; + (*state)[3][2] = (*state)[3][1]; + (*state)[3][1] = (*state)[3][0]; + (*state)[3][0] = tmp; +} + +void MixColumns(state_t* state) { + for (int i = 0; i < 4; ++i) { + uint8_t* col = (*state)[i]; + uint8_t t = col[0] ^ col[1] ^ col[2] ^ col[3]; + uint8_t tmp = col[0]; + col[0] ^= t ^ xtime(col[0] ^ col[1]); + col[1] ^= t ^ xtime(col[1] ^ col[2]); + col[2] ^= t ^ xtime(col[2] ^ col[3]); + col[3] ^= t ^ xtime(col[3] ^ tmp); + } +} + +void AES_encrypt_block(uint8_t* input, const uint8_t* key, uint8_t* output) { + state_t* state = (state_t*)input; + uint8_t roundKeys[176]; + + KeyExpansion(key, roundKeys); + AddRoundKey(state, roundKeys); + + for (int round = 1; round < Nr; ++round) { + SubBytes(state); + ShiftRows(state); + MixColumns(state); + AddRoundKey(state, roundKeys + round * 16); + } + + SubBytes(state); + ShiftRows(state); + AddRoundKey(state, roundKeys + Nr * 16); + + memcpy(output, input, 16); +} + +// Convert input string to 16-byte padded block +void pad_input(const char* input, uint8_t* output) { + size_t len = strlen(input); + size_t i; + for (i = 0; i < 16; i++) { + if (i < len) + output[i] = input[i]; + else + output[i] = 16 - len; // simple padding + } +} + +void print_hex(uint8_t* data, size_t len) { + for (size_t i = 0; i < len; ++i) + printf("%02X", data[i]); + printf("\n"); +} + +void to_hex_string(const uint8_t* data, size_t len, char* output) { + const char hex_chars[] = "0123456789ABCDEF"; + for (size_t i = 0; i < len; i++) { + output[i * 2] = hex_chars[(data[i] >> 4) & 0xF]; + output[i * 2 + 1] = hex_chars[data[i] & 0xF]; + } + output[len * 2] = '\0'; // Null-terminate the string +} + +// Decrypt + +static const uint8_t rsbox[256] = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, +}; + +void InvSubBytes(state_t* state) { + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + (*state)[i][j] = rsbox[(*state)[i][j]]; +} + +void InvShiftRows(state_t* state) { + uint8_t tmp; + + tmp = (*state)[1][3]; + (*state)[1][3] = (*state)[1][2]; + (*state)[1][2] = (*state)[1][1]; + (*state)[1][1] = (*state)[1][0]; + (*state)[1][0] = tmp; + + tmp = (*state)[2][0]; + uint8_t tmp2 = (*state)[2][1]; + (*state)[2][0] = (*state)[2][2]; + (*state)[2][1] = (*state)[2][3]; + (*state)[2][2] = tmp; + (*state)[2][3] = tmp2; + + tmp = (*state)[3][0]; + (*state)[3][0] = (*state)[3][1]; + (*state)[3][1] = (*state)[3][2]; + (*state)[3][2] = (*state)[3][3]; + (*state)[3][3] = tmp; +} + +uint8_t mul(uint8_t a, uint8_t b) { + uint8_t result = 0; + while (b) { + if (b & 1) result ^= a; + a = xtime(a); + b >>= 1; + } + return result; +} + +void InvMixColumns(state_t* state) { + for (int i = 0; i < 4; ++i) { + uint8_t* col = (*state)[i]; + uint8_t a = col[0], b = col[1], c = col[2], d = col[3]; + col[0] = mul(a, 0x0e) ^ mul(b, 0x0b) ^ mul(c, 0x0d) ^ mul(d, 0x09); + col[1] = mul(a, 0x09) ^ mul(b, 0x0e) ^ mul(c, 0x0b) ^ mul(d, 0x0d); + col[2] = mul(a, 0x0d) ^ mul(b, 0x09) ^ mul(c, 0x0e) ^ mul(d, 0x0b); + col[3] = mul(a, 0x0b) ^ mul(b, 0x0d) ^ mul(c, 0x09) ^ mul(d, 0x0e); + } +} + +void AES_decrypt_block(uint8_t* input, const uint8_t* key, uint8_t* output) { + state_t* state = (state_t*)input; + uint8_t roundKeys[176]; + KeyExpansion(key, roundKeys); + + AddRoundKey(state, roundKeys + Nr * 16); + + for (int round = Nr - 1; round > 0; --round) { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, roundKeys + round * 16); + InvMixColumns(state); + } + + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(state, roundKeys); + + memcpy(output, input, 16); +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/aes128.h b/lib/FlipCrypt/ciphers/aes128.h new file mode 100644 index 0000000..181edfc --- /dev/null +++ b/lib/FlipCrypt/ciphers/aes128.h @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +typedef uint8_t state_t[4][4]; + +void KeyExpansion(const uint8_t* key, uint8_t* roundKeys); +void AddRoundKey(state_t* state, const uint8_t* roundKey); +void SubBytes(state_t* state); +void ShiftRows(state_t* state); +void MixColumns(state_t* state); +void AES_encrypt_block(uint8_t* input, const uint8_t* key, uint8_t* output); +void pad_input(const char* input, uint8_t* output); +void print_hex(uint8_t* data, size_t len); +void to_hex_string(const uint8_t* data, size_t len, char* output); + +void AES_decrypt_block(uint8_t* input, const uint8_t* key, uint8_t* output); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/affine.c b/lib/FlipCrypt/ciphers/affine.c new file mode 100644 index 0000000..85c86ca --- /dev/null +++ b/lib/FlipCrypt/ciphers/affine.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +int letter_to_index(char c) { + if (c >= 'A' && c <= 'Z') + return c - 'A' + 1; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 1; + else + return -1; // Not a letter +} + +char index_to_letter(int index, int is_upper) { + if (index < 1 || index > 26) return '?'; + return (is_upper ? 'A' : 'a') + (index - 1); +} + +int mod_inverse(int a, int m) { + a = a % m; + for (int x = 1; x < m; x++) { + if ((a * x) % m == 1) + return x; + } + return -1; // No inverse +} + +char* encode_affine(const char* text, uint8_t a, uint8_t b) { + if (a % 2 == 0) return "First key must be odd"; + if (a == 13) return "First key cannot be 13"; + + size_t len = strlen(text); + char* result = malloc(len + 1); + if (!result) return "memory allocation failed, try again"; + + for (size_t i = 0; i < len; i++) { + char c = text[i]; + int x = letter_to_index(c); + if (x == -1) { + result[i] = c; // Non-letter, so don't modify + } else { + int is_upper = (c >= 'A' && c <= 'Z'); + int newnum = ((a * x + b) % 26); + if (newnum == 0) newnum = 26; + result[i] = index_to_letter(newnum, is_upper); + } + } + + result[len] = '\0'; + return result; +} + +char* decode_affine(const char* text, uint8_t a, uint8_t b) { + size_t len = strlen(text); + char* result = malloc(len + 1); + if (!result) return "memory allocation failed, try again"; + + int a_inv = mod_inverse(a, 26); + if (a_inv == -1) { + strcpy(result, "invalid key: a has no modular inverse\nValid a vals: 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25"); + return result; + } + + for (size_t i = 0; i < len; i++) { + char c = text[i]; + int x = letter_to_index(c); + if (x == -1) { + result[i] = c; // Keep non-letters as-is + } else { + int is_upper = (c >= 'A' && c <= 'Z'); + int y = x; + int orig = (a_inv * ((y - b + 26) % 26)) % 26; + if (orig == 0) orig = 26; + result[i] = index_to_letter(orig, is_upper); + } + } + + result[len] = '\0'; + return result; +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/affine.h b/lib/FlipCrypt/ciphers/affine.h new file mode 100644 index 0000000..c367758 --- /dev/null +++ b/lib/FlipCrypt/ciphers/affine.h @@ -0,0 +1,4 @@ +#include + +char* encode_affine(const char* text, uint8_t a, uint8_t b); +char* decode_affine(const char* text, uint8_t a, uint8_t b); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/atbash.c b/lib/FlipCrypt/ciphers/atbash.c new file mode 100644 index 0000000..b910d67 --- /dev/null +++ b/lib/FlipCrypt/ciphers/atbash.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#include "atbash.h" + +char* atbash_encrypt_or_decrypt(const char* input) { + size_t len = strlen(input); + char* output = (char*)malloc(len + 1); + if (!output) return "memory allocation failed, try again"; + + for (size_t i = 0; i < len; i++) { + char c = input[i]; + if (isupper(c)) { + output[i] = 'Z' - (c - 'A'); + } else if (islower(c)) { + output[i] = 'z' - (c - 'a'); + } else { + output[i] = c; + } + } + output[len] = '\0'; + return output; +} diff --git a/lib/FlipCrypt/ciphers/atbash.h b/lib/FlipCrypt/ciphers/atbash.h new file mode 100644 index 0000000..df8e2d8 --- /dev/null +++ b/lib/FlipCrypt/ciphers/atbash.h @@ -0,0 +1 @@ +char* atbash_encrypt_or_decrypt(const char* input); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/baconian.c b/lib/FlipCrypt/ciphers/baconian.c new file mode 100644 index 0000000..7d4aaab --- /dev/null +++ b/lib/FlipCrypt/ciphers/baconian.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#include "baconian.h" + +const char* baconian_table[26] = { + "AAAAA", "AAAAB", "AAABA", "AAABB", "AABAA", "AABAB", + "AABBA", "AABBB", "ABAAA", "ABAAB", "ABABA", "ABABB", + "ABBAA", "ABBAB", "ABBBA", "ABBBB", "BAAAA", "BAAAB", + "BAABA", "BAABB", "BABAA", "BABAB", "BABBA", "BABBB", + "BBAAA", "BBAAB" +}; + +char* baconian_encrypt(const char* text) { + size_t len = strlen(text); + char* encoded = (char*)malloc(len * 5 + 1); + if (!encoded) return "memory allocation failed, please try again"; + + size_t pos = 0; + + for (size_t i = 0; i < len; i++) { + if (isalpha((unsigned char)text[i])) { + char upper = toupper((unsigned char)text[i]); + int index = upper - 'A'; + if (index >= 0 && index < 26) { + const char* code = baconian_table[index]; + for (int j = 0; j < 5; j++) { + encoded[pos++] = code[j]; + } + } + } else { + encoded[pos++] = text[i]; // Preserve non-letters as-is + } + } + + encoded[pos] = '\0'; + return encoded; +} + +char* baconian_decrypt(const char* encoded) { + size_t len = strlen(encoded); + char* decoded = (char*)malloc(len / 5 + 2); + if (!decoded) return "memory allocation failed, please try again"; + + size_t i = 0; + size_t pos = 0; + + while (i + 4 < len) { + if (!isalpha((unsigned char)encoded[i])) { + i++; // skip non-alpha + continue; + } + + char chunk[6] = {0}; + for (int j = 0; j < 5 && encoded[i + j] != '\0'; j++) { + chunk[j] = toupper((unsigned char)encoded[i + j]); + } + + int matched = 0; + for (int j = 0; j < 26; j++) { + if (strncmp(chunk, baconian_table[j], 5) == 0) { + decoded[pos++] = 'A' + j; + matched = 1; + break; + } + } + + if (!matched) { + decoded[pos++] = '?'; // Unrecognized chunk + } + + i += 5; + } + + decoded[pos] = '\0'; + return decoded; +} diff --git a/lib/FlipCrypt/ciphers/baconian.h b/lib/FlipCrypt/ciphers/baconian.h new file mode 100644 index 0000000..92647a5 --- /dev/null +++ b/lib/FlipCrypt/ciphers/baconian.h @@ -0,0 +1,2 @@ +char* baconian_encrypt(const char* text); +char* baconian_decrypt(const char* encoded); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/beaufort.c b/lib/FlipCrypt/ciphers/beaufort.c new file mode 100644 index 0000000..fe6b44f --- /dev/null +++ b/lib/FlipCrypt/ciphers/beaufort.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "beaufort.h" + +char* beaufort_cipher_encrypt_and_decrypt(const char* text, const char* key) { + int len = strlen(text); + int key_len = strlen(key); + int key_index = 0; + + char* result = malloc(len + 1); + if (!result) return "memory allocation failed, try again"; + + for (int i = 0; i < len; i++) { + char c = text[i]; + + if (isalpha(c)) { + char k = key[key_index % key_len]; + int shift = tolower(k) - 'a'; + + if (isupper(c)) { + result[i] = ((shift - (c - 'A') + 26) % 26) + 'A'; + } else { + result[i] = ((shift - (c - 'a') + 26) % 26) + 'a'; + } + + key_index++; + } else { + result[i] = c; + } + } + + result[len] = '\0'; + return result; +} diff --git a/lib/FlipCrypt/ciphers/beaufort.h b/lib/FlipCrypt/ciphers/beaufort.h new file mode 100644 index 0000000..5b10a9b --- /dev/null +++ b/lib/FlipCrypt/ciphers/beaufort.h @@ -0,0 +1 @@ +char* beaufort_cipher_encrypt_and_decrypt(const char* text, const char* key); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/blowfish.c b/lib/FlipCrypt/ciphers/blowfish.c new file mode 100644 index 0000000..cea8f28 --- /dev/null +++ b/lib/FlipCrypt/ciphers/blowfish.c @@ -0,0 +1,269 @@ +/********************************************************************* +* Filename: blowfish.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the Blowfish encryption algorithm. + Modes of operation (such as CBC) are not included. + Algorithm specification can be found here: + * http://www.schneier.com/blowfish.html +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "blowfish.h" + +/****************************** MACROS ******************************/ +#define F(x,t) t = keystruct->s[0][(x) >> 24]; \ + t += keystruct->s[1][((x) >> 16) & 0xff]; \ + t ^= keystruct->s[2][((x) >> 8) & 0xff]; \ + t += keystruct->s[3][(x) & 0xff]; +#define swap(r,l,t) t = l; l = r; r = t; +#define ITERATION(l,r,t,pval) l ^= keystruct->p[pval]; F(l,t); r^= t; swap(r,l,t); + +/**************************** VARIABLES *****************************/ +static const WORD p_perm[18] = { + 0x243F6A88,0x85A308D3,0x13198A2E,0x03707344,0xA4093822,0x299F31D0,0x082EFA98, + 0xEC4E6C89,0x452821E6,0x38D01377,0xBE5466CF,0x34E90C6C,0xC0AC29B7,0xC97C50DD, + 0x3F84D5B5,0xB5470917,0x9216D5D9,0x8979FB1B +}; + +static const WORD s_perm[4][256] = { { + 0xD1310BA6,0x98DFB5AC,0x2FFD72DB,0xD01ADFB7,0xB8E1AFED,0x6A267E96,0xBA7C9045,0xF12C7F99, + 0x24A19947,0xB3916CF7,0x0801F2E2,0x858EFC16,0x636920D8,0x71574E69,0xA458FEA3,0xF4933D7E, + 0x0D95748F,0x728EB658,0x718BCD58,0x82154AEE,0x7B54A41D,0xC25A59B5,0x9C30D539,0x2AF26013, + 0xC5D1B023,0x286085F0,0xCA417918,0xB8DB38EF,0x8E79DCB0,0x603A180E,0x6C9E0E8B,0xB01E8A3E, + 0xD71577C1,0xBD314B27,0x78AF2FDA,0x55605C60,0xE65525F3,0xAA55AB94,0x57489862,0x63E81440, + 0x55CA396A,0x2AAB10B6,0xB4CC5C34,0x1141E8CE,0xA15486AF,0x7C72E993,0xB3EE1411,0x636FBC2A, + 0x2BA9C55D,0x741831F6,0xCE5C3E16,0x9B87931E,0xAFD6BA33,0x6C24CF5C,0x7A325381,0x28958677, + 0x3B8F4898,0x6B4BB9AF,0xC4BFE81B,0x66282193,0x61D809CC,0xFB21A991,0x487CAC60,0x5DEC8032, + 0xEF845D5D,0xE98575B1,0xDC262302,0xEB651B88,0x23893E81,0xD396ACC5,0x0F6D6FF3,0x83F44239, + 0x2E0B4482,0xA4842004,0x69C8F04A,0x9E1F9B5E,0x21C66842,0xF6E96C9A,0x670C9C61,0xABD388F0, + 0x6A51A0D2,0xD8542F68,0x960FA728,0xAB5133A3,0x6EEF0B6C,0x137A3BE4,0xBA3BF050,0x7EFB2A98, + 0xA1F1651D,0x39AF0176,0x66CA593E,0x82430E88,0x8CEE8619,0x456F9FB4,0x7D84A5C3,0x3B8B5EBE, + 0xE06F75D8,0x85C12073,0x401A449F,0x56C16AA6,0x4ED3AA62,0x363F7706,0x1BFEDF72,0x429B023D, + 0x37D0D724,0xD00A1248,0xDB0FEAD3,0x49F1C09B,0x075372C9,0x80991B7B,0x25D479D8,0xF6E8DEF7, + 0xE3FE501A,0xB6794C3B,0x976CE0BD,0x04C006BA,0xC1A94FB6,0x409F60C4,0x5E5C9EC2,0x196A2463, + 0x68FB6FAF,0x3E6C53B5,0x1339B2EB,0x3B52EC6F,0x6DFC511F,0x9B30952C,0xCC814544,0xAF5EBD09, + 0xBEE3D004,0xDE334AFD,0x660F2807,0x192E4BB3,0xC0CBA857,0x45C8740F,0xD20B5F39,0xB9D3FBDB, + 0x5579C0BD,0x1A60320A,0xD6A100C6,0x402C7279,0x679F25FE,0xFB1FA3CC,0x8EA5E9F8,0xDB3222F8, + 0x3C7516DF,0xFD616B15,0x2F501EC8,0xAD0552AB,0x323DB5FA,0xFD238760,0x53317B48,0x3E00DF82, + 0x9E5C57BB,0xCA6F8CA0,0x1A87562E,0xDF1769DB,0xD542A8F6,0x287EFFC3,0xAC6732C6,0x8C4F5573, + 0x695B27B0,0xBBCA58C8,0xE1FFA35D,0xB8F011A0,0x10FA3D98,0xFD2183B8,0x4AFCB56C,0x2DD1D35B, + 0x9A53E479,0xB6F84565,0xD28E49BC,0x4BFB9790,0xE1DDF2DA,0xA4CB7E33,0x62FB1341,0xCEE4C6E8, + 0xEF20CADA,0x36774C01,0xD07E9EFE,0x2BF11FB4,0x95DBDA4D,0xAE909198,0xEAAD8E71,0x6B93D5A0, + 0xD08ED1D0,0xAFC725E0,0x8E3C5B2F,0x8E7594B7,0x8FF6E2FB,0xF2122B64,0x8888B812,0x900DF01C, + 0x4FAD5EA0,0x688FC31C,0xD1CFF191,0xB3A8C1AD,0x2F2F2218,0xBE0E1777,0xEA752DFE,0x8B021FA1, + 0xE5A0CC0F,0xB56F74E8,0x18ACF3D6,0xCE89E299,0xB4A84FE0,0xFD13E0B7,0x7CC43B81,0xD2ADA8D9, + 0x165FA266,0x80957705,0x93CC7314,0x211A1477,0xE6AD2065,0x77B5FA86,0xC75442F5,0xFB9D35CF, + 0xEBCDAF0C,0x7B3E89A0,0xD6411BD3,0xAE1E7E49,0x00250E2D,0x2071B35E,0x226800BB,0x57B8E0AF, + 0x2464369B,0xF009B91E,0x5563911D,0x59DFA6AA,0x78C14389,0xD95A537F,0x207D5BA2,0x02E5B9C5, + 0x83260376,0x6295CFA9,0x11C81968,0x4E734A41,0xB3472DCA,0x7B14A94A,0x1B510052,0x9A532915, + 0xD60F573F,0xBC9BC6E4,0x2B60A476,0x81E67400,0x08BA6FB5,0x571BE91F,0xF296EC6B,0x2A0DD915, + 0xB6636521,0xE7B9F9B6,0xFF34052E,0xC5855664,0x53B02D5D,0xA99F8FA1,0x08BA4799,0x6E85076A +},{ + 0x4B7A70E9,0xB5B32944,0xDB75092E,0xC4192623,0xAD6EA6B0,0x49A7DF7D,0x9CEE60B8,0x8FEDB266, + 0xECAA8C71,0x699A17FF,0x5664526C,0xC2B19EE1,0x193602A5,0x75094C29,0xA0591340,0xE4183A3E, + 0x3F54989A,0x5B429D65,0x6B8FE4D6,0x99F73FD6,0xA1D29C07,0xEFE830F5,0x4D2D38E6,0xF0255DC1, + 0x4CDD2086,0x8470EB26,0x6382E9C6,0x021ECC5E,0x09686B3F,0x3EBAEFC9,0x3C971814,0x6B6A70A1, + 0x687F3584,0x52A0E286,0xB79C5305,0xAA500737,0x3E07841C,0x7FDEAE5C,0x8E7D44EC,0x5716F2B8, + 0xB03ADA37,0xF0500C0D,0xF01C1F04,0x0200B3FF,0xAE0CF51A,0x3CB574B2,0x25837A58,0xDC0921BD, + 0xD19113F9,0x7CA92FF6,0x94324773,0x22F54701,0x3AE5E581,0x37C2DADC,0xC8B57634,0x9AF3DDA7, + 0xA9446146,0x0FD0030E,0xECC8C73E,0xA4751E41,0xE238CD99,0x3BEA0E2F,0x3280BBA1,0x183EB331, + 0x4E548B38,0x4F6DB908,0x6F420D03,0xF60A04BF,0x2CB81290,0x24977C79,0x5679B072,0xBCAF89AF, + 0xDE9A771F,0xD9930810,0xB38BAE12,0xDCCF3F2E,0x5512721F,0x2E6B7124,0x501ADDE6,0x9F84CD87, + 0x7A584718,0x7408DA17,0xBC9F9ABC,0xE94B7D8C,0xEC7AEC3A,0xDB851DFA,0x63094366,0xC464C3D2, + 0xEF1C1847,0x3215D908,0xDD433B37,0x24C2BA16,0x12A14D43,0x2A65C451,0x50940002,0x133AE4DD, + 0x71DFF89E,0x10314E55,0x81AC77D6,0x5F11199B,0x043556F1,0xD7A3C76B,0x3C11183B,0x5924A509, + 0xF28FE6ED,0x97F1FBFA,0x9EBABF2C,0x1E153C6E,0x86E34570,0xEAE96FB1,0x860E5E0A,0x5A3E2AB3, + 0x771FE71C,0x4E3D06FA,0x2965DCB9,0x99E71D0F,0x803E89D6,0x5266C825,0x2E4CC978,0x9C10B36A, + 0xC6150EBA,0x94E2EA78,0xA5FC3C53,0x1E0A2DF4,0xF2F74EA7,0x361D2B3D,0x1939260F,0x19C27960, + 0x5223A708,0xF71312B6,0xEBADFE6E,0xEAC31F66,0xE3BC4595,0xA67BC883,0xB17F37D1,0x018CFF28, + 0xC332DDEF,0xBE6C5AA5,0x65582185,0x68AB9802,0xEECEA50F,0xDB2F953B,0x2AEF7DAD,0x5B6E2F84, + 0x1521B628,0x29076170,0xECDD4775,0x619F1510,0x13CCA830,0xEB61BD96,0x0334FE1E,0xAA0363CF, + 0xB5735C90,0x4C70A239,0xD59E9E0B,0xCBAADE14,0xEECC86BC,0x60622CA7,0x9CAB5CAB,0xB2F3846E, + 0x648B1EAF,0x19BDF0CA,0xA02369B9,0x655ABB50,0x40685A32,0x3C2AB4B3,0x319EE9D5,0xC021B8F7, + 0x9B540B19,0x875FA099,0x95F7997E,0x623D7DA8,0xF837889A,0x97E32D77,0x11ED935F,0x16681281, + 0x0E358829,0xC7E61FD6,0x96DEDFA1,0x7858BA99,0x57F584A5,0x1B227263,0x9B83C3FF,0x1AC24696, + 0xCDB30AEB,0x532E3054,0x8FD948E4,0x6DBC3128,0x58EBF2EF,0x34C6FFEA,0xFE28ED61,0xEE7C3C73, + 0x5D4A14D9,0xE864B7E3,0x42105D14,0x203E13E0,0x45EEE2B6,0xA3AAABEA,0xDB6C4F15,0xFACB4FD0, + 0xC742F442,0xEF6ABBB5,0x654F3B1D,0x41CD2105,0xD81E799E,0x86854DC7,0xE44B476A,0x3D816250, + 0xCF62A1F2,0x5B8D2646,0xFC8883A0,0xC1C7B6A3,0x7F1524C3,0x69CB7492,0x47848A0B,0x5692B285, + 0x095BBF00,0xAD19489D,0x1462B174,0x23820E00,0x58428D2A,0x0C55F5EA,0x1DADF43E,0x233F7061, + 0x3372F092,0x8D937E41,0xD65FECF1,0x6C223BDB,0x7CDE3759,0xCBEE7460,0x4085F2A7,0xCE77326E, + 0xA6078084,0x19F8509E,0xE8EFD855,0x61D99735,0xA969A7AA,0xC50C06C2,0x5A04ABFC,0x800BCADC, + 0x9E447A2E,0xC3453484,0xFDD56705,0x0E1E9EC9,0xDB73DBD3,0x105588CD,0x675FDA79,0xE3674340, + 0xC5C43465,0x713E38D8,0x3D28F89E,0xF16DFF20,0x153E21E7,0x8FB03D4A,0xE6E39F2B,0xDB83ADF7 +},{ + 0xE93D5A68,0x948140F7,0xF64C261C,0x94692934,0x411520F7,0x7602D4F7,0xBCF46B2E,0xD4A20068, + 0xD4082471,0x3320F46A,0x43B7D4B7,0x500061AF,0x1E39F62E,0x97244546,0x14214F74,0xBF8B8840, + 0x4D95FC1D,0x96B591AF,0x70F4DDD3,0x66A02F45,0xBFBC09EC,0x03BD9785,0x7FAC6DD0,0x31CB8504, + 0x96EB27B3,0x55FD3941,0xDA2547E6,0xABCA0A9A,0x28507825,0x530429F4,0x0A2C86DA,0xE9B66DFB, + 0x68DC1462,0xD7486900,0x680EC0A4,0x27A18DEE,0x4F3FFEA2,0xE887AD8C,0xB58CE006,0x7AF4D6B6, + 0xAACE1E7C,0xD3375FEC,0xCE78A399,0x406B2A42,0x20FE9E35,0xD9F385B9,0xEE39D7AB,0x3B124E8B, + 0x1DC9FAF7,0x4B6D1856,0x26A36631,0xEAE397B2,0x3A6EFA74,0xDD5B4332,0x6841E7F7,0xCA7820FB, + 0xFB0AF54E,0xD8FEB397,0x454056AC,0xBA489527,0x55533A3A,0x20838D87,0xFE6BA9B7,0xD096954B, + 0x55A867BC,0xA1159A58,0xCCA92963,0x99E1DB33,0xA62A4A56,0x3F3125F9,0x5EF47E1C,0x9029317C, + 0xFDF8E802,0x04272F70,0x80BB155C,0x05282CE3,0x95C11548,0xE4C66D22,0x48C1133F,0xC70F86DC, + 0x07F9C9EE,0x41041F0F,0x404779A4,0x5D886E17,0x325F51EB,0xD59BC0D1,0xF2BCC18F,0x41113564, + 0x257B7834,0x602A9C60,0xDFF8E8A3,0x1F636C1B,0x0E12B4C2,0x02E1329E,0xAF664FD1,0xCAD18115, + 0x6B2395E0,0x333E92E1,0x3B240B62,0xEEBEB922,0x85B2A20E,0xE6BA0D99,0xDE720C8C,0x2DA2F728, + 0xD0127845,0x95B794FD,0x647D0862,0xE7CCF5F0,0x5449A36F,0x877D48FA,0xC39DFD27,0xF33E8D1E, + 0x0A476341,0x992EFF74,0x3A6F6EAB,0xF4F8FD37,0xA812DC60,0xA1EBDDF8,0x991BE14C,0xDB6E6B0D, + 0xC67B5510,0x6D672C37,0x2765D43B,0xDCD0E804,0xF1290DC7,0xCC00FFA3,0xB5390F92,0x690FED0B, + 0x667B9FFB,0xCEDB7D9C,0xA091CF0B,0xD9155EA3,0xBB132F88,0x515BAD24,0x7B9479BF,0x763BD6EB, + 0x37392EB3,0xCC115979,0x8026E297,0xF42E312D,0x6842ADA7,0xC66A2B3B,0x12754CCC,0x782EF11C, + 0x6A124237,0xB79251E7,0x06A1BBE6,0x4BFB6350,0x1A6B1018,0x11CAEDFA,0x3D25BDD8,0xE2E1C3C9, + 0x44421659,0x0A121386,0xD90CEC6E,0xD5ABEA2A,0x64AF674E,0xDA86A85F,0xBEBFE988,0x64E4C3FE, + 0x9DBC8057,0xF0F7C086,0x60787BF8,0x6003604D,0xD1FD8346,0xF6381FB0,0x7745AE04,0xD736FCCC, + 0x83426B33,0xF01EAB71,0xB0804187,0x3C005E5F,0x77A057BE,0xBDE8AE24,0x55464299,0xBF582E61, + 0x4E58F48F,0xF2DDFDA2,0xF474EF38,0x8789BDC2,0x5366F9C3,0xC8B38E74,0xB475F255,0x46FCD9B9, + 0x7AEB2661,0x8B1DDF84,0x846A0E79,0x915F95E2,0x466E598E,0x20B45770,0x8CD55591,0xC902DE4C, + 0xB90BACE1,0xBB8205D0,0x11A86248,0x7574A99E,0xB77F19B6,0xE0A9DC09,0x662D09A1,0xC4324633, + 0xE85A1F02,0x09F0BE8C,0x4A99A025,0x1D6EFE10,0x1AB93D1D,0x0BA5A4DF,0xA186F20F,0x2868F169, + 0xDCB7DA83,0x573906FE,0xA1E2CE9B,0x4FCD7F52,0x50115E01,0xA70683FA,0xA002B5C4,0x0DE6D027, + 0x9AF88C27,0x773F8641,0xC3604C06,0x61A806B5,0xF0177A28,0xC0F586E0,0x006058AA,0x30DC7D62, + 0x11E69ED7,0x2338EA63,0x53C2DD94,0xC2C21634,0xBBCBEE56,0x90BCB6DE,0xEBFC7DA1,0xCE591D76, + 0x6F05E409,0x4B7C0188,0x39720A3D,0x7C927C24,0x86E3725F,0x724D9DB9,0x1AC15BB4,0xD39EB8FC, + 0xED545578,0x08FCA5B5,0xD83D7CD3,0x4DAD0FC4,0x1E50EF5E,0xB161E6F8,0xA28514D9,0x6C51133C, + 0x6FD5C7E7,0x56E14EC4,0x362ABFCE,0xDDC6C837,0xD79A3234,0x92638212,0x670EFA8E,0x406000E0 +},{ + 0x3A39CE37,0xD3FAF5CF,0xABC27737,0x5AC52D1B,0x5CB0679E,0x4FA33742,0xD3822740,0x99BC9BBE, + 0xD5118E9D,0xBF0F7315,0xD62D1C7E,0xC700C47B,0xB78C1B6B,0x21A19045,0xB26EB1BE,0x6A366EB4, + 0x5748AB2F,0xBC946E79,0xC6A376D2,0x6549C2C8,0x530FF8EE,0x468DDE7D,0xD5730A1D,0x4CD04DC6, + 0x2939BBDB,0xA9BA4650,0xAC9526E8,0xBE5EE304,0xA1FAD5F0,0x6A2D519A,0x63EF8CE2,0x9A86EE22, + 0xC089C2B8,0x43242EF6,0xA51E03AA,0x9CF2D0A4,0x83C061BA,0x9BE96A4D,0x8FE51550,0xBA645BD6, + 0x2826A2F9,0xA73A3AE1,0x4BA99586,0xEF5562E9,0xC72FEFD3,0xF752F7DA,0x3F046F69,0x77FA0A59, + 0x80E4A915,0x87B08601,0x9B09E6AD,0x3B3EE593,0xE990FD5A,0x9E34D797,0x2CF0B7D9,0x022B8B51, + 0x96D5AC3A,0x017DA67D,0xD1CF3ED6,0x7C7D2D28,0x1F9F25CF,0xADF2B89B,0x5AD6B472,0x5A88F54C, + 0xE029AC71,0xE019A5E6,0x47B0ACFD,0xED93FA9B,0xE8D3C48D,0x283B57CC,0xF8D56629,0x79132E28, + 0x785F0191,0xED756055,0xF7960E44,0xE3D35E8C,0x15056DD4,0x88F46DBA,0x03A16125,0x0564F0BD, + 0xC3EB9E15,0x3C9057A2,0x97271AEC,0xA93A072A,0x1B3F6D9B,0x1E6321F5,0xF59C66FB,0x26DCF319, + 0x7533D928,0xB155FDF5,0x03563482,0x8ABA3CBB,0x28517711,0xC20AD9F8,0xABCC5167,0xCCAD925F, + 0x4DE81751,0x3830DC8E,0x379D5862,0x9320F991,0xEA7A90C2,0xFB3E7BCE,0x5121CE64,0x774FBE32, + 0xA8B6E37E,0xC3293D46,0x48DE5369,0x6413E680,0xA2AE0810,0xDD6DB224,0x69852DFD,0x09072166, + 0xB39A460A,0x6445C0DD,0x586CDECF,0x1C20C8AE,0x5BBEF7DD,0x1B588D40,0xCCD2017F,0x6BB4E3BB, + 0xDDA26A7E,0x3A59FF45,0x3E350A44,0xBCB4CDD5,0x72EACEA8,0xFA6484BB,0x8D6612AE,0xBF3C6F47, + 0xD29BE463,0x542F5D9E,0xAEC2771B,0xF64E6370,0x740E0D8D,0xE75B1357,0xF8721671,0xAF537D5D, + 0x4040CB08,0x4EB4E2CC,0x34D2466A,0x0115AF84,0xE1B00428,0x95983A1D,0x06B89FB4,0xCE6EA048, + 0x6F3F3B82,0x3520AB82,0x011A1D4B,0x277227F8,0x611560B1,0xE7933FDC,0xBB3A792B,0x344525BD, + 0xA08839E1,0x51CE794B,0x2F32C9B7,0xA01FBAC9,0xE01CC87E,0xBCC7D1F6,0xCF0111C3,0xA1E8AAC7, + 0x1A908749,0xD44FBD9A,0xD0DADECB,0xD50ADA38,0x0339C32A,0xC6913667,0x8DF9317C,0xE0B12B4F, + 0xF79E59B7,0x43F5BB3A,0xF2D519FF,0x27D9459C,0xBF97222C,0x15E6FC2A,0x0F91FC71,0x9B941525, + 0xFAE59361,0xCEB69CEB,0xC2A86459,0x12BAA8D1,0xB6C1075E,0xE3056A0C,0x10D25065,0xCB03A442, + 0xE0EC6E0E,0x1698DB3B,0x4C98A0BE,0x3278E964,0x9F1F9532,0xE0D392DF,0xD3A0342B,0x8971F21E, + 0x1B0A7441,0x4BA3348C,0xC5BE7120,0xC37632D8,0xDF359F8D,0x9B992F2E,0xE60B6F47,0x0FE3F11D, + 0xE54CDA54,0x1EDAD891,0xCE6279CF,0xCD3E7E6F,0x1618B166,0xFD2C1D05,0x848FD2C5,0xF6FB2299, + 0xF523F357,0xA6327623,0x93A83531,0x56CCCD02,0xACF08162,0x5A75EBB5,0x6E163697,0x88D273CC, + 0xDE966292,0x81B949D0,0x4C50901B,0x71C65614,0xE6C6C7BD,0x327A140A,0x45E1D006,0xC3F27B9A, + 0xC9AA53FD,0x62A80F00,0xBB25BFE2,0x35BDD2F6,0x71126905,0xB2040222,0xB6CBCF7C,0xCD769C2B, + 0x53113EC0,0x1640E3D3,0x38ABBD60,0x2547ADF0,0xBA38209C,0xF746CE76,0x77AFA1C5,0x20756060, + 0x85CBFE4E,0x8AE88DD8,0x7AAAF9B0,0x4CF9AA7E,0x1948C25C,0x02FB8A8C,0x01C36AE4,0xD6EBE1F9, + 0x90D4F869,0xA65CDEA0,0x3F09252D,0xC208E69F,0xB74E6132,0xCE77E25B,0x578FDFE3,0x3AC372E6 +} }; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void blowfish_encrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct) +{ + WORD l,r,t; //,i; + + l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]); + r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]); + + ITERATION(l,r,t,0); + ITERATION(l,r,t,1); + ITERATION(l,r,t,2); + ITERATION(l,r,t,3); + ITERATION(l,r,t,4); + ITERATION(l,r,t,5); + ITERATION(l,r,t,6); + ITERATION(l,r,t,7); + ITERATION(l,r,t,8); + ITERATION(l,r,t,9); + ITERATION(l,r,t,10); + ITERATION(l,r,t,11); + ITERATION(l,r,t,12); + ITERATION(l,r,t,13); + ITERATION(l,r,t,14); + l ^= keystruct->p[15]; F(l,t); r^= t; //Last iteration has no swap() + r ^= keystruct->p[16]; + l ^= keystruct->p[17]; + + out[0] = l >> 24; + out[1] = l >> 16; + out[2] = l >> 8; + out[3] = l; + out[4] = r >> 24; + out[5] = r >> 16; + out[6] = r >> 8; + out[7] = r; +} + +void blowfish_decrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct) +{ + WORD l,r,t; //,i; + + l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]); + r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]); + + ITERATION(l,r,t,17); + ITERATION(l,r,t,16); + ITERATION(l,r,t,15); + ITERATION(l,r,t,14); + ITERATION(l,r,t,13); + ITERATION(l,r,t,12); + ITERATION(l,r,t,11); + ITERATION(l,r,t,10); + ITERATION(l,r,t,9); + ITERATION(l,r,t,8); + ITERATION(l,r,t,7); + ITERATION(l,r,t,6); + ITERATION(l,r,t,5); + ITERATION(l,r,t,4); + ITERATION(l,r,t,3); + l ^= keystruct->p[2]; F(l,t); r^= t; //Last iteration has no swap() + r ^= keystruct->p[1]; + l ^= keystruct->p[0]; + + out[0] = l >> 24; + out[1] = l >> 16; + out[2] = l >> 8; + out[3] = l; + out[4] = r >> 24; + out[5] = r >> 16; + out[6] = r >> 8; + out[7] = r; +} + +void blowfish_key_setup(const BYTE user_key[], BLOWFISH_KEY *keystruct, size_t len) +{ + BYTE block[8]; + int idx,idx2; + + // Copy over the constant init array vals (so the originals aren't destroyed). + memcpy(keystruct->p,p_perm,sizeof(WORD) * 18); + memcpy(keystruct->s,s_perm,sizeof(WORD) * 1024); + + // Combine the key with the P box. Assume key is standard 448 bits (56 bytes) or less. + for (idx = 0, idx2 = 0; idx < 18; ++idx, idx2 += 4) + keystruct->p[idx] ^= (user_key[idx2 % len] << 24) | (user_key[(idx2+1) % len] << 16) + | (user_key[(idx2+2) % len] << 8) | (user_key[(idx2+3) % len]); + // Re-calculate the P box. + memset(block, 0, 8); + for (idx = 0; idx < 18; idx += 2) { + blowfish_encrypt(block,block,keystruct); + keystruct->p[idx] = (block[0] << 24) | (block[1] << 16) | (block[2] << 8) | block[3]; + keystruct->p[idx+1]=(block[4] << 24) | (block[5] << 16) | (block[6] << 8) | block[7]; + } + // Recalculate the S-boxes. + for (idx = 0; idx < 4; ++idx) { + for (idx2 = 0; idx2 < 256; idx2 += 2) { + blowfish_encrypt(block,block,keystruct); + keystruct->s[idx][idx2] = (block[0] << 24) | (block[1] << 16) | + (block[2] << 8) | block[3]; + keystruct->s[idx][idx2+1] = (block[4] << 24) | (block[5] << 16) | + (block[6] << 8) | block[7]; + } + } +} diff --git a/lib/FlipCrypt/ciphers/blowfish.h b/lib/FlipCrypt/ciphers/blowfish.h new file mode 100644 index 0000000..d8e9d4a --- /dev/null +++ b/lib/FlipCrypt/ciphers/blowfish.h @@ -0,0 +1,32 @@ +/********************************************************************* +* Filename: blowfish.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding Blowfish implementation. +*********************************************************************/ + +#ifndef BLOWFISH_H +#define BLOWFISH_H + +/*************************** HEADER FILES ***************************/ +#include + +/****************************** MACROS ******************************/ +#define BLOWFISH_BLOCK_SIZE 8 // Blowfish operates on 8 bytes at a time + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + +typedef struct { + WORD p[18]; + WORD s[4][256]; +} BLOWFISH_KEY; + +/*********************** FUNCTION DECLARATIONS **********************/ +void blowfish_key_setup(const BYTE user_key[], BLOWFISH_KEY *keystruct, size_t len); +void blowfish_encrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct); +void blowfish_decrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct); + +#endif // BLOWFISH_H diff --git a/lib/FlipCrypt/ciphers/caesar.c b/lib/FlipCrypt/ciphers/caesar.c new file mode 100644 index 0000000..abb5a95 --- /dev/null +++ b/lib/FlipCrypt/ciphers/caesar.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include + +#include "caesar.h" + +char* encode_caesar(const char* plaintext, int32_t number) { + size_t len = strlen(plaintext); + char* output = malloc(len + 1); + if (!output) return "memory allocation failed, try again"; + + for (size_t i = 0; i < len; i++) { + char c = plaintext[i]; + + if (c >= 'a' && c <= 'z') { + output[i] = 'a' + ((c - 'a' + number) % 26 + 26) % 26; + } else if (c >= 'A' && c <= 'Z') { + output[i] = 'A' + ((c - 'A' + number) % 26 + 26) % 26; + } else { + output[i] = c; + } + } + + output[len] = '\0'; + return output; +} + +char* decode_caesar(const char* ciphertext, int32_t number) { + size_t len = strlen(ciphertext); + char* output = malloc(len + 1); + if (!output) return "memory allocation failed, try again"; + + for (size_t i = 0; i < len; i++) { + char c = ciphertext[i]; + + if (c >= 'a' && c <= 'z') { + output[i] = 'a' + ((c - 'a' - number) % 26 + 26) % 26; + } else if (c >= 'A' && c <= 'Z') { + output[i] = 'A' + ((c - 'A' - number) % 26 + 26) % 26; + } else { + output[i] = c; + } + } + + output[len] = '\0'; + return output; +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/caesar.h b/lib/FlipCrypt/ciphers/caesar.h new file mode 100644 index 0000000..1b39a1d --- /dev/null +++ b/lib/FlipCrypt/ciphers/caesar.h @@ -0,0 +1,4 @@ +#include + +char* encode_caesar(const char* plaintext, int32_t number); +char* decode_caesar(const char* ciphertext, int32_t number); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/des.c b/lib/FlipCrypt/ciphers/des.c new file mode 100644 index 0000000..d20db6f --- /dev/null +++ b/lib/FlipCrypt/ciphers/des.c @@ -0,0 +1,269 @@ +/********************************************************************* +* Filename: des.c +* Author: Brad Conte (brad AT radconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the DES encryption algorithm. + Modes of operation (such as CBC) are not included. + The formal NIST algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "des.h" + +/****************************** MACROS ******************************/ +// Obtain bit "b" from the left and shift it "c" places from the right +#define BITNUM(a,b,c) (((a[(b)/8] >> (7 - (b%8))) & 0x01) << (c)) +#define BITNUMINTR(a,b,c) ((((a) >> (31 - (b))) & 0x00000001) << (c)) +#define BITNUMINTL(a,b,c) ((((a) << (b)) & 0x80000000) >> (c)) + +// This macro converts a 6 bit block with the S-Box row defined as the first and last +// bits to a 6 bit block with the row defined by the first two bits. +#define SBOXBIT(a) (((a) & 0x20) | (((a) & 0x1f) >> 1) | (((a) & 0x01) << 4)) + +/**************************** VARIABLES *****************************/ +static const BYTE sbox1[64] = { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 +}; +static const BYTE sbox2[64] = { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 +}; +static const BYTE sbox3[64] = { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 +}; +static const BYTE sbox4[64] = { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 +}; +static const BYTE sbox5[64] = { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 +}; +static const BYTE sbox6[64] = { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 +}; +static const BYTE sbox7[64] = { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 +}; +static const BYTE sbox8[64] = { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +// Initial (Inv)Permutation step +void IP(WORD state[], const BYTE in[]) +{ + state[0] = BITNUM(in,57,31) | BITNUM(in,49,30) | BITNUM(in,41,29) | BITNUM(in,33,28) | + BITNUM(in,25,27) | BITNUM(in,17,26) | BITNUM(in,9,25) | BITNUM(in,1,24) | + BITNUM(in,59,23) | BITNUM(in,51,22) | BITNUM(in,43,21) | BITNUM(in,35,20) | + BITNUM(in,27,19) | BITNUM(in,19,18) | BITNUM(in,11,17) | BITNUM(in,3,16) | + BITNUM(in,61,15) | BITNUM(in,53,14) | BITNUM(in,45,13) | BITNUM(in,37,12) | + BITNUM(in,29,11) | BITNUM(in,21,10) | BITNUM(in,13,9) | BITNUM(in,5,8) | + BITNUM(in,63,7) | BITNUM(in,55,6) | BITNUM(in,47,5) | BITNUM(in,39,4) | + BITNUM(in,31,3) | BITNUM(in,23,2) | BITNUM(in,15,1) | BITNUM(in,7,0); + + state[1] = BITNUM(in,56,31) | BITNUM(in,48,30) | BITNUM(in,40,29) | BITNUM(in,32,28) | + BITNUM(in,24,27) | BITNUM(in,16,26) | BITNUM(in,8,25) | BITNUM(in,0,24) | + BITNUM(in,58,23) | BITNUM(in,50,22) | BITNUM(in,42,21) | BITNUM(in,34,20) | + BITNUM(in,26,19) | BITNUM(in,18,18) | BITNUM(in,10,17) | BITNUM(in,2,16) | + BITNUM(in,60,15) | BITNUM(in,52,14) | BITNUM(in,44,13) | BITNUM(in,36,12) | + BITNUM(in,28,11) | BITNUM(in,20,10) | BITNUM(in,12,9) | BITNUM(in,4,8) | + BITNUM(in,62,7) | BITNUM(in,54,6) | BITNUM(in,46,5) | BITNUM(in,38,4) | + BITNUM(in,30,3) | BITNUM(in,22,2) | BITNUM(in,14,1) | BITNUM(in,6,0); +} + +void InvIP(WORD state[], BYTE in[]) +{ + in[0] = BITNUMINTR(state[1],7,7) | BITNUMINTR(state[0],7,6) | BITNUMINTR(state[1],15,5) | + BITNUMINTR(state[0],15,4) | BITNUMINTR(state[1],23,3) | BITNUMINTR(state[0],23,2) | + BITNUMINTR(state[1],31,1) | BITNUMINTR(state[0],31,0); + + in[1] = BITNUMINTR(state[1],6,7) | BITNUMINTR(state[0],6,6) | BITNUMINTR(state[1],14,5) | + BITNUMINTR(state[0],14,4) | BITNUMINTR(state[1],22,3) | BITNUMINTR(state[0],22,2) | + BITNUMINTR(state[1],30,1) | BITNUMINTR(state[0],30,0); + + in[2] = BITNUMINTR(state[1],5,7) | BITNUMINTR(state[0],5,6) | BITNUMINTR(state[1],13,5) | + BITNUMINTR(state[0],13,4) | BITNUMINTR(state[1],21,3) | BITNUMINTR(state[0],21,2) | + BITNUMINTR(state[1],29,1) | BITNUMINTR(state[0],29,0); + + in[3] = BITNUMINTR(state[1],4,7) | BITNUMINTR(state[0],4,6) | BITNUMINTR(state[1],12,5) | + BITNUMINTR(state[0],12,4) | BITNUMINTR(state[1],20,3) | BITNUMINTR(state[0],20,2) | + BITNUMINTR(state[1],28,1) | BITNUMINTR(state[0],28,0); + + in[4] = BITNUMINTR(state[1],3,7) | BITNUMINTR(state[0],3,6) | BITNUMINTR(state[1],11,5) | + BITNUMINTR(state[0],11,4) | BITNUMINTR(state[1],19,3) | BITNUMINTR(state[0],19,2) | + BITNUMINTR(state[1],27,1) | BITNUMINTR(state[0],27,0); + + in[5] = BITNUMINTR(state[1],2,7) | BITNUMINTR(state[0],2,6) | BITNUMINTR(state[1],10,5) | + BITNUMINTR(state[0],10,4) | BITNUMINTR(state[1],18,3) | BITNUMINTR(state[0],18,2) | + BITNUMINTR(state[1],26,1) | BITNUMINTR(state[0],26,0); + + in[6] = BITNUMINTR(state[1],1,7) | BITNUMINTR(state[0],1,6) | BITNUMINTR(state[1],9,5) | + BITNUMINTR(state[0],9,4) | BITNUMINTR(state[1],17,3) | BITNUMINTR(state[0],17,2) | + BITNUMINTR(state[1],25,1) | BITNUMINTR(state[0],25,0); + + in[7] = BITNUMINTR(state[1],0,7) | BITNUMINTR(state[0],0,6) | BITNUMINTR(state[1],8,5) | + BITNUMINTR(state[0],8,4) | BITNUMINTR(state[1],16,3) | BITNUMINTR(state[0],16,2) | + BITNUMINTR(state[1],24,1) | BITNUMINTR(state[0],24,0); +} + +WORD f(WORD state, const BYTE key[]) +{ + BYTE lrgstate[6]; //,i; + WORD t1,t2; + + // Expantion Permutation + t1 = BITNUMINTL(state,31,0) | ((state & 0xf0000000) >> 1) | BITNUMINTL(state,4,5) | + BITNUMINTL(state,3,6) | ((state & 0x0f000000) >> 3) | BITNUMINTL(state,8,11) | + BITNUMINTL(state,7,12) | ((state & 0x00f00000) >> 5) | BITNUMINTL(state,12,17) | + BITNUMINTL(state,11,18) | ((state & 0x000f0000) >> 7) | BITNUMINTL(state,16,23); + + t2 = BITNUMINTL(state,15,0) | ((state & 0x0000f000) << 15) | BITNUMINTL(state,20,5) | + BITNUMINTL(state,19,6) | ((state & 0x00000f00) << 13) | BITNUMINTL(state,24,11) | + BITNUMINTL(state,23,12) | ((state & 0x000000f0) << 11) | BITNUMINTL(state,28,17) | + BITNUMINTL(state,27,18) | ((state & 0x0000000f) << 9) | BITNUMINTL(state,0,23); + + lrgstate[0] = (t1 >> 24) & 0x000000ff; + lrgstate[1] = (t1 >> 16) & 0x000000ff; + lrgstate[2] = (t1 >> 8) & 0x000000ff; + lrgstate[3] = (t2 >> 24) & 0x000000ff; + lrgstate[4] = (t2 >> 16) & 0x000000ff; + lrgstate[5] = (t2 >> 8) & 0x000000ff; + + // Key XOR + lrgstate[0] ^= key[0]; + lrgstate[1] ^= key[1]; + lrgstate[2] ^= key[2]; + lrgstate[3] ^= key[3]; + lrgstate[4] ^= key[4]; + lrgstate[5] ^= key[5]; + + // S-Box Permutation + state = (sbox1[SBOXBIT(lrgstate[0] >> 2)] << 28) | + (sbox2[SBOXBIT(((lrgstate[0] & 0x03) << 4) | (lrgstate[1] >> 4))] << 24) | + (sbox3[SBOXBIT(((lrgstate[1] & 0x0f) << 2) | (lrgstate[2] >> 6))] << 20) | + (sbox4[SBOXBIT(lrgstate[2] & 0x3f)] << 16) | + (sbox5[SBOXBIT(lrgstate[3] >> 2)] << 12) | + (sbox6[SBOXBIT(((lrgstate[3] & 0x03) << 4) | (lrgstate[4] >> 4))] << 8) | + (sbox7[SBOXBIT(((lrgstate[4] & 0x0f) << 2) | (lrgstate[5] >> 6))] << 4) | + sbox8[SBOXBIT(lrgstate[5] & 0x3f)]; + + // P-Box Permutation + state = BITNUMINTL(state,15,0) | BITNUMINTL(state,6,1) | BITNUMINTL(state,19,2) | + BITNUMINTL(state,20,3) | BITNUMINTL(state,28,4) | BITNUMINTL(state,11,5) | + BITNUMINTL(state,27,6) | BITNUMINTL(state,16,7) | BITNUMINTL(state,0,8) | + BITNUMINTL(state,14,9) | BITNUMINTL(state,22,10) | BITNUMINTL(state,25,11) | + BITNUMINTL(state,4,12) | BITNUMINTL(state,17,13) | BITNUMINTL(state,30,14) | + BITNUMINTL(state,9,15) | BITNUMINTL(state,1,16) | BITNUMINTL(state,7,17) | + BITNUMINTL(state,23,18) | BITNUMINTL(state,13,19) | BITNUMINTL(state,31,20) | + BITNUMINTL(state,26,21) | BITNUMINTL(state,2,22) | BITNUMINTL(state,8,23) | + BITNUMINTL(state,18,24) | BITNUMINTL(state,12,25) | BITNUMINTL(state,29,26) | + BITNUMINTL(state,5,27) | BITNUMINTL(state,21,28) | BITNUMINTL(state,10,29) | + BITNUMINTL(state,3,30) | BITNUMINTL(state,24,31); + + // Return the final state value + return(state); +} + +void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode) +{ + WORD i, j, to_gen, C, D; + const WORD key_rnd_shift[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; + const WORD key_perm_c[28] = {56,48,40,32,24,16,8,0,57,49,41,33,25,17, + 9,1,58,50,42,34,26,18,10,2,59,51,43,35}; + const WORD key_perm_d[28] = {62,54,46,38,30,22,14,6,61,53,45,37,29,21, + 13,5,60,52,44,36,28,20,12,4,27,19,11,3}; + const WORD key_compression[48] = {13,16,10,23,0,4,2,27,14,5,20,9, + 22,18,11,3,25,7,15,6,26,19,12,1, + 40,51,30,36,46,54,29,39,50,44,32,47, + 43,48,38,55,33,52,45,41,49,35,28,31}; + + // Permutated Choice #1 (copy the key in, ignoring parity bits). + for (i = 0, j = 31, C = 0; i < 28; ++i, --j) + C |= BITNUM(key,key_perm_c[i],j); + for (i = 0, j = 31, D = 0; i < 28; ++i, --j) + D |= BITNUM(key,key_perm_d[i],j); + + // Generate the 16 subkeys. + for (i = 0; i < 16; ++i) { + C = ((C << key_rnd_shift[i]) | (C >> (28-key_rnd_shift[i]))) & 0xfffffff0; + D = ((D << key_rnd_shift[i]) | (D >> (28-key_rnd_shift[i]))) & 0xfffffff0; + + // Decryption subkeys are reverse order of encryption subkeys so + // generate them in reverse if the key schedule is for decryption useage. + if (mode == DES_DECRYPT) + to_gen = 15 - i; + else /*(if mode == DES_ENCRYPT)*/ + to_gen = i; + // Initialize the array + for (j = 0; j < 6; ++j) + schedule[to_gen][j] = 0; + for (j = 0; j < 24; ++j) + schedule[to_gen][j/8] |= BITNUMINTR(C,key_compression[j],7 - (j%8)); + for ( ; j < 48; ++j) + schedule[to_gen][j/8] |= BITNUMINTR(D,key_compression[j] - 28,7 - (j%8)); + } +} + +void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]) +{ + WORD state[2],idx,t; + + IP(state,in); + + for (idx=0; idx < 15; ++idx) { + t = state[1]; + state[1] = f(state[1],key[idx]) ^ state[0]; + state[0] = t; + } + // Perform the final loop manually as it doesn't switch sides + state[0] = f(state[1],key[15]) ^ state[0]; + + InvIP(state,out); +} + +void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode) +{ + if (mode == DES_ENCRYPT) { + des_key_setup(&key[0],schedule[0],mode); + des_key_setup(&key[8],schedule[1],!mode); + des_key_setup(&key[16],schedule[2],mode); + } + else /*if (mode == DES_DECRYPT*/ { + des_key_setup(&key[16],schedule[0],mode); + des_key_setup(&key[8],schedule[1],!mode); + des_key_setup(&key[0],schedule[2],mode); + } +} + +void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]) +{ + des_crypt(in,out,key[0]); + des_crypt(out,out,key[1]); + des_crypt(out,out,key[2]); +} diff --git a/lib/FlipCrypt/ciphers/des.h b/lib/FlipCrypt/ciphers/des.h new file mode 100644 index 0000000..1503772 --- /dev/null +++ b/lib/FlipCrypt/ciphers/des.h @@ -0,0 +1,37 @@ +/********************************************************************* +* Filename: des.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding DES implementation. + Note that encryption and decryption are defined by how + the key setup is performed, the actual en/de-cryption is + performed by the same function. +*********************************************************************/ + +#ifndef DES_H +#define DESH + +/*************************** HEADER FILES ***************************/ +#include + +/****************************** MACROS ******************************/ +#define DES_BLOCK_SIZE 8 // DES operates on 8 bytes at a time + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + +typedef enum { + DES_ENCRYPT, + DES_DECRYPT +} DES_MODE; + +/*********************** FUNCTION DECLARATIONS **********************/ +void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode); +void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]); + +void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode); +void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]); + +#endif // DES_H diff --git a/lib/FlipCrypt/ciphers/playfair.c b/lib/FlipCrypt/ciphers/playfair.c new file mode 100644 index 0000000..2eca287 --- /dev/null +++ b/lib/FlipCrypt/ciphers/playfair.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +#include "playfair.h" + +char* playfair_make_table(const char* keyword) { + char* table_output = (char*)malloc(26); + if (!table_output) return "memory allocation failed"; + + int used[26] = {0}; // Track used letters (a-z), index 0 = 'a' + size_t table_index = 0; + + // Process keyword: add unique letters to table, convert to lowercase, merge 'j' into 'i' + for (size_t i = 0; keyword[i] != '\0'; i++) { + char c = tolower(keyword[i]); + if (c == 'j') c = 'i'; // Merge j to i + + if (c < 'a' || c > 'z') continue; // Skip non-alpha chars + + int idx = c - 'a'; + if (!used[idx]) { + used[idx] = 1; + table_output[table_index++] = c; + } + } + + // Fill the rest of the table with remaining letters (except 'j') + for (char c = 'a'; c <= 'z'; c++) { + if (c == 'j') continue; // skip 'j' + int idx = c - 'a'; + if (!used[idx]) { + used[idx] = 1; + table_output[table_index++] = c; + } + } + + table_output[table_index] = '\0'; + + return table_output; +} + +void find_position(char letter, const char* table, int* row, int* col) { + if (letter == 'j') letter = 'i'; // j maps to i + for (int i = 0; i < 25; i++) { + if (table[i] == letter) { + *row = i / 5; + *col = i % 5; + return; + } + } + // Not found (shouldn't happen), return -1 + *row = -1; + *col = -1; +} + +char* playfair_encrypt(const char* plaintext, const char* table) { + size_t len = strlen(plaintext); + // Max encrypted length can be up to twice plaintext length (worst case) + char* prepared = (char*)malloc(len * 2 + 1); + if (!prepared) return "memory allocation failed"; + + // Step 1: Prepare plaintext as digraphs + size_t prep_index = 0; + for (size_t i = 0; i < len; i++) { + char c = tolower(plaintext[i]); + if (c < 'a' || c > 'z') continue; // skip non-alpha chars + if (c == 'j') c = 'i'; // j -> i + + // Add first letter of pair + if (prep_index % 2 == 1 && prepared[prep_index - 1] == c) { + // If duplicate letters in pair, insert 'x' before current letter + prepared[prep_index++] = 'x'; + } + + prepared[prep_index++] = c; + } + // If odd length, add 'x' to last + if (prep_index % 2 != 0) { + prepared[prep_index++] = 'x'; + } + prepared[prep_index] = '\0'; + + // Step 2: Encrypt each digraph + char* encrypted = (char*)malloc(prep_index + 1); + if (!encrypted) { + free(prepared); + return "memory allocation failed"; + } + + for (size_t i = 0; i < prep_index; i += 2) { + int r1, c1, r2, c2; + find_position(prepared[i], table, &r1, &c1); + find_position(prepared[i+1], table, &r2, &c2); + + if (r1 == r2) { + // Same row: replace with letter to right (wrap around) + encrypted[i] = table[r1 * 5 + (c1 + 1) % 5]; + encrypted[i+1] = table[r2 * 5 + (c2 + 1) % 5]; + } else if (c1 == c2) { + // Same column: replace with letter below (wrap around) + encrypted[i] = table[((r1 + 1) % 5) * 5 + c1]; + encrypted[i+1] = table[((r2 + 1) % 5) * 5 + c2]; + } else { + // Rectangle swap: swap columns + encrypted[i] = table[r1 * 5 + c2]; + encrypted[i+1] = table[r2 * 5 + c1]; + } + } + encrypted[prep_index] = '\0'; + + free(prepared); + return encrypted; +} + +char* playfair_decrypt(const char* ciphertext, const char* table) { + size_t len = strlen(ciphertext); + if (len % 2 != 0) { + return "text to decrypt must be of an even length"; + } + + // Allocate and convert ciphertext to lowercase + char* lower_text = (char*)malloc(len + 1); + if (!lower_text) { + return "memory allocation failure"; + } + for (size_t i = 0; i < len; ++i) { + lower_text[i] = tolower((unsigned char)ciphertext[i]); + } + lower_text[len] = '\0'; + + // Allocate memory for the decrypted text + char* decrypted = (char*)malloc(len + 1); + if (!decrypted) { + free(lower_text); + return "memory allocation failure"; + } + + for (size_t i = 0; i < len; i += 2) { + int r1, c1, r2, c2; + find_position(lower_text[i], table, &r1, &c1); + find_position(lower_text[i+1], table, &r2, &c2); + + if (r1 == r2) { + // Same row: letter to the left (wrap around) + decrypted[i] = table[r1 * 5 + (c1 + 5 - 1) % 5]; + decrypted[i+1] = table[r2 * 5 + (c2 + 5 - 1) % 5]; + } else if (c1 == c2) { + // Same column: letter above (wrap around) + decrypted[i] = table[((r1 + 5 - 1) % 5) * 5 + c1]; + decrypted[i+1] = table[((r2 + 5 - 1) % 5) * 5 + c2]; + } else { + // Rectangle swap: swap columns + decrypted[i] = table[r1 * 5 + c2]; + decrypted[i+1] = table[r2 * 5 + c1]; + } + } + + decrypted[len] = '\0'; + free(lower_text); + return decrypted; +} + diff --git a/lib/FlipCrypt/ciphers/playfair.h b/lib/FlipCrypt/ciphers/playfair.h new file mode 100644 index 0000000..7c798df --- /dev/null +++ b/lib/FlipCrypt/ciphers/playfair.h @@ -0,0 +1,4 @@ +char* playfair_make_table(const char* keyword); +void find_position(char letter, const char* table, int* row, int* col); +char* playfair_encrypt(const char* plaintext, const char* table); +char* playfair_decrypt(const char* ciphertext, const char* table); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/polybius.c b/lib/FlipCrypt/ciphers/polybius.c new file mode 100644 index 0000000..c141d54 --- /dev/null +++ b/lib/FlipCrypt/ciphers/polybius.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#include "polybius.h" + +const char* polybius_square[5][5] = { + {"A","B","C","D","E"}, + {"F","G","H","I","K"}, + {"L","M","N","O","P"}, + {"Q","R","S","T","U"}, + {"V","W","X","Y","Z"} +}; + +// Find coordinates of a character in the square (row and col are 1-based) +void find_coords(char c, int* row, int* col) { + if (c == 'J') c = 'I'; // Treat I/J the same + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + if (polybius_square[i][j][0] == c) { + *row = i + 1; + *col = j + 1; + return; + } + } + } +} + +char* encrypt_polybius(const char* plaintext) { + size_t len = strlen(plaintext); + char* ciphertext = malloc(len * 2 + 1); // Each letter -> 2 digits + if (!ciphertext) return "memory allocation failed, try again"; + + size_t pos = 0; + for (size_t i = 0; i < len; i++) { + if (isalpha((unsigned char)plaintext[i])) { + char upper = toupper(plaintext[i]); + int row, col; + find_coords(upper, &row, &col); + ciphertext[pos++] = '0' + row; + ciphertext[pos++] = '0' + col; + } + } + ciphertext[pos] = '\0'; + return ciphertext; +} + +char* decrypt_polybius(const char* ciphertext) { + size_t len = strlen(ciphertext); + char* plaintext = malloc(len / 2 + 1); // Each 2 digits -> 1 letter + if (!plaintext) return "memory allocation failed, try again"; + + size_t pos = 0; + for (size_t i = 0; i + 1 < len; i += 2) { + if (isdigit((unsigned char)ciphertext[i]) && isdigit((unsigned char)ciphertext[i + 1])) { + int row = ciphertext[i] - '0' - 1; + int col = ciphertext[i + 1] - '0' - 1; + if (row >= 0 && row < 5 && col >= 0 && col < 5) { + plaintext[pos++] = polybius_square[row][col][0]; + } + } + } + plaintext[pos] = '\0'; + return plaintext; +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/polybius.h b/lib/FlipCrypt/ciphers/polybius.h new file mode 100644 index 0000000..3dee0e7 --- /dev/null +++ b/lib/FlipCrypt/ciphers/polybius.h @@ -0,0 +1,2 @@ +char* encrypt_polybius(const char* plaintext); +char* decrypt_polybius(const char* ciphertext); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/porta.c b/lib/FlipCrypt/ciphers/porta.c new file mode 100644 index 0000000..a463c2a --- /dev/null +++ b/lib/FlipCrypt/ciphers/porta.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +#include "porta.h" + +static const char* porta_rows[13] = { + "NOPQRSTUVWXYZABCDEFGHIJKLM", // A/B + "OPQRSTUVWXYZABCDEFGHIJKLMN", // C/D + "PQRSTUVWXYZABCDEFGHIJKLMNO", // E/F + "QRSTUVWXYZABCDEFGHIJKLMNOP", // G/H + "RSTUVWXYZABCDEFGHIJKLMNOPQ", // I/J + "STUVWXYZABCDEFGHIJKLMNOPQR", // K/L + "TUVWXYZABCDEFGHIJKLMNOPQRS", // M/N + "UVWXYZABCDEFGHIJKLMNOPQRST", // O/P + "VWXYZABCDEFGHIJKLMNOPQRSTU", // Q/R + "WXYZABCDEFGHIJKLMNOPQRSTUV", // S/T + "XYZABCDEFGHIJKLMNOPQRSTUVW", // U/V + "YZABCDEFGHIJKLMNOPQRSTUVWX", // W/X + "ZABCDEFGHIJKLMNOPQRSTUVWXY" // Y/Z +}; + +static int number_to_index(char letter) { + if (letter >= 'a' && letter <= 'z') { + return letter - 'a'; + } else if (letter >= 'A' && letter <= 'Z') { + return letter - 'A'; + } else { + return -1; // not a letter + } +} + +char* porta_encrypt(const char* plaintext, const char* keyword) { + size_t plaintext_length = strlen(plaintext); + size_t keyword_length = strlen(keyword); + + char* output = malloc(plaintext_length + 1); + if (!output) return "memory allocation failed, try again"; + + for (size_t i = 0; i < plaintext_length; i++) { + char p = plaintext[i]; + int p_index = number_to_index(p); + + if (p_index >= 0) { + char k = keyword[i % keyword_length]; + int k_index = number_to_index(k); + + // Determine row number + int table = k_index / 2; + // Porta cipher: letters A–M get substituted from row, N–Z get substituted from first half + if (p_index < 13) { + output[i] = porta_rows[table][p_index]; + } else { + output[i] = porta_rows[table][p_index - 13]; + } + + // Preserve case + if (islower(p)) { + output[i] = tolower(output[i]); + } + } else { + // Non-letter, copy directly + output[i] = p; + } + } + + output[plaintext_length] = '\0'; + return output; +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/porta.h b/lib/FlipCrypt/ciphers/porta.h new file mode 100644 index 0000000..41acd5b --- /dev/null +++ b/lib/FlipCrypt/ciphers/porta.h @@ -0,0 +1 @@ +char* porta_encrypt(const char* plaintext, const char* keyword); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/railfence.c b/lib/FlipCrypt/ciphers/railfence.c new file mode 100644 index 0000000..3a527cd --- /dev/null +++ b/lib/FlipCrypt/ciphers/railfence.c @@ -0,0 +1,99 @@ +#include +#include +#include + +#include "railfence.h" + +char* rail_fence_encrypt(const char* text, int rails) { + if (rails <= 1 || text == NULL) return "improper input, try again"; + + int len = strlen(text); + char** rail = malloc(rails * sizeof(char*)); + int* pos = calloc(rails, sizeof(int)); + + // Allocate memory for each rail + for (int i = 0; i < rails; i++) { + rail[i] = malloc((len + 1) * sizeof(char)); + rail[i][0] = '\0'; // initialize as empty string + } + + int row = 0; + int direction = 1; // 1 = down, -1 = up + + for (int i = 0; i < len; i++) { + rail[row][pos[row]] = text[i]; + pos[row]++; + + row += direction; + if (row == rails - 1) direction = -1; + else if (row == 0) direction = 1; + } + + // Concatenate all rails + char* result = malloc(len + 1); + int idx = 0; + + for (int i = 0; i < rails; i++) { + for (int j = 0; j < pos[i]; j++) { + result[idx++] = rail[i][j]; + } + free(rail[i]); + } + free(rail); + free(pos); + + result[idx] = '\0'; + return result; +} + +char* rail_fence_decrypt(const char* cipher, int rails) { + if (rails <= 1 || cipher == NULL) return "improper input, try again"; + + int len = strlen(cipher); + char* result = malloc(len + 1); + char** fence = malloc(rails * sizeof(char*)); + + for (int i = 0; i < rails; i++) { + fence[i] = calloc(len, sizeof(char)); + } + + // Mark the zigzag pattern with '*' + int row = 0, direction = 1; + for (int i = 0; i < len; i++) { + fence[row][i] = '*'; + row += direction; + + if (row == rails - 1) direction = -1; + else if (row == 0) direction = 1; + } + + // Fill in the pattern with ciphertext + int idx = 0; + for (int r = 0; r < rails; r++) { + for (int c = 0; c < len; c++) { + if (fence[r][c] == '*' && idx < len) { + fence[r][c] = cipher[idx++]; + } + } + } + + // Read the zigzag pattern to build the plaintext + row = 0; + direction = 1; + for (int i = 0; i < len; i++) { + result[i] = fence[row][i]; + row += direction; + + if (row == rails - 1) direction = -1; + else if (row == 0) direction = 1; + } + + result[len] = '\0'; + + for (int i = 0; i < rails; i++) { + free(fence[i]); + } + free(fence); + + return result; +} diff --git a/lib/FlipCrypt/ciphers/railfence.h b/lib/FlipCrypt/ciphers/railfence.h new file mode 100644 index 0000000..f9d501f --- /dev/null +++ b/lib/FlipCrypt/ciphers/railfence.h @@ -0,0 +1,2 @@ +char* rail_fence_encrypt(const char* text, int rails); +char* rail_fence_decrypt(const char* cipher, int rails); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/rc4.c b/lib/FlipCrypt/ciphers/rc4.c new file mode 100644 index 0000000..ad66820 --- /dev/null +++ b/lib/FlipCrypt/ciphers/rc4.c @@ -0,0 +1,69 @@ +#include +#include +#include + +#include "rc4.h" + +unsigned char* rc4_encrypt_and_decrypt(const char* key, const unsigned char* input, size_t inputlen) { + unsigned char S[256]; + unsigned int i, j = 0, k, t; + size_t keylen = strlen(key); + + unsigned char* output = malloc(inputlen); + if (!output) return NULL; + + // KSA + for(i = 0; i < 256; i++) { + S[i] = i; + } + for(i = 0; i < 256; i++) { + j = (j + S[i] + (unsigned char)key[i % keylen]) & 0xFF; + unsigned char temp = S[i]; + S[i] = S[j]; + S[j] = temp; + } + + // PRGA + i = j = 0; + for(k = 0; k < inputlen; k++) { + i = (i + 1) & 0xFF; + j = (j + S[i]) & 0xFF; + unsigned char temp = S[i]; + S[i] = S[j]; + S[j] = temp; + t = (S[i] + S[j]) & 0xFF; + output[k] = input[k] ^ S[t]; + } + + return output; +} + +char* rc4_to_hex(const char* data, size_t len) { + char* hex = malloc(len * 2 + 1); + if(!hex) return NULL; + for(size_t i = 0; i < len; i++) { + snprintf(hex + i * 2, 3, "%02X", (unsigned char)data[i]); + } + hex[len * 2] = '\0'; + return hex; +} + +unsigned char* rc4_hex_to_bytes(const char* hex, size_t* out_len) { + size_t hex_len = strlen(hex); + if(hex_len % 2 != 0) return NULL; + + *out_len = hex_len / 2; + unsigned char* bytes = malloc(*out_len); + if(!bytes) return NULL; + + for(size_t i = 0; i < *out_len; i++) { + unsigned int byte; + if(sscanf(hex + 2 * i, "%2X", &byte) != 1) { + free(bytes); + return NULL; + } + bytes[i] = (unsigned char)byte; + } + + return bytes; +} diff --git a/lib/FlipCrypt/ciphers/rc4.h b/lib/FlipCrypt/ciphers/rc4.h new file mode 100644 index 0000000..314b1cc --- /dev/null +++ b/lib/FlipCrypt/ciphers/rc4.h @@ -0,0 +1,5 @@ +#include + +unsigned char* rc4_encrypt_and_decrypt(const char* key, const unsigned char* input, size_t inputlen); +char* rc4_to_hex(const char* data, size_t len); +unsigned char* rc4_hex_to_bytes(const char* hex, size_t* out_len); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/rot13.c b/lib/FlipCrypt/ciphers/rot13.c new file mode 100644 index 0000000..1d6c9fd --- /dev/null +++ b/lib/FlipCrypt/ciphers/rot13.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include + +#include "rot13.h" + +char* encrypt_rot13(const char* plaintext) { + size_t len = strlen(plaintext); + char* output = malloc(len + 1); + if (!output) return "memory allocation failed, try again"; + + for (size_t i = 0; i < len; i++) { + char c = plaintext[i]; + + if (c >= 'a' && c <= 'z') { + output[i] = 'a' + ((c - 'a' + 13) % 26 + 26) % 26; + } else if (c >= 'A' && c <= 'Z') { + output[i] = 'A' + ((c - 'A' + 13) % 26 + 26) % 26; + } else { + output[i] = c; + } + } + + output[len] = '\0'; + return output; +} + +char* decrypt_rot13(const char* ciphertext) { + size_t len = strlen(ciphertext); + char* output = malloc(len + 1); + if (!output) return "memory allocation failed, try again"; + + for (size_t i = 0; i < len; i++) { + char c = ciphertext[i]; + + if (c >= 'a' && c <= 'z') { + output[i] = 'a' + ((c - 'a' - 13) % 26 + 26) % 26; + } else if (c >= 'A' && c <= 'Z') { + output[i] = 'A' + ((c - 'A' - 13) % 26 + 26) % 26; + } else { + output[i] = c; + } + } + + output[len] = '\0'; + return output; +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/rot13.h b/lib/FlipCrypt/ciphers/rot13.h new file mode 100644 index 0000000..24f0120 --- /dev/null +++ b/lib/FlipCrypt/ciphers/rot13.h @@ -0,0 +1,2 @@ +char* encrypt_rot13(const char* plaintext); +char* decrypt_rot13(const char* ciphertext); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/scytale.c b/lib/FlipCrypt/ciphers/scytale.c new file mode 100644 index 0000000..281f054 --- /dev/null +++ b/lib/FlipCrypt/ciphers/scytale.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +char* scytale_encrypt(const char* plaintext, int32_t key) { + if (key <= 1) return strdup("key <= 1, encryption failed"); + if (!plaintext) return strdup("text input does not exist, try again"); + + int len = strlen(plaintext); + int cols = (int)ceil((double)len / key); // columns + char* ciphertext = malloc(len + 1); + if (!ciphertext) return strdup("memory allocation failed, try again"); + + int index = 0; + for (int i = 0; i < cols; i++) { + for (int j = 0; j < key; j++) { + int pos = j * cols + i; + if (pos < len) { + ciphertext[index++] = plaintext[pos]; + } + } + } + + ciphertext[index] = '\0'; + return ciphertext; +} + + +char* scytale_decrypt(const char* ciphertext, int32_t key) { + if (key <= 1) return strdup("key <= 1, decryption failed"); + if (!ciphertext) return strdup("text input does not exist, try again"); + + int len = strlen(ciphertext); + int cols = (int)ceil((double)len / key); + char* plaintext = malloc(len + 1); + if (!plaintext) return strdup("memory allocation failed, try again"); + + int index = 0; + for (int j = 0; j < key; j++) { + for (int i = 0; i < cols; i++) { + int pos = i * key + j; + if (pos < len) { + plaintext[index++] = ciphertext[pos]; + } + } + } + + plaintext[index] = '\0'; + return plaintext; +} diff --git a/lib/FlipCrypt/ciphers/scytale.h b/lib/FlipCrypt/ciphers/scytale.h new file mode 100644 index 0000000..f77f0db --- /dev/null +++ b/lib/FlipCrypt/ciphers/scytale.h @@ -0,0 +1,2 @@ +char* scytale_encrypt(const char* plaintext, int32_t key); +char* scytale_decrypt(const char* ciphertext, int key); \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/vigenere.c b/lib/FlipCrypt/ciphers/vigenere.c new file mode 100644 index 0000000..10211d5 --- /dev/null +++ b/lib/FlipCrypt/ciphers/vigenere.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include "vigenere.h" + +char* vigenere_encrypt(const char* text, const char* key) { + int len = strlen(text); + int key_len = strlen(key); + int key_index = 0; + + char* ciphertext = malloc(len + 1); + if (!ciphertext) return "memory allocation failed, try again"; + + for (int i = 0; i < len; i++) { + char c = text[i]; + + if (isalpha(c)) { + char k = key[key_index % key_len]; + int shift = tolower(k) - 'a'; + + if (isupper(c)) { + ciphertext[i] = ((c - 'A' + shift) % 26) + 'A'; + } else { + ciphertext[i] = ((c - 'a' + shift) % 26) + 'a'; + } + + key_index++; + } else { + ciphertext[i] = c; + } + } + + ciphertext[len] = '\0'; + return ciphertext; +} + +char* vigenere_decrypt(const char* text, const char* key) { + int len = strlen(text); + int key_len = strlen(key); + int key_index = 0; + + char* plaintext = malloc(len + 1); + if (!plaintext) return "memory allocation failed, try again"; + + for (int i = 0; i < len; i++) { + char c = text[i]; + + if (isalpha(c)) { + char k = key[key_index % key_len]; + int shift = tolower(k) - 'a'; + + if (isupper(c)) { + plaintext[i] = ((c - 'A' - shift + 26) % 26) + 'A'; + } else { + plaintext[i] = ((c - 'a' - shift + 26) % 26) + 'a'; + } + + key_index++; + } else { + plaintext[i] = c; + } + } + + plaintext[len] = '\0'; + return plaintext; +} \ No newline at end of file diff --git a/lib/FlipCrypt/ciphers/vigenere.h b/lib/FlipCrypt/ciphers/vigenere.h new file mode 100644 index 0000000..668bcb3 --- /dev/null +++ b/lib/FlipCrypt/ciphers/vigenere.h @@ -0,0 +1,2 @@ +char* vigenere_encrypt(const char* text, const char* key); +char* vigenere_decrypt(const char* text, const char* key); \ No newline at end of file diff --git a/lib/FlipCrypt/docs/CHANGELOG.md b/lib/FlipCrypt/docs/CHANGELOG.md new file mode 100644 index 0000000..d0faf6f --- /dev/null +++ b/lib/FlipCrypt/docs/CHANGELOG.md @@ -0,0 +1,26 @@ +## V0.6 +- Decreased RAM usage. +- Add Porta cipher. + +## V0.5 +- Updated widget text, fixed some typos. + +## V0.4 +- Fixed NFC emulation issue. + +## V0.3 +- Added ROT-13 cipher. +- Added MD2 hash. +- Large under the hood improvements regarding storage. + +## V0.2 +- Fixed typos. +- Added menu to select between tool categories on main menu. +- Added NFC emulation, QR code generation, and save to file options for output. +- Added AES-128, Affine, Beaufort, Caesar, Polybius, Scytale, and RC4 ciphers. +- Added MurmurHash3, FNV-1A, SipHash, SHA 224, SHA 384, SHA 512, and XX hashes. +- Added Base32, Base58, and Base64 encoding. +- Updated screenshots. + +## V0.1 +- Initial release. diff --git a/lib/FlipCrypt/encoders/base32.c b/lib/FlipCrypt/encoders/base32.c new file mode 100644 index 0000000..542452e --- /dev/null +++ b/lib/FlipCrypt/encoders/base32.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +#include "base32.h" + +char* base32_encode(const uint8_t* data, size_t input_length) { + const char* base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + size_t output_length = (input_length * 8 + 4) / 5; // ceil(bits / 5) + size_t encoded_length = (output_length + 7) / 8 * 8; // pad to full 8 chars + char* encoded = (char*)malloc(encoded_length + 1); // +1 for null terminator + + if (!encoded) return "memory allocation failed, try again"; + + size_t buffer = 0; + int bits_left = 0; + size_t index = 0; + + for (size_t i = 0; i < input_length; i++) { + buffer <<= 8; + buffer |= data[i]; + bits_left += 8; + + while (bits_left >= 5) { + encoded[index++] = base32_chars[(buffer >> (bits_left - 5)) & 0x1F]; + bits_left -= 5; + } + } + + if (bits_left > 0) { + buffer <<= (5 - bits_left); + encoded[index++] = base32_chars[buffer & 0x1F]; + } + + while (index < encoded_length) { + encoded[index++] = '='; + } + + encoded[index] = '\0'; + return encoded; +} + +int base32_char_to_value(char c) { + if ('A' <= c && c <= 'Z') return c - 'A'; + if ('2' <= c && c <= '7') return c - '2' + 26; + return -1; // Invalid character +} + +uint8_t* base32_decode(const char* input, size_t* output_length) { + size_t input_length = strlen(input); + size_t buffer = 0; + int bits_left = 0; + size_t index = 0; + + // Allocate maximum possible decoded size + uint8_t* decoded = (uint8_t*)malloc((input_length * 5 / 8) + 1); + if (!decoded) return (uint8_t *)"memory allocation failed, try again"; + + for (size_t i = 0; i < input_length; i++) { + char c = toupper(input[i]); + + if (c == '=') break; // Padding, stop reading + int val = base32_char_to_value(c); + if (val == -1) continue; // Skip invalid characters + + buffer <<= 5; + buffer |= val; + bits_left += 5; + + if (bits_left >= 8) { + decoded[index++] = (buffer >> (bits_left - 8)) & 0xFF; + bits_left -= 8; + } + } + + if (output_length) { + *output_length = index; + } + + return decoded; +} diff --git a/lib/FlipCrypt/encoders/base32.h b/lib/FlipCrypt/encoders/base32.h new file mode 100644 index 0000000..a2af9aa --- /dev/null +++ b/lib/FlipCrypt/encoders/base32.h @@ -0,0 +1,2 @@ +char* base32_encode(const uint8_t* data, size_t input_length); +uint8_t* base32_decode(const char* input, size_t* output_length); \ No newline at end of file diff --git a/lib/FlipCrypt/encoders/base58.c b/lib/FlipCrypt/encoders/base58.c new file mode 100644 index 0000000..519b401 --- /dev/null +++ b/lib/FlipCrypt/encoders/base58.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +#include "base58.h" + +static const char* BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +char* base58_encode(const char* input) { + size_t len = strlen(input); + const uint8_t* bytes = (const uint8_t*)input; + + size_t result_size = len * 2; + char* result = calloc(result_size + 1, 1); + if (!result) return "memory allocation failed, try again"; + + uint8_t* temp = calloc(len * 2, 1); + if (!temp) { + free(result); + return NULL; + } + + size_t i, high = 0, zcount = 0; + while (zcount < len && bytes[zcount] == 0) { + ++zcount; + } + + memcpy(temp, bytes, len); + + while (len > 0 && high < result_size) { + int carry = 0; + size_t newlen = 0; + + for (i = 0; i < len; i++) { + carry = carry * 256 + temp[i]; + if (newlen || carry / 58) { + temp[newlen++] = carry / 58; + } + carry %= 58; + } + + result[high++] = BASE58_ALPHABET[carry]; + len = newlen; + } + + while (zcount--) { + result[high++] = BASE58_ALPHABET[0]; + } + + // Reverse the result + for (i = 0; i < high / 2; i++) { + char tmp = result[i]; + result[i] = result[high - 1 - i]; + result[high - 1 - i] = tmp; + } + + result[high] = '\0'; + free(temp); + return result; +} + +int base58_char_value(char c) { + const char* p = strchr(BASE58_ALPHABET, c); + return (p ? (int)(p - BASE58_ALPHABET) : -1); +} + +char* base58_decode(const char* input) { + size_t len = strlen(input); + uint8_t* temp = calloc(len, 1); + if (!temp) return "memory allocation failed, try again"; + + size_t i, j, zcount = 0, high = 0; + + while (zcount < len && input[zcount] == BASE58_ALPHABET[0]) { + ++zcount; + } + + for (i = 0; i < len; i++) { + int carry = base58_char_value(input[i]); + if (carry < 0) { + free(temp); + return "memory allocation failed, try again"; + } + + for (j = 0; j < high; j++) { + carry += 58 * temp[j]; + temp[j] = carry % 256; + carry /= 256; + } + + while (carry) { + temp[high++] = carry % 256; + carry /= 256; + } + } + + size_t total_len = zcount + high; + char* result = calloc(total_len + 1, 1); + if (!result) { + free(temp); + return "memory allocation failed, try again"; + } + + for (i = 0; i < zcount; i++) { + result[i] = 0x00; + } + + for (i = 0; i < high; i++) { + result[zcount + i] = temp[high - 1 - i]; + } + + result[total_len] = '\0'; + free(temp); + return result; +} diff --git a/lib/FlipCrypt/encoders/base58.h b/lib/FlipCrypt/encoders/base58.h new file mode 100644 index 0000000..281d9e0 --- /dev/null +++ b/lib/FlipCrypt/encoders/base58.h @@ -0,0 +1,2 @@ +char* base58_encode(const char* input); +char* base58_decode(const char* input); \ No newline at end of file diff --git a/lib/FlipCrypt/encoders/base64.c b/lib/FlipCrypt/encoders/base64.c new file mode 100644 index 0000000..7779507 --- /dev/null +++ b/lib/FlipCrypt/encoders/base64.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +#include "base64.h" + +static const char base64_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static int is_base64(unsigned char c) { + return (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '+') || (c == '/'); +} + +char* base64_encode(const unsigned char* input, size_t len) { + char* output; + int i = 0, j = 0; + unsigned char array3[3], array4[4]; + + output = (char*)malloc(((len + 2) / 3) * 4 + 1); + if (!output) return "memory allocation failed, try again"; + + size_t out_index = 0; + while (len--) { + array3[i++] = *(input++); + if (i == 3) { + array4[0] = (array3[0] & 0xfc) >> 2; + array4[1] = ((array3[0] & 0x03) << 4) + ((array3[1] & 0xf0) >> 4); + array4[2] = ((array3[1] & 0x0f) << 2) + ((array3[2] & 0xc0) >> 6); + array4[3] = array3[2] & 0x3f; + + for (i = 0; i < 4; i++) + output[out_index++] = base64_chars[array4[i]]; + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) + array3[j] = '\0'; + + array4[0] = (array3[0] & 0xfc) >> 2; + array4[1] = ((array3[0] & 0x03) << 4) + ((array3[1] & 0xf0) >> 4); + array4[2] = ((array3[1] & 0x0f) << 2) + ((array3[2] & 0xc0) >> 6); + array4[3] = array3[2] & 0x3f; + + for (j = 0; j < i + 1; j++) + output[out_index++] = base64_chars[array4[j]]; + + while (i++ < 3) + output[out_index++] = '='; + } + + output[out_index] = '\0'; + return output; +} + +unsigned char* base64_decode(const char* input, size_t* out_len) { + int len = strlen(input); + int i = 0, j = 0, in = 0; + unsigned char array4[4], array3[3]; + unsigned char* output = (unsigned char*)malloc(len * 3 / 4 + 1); + if (!output) return (uint8_t *)"memory allocation failed, try again"; + + size_t out_index = 0; + while (len-- && (input[in] != '=') && is_base64(input[in])) { + array4[i++] = input[in++]; + if (i == 4) { + for (i = 0; i < 4; i++) + array4[i] = strchr(base64_chars, array4[i]) - base64_chars; + + array3[0] = (array4[0] << 2) + ((array4[1] & 0x30) >> 4); + array3[1] = ((array4[1] & 0xf) << 4) + ((array4[2] & 0x3c) >> 2); + array3[2] = ((array4[2] & 0x3) << 6) + array4[3]; + + for (i = 0; i < 3; i++) + output[out_index++] = array3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) + array4[j] = 0; + + for (j = 0; j < 4; j++) + array4[j] = strchr(base64_chars, array4[j]) - base64_chars; + + array3[0] = (array4[0] << 2) + ((array4[1] & 0x30) >> 4); + array3[1] = ((array4[1] & 0xf) << 4) + ((array4[2] & 0x3c) >> 2); + array3[2] = ((array4[2] & 0x3) << 6) + array4[3]; + + for (j = 0; j < i - 1; j++) + output[out_index++] = array3[j]; + } + + output[out_index] = '\0'; + if (out_len) + *out_len = out_index; + return output; +} \ No newline at end of file diff --git a/lib/FlipCrypt/encoders/base64.h b/lib/FlipCrypt/encoders/base64.h new file mode 100644 index 0000000..735884f --- /dev/null +++ b/lib/FlipCrypt/encoders/base64.h @@ -0,0 +1,4 @@ +#include + +char* base64_encode(const unsigned char* input, size_t len); +unsigned char* base64_decode(const char* input, size_t* out_len); \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/blake2.c b/lib/FlipCrypt/hashes/blake2.c new file mode 100644 index 0000000..723d04a --- /dev/null +++ b/lib/FlipCrypt/hashes/blake2.c @@ -0,0 +1,125 @@ +#include "blake2.h" +#include +#include + +#define ROTR32(x, y) (((x) >> (y)) ^ ((x) << (32 - (y)))) +#define B2S_BLOCKBYTES 64 + +static const uint32_t blake2s_iv[8] = { + 0x6A09E667U, 0xBB67AE85U, + 0x3C6EF372U, 0xA54FF53AU, + 0x510E527FU, 0x9B05688CU, + 0x1F83D9ABU, 0x5BE0CD19U +}; + +static const uint8_t sigma[10][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 }, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3 }, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8 }, + { 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13 }, + { 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9 }, + {12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11 }, + {13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10 }, + { 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5 }, + {10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13, 0 } +}; + +static void blake2s_compress(Blake2sContext* ctx, const uint8_t block[64]) { + uint32_t m[16]; + uint32_t v[16]; + + for (int i = 0; i < 16; ++i) { + m[i] = ((uint32_t)block[i * 4 + 0] << 0) | + ((uint32_t)block[i * 4 + 1] << 8) | + ((uint32_t)block[i * 4 + 2] << 16) | + ((uint32_t)block[i * 4 + 3] << 24); + } + + for (int i = 0; i < 8; ++i) v[i] = ctx->h[i]; + for (int i = 0; i < 8; ++i) v[i + 8] = blake2s_iv[i]; + + v[12] ^= ctx->t[0]; + v[13] ^= ctx->t[1]; + v[14] ^= ctx->f[0]; + v[15] ^= ctx->f[1]; + + #define G(a,b,c,d,x,y) \ + a = a + b + x; d = ROTR32(d ^ a, 16); \ + c = c + d; b = ROTR32(b ^ c, 12); \ + a = a + b + y; d = ROTR32(d ^ a, 8); \ + c = c + d; b = ROTR32(b ^ c, 7); + + for (int i = 0; i < 10; ++i) { + const uint8_t* s = sigma[i]; + G(v[0], v[4], v[8], v[12], m[s[0]], m[s[1]]); + G(v[1], v[5], v[9], v[13], m[s[2]], m[s[3]]); + G(v[2], v[6], v[10], v[14], m[s[4]], m[s[5]]); + G(v[3], v[7], v[11], v[15], m[s[6]], m[s[7]]); + G(v[0], v[5], v[10], v[15], m[s[8]], m[s[9]]); + G(v[1], v[6], v[11], v[12], m[s[10]], m[s[11]]); + G(v[2], v[7], v[8], v[13], m[s[12]], m[s[13]]); + G(v[3], v[4], v[9], v[14], m[s[14]], m[s[15]]); + } + + #undef G + + for (int i = 0; i < 8; ++i) { + ctx->h[i] ^= v[i] ^ v[i + 8]; + } +} + +void blake2s_init(Blake2sContext* ctx) { + memset(ctx, 0, sizeof(Blake2sContext)); + ctx->outlen = BLAKE2S_OUTLEN; + + for (int i = 0; i < 8; ++i) + ctx->h[i] = blake2s_iv[i]; + ctx->h[0] ^= 0x01010000 | ctx->outlen; // parameter block +} + +void blake2s_update(Blake2sContext* ctx, const uint8_t* in, size_t inlen) { + while (inlen > 0) { + size_t left = ctx->buflen; + size_t fill = 64 - left; + + if (inlen > fill) { + memcpy(ctx->buf + left, in, fill); + ctx->t[0] += 64; + if (ctx->t[0] < 64) ctx->t[1]++; + blake2s_compress(ctx, ctx->buf); + ctx->buflen = 0; + in += fill; + inlen -= fill; + } else { + memcpy(ctx->buf + left, in, inlen); + ctx->buflen += inlen; + return; + } + } +} + +void blake2s_finalize(Blake2sContext* ctx, uint8_t* out) { + ctx->t[0] += ctx->buflen; + if (ctx->t[0] < ctx->buflen) ctx->t[1]++; + ctx->f[0] = 0xFFFFFFFF; + + while (ctx->buflen < 64) + ctx->buf[ctx->buflen++] = 0; + + blake2s_compress(ctx, ctx->buf); + + for (int i = 0; i < 8; ++i) { + out[i * 4 + 0] = (ctx->h[i] >> 0) & 0xFF; + out[i * 4 + 1] = (ctx->h[i] >> 8) & 0xFF; + out[i * 4 + 2] = (ctx->h[i] >> 16) & 0xFF; + out[i * 4 + 3] = (ctx->h[i] >> 24) & 0xFF; + } +} + +void blake2s_to_hex(const uint8_t hash[BLAKE2S_OUTLEN], char* output) { + for (int i = 0; i < BLAKE2S_OUTLEN; ++i) { + snprintf(output + i * 2, 3, "%02x", hash[i]); + } + output[BLAKE2S_OUTLEN * 2] = '\0'; +} \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/blake2.h b/lib/FlipCrypt/hashes/blake2.h new file mode 100644 index 0000000..5d50cb4 --- /dev/null +++ b/lib/FlipCrypt/hashes/blake2.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +#define BLAKE2S_OUTLEN 32 + +typedef struct { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[64]; + size_t buflen; + size_t outlen; +} Blake2sContext; + +void blake2s_init(Blake2sContext* ctx); +void blake2s_update(Blake2sContext* ctx, const uint8_t* input, size_t inlen); +void blake2s_finalize(Blake2sContext* ctx, uint8_t* out); +void blake2s_to_hex(const uint8_t hash[BLAKE2S_OUTLEN], char* output); \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/fnv.c b/lib/FlipCrypt/hashes/fnv.c new file mode 100644 index 0000000..c82b8ae --- /dev/null +++ b/lib/FlipCrypt/hashes/fnv.c @@ -0,0 +1,406 @@ +/* + * fnv - Fowler/Noll/Vo- hash code + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + * + *** + * + * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the + * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str(). + * + * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the + * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str(). + * + * To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the + * Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str(). + * + * To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the + * Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str(). + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +#include +#include "fnv.h" + +/* + * 32 bit magic FNV-0 and FNV-1 prime + */ +#define FNV_32_PRIME ((Fnv32_t)0x01000193) + + +/* + * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer + * + * input: + * buf - start of buffer to hash + * len - length of buffer in octets + * hval - previous hash value or 0 if first call + * + * returns: + * 32 bit hash as a static hash type + * + * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval + * argument on the first call to either fnv_32_buf() or fnv_32_str(). + * + * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval + * argument on the first call to either fnv_32_buf() or fnv_32_str(). + */ +Fnv32_t +fnv_32_buf(void *buf, size_t len, Fnv32_t hval) +{ + unsigned char *bp = (unsigned char *)buf; /* start of buffer */ + unsigned char *be = bp + len; /* beyond end of buffer */ + + /* + * FNV-1 hash each octet in the buffer + */ + while (bp < be) { + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_32_PRIME; +#else + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); +#endif + + /* xor the bottom with the current octet */ + hval ^= (Fnv32_t)*bp++; + } + + /* return our new hash value */ + return hval; +} + + +/* + * fnv_32_str - perform a 32 bit Fowler/Noll/Vo hash on a string + * + * input: + * str - string to hash + * hval - previous hash value or 0 if first call + * + * returns: + * 32 bit hash as a static hash type + * + * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval + * argument on the first call to either fnv_32_buf() or fnv_32_str(). + * + * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval + * argument on the first call to either fnv_32_buf() or fnv_32_str(). + */ +Fnv32_t +fnv_32_str(char *str, Fnv32_t hval) +{ + unsigned char *s = (unsigned char *)str; /* unsigned string */ + + /* + * FNV-1 hash each octet in the buffer + */ + while (*s) { + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_32_PRIME; +#else + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); +#endif + + /* xor the bottom with the current octet */ + hval ^= (Fnv32_t)*s++; + } + + /* return our new hash value */ + return hval; +} + + +/* + * fnv_32a_buf - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * + * input: + * buf - start of buffer to hash + * len - length of buffer in octets + * hval - previous hash value or 0 if first call + * + * returns: + * 32 bit hash as a static hash type + * + * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the + * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str(). + */ +Fnv32_t +fnv_32a_buf(void *buf, size_t len, Fnv32_t hval) +{ + unsigned char *bp = (unsigned char *)buf; /* start of buffer */ + unsigned char *be = bp + len; /* beyond end of buffer */ + + /* + * FNV-1a hash each octet in the buffer + */ + while (bp < be) { + + /* xor the bottom with the current octet */ + hval ^= (Fnv32_t)*bp++; + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_32_PRIME; +#else + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); +#endif + } + + /* return our new hash value */ + return hval; +} + + +/* + * fnv_32a_str - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a string + * + * input: + * str - string to hash + * hval - previous hash value or 0 if first call + * + * returns: + * 32 bit hash as a static hash type + * + * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the + * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str(). + */ +Fnv32_t +fnv_32a_str(char *str, Fnv32_t hval) +{ + unsigned char *s = (unsigned char *)str; /* unsigned string */ + + /* + * FNV-1a hash each octet in the buffer + */ + while (*s) { + + /* xor the bottom with the current octet */ + hval ^= (Fnv32_t)*s++; + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_32_PRIME; +#else + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); +#endif + } + + /* return our new hash value */ + return hval; +} + +/* + * 64 bit magic FNV-0 and FNV-1 prime + */ +#define FNV_64_PRIME ((Fnv64_t)0x100000001b3ULL) + +/* + * fnv_64_buf - perform a 64 bit Fowler/Noll/Vo hash on a buffer + * + * input: + * buf - start of buffer to hash + * len - length of buffer in octets + * hval - previous hash value or 0 if first call + * + * returns: + * 64 bit hash as a static hash type + * + * NOTE: To use the 64 bit FNV-0 historic hash, use FNV0_64_INIT as the hval + * argument on the first call to either fnv_64_buf() or fnv_64_str(). + * + * NOTE: To use the recommended 64 bit FNV-1 hash, use FNV1_64_INIT as the hval + * argument on the first call to either fnv_64_buf() or fnv_64_str(). + */ +Fnv64_t +fnv_64_buf(void *buf, size_t len, Fnv64_t hval) +{ + unsigned char *bp = (unsigned char *)buf; /* start of buffer */ + unsigned char *be = bp + len; /* beyond end of buffer */ + + /* + * FNV-1 hash each octet of the buffer + */ + while (bp < be) { + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_64_PRIME; +#else /* NO_FNV_GCC_OPTIMIZATION */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); +#endif /* NO_FNV_GCC_OPTIMIZATION */ + + /* xor the bottom with the current octet */ + hval ^= (Fnv64_t)*bp++; + } + + /* return our new hash value */ + return hval; +} + + +/* + * fnv_64_str - perform a 64 bit Fowler/Noll/Vo hash on a buffer + * + * input: + * buf - start of buffer to hash + * hval - previous hash value or 0 if first call + * + * returns: + * 64 bit hash as a static hash type + * + * NOTE: To use the 64 bit FNV-0 historic hash, use FNV0_64_INIT as the hval + * argument on the first call to either fnv_64_buf() or fnv_64_str(). + * + * NOTE: To use the recommended 64 bit FNV-1 hash, use FNV1_64_INIT as the hval + * argument on the first call to either fnv_64_buf() or fnv_64_str(). + */ +Fnv64_t +fnv_64_str(char *str, Fnv64_t hval) +{ + unsigned char *s = (unsigned char *)str; /* unsigned string */ + + /* + * FNV-1 hash each octet of the string + */ + while (*s) { + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_64_PRIME; +#else /* NO_FNV_GCC_OPTIMIZATION */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); +#endif /* NO_FNV_GCC_OPTIMIZATION */ + + /* xor the bottom with the current octet */ + hval ^= (Fnv64_t)*s++; + } + + /* return our new hash value */ + return hval; +} + + +/* + * fnv_64a_buf - perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * + * input: + * buf - start of buffer to hash + * len - length of buffer in octets + * hval - previous hash value or 0 if first call + * + * returns: + * 64 bit hash as a static hash type + * + * NOTE: To use the recommended 64 bit FNV-1a hash, use FNV1A_64_INIT as the + * hval arg on the first call to either fnv_64a_buf() or fnv_64a_str(). + */ +Fnv64_t +fnv_64a_buf(void *buf, size_t len, Fnv64_t hval) +{ + unsigned char *bp = (unsigned char *)buf; /* start of buffer */ + unsigned char *be = bp + len; /* beyond end of buffer */ + + /* + * FNV-1a hash each octet of the buffer + */ + while (bp < be) { + + /* xor the bottom with the current octet */ + hval ^= (Fnv64_t)*bp++; + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_64_PRIME; +#else /* NO_FNV_GCC_OPTIMIZATION */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); +#endif /* NO_FNV_GCC_OPTIMIZATION */ + } + + /* return our new hash value */ + return hval; +} + + +/* + * fnv_64a_str - perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * + * input: + * buf - start of buffer to hash + * hval - previous hash value or 0 if first call + * + * returns: + * 64 bit hash as a static hash type + * + * NOTE: To use the recommended 64 bit FNV-1a hash, use FNV1A_64_INIT as the + * hval arg on the first call to either fnv_64a_buf() or fnv_64a_str(). + */ +Fnv64_t +fnv_64a_str(char *str, Fnv64_t hval) +{ + unsigned char *s = (unsigned char *)str; /* unsigned string */ + + /* + * FNV-1a hash each octet of the string + */ + while (*s) { + + /* xor the bottom with the current octet */ + hval ^= (Fnv64_t)*s++; + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_64_PRIME; +#else /* NO_FNV_GCC_OPTIMIZATION */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); +#endif /* NO_FNV_GCC_OPTIMIZATION */ + } + + /* return our new hash value */ + return hval; +} + diff --git a/lib/FlipCrypt/hashes/fnv.h b/lib/FlipCrypt/hashes/fnv.h new file mode 100644 index 0000000..113e7ae --- /dev/null +++ b/lib/FlipCrypt/hashes/fnv.h @@ -0,0 +1,99 @@ +/* + * fnv - Fowler/Noll/Vo- hash code + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + * + *** + * + * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the + * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str(). + * + * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the + * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str(). + * + * To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the + * Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str(). + * + * To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the + * Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str(). + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +#if !defined(__FNV_H__) +#define __FNV_H__ + +#include + +#define FNV_VERSION "5.0.2" /* @(#) FNV Version */ + + +typedef u_int32_t Fnv32_t; + +/* + * 32 bit FNV-1 and FNV-1a non-zero initial basis + * NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition. + */ +#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5) +#define FNV1_32A_INIT FNV1_32_INIT + +extern Fnv32_t fnv_32_buf(void *buf, size_t len, Fnv32_t hashval); +extern Fnv32_t fnv_32_str(char *buf, Fnv32_t hashval); + +extern Fnv32_t fnv_32a_buf(void *buf, size_t len, Fnv32_t hashval); +extern Fnv32_t fnv_32a_str(char *buf, Fnv32_t hashval); + + +typedef u_int64_t Fnv64_t; + +/* + * 64 bit FNV-1 non-zero initial basis + * NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition. + */ +#define FNV1_64_INIT ((Fnv64_t)0xcbf29ce484222325ULL) +#define FNV1A_64_INIT FNV1_64_INIT + +extern Fnv64_t fnv_64_buf(void *buf, size_t len, Fnv64_t hashval); +extern Fnv64_t fnv_64_str(char *buf, Fnv64_t hashval); + +extern Fnv64_t fnv_64a_buf(void *buf, size_t len, Fnv64_t hashval); +extern Fnv64_t fnv_64a_str(char *buf, Fnv64_t hashval); + +#endif /* __FNV_H__ */ + diff --git a/lib/FlipCrypt/hashes/md2.c b/lib/FlipCrypt/hashes/md2.c new file mode 100644 index 0000000..de6bb6b --- /dev/null +++ b/lib/FlipCrypt/hashes/md2.c @@ -0,0 +1,104 @@ +/********************************************************************* +* Filename: md2.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the MD2 hashing algorithm. + Algorithm specification can be found here: + * http://tools.ietf.org/html/rfc1319 . + Input is little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "md2.h" + +/**************************** VARIABLES *****************************/ +static const BYTE s[256] = { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void md2_transform(MD2_CTX *ctx, BYTE data[]) +{ + int j,k,t; + + //memcpy(&ctx->state[16], data); + for (j=0; j < 16; ++j) { + ctx->state[j + 16] = data[j]; + ctx->state[j + 32] = (ctx->state[j+16] ^ ctx->state[j]); + } + + t = 0; + for (j = 0; j < 18; ++j) { + for (k = 0; k < 48; ++k) { + ctx->state[k] ^= s[t]; + t = ctx->state[k]; + } + t = (t+j) & 0xFF; + } + + t = ctx->checksum[15]; + for (j=0; j < 16; ++j) { + ctx->checksum[j] ^= s[data[j] ^ t]; + t = ctx->checksum[j]; + } +} + +void md2_init(MD2_CTX *ctx) +{ + int i; + + for (i=0; i < 48; ++i) + ctx->state[i] = 0; + for (i=0; i < 16; ++i) + ctx->checksum[i] = 0; + ctx->len = 0; +} + +void md2_update(MD2_CTX *ctx, const BYTE data[], size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->len] = data[i]; + ctx->len++; + if (ctx->len == MD2_BLOCK_SIZE) { + md2_transform(ctx, ctx->data); + ctx->len = 0; + } + } +} + +void md2_final(MD2_CTX *ctx, BYTE hash[]) +{ + int to_pad; + + to_pad = MD2_BLOCK_SIZE - ctx->len; + + while (ctx->len < MD2_BLOCK_SIZE) + ctx->data[ctx->len++] = to_pad; + + md2_transform(ctx, ctx->data); + md2_transform(ctx, ctx->checksum); + + memcpy(hash, ctx->state, MD2_BLOCK_SIZE); +} diff --git a/lib/FlipCrypt/hashes/md2.h b/lib/FlipCrypt/hashes/md2.h new file mode 100644 index 0000000..97706af --- /dev/null +++ b/lib/FlipCrypt/hashes/md2.h @@ -0,0 +1,33 @@ +/********************************************************************* +* Filename: md2.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding MD2 implementation. +*********************************************************************/ + +#ifndef MD2_H +#define MD2_H + +/*************************** HEADER FILES ***************************/ +#include + +/****************************** MACROS ******************************/ +#define MD2_BLOCK_SIZE 16 + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte + +typedef struct { + BYTE data[16]; + BYTE state[48]; + BYTE checksum[16]; + int len; +} MD2_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void md2_init(MD2_CTX *ctx); +void md2_update(MD2_CTX *ctx, const BYTE data[], size_t len); +void md2_final(MD2_CTX *ctx, BYTE hash[]); // size of hash must be MD2_BLOCK_SIZE + +#endif // MD2_H diff --git a/lib/FlipCrypt/hashes/md5.c b/lib/FlipCrypt/hashes/md5.c new file mode 100644 index 0000000..1e6ad6c --- /dev/null +++ b/lib/FlipCrypt/hashes/md5.c @@ -0,0 +1,130 @@ +#include +#include "md5.h" +#include +#include +#include + +#define F(x,y,z) ((x & y) | (~x & z)) +#define G(x,y,z) ((x & z) | (y & ~z)) +#define H(x,y,z) (x ^ y ^ z) +#define I(x,y,z) (y ^ (x | ~z)) + +#define LEFTROTATE(x,c) (((x) << (c)) | ((x) >> (32 - (c)))) + +static const uint32_t k[] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +}; + +static const uint32_t r[] = { + 7,12,17,22, 7,12,17,22, 7,12,17,22, 7,12,17,22, + 5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20, + 4,11,16,23, 4,11,16,23, 4,11,16,23, 4,11,16,23, + 6,10,15,21, 6,10,15,21, 6,10,15,21, 6,10,15,21 +}; + +void md5_init(MD5Context* ctx) { + ctx->length = 0; + ctx->h[0] = 0x67452301; + ctx->h[1] = 0xefcdab89; + ctx->h[2] = 0x98badcfe; + ctx->h[3] = 0x10325476; +} + +static void md5_process(MD5Context* ctx, const uint8_t data[64]) { + uint32_t a, b, c, d, f, g, temp, m[16]; + + for (int i = 0; i < 16; ++i) + m[i] = (uint32_t)data[i*4] | ((uint32_t)data[i*4+1] << 8) | + ((uint32_t)data[i*4+2] << 16) | ((uint32_t)data[i*4+3] << 24); + + a = ctx->h[0]; b = ctx->h[1]; c = ctx->h[2]; d = ctx->h[3]; + + for (int i = 0; i < 64; ++i) { + if (i < 16) { + f = F(b,c,d); + g = i; + } else if (i < 32) { + f = G(b,c,d); + g = (5*i + 1) % 16; + } else if (i < 48) { + f = H(b,c,d); + g = (3*i + 5) % 16; + } else { + f = I(b,c,d); + g = (7*i) % 16; + } + + temp = d; + d = c; + c = b; + b = b + LEFTROTATE((a + f + k[i] + m[g]), r[i]); + a = temp; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; +} + +void md5_update(MD5Context* ctx, const uint8_t* data, size_t len) { + size_t index = ctx->length % 64; + ctx->length += len; + + for (size_t i = 0; i < len; ++i) { + ctx->buffer[index++] = data[i]; + if (index == 64) { + md5_process(ctx, ctx->buffer); + index = 0; + } + } +} + +void md5_finalize(MD5Context* ctx, uint8_t hash[16]) { + size_t index = ctx->length % 64; + ctx->buffer[index++] = 0x80; + + if (index > 56) { + while (index < 64) ctx->buffer[index++] = 0x00; + md5_process(ctx, ctx->buffer); + index = 0; + } + + while (index < 56) ctx->buffer[index++] = 0x00; + + uint64_t bits_len = ctx->length * 8; + for (int i = 0; i < 8; ++i) + ctx->buffer[56 + i] = (uint8_t)(bits_len >> (8 * i)); + + md5_process(ctx, ctx->buffer); + + for (int i = 0; i < 4; ++i) { + hash[i*4] = (uint8_t)(ctx->h[i] & 0xFF); + hash[i*4 + 1] = (uint8_t)((ctx->h[i] >> 8) & 0xFF); + hash[i*4 + 2] = (uint8_t)((ctx->h[i] >> 16) & 0xFF); + hash[i*4 + 3] = (uint8_t)((ctx->h[i] >> 24) & 0xFF); + } +} + +void md5_to_hex(const uint8_t hash[16], char* output) { + for (int i = 0; i < 16; ++i) { + snprintf(output + i * 2, 3, "%02x", hash[i]); + } + output[32] = '\0'; +} + diff --git a/lib/FlipCrypt/hashes/md5.h b/lib/FlipCrypt/hashes/md5.h new file mode 100644 index 0000000..a6eae08 --- /dev/null +++ b/lib/FlipCrypt/hashes/md5.h @@ -0,0 +1,19 @@ +#ifndef MD5_H +#define MD5_H + +#include +#include +#include + +typedef struct { + uint32_t h[4]; + uint8_t buffer[64]; + uint64_t length; +} MD5Context; + +void md5_init(MD5Context* ctx); +void md5_update(MD5Context* ctx, const uint8_t* data, size_t len); +void md5_finalize(MD5Context* ctx, uint8_t hash[16]); +void md5_to_hex(const uint8_t hash[16], char* output); + +#endif \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/murmur3.c b/lib/FlipCrypt/hashes/murmur3.c new file mode 100644 index 0000000..59bb9f9 --- /dev/null +++ b/lib/FlipCrypt/hashes/murmur3.c @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +#include +#include +#include "murmur3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +#ifdef __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE inline +#endif + +static FORCE_INLINE uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +static FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +#define getblock(p, i) (p[i]) + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +static FORCE_INLINE uint64_t fmix64 ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +char* MurmurHash3_x86_32(const void* key, int len, uint32_t seed) { + const uint8_t* data = (const uint8_t*)key; + const int nblocks = len / 4; + int i; + + uint32_t h1 = seed; + + uint32_t c1 = 0xcc9e2d51; + uint32_t c2 = 0x1b873593; + + // Body + const uint32_t* blocks = (const uint32_t*)(data); + for (i = 0; i < nblocks; i++) { + uint32_t k1 = getblock(blocks, i); + + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + + // Tail + const uint8_t* tail = (const uint8_t*)(data + nblocks * 4); + + uint32_t k1 = 0; + + switch (len & 3) { + case 3: k1 ^= tail[2] << 16; // fall-through + case 2: k1 ^= tail[1] << 8; // fall-through + case 1: k1 ^= tail[0]; // fall-through + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + h1 ^= k1; + } + + // Finalization + h1 ^= len; + h1 = fmix32(h1); + + char* result = malloc(11); // up to 10 digits + null terminator + if (result) { + snprintf(result, 11, "%lu", h1); + } + + return result; +} \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/murmur3.h b/lib/FlipCrypt/hashes/murmur3.h new file mode 100644 index 0000000..53dc558 --- /dev/null +++ b/lib/FlipCrypt/hashes/murmur3.h @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the +// public domain. The author hereby disclaims copyright to this source +// code. + +#ifndef _MURMURHASH3_H_ +#define _MURMURHASH3_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//----------------------------------------------------------------------------- + +char* MurmurHash3_x86_32(const void* key, int len, uint32_t seed); + +//----------------------------------------------------------------------------- + +#ifdef __cplusplus +} +#endif + +#endif // _MURMURHASH3_H_ diff --git a/lib/FlipCrypt/hashes/sha1.c b/lib/FlipCrypt/hashes/sha1.c new file mode 100644 index 0000000..b882b1b --- /dev/null +++ b/lib/FlipCrypt/hashes/sha1.c @@ -0,0 +1,109 @@ +#include "sha1.h" +#include +#include + +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) + +static void sha1_transform(Sha1Context* ctx, const uint8_t data[]) { + uint32_t a, b, c, d, e, f, k, temp, m[80]; + for(int i = 0, j = 0; i < 16; ++i, j += 4) { + m[i] = (data[j] << 24) | (data[j+1] << 16) | (data[j+2] << 8) | (data[j+3]); + } + + for(int i = 16; i < 80; ++i) { + m[i] = ROTLEFT(m[i-3] ^ m[i-8] ^ m[i-14] ^ m[i-16], 1); + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + + for(int i = 0; i < 80; ++i) { + if (i < 20) { + f = (b & c) | ((~b) & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + temp = ROTLEFT(a, 5) + f + e + k + m[i]; + e = d; + d = c; + c = ROTLEFT(b, 30); + b = a; + a = temp; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; +} + +void sha1_init(Sha1Context* ctx) { + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +void sha1_update(Sha1Context* ctx, const uint8_t* data, size_t len) { + size_t i = ctx->count % 64; + ctx->count += len; + size_t fill = 64 - i; + + if(i && len >= fill) { + memcpy(ctx->buffer + i, data, fill); + sha1_transform(ctx, ctx->buffer); + data += fill; + len -= fill; + i = 0; + } + + while(len >= 64) { + sha1_transform(ctx, data); + data += 64; + len -= 64; + } + + if(len > 0) + memcpy(ctx->buffer + i, data, len); +} + +void sha1_finalize(Sha1Context* ctx, uint8_t hash[20]) { + uint8_t pad[64] = {0x80}; + uint64_t bits = ctx->count * 8; + uint8_t len_pad[8]; + + for(int i = 0; i < 8; i++) + len_pad[7 - i] = bits >> (i * 8); + + size_t pad_len = (ctx->count % 64 < 56) ? 56 - (ctx->count % 64) : 120 - (ctx->count % 64); + sha1_update(ctx, pad, pad_len); + sha1_update(ctx, len_pad, 8); + + for(int i = 0; i < 5; i++) { + hash[i*4] = (ctx->state[i] >> 24) & 0xff; + hash[i*4+1] = (ctx->state[i] >> 16) & 0xff; + hash[i*4+2] = (ctx->state[i] >> 8) & 0xff; + hash[i*4+3] = ctx->state[i] & 0xff; + } +} + +void sha1_to_hex(const uint8_t hash[20], char* output) { + for (int i = 0; i < 20; ++i) { + snprintf(output + i * 2, 3, "%02x", hash[i]); + } + output[40] = '\0'; +} \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/sha1.h b/lib/FlipCrypt/hashes/sha1.h new file mode 100644 index 0000000..7de5f6e --- /dev/null +++ b/lib/FlipCrypt/hashes/sha1.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +typedef struct { + uint32_t state[5]; + uint64_t count; + uint8_t buffer[64]; +} Sha1Context; + +void sha1_init(Sha1Context* ctx); +void sha1_update(Sha1Context* ctx, const uint8_t* data, size_t len); +void sha1_finalize(Sha1Context* ctx, uint8_t hash[20]); +void sha1_to_hex(const uint8_t hash[20], char* output); \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/sha2.c b/lib/FlipCrypt/hashes/sha2.c new file mode 100644 index 0000000..64eeb1a --- /dev/null +++ b/lib/FlipCrypt/hashes/sha2.c @@ -0,0 +1,1131 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * + * Copyright (C) 2005-2023 Olivier Gay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#define UNROLL_LOOPS /* Enable loops unrolling */ +#endif + +#include + +#include "sha2.h" + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof (x) << 3) - n))) +#define ROTL(x, n) ((x << n) | (x >> ((sizeof (x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7)) +#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6)) + +#define UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8) ((x) ); \ + *((str) + 2) = (uint8) ((x) >> 8); \ + *((str) + 1) = (uint8) ((x) >> 16); \ + *((str) + 0) = (uint8) ((x) >> 24); \ +} + +#define PACK32(str, x) \ +{ \ + *(x) = ((uint32) *((str) + 3) ) \ + | ((uint32) *((str) + 2) << 8) \ + | ((uint32) *((str) + 1) << 16) \ + | ((uint32) *((str) + 0) << 24); \ +} + +#define UNPACK64(x, str) \ +{ \ + *((str) + 7) = (uint8) ((x) ); \ + *((str) + 6) = (uint8) ((x) >> 8); \ + *((str) + 5) = (uint8) ((x) >> 16); \ + *((str) + 4) = (uint8) ((x) >> 24); \ + *((str) + 3) = (uint8) ((x) >> 32); \ + *((str) + 2) = (uint8) ((x) >> 40); \ + *((str) + 1) = (uint8) ((x) >> 48); \ + *((str) + 0) = (uint8) ((x) >> 56); \ +} + +#define PACK64(str, x) \ +{ \ + *(x) = ((uint64) *((str) + 7) ) \ + | ((uint64) *((str) + 6) << 8) \ + | ((uint64) *((str) + 5) << 16) \ + | ((uint64) *((str) + 4) << 24) \ + | ((uint64) *((str) + 3) << 32) \ + | ((uint64) *((str) + 2) << 40) \ + | ((uint64) *((str) + 1) << 48) \ + | ((uint64) *((str) + 0) << 56); \ +} + +/* Macros used for loops unrolling */ + +#define SHA256_SCR(i) \ +{ \ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + + SHA256_F3(w[i - 15]) + w[i - 16]; \ +} + +#define SHA512_SCR(i) \ +{ \ + w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \ + + SHA512_F3(w[i - 15]) + w[i - 16]; \ +} + +#define SHA256_EXP(a, b, c, d, e, f, g, h, j) \ +{ \ + t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ + + sha256_k[j] + w[j]; \ + t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ + wv[d] += t1; \ + wv[h] = t1 + t2; \ +} + +#define SHA512_EXP(a, b, c, d, e, f, g ,h, j) \ +{ \ + t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \ + + sha512_k[j] + w[j]; \ + t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \ + wv[d] += t1; \ + wv[h] = t1 + t2; \ +} + +static const uint32 sha224_h0[8] = + {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; + +static const uint32 sha256_h0[8] = + {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +static const uint64 sha384_h0[8] = + {0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL}; + +static const uint64 sha512_h0[8] = + {0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; + +static const uint32 sha256_k[64] = + {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +static const uint64 sha512_k[80] = + {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +/* SHA-2 internal function */ + +static void sha256_transf(sha256_ctx *ctx, const uint8 *message, + uint64 block_nb) +{ + uint32 w[64]; + uint32 wv[8]; + uint32 t1, t2; + const uint8 *sub_block; + uint64 i; + +#ifndef UNROLL_LOOPS + int j; +#endif + + for (i = 0; i < block_nb; i++) { + sub_block = message + (i << 6); + +#ifndef UNROLL_LOOPS + for (j = 0; j < 16; j++) { + PACK32(&sub_block[j << 2], &w[j]); + } + + for (j = 16; j < 64; j++) { + SHA256_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 64; j++) { + t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha256_k[j] + w[j]; + t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } +#else + PACK32(&sub_block[ 0], &w[ 0]); PACK32(&sub_block[ 4], &w[ 1]); + PACK32(&sub_block[ 8], &w[ 2]); PACK32(&sub_block[12], &w[ 3]); + PACK32(&sub_block[16], &w[ 4]); PACK32(&sub_block[20], &w[ 5]); + PACK32(&sub_block[24], &w[ 6]); PACK32(&sub_block[28], &w[ 7]); + PACK32(&sub_block[32], &w[ 8]); PACK32(&sub_block[36], &w[ 9]); + PACK32(&sub_block[40], &w[10]); PACK32(&sub_block[44], &w[11]); + PACK32(&sub_block[48], &w[12]); PACK32(&sub_block[52], &w[13]); + PACK32(&sub_block[56], &w[14]); PACK32(&sub_block[60], &w[15]); + + SHA256_SCR(16); SHA256_SCR(17); SHA256_SCR(18); SHA256_SCR(19); + SHA256_SCR(20); SHA256_SCR(21); SHA256_SCR(22); SHA256_SCR(23); + SHA256_SCR(24); SHA256_SCR(25); SHA256_SCR(26); SHA256_SCR(27); + SHA256_SCR(28); SHA256_SCR(29); SHA256_SCR(30); SHA256_SCR(31); + SHA256_SCR(32); SHA256_SCR(33); SHA256_SCR(34); SHA256_SCR(35); + SHA256_SCR(36); SHA256_SCR(37); SHA256_SCR(38); SHA256_SCR(39); + SHA256_SCR(40); SHA256_SCR(41); SHA256_SCR(42); SHA256_SCR(43); + SHA256_SCR(44); SHA256_SCR(45); SHA256_SCR(46); SHA256_SCR(47); + SHA256_SCR(48); SHA256_SCR(49); SHA256_SCR(50); SHA256_SCR(51); + SHA256_SCR(52); SHA256_SCR(53); SHA256_SCR(54); SHA256_SCR(55); + SHA256_SCR(56); SHA256_SCR(57); SHA256_SCR(58); SHA256_SCR(59); + SHA256_SCR(60); SHA256_SCR(61); SHA256_SCR(62); SHA256_SCR(63); + + wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; + wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; + wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; + wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; + + SHA256_EXP(0,1,2,3,4,5,6,7, 0); SHA256_EXP(7,0,1,2,3,4,5,6, 1); + SHA256_EXP(6,7,0,1,2,3,4,5, 2); SHA256_EXP(5,6,7,0,1,2,3,4, 3); + SHA256_EXP(4,5,6,7,0,1,2,3, 4); SHA256_EXP(3,4,5,6,7,0,1,2, 5); + SHA256_EXP(2,3,4,5,6,7,0,1, 6); SHA256_EXP(1,2,3,4,5,6,7,0, 7); + SHA256_EXP(0,1,2,3,4,5,6,7, 8); SHA256_EXP(7,0,1,2,3,4,5,6, 9); + SHA256_EXP(6,7,0,1,2,3,4,5,10); SHA256_EXP(5,6,7,0,1,2,3,4,11); + SHA256_EXP(4,5,6,7,0,1,2,3,12); SHA256_EXP(3,4,5,6,7,0,1,2,13); + SHA256_EXP(2,3,4,5,6,7,0,1,14); SHA256_EXP(1,2,3,4,5,6,7,0,15); + SHA256_EXP(0,1,2,3,4,5,6,7,16); SHA256_EXP(7,0,1,2,3,4,5,6,17); + SHA256_EXP(6,7,0,1,2,3,4,5,18); SHA256_EXP(5,6,7,0,1,2,3,4,19); + SHA256_EXP(4,5,6,7,0,1,2,3,20); SHA256_EXP(3,4,5,6,7,0,1,2,21); + SHA256_EXP(2,3,4,5,6,7,0,1,22); SHA256_EXP(1,2,3,4,5,6,7,0,23); + SHA256_EXP(0,1,2,3,4,5,6,7,24); SHA256_EXP(7,0,1,2,3,4,5,6,25); + SHA256_EXP(6,7,0,1,2,3,4,5,26); SHA256_EXP(5,6,7,0,1,2,3,4,27); + SHA256_EXP(4,5,6,7,0,1,2,3,28); SHA256_EXP(3,4,5,6,7,0,1,2,29); + SHA256_EXP(2,3,4,5,6,7,0,1,30); SHA256_EXP(1,2,3,4,5,6,7,0,31); + SHA256_EXP(0,1,2,3,4,5,6,7,32); SHA256_EXP(7,0,1,2,3,4,5,6,33); + SHA256_EXP(6,7,0,1,2,3,4,5,34); SHA256_EXP(5,6,7,0,1,2,3,4,35); + SHA256_EXP(4,5,6,7,0,1,2,3,36); SHA256_EXP(3,4,5,6,7,0,1,2,37); + SHA256_EXP(2,3,4,5,6,7,0,1,38); SHA256_EXP(1,2,3,4,5,6,7,0,39); + SHA256_EXP(0,1,2,3,4,5,6,7,40); SHA256_EXP(7,0,1,2,3,4,5,6,41); + SHA256_EXP(6,7,0,1,2,3,4,5,42); SHA256_EXP(5,6,7,0,1,2,3,4,43); + SHA256_EXP(4,5,6,7,0,1,2,3,44); SHA256_EXP(3,4,5,6,7,0,1,2,45); + SHA256_EXP(2,3,4,5,6,7,0,1,46); SHA256_EXP(1,2,3,4,5,6,7,0,47); + SHA256_EXP(0,1,2,3,4,5,6,7,48); SHA256_EXP(7,0,1,2,3,4,5,6,49); + SHA256_EXP(6,7,0,1,2,3,4,5,50); SHA256_EXP(5,6,7,0,1,2,3,4,51); + SHA256_EXP(4,5,6,7,0,1,2,3,52); SHA256_EXP(3,4,5,6,7,0,1,2,53); + SHA256_EXP(2,3,4,5,6,7,0,1,54); SHA256_EXP(1,2,3,4,5,6,7,0,55); + SHA256_EXP(0,1,2,3,4,5,6,7,56); SHA256_EXP(7,0,1,2,3,4,5,6,57); + SHA256_EXP(6,7,0,1,2,3,4,5,58); SHA256_EXP(5,6,7,0,1,2,3,4,59); + SHA256_EXP(4,5,6,7,0,1,2,3,60); SHA256_EXP(3,4,5,6,7,0,1,2,61); + SHA256_EXP(2,3,4,5,6,7,0,1,62); SHA256_EXP(1,2,3,4,5,6,7,0,63); + + ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; + ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; + ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; + ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; +#endif /* !UNROLL_LOOPS */ + } +} + +static void sha512_transf(sha512_ctx *ctx, const uint8 *message, + uint64 block_nb) +{ + uint64 w[80]; + uint64 wv[8]; + uint64 t1, t2; + const uint8 *sub_block; + uint64 i; + int j; + + for (i = 0; i < block_nb; i++) { + sub_block = message + (i << 7); + +#ifndef UNROLL_LOOPS + for (j = 0; j < 16; j++) { + PACK64(&sub_block[j << 3], &w[j]); + } + + for (j = 16; j < 80; j++) { + SHA512_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 80; j++) { + t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha512_k[j] + w[j]; + t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } +#else + PACK64(&sub_block[ 0], &w[ 0]); PACK64(&sub_block[ 8], &w[ 1]); + PACK64(&sub_block[ 16], &w[ 2]); PACK64(&sub_block[ 24], &w[ 3]); + PACK64(&sub_block[ 32], &w[ 4]); PACK64(&sub_block[ 40], &w[ 5]); + PACK64(&sub_block[ 48], &w[ 6]); PACK64(&sub_block[ 56], &w[ 7]); + PACK64(&sub_block[ 64], &w[ 8]); PACK64(&sub_block[ 72], &w[ 9]); + PACK64(&sub_block[ 80], &w[10]); PACK64(&sub_block[ 88], &w[11]); + PACK64(&sub_block[ 96], &w[12]); PACK64(&sub_block[104], &w[13]); + PACK64(&sub_block[112], &w[14]); PACK64(&sub_block[120], &w[15]); + + SHA512_SCR(16); SHA512_SCR(17); SHA512_SCR(18); SHA512_SCR(19); + SHA512_SCR(20); SHA512_SCR(21); SHA512_SCR(22); SHA512_SCR(23); + SHA512_SCR(24); SHA512_SCR(25); SHA512_SCR(26); SHA512_SCR(27); + SHA512_SCR(28); SHA512_SCR(29); SHA512_SCR(30); SHA512_SCR(31); + SHA512_SCR(32); SHA512_SCR(33); SHA512_SCR(34); SHA512_SCR(35); + SHA512_SCR(36); SHA512_SCR(37); SHA512_SCR(38); SHA512_SCR(39); + SHA512_SCR(40); SHA512_SCR(41); SHA512_SCR(42); SHA512_SCR(43); + SHA512_SCR(44); SHA512_SCR(45); SHA512_SCR(46); SHA512_SCR(47); + SHA512_SCR(48); SHA512_SCR(49); SHA512_SCR(50); SHA512_SCR(51); + SHA512_SCR(52); SHA512_SCR(53); SHA512_SCR(54); SHA512_SCR(55); + SHA512_SCR(56); SHA512_SCR(57); SHA512_SCR(58); SHA512_SCR(59); + SHA512_SCR(60); SHA512_SCR(61); SHA512_SCR(62); SHA512_SCR(63); + SHA512_SCR(64); SHA512_SCR(65); SHA512_SCR(66); SHA512_SCR(67); + SHA512_SCR(68); SHA512_SCR(69); SHA512_SCR(70); SHA512_SCR(71); + SHA512_SCR(72); SHA512_SCR(73); SHA512_SCR(74); SHA512_SCR(75); + SHA512_SCR(76); SHA512_SCR(77); SHA512_SCR(78); SHA512_SCR(79); + + wv[0] = ctx->h[0]; wv[1] = ctx->h[1]; + wv[2] = ctx->h[2]; wv[3] = ctx->h[3]; + wv[4] = ctx->h[4]; wv[5] = ctx->h[5]; + wv[6] = ctx->h[6]; wv[7] = ctx->h[7]; + + j = 0; + + do { + SHA512_EXP(0,1,2,3,4,5,6,7,j); j++; + SHA512_EXP(7,0,1,2,3,4,5,6,j); j++; + SHA512_EXP(6,7,0,1,2,3,4,5,j); j++; + SHA512_EXP(5,6,7,0,1,2,3,4,j); j++; + SHA512_EXP(4,5,6,7,0,1,2,3,j); j++; + SHA512_EXP(3,4,5,6,7,0,1,2,j); j++; + SHA512_EXP(2,3,4,5,6,7,0,1,j); j++; + SHA512_EXP(1,2,3,4,5,6,7,0,j); j++; + } while (j < 80); + + ctx->h[0] += wv[0]; ctx->h[1] += wv[1]; + ctx->h[2] += wv[2]; ctx->h[3] += wv[3]; + ctx->h[4] += wv[4]; ctx->h[5] += wv[5]; + ctx->h[6] += wv[6]; ctx->h[7] += wv[7]; +#endif /* !UNROLL_LOOPS */ + } +} + +/* SHA-224 functions */ + +void sha224(const uint8 *message, uint64 len, uint8 *digest) +{ + sha224_ctx ctx; + + sha224_init(&ctx); + sha224_update(&ctx, message, len); + sha224_final(&ctx, digest); +} + +void sha224_init(sha224_ctx *ctx) +{ +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha224_h0[i]; + } +#else + ctx->h[0] = sha224_h0[0]; ctx->h[1] = sha224_h0[1]; + ctx->h[2] = sha224_h0[2]; ctx->h[3] = sha224_h0[3]; + ctx->h[4] = sha224_h0[4]; ctx->h[5] = sha224_h0[5]; + ctx->h[6] = sha224_h0[6]; ctx->h[7] = sha224_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha224_update(sha224_ctx *ctx, const uint8 *message, uint64 len) +{ + uint64 block_nb; + uint64 new_len, rem_len, tmp_len; + const uint8 *shifted_message; + + tmp_len = SHA224_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + + if (ctx->len + len < SHA224_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA224_BLOCK_SIZE; + + shifted_message = message + rem_len; + + sha256_transf(ctx, ctx->block, 1); + sha256_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA224_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +void sha224_final(sha224_ctx *ctx, uint8 *digest) +{ + uint64 block_nb; + uint64 pm_len; + uint64 len_b; + uint64 tot_len; + +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = (1 + ((SHA224_BLOCK_SIZE - 9) + < (ctx->len % SHA224_BLOCK_SIZE))); + + tot_len = ctx->tot_len + ctx->len; + ctx->tot_len = tot_len; + + len_b = tot_len << 3; + pm_len = block_nb << 6; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK64(len_b, ctx->block + pm_len - 8); + + sha256_transf(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0 ; i < 7; i++) { + UNPACK32(ctx->h[i], &digest[i << 2]); + } +#else + UNPACK32(ctx->h[0], &digest[ 0]); + UNPACK32(ctx->h[1], &digest[ 4]); + UNPACK32(ctx->h[2], &digest[ 8]); + UNPACK32(ctx->h[3], &digest[12]); + UNPACK32(ctx->h[4], &digest[16]); + UNPACK32(ctx->h[5], &digest[20]); + UNPACK32(ctx->h[6], &digest[24]); +#endif /* !UNROLL_LOOPS */ +} + +/* SHA-256 functions */ + +void sha256(const uint8 *message, uint64 len, uint8 *digest) +{ + sha256_ctx ctx; + + sha256_init(&ctx); + sha256_update(&ctx, message, len); + sha256_final(&ctx, digest); +} + +void sha256_init(sha256_ctx *ctx) +{ +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha256_h0[i]; + } +#else + ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1]; + ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3]; + ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5]; + ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha256_update(sha256_ctx *ctx, const uint8 *message, uint64 len) +{ + uint64 block_nb; + uint64 new_len, rem_len, tmp_len; + const uint8 *shifted_message; + + tmp_len = SHA256_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + + if (ctx->len + len < SHA256_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA256_BLOCK_SIZE; + + shifted_message = message + rem_len; + + sha256_transf(ctx, ctx->block, 1); + sha256_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA256_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +void sha256_final(sha256_ctx *ctx, uint8 *digest) +{ + uint64 block_nb; + uint64 pm_len; + uint64 len_b; + uint64 tot_len; + +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) + < (ctx->len % SHA256_BLOCK_SIZE))); + + tot_len = ctx->tot_len + ctx->len; + ctx->tot_len = tot_len; + + len_b = tot_len << 3; + pm_len = block_nb << 6; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK64(len_b, ctx->block + pm_len - 8); + + sha256_transf(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0 ; i < 8; i++) { + UNPACK32(ctx->h[i], &digest[i << 2]); + } +#else + UNPACK32(ctx->h[0], &digest[ 0]); + UNPACK32(ctx->h[1], &digest[ 4]); + UNPACK32(ctx->h[2], &digest[ 8]); + UNPACK32(ctx->h[3], &digest[12]); + UNPACK32(ctx->h[4], &digest[16]); + UNPACK32(ctx->h[5], &digest[20]); + UNPACK32(ctx->h[6], &digest[24]); + UNPACK32(ctx->h[7], &digest[28]); +#endif /* !UNROLL_LOOPS */ +} + +/* SHA-384 functions */ + +void sha384(const uint8 *message, uint64 len, uint8 *digest) +{ + sha384_ctx ctx; + + sha384_init(&ctx); + sha384_update(&ctx, message, len); + sha384_final(&ctx, digest); +} + +void sha384_init(sha384_ctx *ctx) +{ +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha384_h0[i]; + } +#else + ctx->h[0] = sha384_h0[0]; ctx->h[1] = sha384_h0[1]; + ctx->h[2] = sha384_h0[2]; ctx->h[3] = sha384_h0[3]; + ctx->h[4] = sha384_h0[4]; ctx->h[5] = sha384_h0[5]; + ctx->h[6] = sha384_h0[6]; ctx->h[7] = sha384_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha384_update(sha384_ctx *ctx, const uint8 *message, uint64 len) +{ + uint64 block_nb; + uint64 new_len, rem_len, tmp_len; + const uint8 *shifted_message; + + tmp_len = SHA384_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + + if (ctx->len + len < SHA384_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA384_BLOCK_SIZE; + + shifted_message = message + rem_len; + + sha512_transf(ctx, ctx->block, 1); + sha512_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA384_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_message[block_nb << 7], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 7; +} + +void sha384_final(sha384_ctx *ctx, uint8 *digest) +{ + uint64 block_nb; + uint64 pm_len; + uint64 len_b; + uint64 tot_len; + +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = (1 + ((SHA384_BLOCK_SIZE - 17) + < (ctx->len % SHA384_BLOCK_SIZE))); + + tot_len = ctx->tot_len + ctx->len; + ctx->tot_len = tot_len; + + len_b = tot_len << 3; + pm_len = block_nb << 7; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK64(len_b, ctx->block + pm_len - 8); + + sha512_transf(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0 ; i < 6; i++) { + UNPACK64(ctx->h[i], &digest[i << 3]); + } +#else + UNPACK64(ctx->h[0], &digest[ 0]); + UNPACK64(ctx->h[1], &digest[ 8]); + UNPACK64(ctx->h[2], &digest[16]); + UNPACK64(ctx->h[3], &digest[24]); + UNPACK64(ctx->h[4], &digest[32]); + UNPACK64(ctx->h[5], &digest[40]); +#endif /* !UNROLL_LOOPS */ +} + +/* SHA-512 functions */ + +void sha512(const uint8 *message, uint64 len, uint8 *digest) +{ + sha512_ctx ctx; + + sha512_init(&ctx); + sha512_update(&ctx, message, len); + sha512_final(&ctx, digest); +} + +void sha512_init(sha512_ctx *ctx) +{ +#ifndef UNROLL_LOOPS + int i; + for (i = 0; i < 8; i++) { + ctx->h[i] = sha512_h0[i]; + } +#else + ctx->h[0] = sha512_h0[0]; ctx->h[1] = sha512_h0[1]; + ctx->h[2] = sha512_h0[2]; ctx->h[3] = sha512_h0[3]; + ctx->h[4] = sha512_h0[4]; ctx->h[5] = sha512_h0[5]; + ctx->h[6] = sha512_h0[6]; ctx->h[7] = sha512_h0[7]; +#endif /* !UNROLL_LOOPS */ + + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha512_update(sha512_ctx *ctx, const uint8 *message, uint64 len) +{ + uint64 block_nb; + uint64 new_len, rem_len, tmp_len; + const uint8 *shifted_message; + + tmp_len = SHA512_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + + if (ctx->len + len < SHA512_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA512_BLOCK_SIZE; + + shifted_message = message + rem_len; + + sha512_transf(ctx, ctx->block, 1); + sha512_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA512_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_message[block_nb << 7], rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 7; +} + +void sha512_final(sha512_ctx *ctx, uint8 *digest) +{ + uint64 block_nb; + uint64 pm_len; + uint64 len_b; + uint64 tot_len; + +#ifndef UNROLL_LOOPS + int i; +#endif + + block_nb = 1 + ((SHA512_BLOCK_SIZE - 17) + < (ctx->len % SHA512_BLOCK_SIZE)); + + tot_len = ctx->tot_len + ctx->len; + ctx->tot_len = tot_len; + + len_b = tot_len << 3; + pm_len = block_nb << 7; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK64(len_b, ctx->block + pm_len - 8); + + sha512_transf(ctx, ctx->block, block_nb); + +#ifndef UNROLL_LOOPS + for (i = 0 ; i < 8; i++) { + UNPACK64(ctx->h[i], &digest[i << 3]); + } +#else + UNPACK64(ctx->h[0], &digest[ 0]); + UNPACK64(ctx->h[1], &digest[ 8]); + UNPACK64(ctx->h[2], &digest[16]); + UNPACK64(ctx->h[3], &digest[24]); + UNPACK64(ctx->h[4], &digest[32]); + UNPACK64(ctx->h[5], &digest[40]); + UNPACK64(ctx->h[6], &digest[48]); + UNPACK64(ctx->h[7], &digest[56]); +#endif /* !UNROLL_LOOPS */ +} + +// #ifdef TEST_VECTORS + +// /* FIPS 180-2 Validation tests */ + +// #include +// #include + +// void test(const char *vector, uint8 *digest, uint32 digest_size) +// { +// char output[2 * SHA512_DIGEST_SIZE + 1]; +// int i; + +// output[2 * digest_size] = '\0'; + +// for (i = 0; i < (int) digest_size ; i++) { +// sprintf(output + 2 * i, "%02x", digest[i]); +// } + +// printf("H: %s\n", output); +// if (strcmp(vector, output)) { +// fprintf(stderr, "Test failed.\n"); +// exit(EXIT_FAILURE); +// } +// } + +// static void test_sha224_message4(uint8 *digest) +// { +// /* Message of 929271 bytes */ + +// sha224_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha224_init(&ctx); +// for (i = 0; i < 929; i++) { +// sha224_update(&ctx, message, sizeof (message)); +// } +// sha224_update(&ctx, message, 271); + +// sha224_final(&ctx, digest); +// } + +// static void test_sha256_message4(uint8 *digest) +// { +// /* Message of 929271 bytes */ + +// sha256_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha256_init(&ctx); +// for (i = 0; i < 929; i++) { +// sha256_update(&ctx, message, sizeof (message)); +// } +// sha256_update(&ctx, message, 271); + +// sha256_final(&ctx, digest); +// } + +// static void test_sha384_message4(uint8 *digest) +// { +// /* Message of 929271 bytes */ + +// sha384_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha384_init(&ctx); +// for (i = 0; i < 929; i++) { +// sha384_update(&ctx, message, sizeof (message)); +// } +// sha384_update(&ctx, message, 271); + +// sha384_final(&ctx, digest); +// } + +// static void test_sha512_message4(uint8 *digest) +// { +// /* Message of 929271 bytes */ + +// sha512_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha512_init(&ctx); +// for (i = 0; i < 929; i++) { +// sha512_update(&ctx, message, sizeof (message)); +// } +// sha512_update(&ctx, message, 271); + +// sha512_final(&ctx, digest); +// } + +// #ifdef TEST_VECTORS_LONG + +// /* Validation tests with a message of 10 GB */ + +// static void test_sha224_long_message(uint8 *digest) +// { +// sha224_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha224_init(&ctx); +// for (i = 0; i < 10000000; i++) { +// sha224_update(&ctx, message, sizeof (message)); +// } +// sha224_final(&ctx, digest); +// } + +// static void test_sha256_long_message(uint8 *digest) +// { +// sha256_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha256_init(&ctx); +// for (i = 0; i < 10000000; i++) { +// sha256_update(&ctx, message, sizeof (message)); +// } +// sha256_final(&ctx, digest); +// } + +// static void test_sha384_long_message(uint8 *digest) +// { +// sha384_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha384_init(&ctx); +// for (i = 0; i < 10000000; i++) { +// sha384_update(&ctx, message, sizeof (message)); +// } +// sha384_final(&ctx, digest); +// } + +// static void test_sha512_long_message(uint8 *digest) +// { +// sha512_ctx ctx; +// uint8 message[1000]; +// int i; + +// memset(message, 'a', sizeof (message)); + +// sha512_init(&ctx); +// for (i = 0; i < 10000000; i++) { +// sha512_update(&ctx, message, sizeof (message)); +// } +// sha512_final(&ctx, digest); +// } + +// #endif /* TEST_VECTORS_LONG */ + +// int main(void) +// { +// static const char *vectors[4][5] = +// { /* SHA-224 */ +// { +// "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", +// "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", +// "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67", +// "c84cf4761583afa849ffd562c52a2e2a5104f1a4071dab6c53560d4f", +// "b7bdc6c1f4f789f1456e68a005779a6c1f6199211008bee2801baf0d", +// }, +// /* SHA-256 */ +// { +// "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", +// "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", +// "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", +// "8c14f43ad81026351e9b60025b5420e6072ff617f5c72145b179599211514947", +// "53748286337dbe36f5df22e7ef1af3ad71530398cf569adc7eb5fefa7af7003c", +// }, +// /* SHA-384 */ +// { +// "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed" +// "8086072ba1e7cc2358baeca134c825a7", +// "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712" +// "fcc7c71a557e2db966c3e9fa91746039", +// "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b" +// "07b8b3dc38ecc4ebae97ddd87f3d8985", +// "3de4a44dba8627278c376148b6d45be0a3a410337330ef3e1d9ca34c4593ecfc" +// "8ce7a8415aefaca6b39d1112078cc3e0", +// "073de8e641532032b2922c4af165baa88dfe5fdafb09575657406894b4b94996" +// "8975eef50c1ef5be59ca0ecdaa996496", +// }, +// /* SHA-512 */ +// { +// "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" +// "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", +// "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" +// "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", +// "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" +// "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b", +// "a7e464bfd1f27201d7d0575c1a302cecef0004828e923e4255c8de6bae958a01" +// "9294e3f851bf9a03013963cd1268687c549916438e465433957d4480bcaa8572", +// "922637075a3ee5d87a7ce3ae7e04083d6daea7b541f264512290157ce3f81f9b" +// "afcd3f9dc2d4fe0a6248a071709b96d0128d96c48220b2ab919b99187cb16fbf" +// } +// }; + +// static const char message1[] = "abc"; +// static const char message2a[] = "abcdbcdecdefdefgefghfghighijhi" +// "jkijkljklmklmnlmnomnopnopq"; +// static const char message2b[] = "abcdefghbcdefghicdefghijdefghijkefghij" +// "klfghijklmghijklmnhijklmnoijklmnopjklm" +// "nopqklmnopqrlmnopqrsmnopqrstnopqrstu"; +// uint8 *message3; +// uint32 message3_len = 1000000; +// uint8 digest[SHA512_DIGEST_SIZE]; + +// message3 = malloc(message3_len); +// if (message3 == NULL) { +// fprintf(stderr, "Can't allocate memory\n"); +// return -1; +// } +// memset(message3, 'a', message3_len); + +// printf("SHA-2 FIPS 180-2 Validation tests\n\n"); +// printf("SHA-224 Test vectors\n"); + +// sha224((const uint8 *) message1, strlen(message1), digest); +// test(vectors[0][0], digest, SHA224_DIGEST_SIZE); +// sha224((const uint8 *) message2a, strlen(message2a), digest); +// test(vectors[0][1], digest, SHA224_DIGEST_SIZE); +// sha224(message3, message3_len, digest); +// test(vectors[0][2], digest, SHA224_DIGEST_SIZE); +// test_sha224_message4(digest); +// test(vectors[0][3], digest, SHA224_DIGEST_SIZE); +// #ifdef TEST_VECTORS_LONG +// test_sha224_long_message(digest); +// test(vectors[0][4], digest, SHA224_DIGEST_SIZE); +// #endif +// printf("\n"); + +// printf("SHA-256 Test vectors\n"); + +// sha256((const uint8 *) message1, strlen(message1), digest); +// test(vectors[1][0], digest, SHA256_DIGEST_SIZE); +// sha256((const uint8 *) message2a, strlen(message2a), digest); +// test(vectors[1][1], digest, SHA256_DIGEST_SIZE); +// sha256(message3, message3_len, digest); +// test(vectors[1][2], digest, SHA256_DIGEST_SIZE); +// test_sha256_message4(digest); +// test(vectors[1][3], digest, SHA256_DIGEST_SIZE); +// #ifdef TEST_VECTORS_LONG +// test_sha256_long_message(digest); +// test(vectors[1][4], digest, SHA256_DIGEST_SIZE); +// #endif +// printf("\n"); + +// printf("SHA-384 Test vectors\n"); + +// sha384((const uint8 *) message1, strlen(message1), digest); +// test(vectors[2][0], digest, SHA384_DIGEST_SIZE); +// sha384((const uint8 *)message2b, strlen(message2b), digest); +// test(vectors[2][1], digest, SHA384_DIGEST_SIZE); +// sha384(message3, message3_len, digest); +// test(vectors[2][2], digest, SHA384_DIGEST_SIZE); +// test_sha384_message4(digest); +// test(vectors[2][3], digest, SHA384_DIGEST_SIZE); +// #ifdef TEST_VECTORS_LONG +// test_sha384_long_message(digest); +// test(vectors[2][4], digest, SHA384_DIGEST_SIZE); +// #endif +// printf("\n"); + +// printf("SHA-512 Test vectors\n"); + +// sha512((const uint8 *) message1, strlen(message1), digest); +// test(vectors[3][0], digest, SHA512_DIGEST_SIZE); +// sha512((const uint8 *) message2b, strlen(message2b), digest); +// test(vectors[3][1], digest, SHA512_DIGEST_SIZE); +// sha512(message3, message3_len, digest); +// test(vectors[3][2], digest, SHA512_DIGEST_SIZE); +// test_sha512_message4(digest); +// test(vectors[3][3], digest, SHA512_DIGEST_SIZE); +// #ifdef TEST_VECTORS_LONG +// test_sha512_long_message(digest); +// test(vectors[3][4], digest, SHA512_DIGEST_SIZE); +// #endif +// printf("\n"); + +// printf("All tests passed.\n"); + +// return 0; +// } + +// #endif /* TEST_VECTORS */ diff --git a/lib/FlipCrypt/hashes/sha2.h b/lib/FlipCrypt/hashes/sha2.h new file mode 100644 index 0000000..efbe6e5 --- /dev/null +++ b/lib/FlipCrypt/hashes/sha2.h @@ -0,0 +1,97 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * + * Copyright (C) 2005-2023 Olivier Gay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SHA2_H +#define SHA2_H + +#define SHA224_DIGEST_SIZE ( 224 / 8) +#define SHA256_DIGEST_SIZE ( 256 / 8) +#define SHA384_DIGEST_SIZE ( 384 / 8) +#define SHA512_DIGEST_SIZE ( 512 / 8) + +#define SHA256_BLOCK_SIZE ( 512 / 8) +#define SHA512_BLOCK_SIZE (1024 / 8) +#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE +#define SHA224_BLOCK_SIZE SHA256_BLOCK_SIZE + +#ifndef SHA2_TYPES +#define SHA2_TYPES +typedef unsigned char uint8; +typedef unsigned int uint32; +typedef unsigned long long uint64; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint64 tot_len; + uint64 len; + uint8 block[2 * SHA256_BLOCK_SIZE]; + uint32 h[8]; +} sha256_ctx; + +typedef struct { + uint64 tot_len; + uint64 len; + uint8 block[2 * SHA512_BLOCK_SIZE]; + uint64 h[8]; +} sha512_ctx; + +typedef sha512_ctx sha384_ctx; +typedef sha256_ctx sha224_ctx; + +void sha224_init(sha224_ctx *ctx); +void sha224_update(sha224_ctx *ctx, const uint8 *message, uint64 len); +void sha224_final(sha224_ctx *ctx, uint8 *digest); +void sha224(const uint8 *message, uint64 len, uint8 *digest); + +void sha256_init(sha256_ctx * ctx); +void sha256_update(sha256_ctx *ctx, const uint8 *message, uint64 len); +void sha256_final(sha256_ctx *ctx, uint8 *digest); +void sha256(const uint8 *message, uint64 len, uint8 *digest); + +void sha384_init(sha384_ctx *ctx); +void sha384_update(sha384_ctx *ctx, const uint8 *message, uint64 len); +void sha384_final(sha384_ctx *ctx, uint8 *digest); +void sha384(const uint8 *message, uint64 len, uint8 *digest); + +void sha512_init(sha512_ctx *ctx); +void sha512_update(sha512_ctx *ctx, const uint8 *message, uint64 len); +void sha512_final(sha512_ctx *ctx, uint8 *digest); +void sha512(const uint8 *message, uint64 len, uint8 *digest); + +#ifdef __cplusplus +} +#endif + +#endif /* !SHA2_H */ diff --git a/lib/FlipCrypt/hashes/siphash.c b/lib/FlipCrypt/hashes/siphash.c new file mode 100644 index 0000000..d1863f2 --- /dev/null +++ b/lib/FlipCrypt/hashes/siphash.c @@ -0,0 +1,196 @@ +// The github repo where I got this was licensed under the MIT license, of which the license text is shown below. +// https://github.com/veorq/SipHash/tree/master +// Copyright 2012-2024 JP Aumasson + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +/* + SipHash reference C implementation + + Copyright (c) 2012-2022 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with + this software. If not, see + . + */ + +#include "siphash.h" +#include +#include +#include + +/* default: SipHash-2-4 */ +#ifndef cROUNDS +#define cROUNDS 2 +#endif +#ifndef dROUNDS +#define dROUNDS 4 +#endif + +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define U32TO8_LE(p, v) \ + (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); + +#define U64TO8_LE(p, v) \ + U32TO8_LE((p), (uint32_t)((v))); \ + U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); + +#define U8TO64_LE(p) \ + (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ + ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ + ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ + ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) + +#define SIPROUND \ + do { \ + v0 += v1; \ + v1 = ROTL(v1, 13); \ + v1 ^= v0; \ + v0 = ROTL(v0, 32); \ + v2 += v3; \ + v3 = ROTL(v3, 16); \ + v3 ^= v2; \ + v0 += v3; \ + v3 = ROTL(v3, 21); \ + v3 ^= v0; \ + v2 += v1; \ + v1 = ROTL(v1, 17); \ + v1 ^= v2; \ + v2 = ROTL(v2, 32); \ + } while (0) + +#ifdef DEBUG_SIPHASH +#include + +#define TRACE \ + do { \ + printf("(%3zu) v0 %016" PRIx64 "\n", inlen, v0); \ + printf("(%3zu) v1 %016" PRIx64 "\n", inlen, v1); \ + printf("(%3zu) v2 %016" PRIx64 "\n", inlen, v2); \ + printf("(%3zu) v3 %016" PRIx64 "\n", inlen, v3); \ + } while (0) +#else +#define TRACE +#endif + +/* + Computes a SipHash value + *in: pointer to input data (read-only) + inlen: input data length in bytes (any size_t value) + *k: pointer to the key data (read-only), must be 16 bytes + *out: pointer to output data (write-only), outlen bytes must be allocated + outlen: length of the output in bytes, must be 8 or 16 +*/ +int siphash(const void *in, const size_t inlen, const void *k, uint8_t *out, + const size_t outlen) { + + const unsigned char *ni = (const unsigned char *)in; + const unsigned char *kk = (const unsigned char *)k; + + assert((outlen == 8) || (outlen == 16)); + uint64_t v0 = UINT64_C(0x736f6d6570736575); + uint64_t v1 = UINT64_C(0x646f72616e646f6d); + uint64_t v2 = UINT64_C(0x6c7967656e657261); + uint64_t v3 = UINT64_C(0x7465646279746573); + uint64_t k0 = U8TO64_LE(kk); + uint64_t k1 = U8TO64_LE(kk + 8); + uint64_t m; + int i; + const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + uint64_t b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + if (outlen == 16) + v1 ^= 0xee; + + for (; ni != end; ni += 8) { + m = U8TO64_LE(ni); + v3 ^= m; + + TRACE; + for (i = 0; i < cROUNDS; ++i) + SIPROUND; + + v0 ^= m; + } + + switch (left) { + case 7: + b |= ((uint64_t)ni[6]) << 48; + /* FALLTHRU */ + case 6: + b |= ((uint64_t)ni[5]) << 40; + /* FALLTHRU */ + case 5: + b |= ((uint64_t)ni[4]) << 32; + /* FALLTHRU */ + case 4: + b |= ((uint64_t)ni[3]) << 24; + /* FALLTHRU */ + case 3: + b |= ((uint64_t)ni[2]) << 16; + /* FALLTHRU */ + case 2: + b |= ((uint64_t)ni[1]) << 8; + /* FALLTHRU */ + case 1: + b |= ((uint64_t)ni[0]); + break; + case 0: + break; + } + + v3 ^= b; + + TRACE; + for (i = 0; i < cROUNDS; ++i) + SIPROUND; + + v0 ^= b; + + if (outlen == 16) + v2 ^= 0xee; + else + v2 ^= 0xff; + + TRACE; + for (i = 0; i < dROUNDS; ++i) + SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; + U64TO8_LE(out, b); + + if (outlen == 8) + return 0; + + v1 ^= 0xdd; + + TRACE; + for (i = 0; i < dROUNDS; ++i) + SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; + U64TO8_LE(out + 8, b); + + return 0; +} diff --git a/lib/FlipCrypt/hashes/siphash.h b/lib/FlipCrypt/hashes/siphash.h new file mode 100644 index 0000000..591571c --- /dev/null +++ b/lib/FlipCrypt/hashes/siphash.h @@ -0,0 +1,32 @@ +// The github repo where I got this was licensed under the MIT license, of which the text is shown below. +// https://github.com/veorq/SipHash/tree/master +// Copyright 2012-2024 JP Aumasson + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* + SipHash reference C implementation + + Copyright (c) 2012-2021 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with + this software. If not, see + . + */ + +#include +#include + +int siphash(const void *in, const size_t inlen, const void *k, uint8_t *out, + const size_t outlen); diff --git a/lib/FlipCrypt/hashes/xxhash.c b/lib/FlipCrypt/hashes/xxhash.c new file mode 100644 index 0000000..130250a --- /dev/null +++ b/lib/FlipCrypt/hashes/xxhash.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#include "xxhash.h" + +uint64_t XXH_rotl64(uint64_t x, int r) { + return (x << r) | (x >> (64 - r)); +} + +uint64_t XXH64(const void* input, size_t len, uint64_t seed) { + const uint64_t PRIME1 = 11400714785074694791ULL; + const uint64_t PRIME2 = 14029467366897019727ULL; + const uint64_t PRIME3 = 1609587929392839161ULL; + const uint64_t PRIME4 = 9650029242287828579ULL; + const uint64_t PRIME5 = 2870177450012600261ULL; + + const uint8_t* p = (const uint8_t*)input; + const uint8_t* end = p + len; + uint64_t h; + + if (len >= 32) { + uint64_t v1 = seed + PRIME1 + PRIME2; + uint64_t v2 = seed + PRIME2; + uint64_t v3 = seed + 0; + uint64_t v4 = seed - PRIME1; + + const uint8_t* const limit = end - 32; + do { + v1 = XXH_rotl64(v1 + (*(uint64_t*)p) * PRIME2, 31) * PRIME1; p += 8; + v2 = XXH_rotl64(v2 + (*(uint64_t*)p) * PRIME2, 31) * PRIME1; p += 8; + v3 = XXH_rotl64(v3 + (*(uint64_t*)p) * PRIME2, 31) * PRIME1; p += 8; + v4 = XXH_rotl64(v4 + (*(uint64_t*)p) * PRIME2, 31) * PRIME1; p += 8; + } while (p <= limit); + + h = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + + v1 *= PRIME2; v1 = XXH_rotl64(v1, 31); v1 *= PRIME1; h ^= v1; h = h * PRIME1 + PRIME4; + v2 *= PRIME2; v2 = XXH_rotl64(v2, 31); v2 *= PRIME1; h ^= v2; h = h * PRIME1 + PRIME4; + v3 *= PRIME2; v3 = XXH_rotl64(v3, 31); v3 *= PRIME1; h ^= v3; h = h * PRIME1 + PRIME4; + v4 *= PRIME2; v4 = XXH_rotl64(v4, 31); v4 *= PRIME1; h ^= v4; h = h * PRIME1 + PRIME4; + } else { + h = seed + PRIME5; + } + + h += (uint64_t)len; + + while (p + 8 <= end) { + uint64_t k1 = (*(uint64_t*)p) * PRIME2; + k1 = XXH_rotl64(k1, 31); + k1 *= PRIME1; + h ^= k1; + h = XXH_rotl64(h, 27) * PRIME1 + PRIME4; + p += 8; + } + + if (p + 4 <= end) { + h ^= (uint64_t)(*(uint32_t*)p) * PRIME1; + h = XXH_rotl64(h, 23) * PRIME2 + PRIME3; + p += 4; + } + + while (p < end) { + h ^= (*p) * PRIME5; + h = XXH_rotl64(h, 11) * PRIME1; + ++p; + } + + h ^= h >> 33; + h *= PRIME2; + h ^= h >> 29; + h *= PRIME3; + h ^= h >> 32; + + return h; +} \ No newline at end of file diff --git a/lib/FlipCrypt/hashes/xxhash.h b/lib/FlipCrypt/hashes/xxhash.h new file mode 100644 index 0000000..1ecc1d5 --- /dev/null +++ b/lib/FlipCrypt/hashes/xxhash.h @@ -0,0 +1 @@ +uint64_t XXH64(const void* input, size_t len, uint64_t seed); \ No newline at end of file diff --git a/lib/FlipCrypt/icon.png b/lib/FlipCrypt/icon.png new file mode 100644 index 0000000..c33d42b Binary files /dev/null and b/lib/FlipCrypt/icon.png differ diff --git a/lib/FlipCrypt/main.c b/lib/FlipCrypt/main.c new file mode 100644 index 0000000..b182f92 --- /dev/null +++ b/lib/FlipCrypt/main.c @@ -0,0 +1,3536 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flip_crypt_icons.h" + +#include "ciphers/aes.h" +#include "ciphers/affine.h" +#include "ciphers/atbash.h" +#include "ciphers/baconian.h" +#include "ciphers/beaufort.h" +#include "ciphers/caesar.h" +#include "ciphers/playfair.h" +#include "ciphers/polybius.h" +#include "ciphers/porta.h" +#include "ciphers/railfence.h" +#include "ciphers/rc4.h" +#include "ciphers/rot13.h" +#include "ciphers/scytale.h" +#include "ciphers/vigenere.h" + +#include "hashes/blake2.h" +#include "hashes/fnv.h" +#include "hashes/md2.h" +#include "hashes/md5.h" +#include "hashes/murmur3.h" +#include "hashes/sha1.h" +#include "hashes/sha2.h" +#include "hashes/siphash.h" +#include "hashes/xxhash.h" + +#include "encoders/base32.h" +#include "encoders/base58.h" +#include "encoders/base64.h" + +#include "qrcode/qrcodegen.h" +#include "storage.h" + +// Scene declarations +typedef enum { + // Main menu + FlipCryptMainMenuScene, + // Main Menu Submenus + FlipCryptCipherSubmenuScene, + FlipCryptHashSubmenuScene, + FlipCryptOtherSubmenuScene, + FlipCryptAboutScene, + // AES scenes + FlipCryptAESSubmenuScene, + FlipCryptAESInputScene, + FlipCryptAESKeyInputScene, + FlipCryptAESDecryptKeyInputScene, + FlipCryptAESOutputScene, + FlipCryptAESDecryptInputScene, + FlipCryptAESDecryptOutputScene, + FlipCryptAESLearnScene, + // Affine scenes + FlipCryptAffineSubmenuScene, + FlipCryptAffineInputScene, + FlipCryptAffineKeyAInputScene, + FlipCryptAffineDecryptKeyAInputScene, + FlipCryptAffineKeyBInputScene, + FlipCryptAffineDecryptKeyBInputScene, + FlipCryptAffineOutputScene, + FlipCryptAffineDecryptInputScene, + FlipCryptAffineDecryptOutputScene, + FlipCryptAffineLearnScene, + // Atbash scenes + FlipCryptAtbashSubmenuScene, + FlipCryptAtbashInputScene, + FlipCryptAtbashOutputScene, + FlipCryptAtbashDecryptInputScene, + FlipCryptAtbashDecryptOutputScene, + FlipCryptAtbashLearnScene, + // Baconian scenes + FlipCryptBaconianSubmenuScene, + FlipCryptBaconianInputScene, + FlipCryptBaconianOutputScene, + FlipCryptBaconianDecryptInputScene, + FlipCryptBaconianDecryptOutputScene, + FlipCryptBaconianLearnScene, + // Beaufort scenes + FlipCryptBeaufortSubmenuScene, + FlipCryptBeaufortInputScene, + FlipCryptBeaufortKeyInputScene, + FlipCryptBeaufortDecryptKeyInputScene, + FlipCryptBeaufortOutputScene, + FlipCryptBeaufortDecryptInputScene, + FlipCryptBeaufortDecryptOutputScene, + FlipCryptBeaufortLearnScene, + // Caesar scenes + FlipCryptCaesarSubmenuScene, + FlipCryptCaesarInputScene, + FlipCryptCaesarKeyInputScene, + FlipCryptCaesarDecryptKeyInputScene, + FlipCryptCaesarOutputScene, + FlipCryptCaesarDecryptInputScene, + FlipCryptCaesarDecryptOutputScene, + FlipCryptCaesarLearnScene, + // Playfair scenes + FlipCryptPlayfairSubmenuScene, + FlipCryptPlayfairInputScene, + FlipCryptPlayfairKeywordInputScene, + FlipCryptPlayfairDecryptKeywordInputScene, + FlipCryptPlayfairOutputScene, + FlipCryptPlayfairDecryptInputScene, + FlipCryptPlayfairDecryptOutputScene, + FlipCryptPlayfairLearnScene, + // Polybius square scenes + FlipCryptPolybiusSubmenuScene, + FlipCryptPolybiusInputScene, + FlipCryptPolybiusOutputScene, + FlipCryptPolybiusDecryptInputScene, + FlipCryptPolybiusDecryptOutputScene, + FlipCryptPolybiusLearnScene, + // Porta scenes + FlipCryptPortaSubmenuScene, + FlipCryptPortaInputScene, + FlipCryptPortaKeywordInputScene, + FlipCryptPortaDecryptKeywordInputScene, + FlipCryptPortaOutputScene, + FlipCryptPortaDecryptInputScene, + FlipCryptPortaDecryptOutputScene, + FlipCryptPortaLearnScene, + // Railfence scenes + FlipCryptRailfenceSubmenuScene, + FlipCryptRailfenceInputScene, + FlipCryptRailfenceKeyInputScene, + FlipCryptRailfenceDecryptKeyInputScene, + FlipCryptRailfenceOutputScene, + FlipCryptRailfenceDecryptInputScene, + FlipCryptRailfenceDecryptOutputScene, + FlipCryptRailfenceLearnScene, + // RC4 Cipher + FlipCryptRC4SubmenuScene, + FlipCryptRC4InputScene, + FlipCryptRC4KeywordInputScene, + FlipCryptRC4DecryptKeywordInputScene, + FlipCryptRC4OutputScene, + FlipCryptRC4DecryptInputScene, + FlipCryptRC4DecryptOutputScene, + FlipCryptRC4LearnScene, + // ROT13 scenes + FlipCryptROT13SubmenuScene, + FlipCryptROT13InputScene, + FlipCryptROT13OutputScene, + FlipCryptROT13DecryptInputScene, + FlipCryptROT13DecryptOutputScene, + FlipCryptROT13LearnScene, + // Scytale Cipher + FlipCryptScytaleSubmenuScene, + FlipCryptScytaleInputScene, + FlipCryptScytaleKeywordInputScene, + FlipCryptScytaleDecryptKeywordInputScene, + FlipCryptScytaleOutputScene, + FlipCryptScytaleDecryptInputScene, + FlipCryptScytaleDecryptOutputScene, + FlipCryptScytaleLearnScene, + // Vigenere Cipher + FlipCryptVigenereSubmenuScene, + FlipCryptVigenereInputScene, + FlipCryptVigenereKeywordInputScene, + FlipCryptVigenereDecryptKeywordInputScene, + FlipCryptVigenereOutputScene, + FlipCryptVigenereDecryptInputScene, + FlipCryptVigenereDecryptOutputScene, + FlipCryptVigenereLearnScene, + // BLAKE2 Hash + FlipCryptBlake2SubmenuScene, + FlipCryptBlake2InputScene, + FlipCryptBlake2OutputScene, + FlipCryptBlake2LearnScene, + // FNV-1A Hash + FlipCryptFNV1ASubmenuScene, + FlipCryptFNV1AInputScene, + FlipCryptFNV1AOutputScene, + FlipCryptFNV1ALearnScene, + // MD2 Hash + FlipCryptMD2SubmenuScene, + FlipCryptMD2InputScene, + FlipCryptMD2OutputScene, + FlipCryptMD2LearnScene, + // MD5 Hash + FlipCryptMD5SubmenuScene, + FlipCryptMD5InputScene, + FlipCryptMD5OutputScene, + FlipCryptMD5LearnScene, + // Murmur3 Hash + FlipCryptMurmur3SubmenuScene, + FlipCryptMurmur3InputScene, + FlipCryptMurmur3OutputScene, + FlipCryptMurmur3LearnScene, + // SipHash + FlipCryptSipSubmenuScene, + FlipCryptSipInputScene, + FlipCryptSipKeywordInputScene, + FlipCryptSipOutputScene, + FlipCryptSipLearnScene, + // SHA1 Hash + FlipCryptSHA1SubmenuScene, + FlipCryptSHA1InputScene, + FlipCryptSHA1OutputScene, + FlipCryptSHA1LearnScene, + // SHA224 Hash + FlipCryptSHA224SubmenuScene, + FlipCryptSHA224InputScene, + FlipCryptSHA224OutputScene, + FlipCryptSHA224LearnScene, + // SHA256 Hash + FlipCryptSHA256SubmenuScene, + FlipCryptSHA256InputScene, + FlipCryptSHA256OutputScene, + FlipCryptSHA256LearnScene, + // SHA384 Hash + FlipCryptSHA384SubmenuScene, + FlipCryptSHA384InputScene, + FlipCryptSHA384OutputScene, + FlipCryptSHA384LearnScene, + // SHA512 Hash + FlipCryptSHA512SubmenuScene, + FlipCryptSHA512InputScene, + FlipCryptSHA512OutputScene, + FlipCryptSHA512LearnScene, + // XX Hash + FlipCryptXXSubmenuScene, + FlipCryptXXInputScene, + FlipCryptXXOutputScene, + FlipCryptXXLearnScene, + // Base32 scenes + FlipCryptBase32SubmenuScene, + FlipCryptBase32InputScene, + FlipCryptBase32OutputScene, + FlipCryptBase32DecryptInputScene, + FlipCryptBase32DecryptOutputScene, + FlipCryptBase32LearnScene, + // Base58 scenes + FlipCryptBase58SubmenuScene, + FlipCryptBase58InputScene, + FlipCryptBase58OutputScene, + FlipCryptBase58DecryptInputScene, + FlipCryptBase58DecryptOutputScene, + FlipCryptBase58LearnScene, + // Base64 scenes + FlipCryptBase64SubmenuScene, + FlipCryptBase64InputScene, + FlipCryptBase64OutputScene, + FlipCryptBase64DecryptInputScene, + FlipCryptBase64DecryptOutputScene, + FlipCryptBase64LearnScene, + // Extra actions scenes + FlipCryptNFCScene, + FlipCryptSaveScene, + FlipCryptSaveTextInputScene, + FlipCryptQRScene, + FlipCryptSceneCount, +} FlipCryptScene; + +// View type declarations +typedef enum { + FlipCryptSubmenuView, + FlipCryptWidgetView, + FlipCryptTextInputView, + FlipCryptNumberInputView, + FlipCryptDialogExView, + FlipCryptCanvasView, +} FlipCryptView; + +// View and variable inits +typedef struct App { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + Widget* widget; + TextInput* text_input; + NumberInput* number_input; + DialogEx* dialog_ex; + Nfc* nfc; + NfcListener* listener; + NfcDevice* nfc_device; + char* universal_input; + uint8_t universal_input_size; + char* save_name_input; + char* last_output_scene; + char* aes_key_input; + int32_t affine_keya_input; + int32_t affine_keyb_input; + char* beaufort_key_input; + int32_t caesar_key_input; + char* playfair_keyword_input; + char* porta_keyword_input; + int32_t railfence_key_input; + char* rc4_keyword_input; + int32_t scytale_keyword_input; + char* vigenere_keyword_input; + char* sip_keyword_input; + uint8_t save_name_input_size; + uint8_t last_output_scene_size; + uint8_t aes_key_input_size; + uint8_t beaufort_key_input_size; + uint8_t playfair_keyword_input_size; + uint8_t porta_keyword_input_size; + uint8_t rc4_keyword_input_size; + uint8_t vigenere_keyword_input_size; + uint8_t sip_keyword_input_size; + uint8_t* qr_buffer; + uint8_t* qrcode; +} App; + +// Menu item indicies +typedef enum { + MainMenuIndexCiphers, + MainMenuIndexHashes, + MainMenuIndexOther, + MainMenuIndexAbout, + MenuIndexAES, + MenuIndexAffine, + MenuIndexAtbash, + MenuIndexBaconian, + MenuIndexBeaufort, + MenuIndexCaesar, + MenuIndexPlayfair, + MenuIndexPolybius, + MenuIndexPorta, + MenuIndexRailfence, + MenuIndexRC4, + MenuIndexROT13, + MenuIndexScytale, + MenuIndexVigenere, + MenuIndexBlake2, + MenuIndexFNV1A, + MenuIndexMD2, + MenuIndexMD5, + MenuIndexMurmur3, + MenuIndexSHA1, + MenuIndexSHA224, + MenuIndexSHA256, + MenuIndexSHA384, + MenuIndexSHA512, + MenuIndexSip, + MenuIndexXX, + MenuIndexBase32, + MenuIndexBase58, + MenuIndexBase64, +} FlipCryptMenuIndices; + +// Menu item events +typedef enum { + EventCiphers, + EventHashes, + EventOther, + EventAbout, + EventAES, + EventAffine, + EventAtbash, + EventBaconian, + EventBeaufort, + EventCaesar, + EventPlayfair, + EventPolybius, + EventPorta, + EventRailfence, + EventRC4, + EventROT13, + EventScytale, + EventVigenere, + EventBlake2, + EventFNV1A, + EventMD2, + EventMD5, + EventMurmur3, + EventSHA1, + EventSHA224, + EventSHA256, + EventSHA384, + EventSHA512, + EventSip, + EventXX, + EventBase32, + EventBase58, + EventBase64, +} FlipCryptMenuCustomEvents; + +// QR Code vars +typedef struct { + uint8_t* qr_buffer; + uint8_t* qrcode; +} QrCodeModel; + +// Main menu functionality +void flip_crypt_menu_callback(void* context, uint32_t index) { + App* app = context; + switch(index) { + case MainMenuIndexCiphers: + scene_manager_handle_custom_event(app->scene_manager, EventCiphers); + break; + case MainMenuIndexHashes: + scene_manager_handle_custom_event(app->scene_manager, EventHashes); + break; + case MainMenuIndexOther: + scene_manager_handle_custom_event(app->scene_manager, EventOther); + break; + case MainMenuIndexAbout: + scene_manager_handle_custom_event(app->scene_manager, EventAbout); + break; + case MenuIndexAES: + scene_manager_handle_custom_event(app->scene_manager, EventAES); + break; + case MenuIndexAffine: + scene_manager_handle_custom_event(app->scene_manager, EventAffine); + break; + case MenuIndexAtbash: + scene_manager_handle_custom_event(app->scene_manager, EventAtbash); + break; + case MenuIndexBaconian: + scene_manager_handle_custom_event(app->scene_manager, EventBaconian); + break; + case MenuIndexBeaufort: + scene_manager_handle_custom_event(app->scene_manager, EventBeaufort); + break; + case MenuIndexCaesar: + scene_manager_handle_custom_event(app->scene_manager, EventCaesar); + break; + case MenuIndexPlayfair: + scene_manager_handle_custom_event(app->scene_manager, EventPlayfair); + break; + case MenuIndexPolybius: + scene_manager_handle_custom_event(app->scene_manager, EventPolybius); + break; + case MenuIndexPorta: + scene_manager_handle_custom_event(app->scene_manager, EventPorta); + break; + case MenuIndexRailfence: + scene_manager_handle_custom_event(app->scene_manager, EventRailfence); + break; + case MenuIndexRC4: + scene_manager_handle_custom_event(app->scene_manager, EventRC4); + break; + case MenuIndexROT13: + scene_manager_handle_custom_event(app->scene_manager, EventROT13); + break; + case MenuIndexScytale: + scene_manager_handle_custom_event(app->scene_manager, EventScytale); + break; + case MenuIndexVigenere: + scene_manager_handle_custom_event(app->scene_manager, EventVigenere); + break; + case MenuIndexBlake2: + scene_manager_handle_custom_event(app->scene_manager, EventBlake2); + break; + case MenuIndexFNV1A: + scene_manager_handle_custom_event(app->scene_manager, EventFNV1A); + break; + case MenuIndexMD2: + scene_manager_handle_custom_event(app->scene_manager, EventMD2); + break; + case MenuIndexMD5: + scene_manager_handle_custom_event(app->scene_manager, EventMD5); + break; + case MenuIndexMurmur3: + scene_manager_handle_custom_event(app->scene_manager, EventMurmur3); + break; + case MenuIndexSip: + scene_manager_handle_custom_event(app->scene_manager, EventSip); + break; + case MenuIndexSHA1: + scene_manager_handle_custom_event(app->scene_manager, EventSHA1); + break; + case MenuIndexSHA224: + scene_manager_handle_custom_event(app->scene_manager, EventSHA224); + break; + case MenuIndexSHA256: + scene_manager_handle_custom_event(app->scene_manager, EventSHA256); + break; + case MenuIndexSHA384: + scene_manager_handle_custom_event(app->scene_manager, EventSHA384); + break; + case MenuIndexSHA512: + scene_manager_handle_custom_event(app->scene_manager, EventSHA512); + break; + case MenuIndexXX: + scene_manager_handle_custom_event(app->scene_manager, EventXX); + break; + case MenuIndexBase32: + scene_manager_handle_custom_event(app->scene_manager, EventBase32); + break; + case MenuIndexBase58: + scene_manager_handle_custom_event(app->scene_manager, EventBase58); + break; + case MenuIndexBase64: + scene_manager_handle_custom_event(app->scene_manager, EventBase64); + break; + } +} + +// Main menu initialization +void flip_crypt_main_menu_scene_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "FlipCrypt"); + submenu_add_item(app->submenu, "Ciphers", MainMenuIndexCiphers, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Hashes", MainMenuIndexHashes, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Other", MainMenuIndexOther, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "About", MainMenuIndexAbout, flip_crypt_menu_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptSubmenuView); +} + +void flip_crypt_cipher_submenu_scene_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Ciphers"); + submenu_add_item(app->submenu, "AES-128 Cipher", MenuIndexAES, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Affine Cipher", MenuIndexAffine, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Atbash Cipher", MenuIndexAtbash, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Baconian Cipher", MenuIndexBaconian, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Beaufort Cipher", MenuIndexBeaufort, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Caesar Cipher", MenuIndexCaesar, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Playfair Cipher", MenuIndexPlayfair, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Polybius Square", MenuIndexPolybius, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Porta Cipher", MenuIndexPorta, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Railfence Cipher", MenuIndexRailfence, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "RC4 Cipher", MenuIndexRC4, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "ROT-13 Cipher", MenuIndexROT13, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Scytale Cipher", MenuIndexScytale, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Vigenere Cipher", MenuIndexVigenere, flip_crypt_menu_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptSubmenuView); +} + +void flip_crypt_hash_submenu_scene_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Hashes"); + submenu_add_item(app->submenu, "BLAKE-2s Hash", MenuIndexBlake2, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "FNV-1A Hash", MenuIndexFNV1A, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "MD2 Hash", MenuIndexMD2, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "MD5 Hash", MenuIndexMD5, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "MurmurHash3", MenuIndexMurmur3, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "SHA-1 Hash", MenuIndexSHA1, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "SHA-224 Hash", MenuIndexSHA224, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "SHA-256 Hash", MenuIndexSHA256, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "SHA-384 Hash", MenuIndexSHA384, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "SHA-512 Hash", MenuIndexSHA512, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "SipHash", MenuIndexSip, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "XXHash64", MenuIndexXX, flip_crypt_menu_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptSubmenuView); +} + +void flip_crypt_other_submenu_scene_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Other"); + submenu_add_item(app->submenu, "Base32 Encoding", MenuIndexBase32, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Base58 Encoding", MenuIndexBase58, flip_crypt_menu_callback, app); + submenu_add_item(app->submenu, "Base64 Encoding", MenuIndexBase64, flip_crypt_menu_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptSubmenuView); +} + +// More main menu functionality +bool flip_crypt_main_menu_scene_on_event(void* context, SceneManagerEvent event) { + App* app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case EventCiphers: + scene_manager_next_scene(app->scene_manager, FlipCryptCipherSubmenuScene); + consumed = true; + break; + case EventHashes: + scene_manager_next_scene(app->scene_manager, FlipCryptHashSubmenuScene); + consumed = true; + break; + case EventOther: + scene_manager_next_scene(app->scene_manager, FlipCryptOtherSubmenuScene); + consumed = true; + break; + case EventAbout: + scene_manager_next_scene(app->scene_manager, FlipCryptAboutScene); + consumed = true; + break; + case EventAES: + scene_manager_next_scene(app->scene_manager, FlipCryptAESSubmenuScene); + consumed = true; + break; + case EventAffine: + scene_manager_next_scene(app->scene_manager, FlipCryptAffineSubmenuScene); + consumed = true; + break; + case EventAtbash: + scene_manager_next_scene(app->scene_manager, FlipCryptAtbashSubmenuScene); + consumed = true; + break; + case EventBaconian: + scene_manager_next_scene(app->scene_manager, FlipCryptBaconianSubmenuScene); + consumed = true; + break; + case EventBeaufort: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortSubmenuScene); + consumed = true; + break; + case EventCaesar: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarSubmenuScene); + consumed = true; + break; + case EventPlayfair: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairSubmenuScene); + consumed = true; + break; + case EventPolybius: + scene_manager_next_scene(app->scene_manager, FlipCryptPolybiusSubmenuScene); + consumed = true; + break; + case EventPorta: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaSubmenuScene); + consumed = true; + break; + case EventRailfence: + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceSubmenuScene); + consumed = true; + break; + case EventRC4: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4SubmenuScene); + consumed = true; + break; + case EventROT13: + scene_manager_next_scene(app->scene_manager, FlipCryptROT13SubmenuScene); + consumed = true; + break; + case EventScytale: + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleSubmenuScene); + consumed = true; + break; + case EventVigenere: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereSubmenuScene); + consumed = true; + break; + case EventBlake2: + scene_manager_next_scene(app->scene_manager, FlipCryptBlake2SubmenuScene); + consumed = true; + break; + case EventFNV1A: + scene_manager_next_scene(app->scene_manager, FlipCryptFNV1ASubmenuScene); + consumed = true; + break; + case EventMD2: + scene_manager_next_scene(app->scene_manager, FlipCryptMD2SubmenuScene); + consumed = true; + break; + case EventMD5: + scene_manager_next_scene(app->scene_manager, FlipCryptMD5SubmenuScene); + consumed = true; + break; + case EventMurmur3: + scene_manager_next_scene(app->scene_manager, FlipCryptMurmur3SubmenuScene); + consumed = true; + break; + case EventSip: + scene_manager_next_scene(app->scene_manager, FlipCryptSipSubmenuScene); + consumed = true; + break; + case EventSHA1: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA1SubmenuScene); + consumed = true; + break; + case EventSHA224: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA224SubmenuScene); + consumed = true; + break; + case EventSHA256: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA256SubmenuScene); + consumed = true; + break; + case EventSHA384: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA384SubmenuScene); + consumed = true; + break; + case EventSHA512: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA512SubmenuScene); + consumed = true; + break; + case EventXX: + scene_manager_next_scene(app->scene_manager, FlipCryptXXSubmenuScene); + consumed = true; + break; + case EventBase32: + scene_manager_next_scene(app->scene_manager, FlipCryptBase32SubmenuScene); + consumed = true; + break; + case EventBase58: + scene_manager_next_scene(app->scene_manager, FlipCryptBase58SubmenuScene); + consumed = true; + break; + case EventBase64: + scene_manager_next_scene(app->scene_manager, FlipCryptBase64SubmenuScene); + consumed = true; + break; + } + } + return consumed; +} + +void flip_crypt_main_menu_scene_on_exit(void* context) { + App* app = context; + submenu_reset(app->submenu); +} + +// Cipher / Hash 'Encrypt' submenu option functionality +void cipher_encrypt_submenu_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + App* app = context; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAESSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESKeyInputScene); + break; + case FlipCryptAffineSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAffineKeyAInputScene); + break; + case FlipCryptAtbashSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAtbashInputScene); + break; + case FlipCryptBaconianSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBaconianInputScene); + break; + case FlipCryptBeaufortSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortKeyInputScene); + break; + case FlipCryptCaesarSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarKeyInputScene); + break; + case FlipCryptPlayfairSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairKeywordInputScene); + break; + case FlipCryptPolybiusSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPolybiusInputScene); + break; + case FlipCryptPortaSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaKeywordInputScene); + break; + case FlipCryptRailfenceSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceKeyInputScene); + break; + case FlipCryptRC4SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4KeywordInputScene); + break; + case FlipCryptROT13SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptROT13InputScene); + break; + case FlipCryptScytaleSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleKeywordInputScene); + break; + case FlipCryptVigenereSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereKeywordInputScene); + break; + case FlipCryptBlake2SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBlake2InputScene); + break; + case FlipCryptFNV1ASubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptFNV1AInputScene); + break; + case FlipCryptMD2SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMD2InputScene); + break; + case FlipCryptMD5SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMD5InputScene); + break; + case FlipCryptMurmur3SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMurmur3InputScene); + break; + case FlipCryptSipSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSipKeywordInputScene); + break; + case FlipCryptSHA1SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA1InputScene); + break; + case FlipCryptSHA224SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA224InputScene); + break; + case FlipCryptSHA256SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA256InputScene); + break; + case FlipCryptSHA384SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA384InputScene); + break; + case FlipCryptSHA512SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA512InputScene); + break; + case FlipCryptXXSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptXXInputScene); + break; + case FlipCryptBase32SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase32InputScene); + break; + case FlipCryptBase58SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase58InputScene); + break; + case FlipCryptBase64SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase64InputScene); + break; + default: + break; + } +} + +// Cipher / Hash 'Decrypt' submenu option functionality +void cipher_decrypt_submenu_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + App* app = context; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAESSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESDecryptKeyInputScene); + break; + case FlipCryptAffineSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAffineDecryptKeyAInputScene); + break; + case FlipCryptAtbashSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAtbashDecryptInputScene); + break; + case FlipCryptBaconianSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBaconianDecryptInputScene); + break; + case FlipCryptBeaufortSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortDecryptKeyInputScene); + break; + case FlipCryptCaesarSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarDecryptKeyInputScene); + break; + case FlipCryptPlayfairSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairDecryptKeywordInputScene); + break; + case FlipCryptPolybiusSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPolybiusDecryptInputScene); + break; + case FlipCryptPortaSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaDecryptKeywordInputScene); + break; + case FlipCryptRailfenceSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceDecryptKeyInputScene); + break; + case FlipCryptRC4SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4DecryptKeywordInputScene); + break; + case FlipCryptROT13SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptROT13DecryptInputScene); + break; + case FlipCryptScytaleSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleDecryptKeywordInputScene); + break; + case FlipCryptVigenereSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereDecryptKeywordInputScene); + break; + // Can't decrypt hashes so they are absent + case FlipCryptBase32SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase32DecryptInputScene); + break; + case FlipCryptBase58SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase58DecryptInputScene); + break; + case FlipCryptBase64SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase64DecryptInputScene); + break; + default: + break; + } +} + +// Cipher / Hash 'Learn' submenu option functionality +void cipher_learn_submenu_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + App* app = context; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAESSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESLearnScene); + break; + case FlipCryptAffineSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAffineLearnScene); + break; + case FlipCryptAtbashSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAtbashLearnScene); + break; + case FlipCryptBaconianSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBaconianLearnScene); + break; + case FlipCryptBeaufortSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortLearnScene); + break; + case FlipCryptCaesarSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarLearnScene); + break; + case FlipCryptPlayfairSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairLearnScene); + break; + case FlipCryptPolybiusSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPolybiusLearnScene); + break; + case FlipCryptPortaSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaLearnScene); + break; + case FlipCryptRailfenceSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceLearnScene); + break; + case FlipCryptRC4SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4LearnScene); + break; + case FlipCryptROT13SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptROT13LearnScene); + break; + case FlipCryptScytaleSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleLearnScene); + break; + case FlipCryptVigenereSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereLearnScene); + break; + case FlipCryptBlake2SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBlake2LearnScene); + break; + case FlipCryptFNV1ASubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptFNV1ALearnScene); + break; + case FlipCryptMD2SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMD2LearnScene); + break; + case FlipCryptMD5SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMD5LearnScene); + break; + case FlipCryptMurmur3SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMurmur3LearnScene); + break; + case FlipCryptSipSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSipLearnScene); + break; + case FlipCryptSHA1SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA1LearnScene); + break; + case FlipCryptSHA224SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA224LearnScene); + break; + case FlipCryptSHA256SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA256LearnScene); + break; + case FlipCryptSHA384SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA384LearnScene); + break; + case FlipCryptSHA512SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA512LearnScene); + break; + case FlipCryptXXSubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptXXLearnScene); + break; + case FlipCryptBase32SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase32LearnScene); + break; + case FlipCryptBase58SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase58LearnScene); + break; + case FlipCryptBase64SubmenuScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase64LearnScene); + break; + default: + break; + } +} + +// Number input stuff +void number_input_scene_callback(void* context, int32_t number) { + App* app = context; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAffineKeyAInputScene: + app->affine_keya_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptAffineKeyBInputScene); + break; + case FlipCryptAffineKeyBInputScene: + app->affine_keyb_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptAffineInputScene); + break; + case FlipCryptAffineDecryptKeyAInputScene: + app->affine_keya_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptAffineDecryptKeyBInputScene); + break; + case FlipCryptAffineDecryptKeyBInputScene: + app->affine_keyb_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptAffineDecryptInputScene); + break; + case FlipCryptCaesarKeyInputScene: + app->caesar_key_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarInputScene); + break; + case FlipCryptCaesarDecryptKeyInputScene: + app->caesar_key_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarDecryptInputScene); + break; + case FlipCryptRailfenceKeyInputScene: + app->railfence_key_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceInputScene); + break; + case FlipCryptRailfenceDecryptKeyInputScene: + app->railfence_key_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceDecryptInputScene); + break; + case FlipCryptScytaleKeywordInputScene: + app->scytale_keyword_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleInputScene); + break; + case FlipCryptScytaleDecryptKeywordInputScene: + app->scytale_keyword_input = number; + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleDecryptInputScene); + break; + default: + break; + } +} + +void number_input_scene_on_enter(void* context) { + furi_assert(context); + App* app = context; + NumberInput* number_input = app->number_input; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAffineKeyAInputScene: + char affine_encrypt_keya_input_str[30]; + snprintf(affine_encrypt_keya_input_str, sizeof(affine_encrypt_keya_input_str), "Odd # between 1-25, not 13"); + number_input_set_header_text(number_input, affine_encrypt_keya_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->affine_keya_input, 1, 25); + break; + case FlipCryptAffineKeyBInputScene: + char affine_encrypt_keyb_input_str[30]; + snprintf(affine_encrypt_keyb_input_str, sizeof(affine_encrypt_keyb_input_str), "Enter key (%d - %d)", 1, 30); + number_input_set_header_text(number_input, affine_encrypt_keyb_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->affine_keyb_input, 1, 30); + break; + case FlipCryptAffineDecryptKeyAInputScene: + char affine_decrypt_keya_input_str[30]; + snprintf(affine_decrypt_keya_input_str, sizeof(affine_decrypt_keya_input_str), "Odd # between 1-25, not 13"); + number_input_set_header_text(number_input, affine_decrypt_keya_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->affine_keya_input, 1, 25); + break; + case FlipCryptAffineDecryptKeyBInputScene: + char affine_decrypt_keyb_input_str[30]; + snprintf(affine_decrypt_keyb_input_str, sizeof(affine_decrypt_keyb_input_str), "Enter key (%d - %d)", 1, 30); + number_input_set_header_text(number_input, affine_decrypt_keyb_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->affine_keyb_input, 1, 30); + break; + case FlipCryptCaesarKeyInputScene: + char caesar_key_input_str[30]; + snprintf(caesar_key_input_str, sizeof(caesar_key_input_str), "Enter key (%d - %d)", 1, 26); + number_input_set_header_text(number_input, caesar_key_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->caesar_key_input, 1, 26); + break; + case FlipCryptCaesarDecryptKeyInputScene: + char caesar_decrypt_key_input_str[30]; + snprintf(caesar_decrypt_key_input_str, sizeof(caesar_decrypt_key_input_str), "Enter key (%d - %d)", 1, 26); + number_input_set_header_text(number_input, caesar_decrypt_key_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->caesar_key_input, 1, 26); + break; + case FlipCryptRailfenceKeyInputScene: + char railfence_key_input_str[30]; + snprintf(railfence_key_input_str, sizeof(railfence_key_input_str), "Enter key (%d - %d)", 1, 8); + number_input_set_header_text(number_input, railfence_key_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->railfence_key_input, 1, 8); + break; + case FlipCryptRailfenceDecryptKeyInputScene: + char railfence_decrypt_key_input_str[30]; + snprintf(railfence_decrypt_key_input_str, sizeof(railfence_decrypt_key_input_str), "Enter key (%d - %d)", 1, 26); + number_input_set_header_text(number_input, railfence_decrypt_key_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->railfence_key_input, 1, 26); + break; + case FlipCryptScytaleKeywordInputScene: + char scytale_keyword_input_str[30]; + snprintf(scytale_keyword_input_str, sizeof(scytale_keyword_input_str), "Enter key (%d - %d)", 1, 9); + number_input_set_header_text(number_input, scytale_keyword_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->scytale_keyword_input, 1, 9); + break; + case FlipCryptScytaleDecryptKeywordInputScene: + char scytale_decrypt_keyword_input_str[30]; + snprintf(scytale_decrypt_keyword_input_str, sizeof(scytale_decrypt_keyword_input_str), "Enter key (%d - %d)", 1, 9); + number_input_set_header_text(number_input, scytale_decrypt_keyword_input_str); + number_input_set_result_callback(number_input, number_input_scene_callback, context, app->scytale_keyword_input, 1, 9); + break; + default: + break; + } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptNumberInputView); +} + +// Cipher / Hash submenu initialization +void cipher_submenu_scene_on_enter(void* context) { + App* app = context; + submenu_reset(app->submenu); + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAESSubmenuScene: + submenu_set_header(app->submenu, "AES-128 Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptAffineSubmenuScene: + submenu_set_header(app->submenu, "Affine Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptAtbashSubmenuScene: + submenu_set_header(app->submenu, "Atbash Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptBaconianSubmenuScene: + submenu_set_header(app->submenu, "Baconian Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptBeaufortSubmenuScene: + submenu_set_header(app->submenu, "Beaufort Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptCaesarSubmenuScene: + submenu_set_header(app->submenu, "Caesar Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptPlayfairSubmenuScene: + submenu_set_header(app->submenu, "Playfair Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptPolybiusSubmenuScene: + submenu_set_header(app->submenu, "Polybius Square"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptPortaSubmenuScene: + submenu_set_header(app->submenu, "Porta Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 3, cipher_learn_submenu_callback, app); + break; + case FlipCryptRailfenceSubmenuScene: + submenu_set_header(app->submenu, "Railfence Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 3, cipher_learn_submenu_callback, app); + break; + case FlipCryptRC4SubmenuScene: + submenu_set_header(app->submenu, "RC4 Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptROT13SubmenuScene: + submenu_set_header(app->submenu, "ROT-13 Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptScytaleSubmenuScene: + submenu_set_header(app->submenu, "Scytale Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptVigenereSubmenuScene: + submenu_set_header(app->submenu, "Vigenere Cipher"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptBlake2SubmenuScene: + submenu_set_header(app->submenu, "BLAKE-2s Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptFNV1ASubmenuScene: + submenu_set_header(app->submenu, "FNV-1A Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptMD2SubmenuScene: + submenu_set_header(app->submenu, "MD2 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptMD5SubmenuScene: + submenu_set_header(app->submenu, "MD5 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptMurmur3SubmenuScene: + submenu_set_header(app->submenu, "MurmurHash3"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptSipSubmenuScene: + submenu_set_header(app->submenu, "SipHash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptSHA1SubmenuScene: + submenu_set_header(app->submenu, "SHA-1 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptSHA224SubmenuScene: + submenu_set_header(app->submenu, "SHA-224 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptSHA256SubmenuScene: + submenu_set_header(app->submenu, "SHA-256 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptSHA384SubmenuScene: + submenu_set_header(app->submenu, "SHA-384 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptSHA512SubmenuScene: + submenu_set_header(app->submenu, "SHA-512 Hash"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptXXSubmenuScene: + submenu_set_header(app->submenu, "XXHash64"); + submenu_add_item(app->submenu, "Hash Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 1, cipher_learn_submenu_callback, app); + break; + case FlipCryptBase32SubmenuScene: + submenu_set_header(app->submenu, "Base32 Encoding"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptBase58SubmenuScene: + submenu_set_header(app->submenu, "Base58 Encoding"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + case FlipCryptBase64SubmenuScene: + submenu_set_header(app->submenu, "Base64 Encoding"); + submenu_add_item(app->submenu, "Encode Text", 0, cipher_encrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Decode Text", 1, cipher_decrypt_submenu_callback, app); + submenu_add_item(app->submenu, "Learn", 2, cipher_learn_submenu_callback, app); + break; + default: + submenu_set_header(app->submenu, "Error!"); + break; + } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptSubmenuView); +} + +// Moves view forward from input to output scenes +void flip_crypt_text_input_callback(void* context) { + App* app = context; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptSaveTextInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSaveScene); + break; + case FlipCryptAESKeyInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESInputScene); + break; + case FlipCryptAESInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESOutputScene); + break; + case FlipCryptAESDecryptKeyInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESDecryptInputScene); + break; + case FlipCryptAESDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAESDecryptOutputScene); + break; + case FlipCryptAffineInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAffineOutputScene); + break; + case FlipCryptAffineDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAffineDecryptOutputScene); + break; + case FlipCryptAtbashInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAtbashOutputScene); + break; + case FlipCryptAtbashDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptAtbashDecryptOutputScene); + break; + case FlipCryptBaconianInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBaconianOutputScene); + break; + case FlipCryptBaconianDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBaconianDecryptOutputScene); + break; + case FlipCryptBeaufortKeyInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortInputScene); + break; + case FlipCryptBeaufortInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortOutputScene); + break; + case FlipCryptBeaufortDecryptKeyInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortDecryptInputScene); + break; + case FlipCryptBeaufortDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBeaufortDecryptOutputScene); + break; + case FlipCryptCaesarKeyInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarInputScene); + break; + case FlipCryptCaesarInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarOutputScene); + break; + case FlipCryptCaesarDecryptKeyInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarDecryptInputScene); + break; + case FlipCryptCaesarDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptCaesarDecryptOutputScene); + break; + case FlipCryptPlayfairKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairInputScene); + break; + case FlipCryptPlayfairInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairOutputScene); + break; + case FlipCryptPlayfairDecryptKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairDecryptInputScene); + break; + case FlipCryptPlayfairDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPlayfairDecryptOutputScene); + break; + case FlipCryptPolybiusInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPolybiusOutputScene); + break; + case FlipCryptPolybiusDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPolybiusDecryptOutputScene); + break; + case FlipCryptPortaInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaOutputScene); + break; + case FlipCryptPortaDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaDecryptOutputScene); + break; + case FlipCryptPortaKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaInputScene); + break; + case FlipCryptPortaDecryptKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptPortaDecryptInputScene); + break; + case FlipCryptRailfenceInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceOutputScene); + break; + case FlipCryptRailfenceDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRailfenceDecryptOutputScene); + break; + case FlipCryptRC4InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4OutputScene); + break; + case FlipCryptRC4KeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4InputScene); + break; + case FlipCryptRC4DecryptKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4DecryptInputScene); + break; + case FlipCryptRC4DecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptRC4DecryptOutputScene); + break; + case FlipCryptROT13InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptROT13OutputScene); + break; + case FlipCryptROT13DecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptROT13DecryptOutputScene); + break; + case FlipCryptScytaleInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleOutputScene); + break; + case FlipCryptScytaleDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptScytaleDecryptOutputScene); + break; + case FlipCryptVigenereInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereOutputScene); + break; + case FlipCryptVigenereKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereInputScene); + break; + case FlipCryptVigenereDecryptKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereDecryptInputScene); + break; + case FlipCryptVigenereDecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptVigenereDecryptOutputScene); + break; + case FlipCryptBlake2InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBlake2OutputScene); + break; + case FlipCryptFNV1AInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptFNV1AOutputScene); + break; + case FlipCryptMD2InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMD2OutputScene); + break; + case FlipCryptMD5InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMD5OutputScene); + break; + case FlipCryptMurmur3InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptMurmur3OutputScene); + break; + case FlipCryptSipInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSipOutputScene); + break; + case FlipCryptSipKeywordInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSipInputScene); + break; + case FlipCryptSHA1InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA1OutputScene); + break; + case FlipCryptSHA224InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA224OutputScene); + break; + case FlipCryptSHA256InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA256OutputScene); + break; + case FlipCryptSHA384InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA384OutputScene); + break; + case FlipCryptSHA512InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptSHA512OutputScene); + break; + case FlipCryptXXInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptXXOutputScene); + break; + case FlipCryptBase32InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase32OutputScene); + break; + case FlipCryptBase32DecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase32DecryptOutputScene); + break; + case FlipCryptBase58InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase58OutputScene); + break; + case FlipCryptBase58DecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase58DecryptOutputScene); + break; + case FlipCryptBase64InputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase64OutputScene); + break; + case FlipCryptBase64DecryptInputScene: + scene_manager_next_scene(app->scene_manager, FlipCryptBase64DecryptOutputScene); + break; + default: + break; + } +} + +// Text input views +void cipher_input_scene_on_enter(void* context) { + App* app = context; + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter text"); + + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptSaveTextInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter file name"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->save_name_input, app->save_name_input_size, true); + break; + case FlipCryptAESInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptAESKeyInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key (sixteen chars)"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->aes_key_input, app->aes_key_input_size, true); + break; + case FlipCryptAESDecryptKeyInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key (sixteen chars)"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->aes_key_input, app->aes_key_input_size, true); + break; + case FlipCryptAESDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptAffineInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptAffineDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptAtbashInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptAtbashDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBaconianInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBaconianDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBeaufortInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBeaufortKeyInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->beaufort_key_input, app->beaufort_key_input_size, true); + break; + case FlipCryptBeaufortDecryptKeyInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->beaufort_key_input, app->beaufort_key_input_size, true); + break; + case FlipCryptBeaufortDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptCaesarInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptCaesarDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPlayfairInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPlayfairKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->playfair_keyword_input, app->playfair_keyword_input_size, true); + break; + case FlipCryptPlayfairDecryptKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->playfair_keyword_input, app->playfair_keyword_input_size, true); + break; + case FlipCryptPlayfairDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPolybiusInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPolybiusDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPortaInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPortaDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPortaKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptPortaDecryptKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptRailfenceInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptRailfenceDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptRC4KeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->rc4_keyword_input, app->rc4_keyword_input_size, true); + break; + case FlipCryptRC4DecryptKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->rc4_keyword_input, app->rc4_keyword_input_size, true); + break; + case FlipCryptRC4InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptRC4DecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptROT13InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptROT13DecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptScytaleInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptScytaleDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptVigenereKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->vigenere_keyword_input, app->vigenere_keyword_input_size, true); + break; + case FlipCryptVigenereDecryptKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter key"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->vigenere_keyword_input, app->vigenere_keyword_input_size, true); + break; + case FlipCryptVigenereInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptVigenereDecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBlake2InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptFNV1AInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptMD2InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptMD5InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptMurmur3InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptSipKeywordInputScene: + text_input_reset(app->text_input); + text_input_set_header_text(app->text_input, "Enter 16 char keyword"); + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->sip_keyword_input, app->sip_keyword_input_size, true); + break; + case FlipCryptSipInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptSHA1InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptSHA224InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptSHA256InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptSHA384InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptSHA512InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptXXInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBase32InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBase32DecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBase58InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBase58DecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBase64InputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + case FlipCryptBase64DecryptInputScene: + text_input_set_result_callback(app->text_input, flip_crypt_text_input_callback, app, app->universal_input, app->universal_input_size, true); + break; + default: + break; + } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptTextInputView); +} + +// Output dialog +static void dialog_ex_callback(DialogExResult result, void* context) { + App* app = context; + + switch(result) { + case DialogExResultLeft: + case DialogExPressLeft: + scene_manager_next_scene(app->scene_manager, FlipCryptNFCScene); + break; + + case DialogExResultRight: + case DialogExPressRight: + scene_manager_next_scene(app->scene_manager, FlipCryptQRScene); + break; + + case DialogExResultCenter: + case DialogExPressCenter: + scene_manager_next_scene(app->scene_manager, FlipCryptSaveTextInputScene); + break; + + case DialogExReleaseLeft: + case DialogExReleaseRight: + case DialogExReleaseCenter: + break; + } +} + +void dialog_cipher_output_scene_on_enter(void* context) { + App* app = context; + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + static const char sha_hex_chars[] = "0123456789abcdef"; + switch(current) { + case FlipCryptAESOutputScene: + if(strlen(app->aes_key_input) != 16) { + dialog_ex_set_text(app->dialog_ex, "Key must be 16 bytes", 64, 18, AlignCenter, AlignCenter); + break; + } + size_t aes_encrypt_len = strlen(app->universal_input); + if(aes_encrypt_len > 128) aes_encrypt_len = 128; + uint8_t key[16]; + memcpy(key, app->aes_key_input, 16); + uint8_t iv[16] = {0}; + uint8_t aes_encrypt_encrypted[128]; + memcpy(aes_encrypt_encrypted, app->universal_input, aes_encrypt_len); + struct AES_ctx aes_ctx; + AES_init_ctx_iv(&aes_ctx, key, iv); + AES_CTR_xcrypt_buffer(&aes_ctx, aes_encrypt_encrypted, aes_encrypt_len); + char aes_output_text[2 * 128 + 1]; + aes_bytes_to_hex(aes_encrypt_encrypted, aes_encrypt_len, aes_output_text); + dialog_ex_set_text(app->dialog_ex, aes_output_text, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("aes.txt"), aes_output_text); + app->last_output_scene = "AES"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + + case FlipCryptAESDecryptOutputScene: + if(strlen(app->aes_key_input) != 16) { + dialog_ex_set_text(app->dialog_ex, "Key must be 16 bytes", 64, 18, AlignCenter, AlignCenter); + break; + } + size_t aes_decrypt_input_len = strlen(app->universal_input); + if(aes_decrypt_input_len > 256) aes_decrypt_input_len = 256; + if(aes_decrypt_input_len % 2 != 0) { + dialog_ex_set_text(app->dialog_ex, "Invalid hex length", 64, 18, AlignCenter, AlignCenter); + break; + } + size_t aes_decrypt_len = aes_decrypt_input_len / 2; + uint8_t aes_decrypt_key[16]; + memcpy(aes_decrypt_key, app->aes_key_input, 16); + uint8_t aes_iv[16] = {0}; + uint8_t aes_decrypt_encrypted[128]; + aes_hex_to_bytes(app->universal_input, aes_decrypt_encrypted, aes_decrypt_input_len); + struct AES_ctx aes_decrypt_ctx; + AES_init_ctx_iv(&aes_decrypt_ctx, aes_decrypt_key, aes_iv); + AES_CTR_xcrypt_buffer(&aes_decrypt_ctx, aes_decrypt_encrypted, aes_decrypt_len); + char aes_decrypt_output_text[129]; + memcpy(aes_decrypt_output_text, aes_decrypt_encrypted, aes_decrypt_len); + aes_decrypt_output_text[aes_decrypt_len] = '\0'; + dialog_ex_set_text(app->dialog_ex, aes_decrypt_output_text, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("aes_decrypt.txt"), aes_decrypt_output_text); + app->last_output_scene = "AESDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptAffineOutputScene: + dialog_ex_set_text(app->dialog_ex, encode_affine(app->universal_input, app->affine_keya_input, app->affine_keyb_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("affine.txt"), encode_affine(app->universal_input, app->affine_keya_input, app->affine_keyb_input)); + app->last_output_scene = "Affine"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptAffineDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, decode_affine(app->universal_input, app->affine_keya_input, app->affine_keyb_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("affine_decrypt.txt"), decode_affine(app->universal_input, app->affine_keya_input, app->affine_keyb_input)); + app->last_output_scene = "AffineDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptAtbashOutputScene: + dialog_ex_set_text(app->dialog_ex, atbash_encrypt_or_decrypt(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("atbash.txt"), atbash_encrypt_or_decrypt(app->universal_input)); + app->last_output_scene = "Atbash"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptAtbashDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, atbash_encrypt_or_decrypt(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("atbash_decrypt.txt"), atbash_encrypt_or_decrypt(app->universal_input)); + app->last_output_scene = "AtbashDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBaconianOutputScene: + dialog_ex_set_text(app->dialog_ex, baconian_encrypt(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("baconian.txt"), baconian_encrypt(app->universal_input)); + app->last_output_scene = "Baconian"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBaconianDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, baconian_decrypt(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("baconian_decrypt.txt"), baconian_decrypt(app->universal_input)); + app->last_output_scene = "BaconianDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBeaufortOutputScene: + dialog_ex_set_text(app->dialog_ex, beaufort_cipher_encrypt_and_decrypt(app->universal_input, app->beaufort_key_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("beaufort.txt"), beaufort_cipher_encrypt_and_decrypt(app->universal_input, app->beaufort_key_input)); + app->last_output_scene = "Beaufort"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBeaufortDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, beaufort_cipher_encrypt_and_decrypt(app->universal_input, app->beaufort_key_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("beaufort_decrypt.txt"), beaufort_cipher_encrypt_and_decrypt(app->universal_input, app->beaufort_key_input)); + app->last_output_scene = "BeaufortDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptCaesarOutputScene: + dialog_ex_set_text(app->dialog_ex, encode_caesar(app->universal_input, app->caesar_key_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("caesar.txt"), encode_caesar(app->universal_input, app->caesar_key_input)); + app->last_output_scene = "Caesar"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptCaesarDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, decode_caesar(app->universal_input, app->caesar_key_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("caesar_decrypt.txt"), decode_caesar(app->universal_input, app->caesar_key_input)); + app->last_output_scene = "CaesarDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptPlayfairOutputScene: + dialog_ex_set_text(app->dialog_ex, playfair_encrypt(app->universal_input, playfair_make_table(app->playfair_keyword_input)), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("playfair.txt"), playfair_encrypt(app->universal_input, playfair_make_table(app->playfair_keyword_input))); + app->last_output_scene = "Playfair"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptPlayfairDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, playfair_decrypt(app->universal_input, playfair_make_table(app->playfair_keyword_input)), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("playfair_decrypt.txt"), playfair_decrypt(app->universal_input, playfair_make_table(app->playfair_keyword_input))); + app->last_output_scene = "PlayfairDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptPolybiusOutputScene: + dialog_ex_set_text(app->dialog_ex, encrypt_polybius(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("polybius.txt"), encrypt_polybius(app->universal_input)); + app->last_output_scene = "Polybius"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptPolybiusDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, decrypt_polybius(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("polybius_decrypt.txt"), decrypt_polybius(app->universal_input)); + app->last_output_scene = "PolybiusDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptPortaOutputScene: + dialog_ex_set_text(app->dialog_ex, porta_encrypt(app->universal_input, app->porta_keyword_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("porta.txt"), porta_encrypt(app->universal_input, app->porta_keyword_input)); + app->last_output_scene = "Porta"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptPortaDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, "test", 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("porta_decrypt.txt"), "test"); + app->last_output_scene = "PortaDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptRailfenceOutputScene: + dialog_ex_set_text(app->dialog_ex, rail_fence_encrypt(app->universal_input, app->railfence_key_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("railfence.txt"), rail_fence_encrypt(app->universal_input, app->railfence_key_input)); + app->last_output_scene = "Railfence"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptRailfenceDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, rail_fence_decrypt(app->universal_input, app->railfence_key_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("railfence_decrypt.txt"), rail_fence_decrypt(app->universal_input, app->railfence_key_input)); + app->last_output_scene = "RailfenceDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptRC4OutputScene: + size_t rc4_input_len = strlen(app->universal_input); + unsigned char* rc4_encrypted = rc4_encrypt_and_decrypt(app->rc4_keyword_input, (const unsigned char*)app->universal_input, rc4_input_len); + if (!rc4_encrypted) { + dialog_ex_set_text(app->dialog_ex, "Encryption failed", 64, 18, AlignCenter, AlignCenter); + break; + } + char* rc4_encrypt_hex_output = rc4_to_hex((const char*)rc4_encrypted, rc4_input_len); + dialog_ex_set_text(app->dialog_ex, rc4_encrypt_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("rc4.txt"), rc4_encrypt_hex_output); + app->last_output_scene = "RC4"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + free(rc4_encrypt_hex_output); + free(rc4_encrypted); + break; + case FlipCryptRC4DecryptOutputScene: + size_t rc4_encrypted_len; + unsigned char* rc4_encrypted_bytes = rc4_hex_to_bytes(app->universal_input, &rc4_encrypted_len); + if (!rc4_encrypted_bytes) { + dialog_ex_set_text(app->dialog_ex, "Invalid hex input", 64, 18, AlignCenter, AlignCenter); + break; + } + unsigned char* rc4_decrypted = rc4_encrypt_and_decrypt(app->rc4_keyword_input, rc4_encrypted_bytes, rc4_encrypted_len); + if (!rc4_decrypted) { + dialog_ex_set_text(app->dialog_ex, "Decryption failed", 64, 18, AlignCenter, AlignCenter); + free(rc4_encrypted_bytes); + break; + } + char* rc4_decrypted_str = malloc(rc4_encrypted_len + 1); + if (rc4_decrypted_str) { + memcpy(rc4_decrypted_str, rc4_decrypted, rc4_encrypted_len); + rc4_decrypted_str[rc4_encrypted_len] = '\0'; + dialog_ex_set_text(app->dialog_ex, rc4_decrypted_str, 64, 18, AlignCenter, AlignCenter); + free(rc4_decrypted_str); + } + save_result_generic(APP_DATA_PATH("rc4_decrypt.txt"), rc4_decrypted_str); + app->last_output_scene = "RC4Decrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + free(rc4_encrypted_bytes); + free(rc4_decrypted); + break; + case FlipCryptROT13OutputScene: + dialog_ex_set_text(app->dialog_ex, encrypt_rot13(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("rot13.txt"), encrypt_rot13(app->universal_input)); + app->last_output_scene = "ROT13"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptROT13DecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, decrypt_rot13(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("rot13_decrypt.txt"), decrypt_rot13(app->universal_input)); + app->last_output_scene = "ROT13Decrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptScytaleOutputScene: + dialog_ex_set_text(app->dialog_ex, scytale_encrypt(app->universal_input, app->scytale_keyword_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("scytale.txt"), scytale_encrypt(app->universal_input, app->scytale_keyword_input)); + app->last_output_scene = "Scytale"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptScytaleDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, scytale_decrypt(app->universal_input, app->scytale_keyword_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("scytale_decrypt.txt"), scytale_decrypt(app->universal_input, app->scytale_keyword_input)); + app->last_output_scene = "ScytaleDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptVigenereOutputScene: + dialog_ex_set_text(app->dialog_ex, vigenere_encrypt(app->universal_input, app->vigenere_keyword_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("vigenere.txt"), vigenere_encrypt(app->universal_input, app->vigenere_keyword_input)); + app->last_output_scene = "Vigenere"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptVigenereDecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, vigenere_decrypt(app->universal_input, app->vigenere_keyword_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("vigenere_decrypt.txt"), vigenere_decrypt(app->universal_input, app->vigenere_keyword_input)); + app->last_output_scene = "VigenereDecrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBlake2OutputScene: + Blake2sContext blake2_ctx; + uint8_t blake2_hash[BLAKE2S_OUTLEN]; + char blake2_hex_output[BLAKE2S_OUTLEN * 2 + 1] = {0}; + blake2s_init(&blake2_ctx); + blake2s_update(&blake2_ctx, (const uint8_t*)app->universal_input, strlen(app->universal_input)); + blake2s_finalize(&blake2_ctx, blake2_hash); + blake2s_to_hex(blake2_hash, blake2_hex_output); + dialog_ex_set_text(app->dialog_ex, blake2_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("blake2.txt"), blake2_hex_output); + app->last_output_scene = "Blake2"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptFNV1AOutputScene: + char fnv1a_hash_str[11]; + Fnv32_t fnv1a_hash = fnv_32a_str(app->universal_input, FNV1_32A_INIT); + snprintf(fnv1a_hash_str, sizeof(fnv1a_hash_str), "0x%08lx", fnv1a_hash); + dialog_ex_set_text(app->dialog_ex, fnv1a_hash_str, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("fnv1a.txt"), fnv1a_hash_str); + app->last_output_scene = "FNV1A"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptMD2OutputScene: + BYTE hash[MD2_BLOCK_SIZE]; + MD2_CTX ctx; + md2_init(&ctx); + md2_update(&ctx, (const BYTE*)app->universal_input, strlen(app->universal_input)); + md2_final(&ctx, hash); + char md2_hash_str[MD2_BLOCK_SIZE * 2 + 1]; + for (int i = 0; i < MD2_BLOCK_SIZE; ++i) { + snprintf(&md2_hash_str[i * 2], 3, "%02x", hash[i]); + } + dialog_ex_set_text(app->dialog_ex, md2_hash_str, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("md2.txt"), md2_hash_str); + app->last_output_scene = "MD2"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptMD5OutputScene: + uint8_t md5_hash[16]; + char md5_hex_output[33] = {0}; + MD5Context md5_ctx; + md5_init(&md5_ctx); + md5_update(&md5_ctx, (const uint8_t*)app->universal_input, strlen(app->universal_input)); + md5_finalize(&md5_ctx, md5_hash); + md5_to_hex(md5_hash, md5_hex_output); + dialog_ex_set_text(app->dialog_ex, md5_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("md5.txt"), md5_hex_output); + app->last_output_scene = "MD5"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptMurmur3OutputScene: + dialog_ex_set_text(app->dialog_ex, MurmurHash3_x86_32(app->universal_input, strlen(app->universal_input), 0), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("murmur3.txt"), MurmurHash3_x86_32(app->universal_input, strlen(app->universal_input), 0)); + app->last_output_scene = "Murmur3"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptSipOutputScene: + uint8_t siphash_output[8]; + siphash(app->universal_input, strlen(app->universal_input), app->sip_keyword_input, siphash_output, 8); + char siphash_str[17]; + for (int i = 0; i < 8; ++i) { + snprintf(&siphash_str[i * 2], 3, "%02x", siphash_output[i]); + } + siphash_str[16] = '\0'; + dialog_ex_set_text(app->dialog_ex, siphash_str, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("sip.txt"), siphash_str); + app->last_output_scene = "Sip"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptSHA1OutputScene: + Sha1Context sha1_ctx; + uint8_t sha1_hash[20]; + char sha1_hex_output[41] = {0}; + sha1_init(&sha1_ctx); + sha1_update(&sha1_ctx, (const uint8_t*)app->universal_input, strlen(app->universal_input)); + sha1_finalize(&sha1_ctx, sha1_hash); + sha1_to_hex(sha1_hash, sha1_hex_output); + dialog_ex_set_text(app->dialog_ex, sha1_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("sha1.txt"), sha1_hex_output); + app->last_output_scene = "SHA1"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptSHA224OutputScene: + uint8_t sha224_hash[28]; + char sha224_hex_output[57] = {0}; + sha224((const uint8_t *)app->universal_input, (uint64)strlen(app->universal_input), sha224_hash); + for (int i = 0; i < 28; i++) { + sha224_hex_output[i * 2] = sha_hex_chars[(sha224_hash[i] >> 4) & 0xF]; + sha224_hex_output[i * 2 + 1] = sha_hex_chars[sha224_hash[i] & 0xF]; + } + sha224_hex_output[56] = '\0'; + dialog_ex_set_text(app->dialog_ex, sha224_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("sha224.txt"), sha224_hex_output); + app->last_output_scene = "SHA224"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptSHA256OutputScene: + uint8_t sha256_hash[32]; + char sha256_hex_output[65] = {0}; + sha256((const uint8_t *)app->universal_input, (uint64)strlen(app->universal_input), sha256_hash); + for (int i = 0; i < 32; i++) { + sha256_hex_output[i * 2] = sha_hex_chars[(sha256_hash[i] >> 4) & 0xF]; + sha256_hex_output[i * 2 + 1] = sha_hex_chars[sha256_hash[i] & 0xF]; + } + sha256_hex_output[64] = '\0'; + dialog_ex_set_text(app->dialog_ex, sha256_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("sha256.txt"), sha256_hex_output); + app->last_output_scene = "SHA256"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptSHA384OutputScene: + uint8_t sha384_hash[48]; + char sha384_hex_output[97] = {0}; + sha384((const uint8_t *)app->universal_input, (uint64)strlen(app->universal_input), sha384_hash); + for (int i = 0; i < 48; i++) { + sha384_hex_output[i * 2] = sha_hex_chars[(sha384_hash[i] >> 4) & 0xF]; + sha384_hex_output[i * 2 + 1] = sha_hex_chars[sha384_hash[i] & 0xF]; + } + sha384_hex_output[96] = '\0'; + dialog_ex_set_text(app->dialog_ex, sha384_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("sha384.txt"), sha384_hex_output); + app->last_output_scene = "SHA384"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptSHA512OutputScene: + uint8_t sha512_hash[64]; + char sha512_hex_output[129] = {0}; + sha512((const uint8_t *)app->universal_input, (uint64)strlen(app->universal_input), sha512_hash); + for (int i = 0; i < 64; i++) { + sha512_hex_output[i * 2] = sha_hex_chars[(sha512_hash[i] >> 4) & 0xF]; + sha512_hex_output[i * 2 + 1] = sha_hex_chars[sha512_hash[i] & 0xF]; + } + sha512_hex_output[128] = '\0'; + dialog_ex_set_text(app->dialog_ex, sha512_hex_output, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("sha512.txt"), sha512_hex_output); + app->last_output_scene = "SHA512"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptXXOutputScene: + uint64_t xxhash = XXH64(app->universal_input, strlen(app->universal_input), 0); + char xxhash_str[17]; + snprintf(xxhash_str, sizeof(xxhash_str), "%016llX", xxhash); + dialog_ex_set_text(app->dialog_ex, xxhash_str, 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("xx.txt"), xxhash_str); + app->last_output_scene = "XX"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBase32OutputScene: + dialog_ex_set_text(app->dialog_ex, base32_encode((const uint8_t*)app->universal_input, strlen(app->universal_input)), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("base32.txt"), base32_encode((const uint8_t*)app->universal_input, strlen(app->universal_input))); + app->last_output_scene = "Base32"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBase32DecryptOutputScene: + size_t base32_decoded_len; + dialog_ex_set_text(app->dialog_ex, (const char*)base32_decode(app->universal_input, &base32_decoded_len), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("base32_decrypt.txt"), (const char*)base32_decode(app->universal_input, &base32_decoded_len)); + app->last_output_scene = "Base32Decrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBase58OutputScene: + dialog_ex_set_text(app->dialog_ex, base58_encode(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("base58.txt"), base58_encode(app->universal_input)); + app->last_output_scene = "Base58"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBase58DecryptOutputScene: + dialog_ex_set_text(app->dialog_ex, base58_decode(app->universal_input), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("base58_decrypt.txt"), base58_decode(app->universal_input)); + app->last_output_scene = "Base58Decrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBase64OutputScene: + dialog_ex_set_text(app->dialog_ex, base64_encode((const unsigned char*)app->universal_input, strlen(app->universal_input)), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("base64.txt"), base64_encode((const unsigned char*)app->universal_input, strlen(app->universal_input))); + app->last_output_scene = "Base64"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + case FlipCryptBase64DecryptOutputScene: + size_t base64_decoded_len; + dialog_ex_set_text(app->dialog_ex, (const char*)base64_decode(app->universal_input, &base64_decoded_len), 64, 18, AlignCenter, AlignCenter); + save_result_generic(APP_DATA_PATH("base64_decrypt.txt"), (const char*)base64_decode(app->universal_input, &base64_decoded_len)); + app->last_output_scene = "Base64Decrypt"; + dialog_ex_set_left_button_text(app->dialog_ex, "NFC"); + dialog_ex_set_center_button_text(app->dialog_ex, "Save"); + dialog_ex_set_right_button_text(app->dialog_ex, "QR"); + break; + default: + break; + } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptDialogExView); +} + +// Learn screen views +void cipher_learn_scene_on_enter(void* context) { + App* app = context; + widget_reset(app->widget); + FlipCryptScene current = scene_manager_get_current_scene(app->scene_manager); + switch(current) { + case FlipCryptAESLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "AES-128 (Advanced Encryption Standard with a 128-bit key) is a symmetric block cipher widely used for data encryption. It encrypts data in fixed blocks of 128 bits using a 128-bit key and operates through 10 rounds of transformations, including substitution, permutation, mixing, and key addition. AES-128 is known for its strong security and efficiency, and is a standard for protecting sensitive data in everything from government communications to online banking. Unlike classical ciphers, AES relies on complex mathematical operations and is resistant to all known practical cryptographic attacks when implemented properly."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptAffineLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Affine cipher is a type of monoalphabetic substitution cipher that uses a mathematical formula to encrypt each letter: E(x) = (a x x + b) mod 26, where x is the position of the plaintext letter in the alphabet (A = 0, B = 1, etc.), and a and b are keys. The value of a must be coprime with 26 to ensure that each letter maps uniquely. Decryption uses the inverse of a with the formula D(x) = a^-1 x (x - b) mod 26. The Affine cipher combines multiplicative and additive shifts, making it slightly more secure than a Caesar cipher, but still vulnerable to frequency analysis."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptAtbashLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Atbash cipher is a classical substitution cipher originally used for the Hebrew alphabet. It works by reversing the alphabet so that the first letter becomes the last, the second becomes the second-to-last, and so on. For example, in the Latin alphabet, \'A\' becomes \'Z\', \'B\' becomes \'Y\', and \'C\' becomes \'X\'. It is a simple and symmetric cipher, meaning that the same algorithm is used for both encryption and decryption. Though not secure by modern standards, the Atbash cipher is often studied for its historical significance."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptBaconianLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Baconian cipher, developed by Francis Bacon in the early 17th century, is a steganographic cipher that encodes each letter of the alphabet into a series of five binary characters, typically using combinations of \'A\' and \'B\'. For example, the letter \'A\' is represented as \'AAAAA\', \'B\' as \'AAAAB\', and so on. This binary code can then be hidden within text, images, or formatting, making it a method of concealed rather than encrypted communication. The Baconian cipher is notable for being an early example of steganography and is often used in historical or educational contexts."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptBeaufortLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Beaufort cipher is a polyalphabetic substitution cipher that is similar to the Vigenere cipher but uses a slightly different encryption algorithm. Instead of adding the key to the plaintext, it subtracts the plaintext letter from the key letter using a tabula recta, meaning that the same process is used for both encryption and decryption. The cipher was named after Sir Francis Beaufort and was historically used in applications like encrypting naval signals. While more secure than simple ciphers like Caesar, it is still vulnerable to modern cryptanalysis techniques."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptCaesarLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Caesar cipher is a simple and well-known substitution cipher named after Julius Caesar, who reportedly used it to protect military messages. It works by shifting each letter in the plaintext a fixed number of positions down the alphabet. For example, with a shift of 3, \'A\' becomes \'D\', \'B\' becomes \'E\', and so on. After \'Z\', the cipher wraps around to the beginning of the alphabet. While easy to understand and implement, the Caesar cipher is also extremely easy to break."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptPlayfairLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Playfair cipher is a manual symmetric encryption technique invented in 1854 by Charles Wheatstone but popularized by Lord Playfair. It encrypts pairs of letters (digraphs) instead of single letters, making it more secure than simple substitution ciphers. The cipher uses a 5x5 grid of letters constructed from a keyword, combining \'I\' and \'J\' to fit the alphabet into 25 cells. To encrypt, each pair of letters is located in the grid, and various rules are applied based on their positions like same row, same column, or rectangle to substitute them with new letters. The Playfair cipher was used historically for military communication due to its relative ease of use and stronger encryption for its time."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptPolybiusLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Polybius square is a classical cipher that uses a 5x5 grid filled with letters of the alphabet to convert plaintext into pairs of numbers. Each letter is identified by its row and column numbers in the grid. For example, 'A' might be encoded as '11', 'B' as '12', and so on. Since the Latin alphabet has 26 letters, 'I' and 'J' are typically combined to fit into the 25-cell grid. The Polybius square is easy to implement and was historically used for signaling and cryptography in wartime. It is simple and easy to decode, and therefore offers minimal security by modern standards."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptPortaLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "Porta cipher learn here!"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptRailfenceLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Rail Fence cipher is a form of transposition cipher that rearranges the letters of the plaintext in a zigzag pattern across multiple \'rails\' (or rows), and then reads them off row by row to create the ciphertext. For example, using 3 rails, the message \'HELLO WORLD\' would be written in a zigzag across three lines and then read horizontally to produce the encrypted message. It\'s a simple method that relies on obscuring the letter order rather than substituting characters, and it\'s relatively easy to decrypt with enough trial and error."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptRC4LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "RC4 is a stream cipher designed by Ron Rivest in 1987 and widely used for its speed and simplicity. It generates a pseudorandom keystream that is XORed with the plaintext to produce ciphertext. RC4\'s internal state consists of a 256-byte array and a pair of index pointers, updated in a key-dependent manner. While once popular in protocols like SSL and WEP, RC4 has been found to have significant vulnerabilities, especially related to key scheduling, and is now considered insecure for modern uses."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptROT13LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "ROT13 (short for 'rotate by 13 places') is a simple letter substitution cipher used primarily to obscure text rather than securely encrypt it. It works by shifting each letter of the alphabet 13 positions forward, wrapping around from Z back to A if necessary. Because the alphabet has 26 letters, applying ROT13 twice returns the original text, making it a symmetric cipher. ROT13 is commonly used in online forums to hide spoilers, puzzles, or offensive content, but it offers no real security and can be easily reversed without a key."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptScytaleLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Scytale cipher is an ancient transposition cipher used by the Spartans. It involves wrapping a strip of parchment around a rod (scytale) of a fixed diameter and writing the message along the rod's surface. When unwrapped, the text appears scrambled unless it is rewrapped around a rod of the same size. The security relies on the secrecy of the rod's diameter. Although simple and easy to use, the Scytale cipher offers almost no security by modern standards and just of historical interest."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptVigenereLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "The Vigenere cipher is a classical polyalphabetic substitution cipher that uses a keyword to determine the shift for each letter of the plaintext. Each letter of the keyword corresponds to a Caesar cipher shift, which is applied cyclically over the plaintext. For example, with the keyword \'KEY\', the first letter of the plaintext is shifted by the position of \'K\', the second by \'E\', and so on. This method makes frequency analysis more difficult than in simple substitution ciphers, and it was considered unbreakable for centuries until modern cryptanalysis techniques were developed."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptBlake2LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "BLAKE-2s is a high performance cryptographic hash function designed as an improved alternative to MD5, SHA-1, and even SHA-2, offering strong security with faster hashing speeds. Developed by Jean-Philippe Aumasson and others, it builds on the cryptographic foundations of the BLAKE algorithm (a finalist in the SHA-3 competition) but is optimized for practical use. BLAKE2 comes in two main variants: BLAKE2b (optimized for 64-bit platforms) and BLAKE2s (for 8- to 32-bit platforms). It provides features like keyed hashing, salting, and personalization, making it suitable for applications like password hashing, digital signatures, and message authentication. BLAKE2 is widely adopted due to its balance of speed, simplicity, and security, and is used in software like Argon2 (a password hashing algorithm) and various blockchain projects."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptFNV1ALearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "FNV-1a (Fowler-Noll-Vo hash, variant 1a) is a simple, fast, and widely used non-cryptographic hash function designed for use in hash tables and data indexing. It works by starting with a fixed offset basis, then for each byte of input, it XORs the byte with the hash and multiplies the result by a prime number (commonly 16777619 for 32-bit or 1099511628211 for 64-bit). FNV-1a is known for its good distribution and performance on small inputs, but it's not cryptographically secure and should not be used for security-sensitive applications. Its simplicity and efficiency make it a favorite in performance-critical systems and embedded environments."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptMD2LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "MD2 (Message Digest 2) is a cryptographic hash function designed by Ronald Rivest in 1989. It produces a 128-bit (16-byte) hash value from an input of any length, typically used to verify data integrity. Although it was once widely used, MD2 is now considered obsolete due to its slow performance and vulnerabilities to collision attacks. As a result, more secure and efficient hash functions like SHA-2 or SHA-3 are recommended for modern applications. Despite its weaknesses, MD2 remains an important part of cryptographic history and legacy systems."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptMD5LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "MD5 (Message Digest Algorithm 5) is a widely used cryptographic hash function that produces a 128-bit (16 byte) hash value, typically rendered as a 32-character hexadecimal number. Originally designed for digital signatures and file integrity verification, MD5 is now considered cryptographically broken due to known collision vulnerabilities. While still used in some non-security-critical contexts, it is not recommended for new cryptographic applications."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptMurmur3LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "MurmurHash3 is a non-cryptographic hash function designed for fast hashing performance, primarily used in hash-based data structures like hash tables and bloom filters. It was developed by Austin Appleby and is the third and final version of the MurmurHash family. MurmurHash3 offers excellent distribution and low collision rates for general-purpose use, with versions optimized for both 32-bit and 128-bit outputs. Its speed and simplicity make it a popular choice in software like databases, compilers, and networking tools. However, it is not suitable for cryptographic purposes because it lacks the security properties needed to resist things like collision or preimage attacks."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptSipLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "SipHash is a fast, cryptographically secure hash function designed specifically to protect hash tables from DoS attacks caused by hash collisions. Developed by Jean-Philippe Aumasson and Daniel J. Bernstein, SipHash uses a secret key to produce a 64-bit (or 128-bit) hash, making it resistant to hash-flooding attacks where an attacker intentionally causes many collisions. While not as fast as non-cryptographic hashes like MurmurHash, it strikes a balance between speed and security, making it ideal for situations where untrusted input needs to be safely hashed, such as in web servers and language runtimes."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptSHA1LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "SHA-1 (Secure Hash Algorithm 1) is a cryptographic hash function that produces a 160-bit (20-byte) hash value. Once widely used in SSL certificates and digital signatures, SHA-1 has been deprecated due to demonstrated collision attacks, where two different inputs produce the same hash. As a result, it\'s no longer considered secure for cryptographic purposes and has largely been replaced by stronger alternatives."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptSHA224LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "SHA-224 is a cryptographic hash function from the SHA-2 family that produces a 224-bit (28-byte) hash value. It is a truncated version of SHA-256, using the same algorithm but outputting a shorter digest. SHA-224 is used when a smaller hash size is preferred while maintaining strong security, commonly in digital signatures and certificate generation."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptSHA256LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "SHA-256 is part of the SHA-2 family of cryptographic hash functions and generates a 256-bit (32-byte) hash value. It is currently considered secure and is widely used in blockchain, password hashing, digital signatures, and data integrity verification. SHA-256 offers strong resistance against collision and preimage attacks, making it a trusted standard today."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptSHA384LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "SHA-384 is a variant of the SHA-2 hash family that generates a 384-bit (48-byte) hash. It is a truncated version of SHA-512, optimized for 64-bit processors. SHA-384 provides a higher security margin than SHA-256 and SHA-224 due to its longer output and 64-bit internal operations, making it suitable for applications requiring robust collision resistance, such as secure communication protocols and cryptographic signatures."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptSHA512LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "SHA-512 (Secure Hash Algorithm 512) is a member of the SHA-2 family of cryptographic hash functions, developed by the NSA and standardized by NIST. It produces a 512-bit (64-byte) hash from any input, making it very resistant to collision and preimage attacks. SHA-512 operates on 1024-bit blocks and performs 80 rounds of complex mathematical operations involving bitwise logic, modular addition, and message expansion. It's widely used in digital signatures, certificates, and data integrity checks where strong cryptographic security is required. Although slower on 32-bit systems due to its large word size, SHA-512 is very efficient on 64-bit processors and remains a trusted standard in secure applications."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptXXLearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "XXHash is a non-cryptographic hash function designed for high-performance hashing. Developed by Yann Collet, XXHash focuses on speed and efficiency, outperforming many traditional hash functions while maintaining a low collision rate. It comes in several versions, including XXH32, XXH64, and the newer XXH3, which offers improved performance and adaptability to modern CPUs. While XXHash is not suitable for cryptographic purposes due to its lack of security guarantees, it is widely used in performance-critical applications like databases, file systems, and compression tools."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptBase32LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "Base32 is an encoding scheme that converts binary data into a set of 32 ASCII characters, using the characters A-Z and 2-7. It is commonly used for representing binary data in a human-readable and URL-safe format, especially when Base64 is not ideal due to case sensitivity or special characters. Each Base32 character represents 5 bits of data, making it slightly less space-efficient than Base64 but easier to handle in contexts like QR codes, file names, or secret keys (e.g., in two-factor authentication). Unlike encryption or hashing, Base32 is not secure-it's simply a reversible way to encode data."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptBase58LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "Base-58 is a binary-to-text encoding scheme used to represent large numbers or binary data in a more human-friendly format. It's most commonly used in cryptocurrencies like Bitcoin to encode addresses. Unlike Base-64, Base-58 removes easily confused characters such as zero, capital o, capital i, and lowercase L, to reduce human error when copying or typing. This makes Base-58 more suitable for user-facing strings while still being compact and efficient for encoding data."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + case FlipCryptBase64LearnScene: + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "Base64 is a binary-to-text encoding scheme that represents binary data using 64 ASCII characters: A-Z, a-z, 0-9, +, and /. It works by dividing the input into 6-bit chunks and mapping each chunk to a character from the Base64 alphabet, often adding = as padding at the end to align the output. Base64 is commonly used to encode data for transmission over media that are designed to handle text, such as embedding images in HTML or safely transmitting binary data in email or JSON. Like Base-32 and Base-58, Base-64 is not secure as it is fully reversible."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); + break; + default: + break; + } +} + +// NFC functionality on output screen +void create_nfc_tag(App* app, const char* message) { + app->nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeNTAG215, app->nfc_device); + + const MfUltralightData* data_original = nfc_device_get_data(app->nfc_device, NfcProtocolMfUltralight); + // Create a mutable copy of the data + MfUltralightData* data = malloc(sizeof(MfUltralightData)); + furi_assert(data); // Check for successful allocation + memcpy(data, data_original, sizeof(MfUltralightData)); + + // Ensure iso14443_3a_data is also a copy if it's not directly embedded + Iso14443_3aData* isodata = malloc(sizeof(Iso14443_3aData)); + furi_assert(isodata); // Check for successful allocation + memcpy(isodata, data_original->iso14443_3a_data, sizeof(Iso14443_3aData)); + data->iso14443_3a_data = isodata; + + const char* lang = "en"; + uint8_t lang_len = strlen(lang); + size_t msg_len = strlen(message); + + // NDEF Text Record Payload: Status Byte + Language Code + Message + size_t text_payload_len = 1 + lang_len + msg_len; + + // NDEF Record Header: TNF_Flags (1) + Type_Length (1) + Payload_Length (1) + Type (1) + size_t ndef_record_header_len = 4; // D1, 01, Payload_Len, T + + // Total NDEF Message Length (excluding TLV 0x03 and its length byte) + // This is the length that goes into data->page[4].data[1] + size_t ndef_message_total_len = ndef_record_header_len + text_payload_len; + + // Set up TLV Header (starting at Page 4, Byte 0) + data->page[4].data[0] = 0x03; // TLV: NDEF Message + data->page[4].data[1] = ndef_message_total_len; // Length of NDEF Message (excluding TLV itself) + + // NDEF Record Header (starting at Page 4, Byte 2) + data->page[4].data[2] = 0xD1; // MB, ME, SR, TNF=1 (Well-Known Type) + data->page[4].data[3] = 0x01; // Type Length = 1 ('T') + + // NDEF Record Payload Start (starting at Page 5, Byte 0) + data->page[5].data[0] = text_payload_len; // Payload Length + data->page[5].data[1] = 'T'; // Type = text + uint8_t status_byte = (0 << 7) | (lang_len & 0x3F); // UTF-8 (bit 7 = 0), Language Length + data->page[5].data[2] = status_byte; // Status byte + + // Begin writing language and message + size_t current_byte_idx = 3; // Starting at Page 5, Byte 3 for language code + size_t current_page_idx = 5; + + // Write language code + for (size_t i = 0; i < lang_len; ++i) { + data->page[current_page_idx].data[current_byte_idx++] = lang[i]; + if (current_byte_idx > 3) { // Moved to next byte in page, if overflowed + current_byte_idx = 0; + current_page_idx++; + } + } + + // Write actual message + for (size_t i = 0; i < msg_len; ++i) { + data->page[current_page_idx].data[current_byte_idx++] = message[i]; + if (current_byte_idx > 3) { // Moved to next byte in page, if overflowed + current_byte_idx = 0; + current_page_idx++; + } + } + + // NDEF TLV Terminator (0xFE) - placed after the entire NDEF message + // It should be at the next available byte after the message payload. + data->page[current_page_idx].data[current_byte_idx++] = 0xFE; + + // Finalize and store + nfc_device_set_data(app->nfc_device, NfcProtocolMfUltralight, data); + + // Free the allocated memory + free(data); + free(isodata); +} + +void flip_crypt_nfc_scene_on_enter(void* context) { + App* app = context; + widget_reset(app->widget); + widget_add_icon_element(app->widget, 0, 3, &I_NFC_dolphin_emulation_51x64); + widget_add_string_element(app->widget, 90, 25, AlignCenter, AlignTop, FontPrimary, "Emulating..."); + if (strcmp(app->last_output_scene, "AES") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("aes.txt"))); + } else if (strcmp(app->last_output_scene, "AESDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("aes_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Atbash") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("atbash.txt"))); + } else if (strcmp(app->last_output_scene, "AtbashDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("atbash_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Affine") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("affine.txt"))); + } else if (strcmp(app->last_output_scene, "AffineDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("affine_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Baconian") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("baconian.txt"))); + } else if (strcmp(app->last_output_scene, "BaconianDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("baconian_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Beaufort") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("beaufort.txt"))); + } else if (strcmp(app->last_output_scene, "BeaufortDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("beaufort_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Caesar") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("caesar.txt"))); + } else if (strcmp(app->last_output_scene, "CaesarDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("caesar_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Playfair") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("playfair.txt"))); + } else if (strcmp(app->last_output_scene, "PlayfairDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("playfair_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Polybius") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("polybius.txt"))); + } else if (strcmp(app->last_output_scene, "PolybiusDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("polybius_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Porta") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("porta.txt"))); + } else if (strcmp(app->last_output_scene, "PortaDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("porta_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Railfence") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("railfence.txt"))); + } else if (strcmp(app->last_output_scene, "RailfenceDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("railfence_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "RC4") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("rc4.txt"))); + } else if (strcmp(app->last_output_scene, "RC4Decrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("rc4_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "ROT13") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("rot13.txt"))); + } else if (strcmp(app->last_output_scene, "ROT13Decrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("rot13_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Scytale") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("scytale.txt"))); + } else if (strcmp(app->last_output_scene, "ScytaleDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("scytale_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Vigenere") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("vigenere.txt"))); + } else if (strcmp(app->last_output_scene, "VigenereDecrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("vigenere_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Blake2") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("blake2.txt"))); + } else if (strcmp(app->last_output_scene, "FNV1A") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("fnv1a.txt"))); + } else if (strcmp(app->last_output_scene, "MD2") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("md2.txt"))); + } else if (strcmp(app->last_output_scene, "MD5") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("md5.txt"))); + } else if (strcmp(app->last_output_scene, "Murmur3") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("murmur3.txt"))); + } else if (strcmp(app->last_output_scene, "Sip") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("sip.txt"))); + } else if (strcmp(app->last_output_scene, "SHA1") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("sha1.txt"))); + } else if (strcmp(app->last_output_scene, "SHA224") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("sha224.txt"))); + } else if (strcmp(app->last_output_scene, "SHA256") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("sha256.txt"))); + } else if (strcmp(app->last_output_scene, "SHA384") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("sha384.txt"))); + } else if (strcmp(app->last_output_scene, "SHA512") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("sha512.txt"))); + } else if (strcmp(app->last_output_scene, "XX") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("xx.txt"))); + } else if (strcmp(app->last_output_scene, "Base32") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("base32.txt"))); + } else if (strcmp(app->last_output_scene, "Base32Decrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("base32_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Base58") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("base58.txt"))); + } else if (strcmp(app->last_output_scene, "Base58Decrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("base58_decrypt.txt"))); + } else if (strcmp(app->last_output_scene, "Base64") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("base64.txt"))); + } else if (strcmp(app->last_output_scene, "Base64Decrypt") == 0) { + create_nfc_tag(context, load_result_generic(APP_DATA_PATH("base64_decrypt.txt"))); + } else { + create_nfc_tag(context, "ERROR"); + } + const MfUltralightData* data = nfc_device_get_data(app->nfc_device, NfcProtocolMfUltralight); + app->listener = nfc_listener_alloc(app->nfc, NfcProtocolMfUltralight, data); + nfc_listener_start(app->listener, NULL, NULL); + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_blink_start_magenta); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); +} + +void flip_crypt_nfc_scene_on_exit(void* context) { + App* app = context; + if(app->listener) { + nfc_listener_stop(app->listener); + nfc_listener_free(app->listener); + app->listener = NULL; + } + widget_reset(app->widget); + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_blink_stop); +} + +// QR Code functionality on output screen +void flip_crypt_qr_scene_on_enter(void* context) { + App* app = context; + bool isTooLong = false; + widget_reset(app->widget); + if (strcmp(app->last_output_scene, "AES") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("aes.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true ); + } else if (strcmp(app->last_output_scene, "AESDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("aes_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Atbash") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("atbash.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "AtbashDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("atbash_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Affine") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("affine.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "AffineDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("affine_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Baconian") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("baconian.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "BaconianDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("baconian_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Beaufort") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("beaufort.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "BeaufortDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("beaufort_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Caesar") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("caesar.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "CaesarDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("caesar_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Playfair") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("playfair.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "PlayfairDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("playfair_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Polybius") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("polybius.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "PolybiusDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("polybius_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Porta") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("porta.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "PortaDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("porta_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Railfence") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("railfence.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "RailfenceDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("railfence_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "RC4") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("rc4.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "RC4Decrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("rc4_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "ROT13") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("rot13.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "ROT13Decrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("rot13_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Scytale") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("scytale.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "ScytaleDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("scytale_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Vigenere") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("vigenere.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "VigenereDecrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("vigenere_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Blake2") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("blake2.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "FNV1A") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("fnv1a.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "MD2") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("md2.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "MD5") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("md5.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Murmur3") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("murmur3.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Sip") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("sip.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "SHA1") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("sha1.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "SHA224") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("sha224.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "SHA256") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("sha256.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "SHA384") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("sha384.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "SHA512") == 0) { + isTooLong = true; + } else if (strcmp(app->last_output_scene, "XX") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("xx.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Base32") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("base32.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Base32Decrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("base32_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Base58") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("base58.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Base58Decrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("base58_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Base64") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("base64.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else if (strcmp(app->last_output_scene, "Base64Decrypt") == 0) { + qrcodegen_encodeText(load_result_generic(APP_DATA_PATH("base64_decrypt.txt")), app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } else { + qrcodegen_encodeText("ERROR", app->qr_buffer, app->qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, 5, qrcodegen_Mask_AUTO, true); + } + if (!isTooLong) { + int size = qrcodegen_getSize(app->qrcode); + const int scale = 1; + const int offset_x = 64 - (size * scale) / 2; + const int offset_y = 32 - (size * scale) / 2; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + if (qrcodegen_getModule(app->qrcode, x, y)) { + // widget_add_rect_element(app->widget, offset_x + x * scale, offset_y + y * scale, scale, scale, 0, true); + widget_add_rect_element(app->widget, offset_x + x, offset_y + y, scale, scale, 0, true); + } + } + } + } else { + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "Output too long"); + } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); +} + +void flip_crypt_qr_scene_on_exit(void* context) { + App* app = context; + widget_reset(app->widget); +} + +void flip_crypt_save_scene_on_enter(void* context) { + App* app = context; + widget_reset(app->widget); + if (strcmp(app->last_output_scene, "AES") == 0) { + save_result(load_result_generic(APP_DATA_PATH("aes.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "AESDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("aes_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Atbash") == 0) { + save_result(load_result_generic(APP_DATA_PATH("atbash.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "AtbashDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("atbash_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Affine") == 0) { + save_result(load_result_generic(APP_DATA_PATH("affine.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "AffineDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("affine_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Baconian") == 0) { + save_result(load_result_generic(APP_DATA_PATH("baconian.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "BaconianDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("baconian_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Beaufort") == 0) { + save_result(load_result_generic(APP_DATA_PATH("beaufort.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "BeaufortDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("beaufort_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Caesar") == 0) { + save_result(load_result_generic(APP_DATA_PATH("caesar.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "CaesarDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("caesar_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Playfair") == 0) { + save_result(load_result_generic(APP_DATA_PATH("playfair.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "PlayfairDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("playfair_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Polybius") == 0) { + save_result(load_result_generic(APP_DATA_PATH("polybius.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "PolybiusDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("polybius_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Porta") == 0) { + save_result(load_result_generic(APP_DATA_PATH("porta.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "PortaDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("porta_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Railfence") == 0) { + save_result(load_result_generic(APP_DATA_PATH("railfence.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "RailfenceDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("railfence_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "RC4") == 0) { + save_result(load_result_generic(APP_DATA_PATH("rc4.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "RC4Decrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("rc4_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "ROT13") == 0) { + save_result(load_result_generic(APP_DATA_PATH("rot13.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "ROT13Decrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("rot13_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Scytale") == 0) { + save_result(load_result_generic(APP_DATA_PATH("scytale.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "ScytaleDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("scytale_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Vigenere") == 0) { + save_result(load_result_generic(APP_DATA_PATH("vigenere.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "VigenereDecrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("vigenere_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Blake2") == 0) { + save_result(load_result_generic(APP_DATA_PATH("blake2.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "FNV1A") == 0) { + save_result(load_result_generic(APP_DATA_PATH("fnv1a.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "MD2") == 0) { + save_result(load_result_generic(APP_DATA_PATH("md2.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "MD5") == 0) { + save_result(load_result_generic(APP_DATA_PATH("md5.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Murmur3") == 0) { + save_result(load_result_generic(APP_DATA_PATH("murmur3.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Sip") == 0) { + save_result(load_result_generic(APP_DATA_PATH("sip.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "SHA1") == 0) { + save_result(load_result_generic(APP_DATA_PATH("sha1.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "SHA224") == 0) { + save_result(load_result_generic(APP_DATA_PATH("sha224.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "SHA256") == 0) { + save_result(load_result_generic(APP_DATA_PATH("sha256.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "SHA384") == 0) { + save_result(load_result_generic(APP_DATA_PATH("sha384.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "SHA512") == 0) { + save_result(load_result_generic(APP_DATA_PATH("sha512.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "XX") == 0) { + save_result(load_result_generic(APP_DATA_PATH("xx.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Base32") == 0) { + save_result(load_result_generic(APP_DATA_PATH("base32.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Base32Decrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("base32_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Base58") == 0) { + save_result(load_result_generic(APP_DATA_PATH("base58.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Base58Decrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("base58_decrypt.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Base64") == 0) { + save_result(load_result_generic(APP_DATA_PATH("base64.txt")), app->save_name_input); + } else if (strcmp(app->last_output_scene, "Base64Decrypt") == 0) { + save_result(load_result_generic(APP_DATA_PATH("base64_decrypt.txt")), app->save_name_input); + } else { + save_result("ERROR", app->save_name_input); + } + widget_add_icon_element(app->widget, 36, 6, &I_DolphinSaved_92x58); + widget_add_string_element(app->widget, 25, 15, AlignCenter, AlignCenter, FontPrimary, "Saved!"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); +} + +// About screen from main menu option +void flip_crypt_about_scene_on_enter(void* context) { + App* app = context; + widget_reset(app->widget); + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, "FlipCrypt\n" + "v0.6\n" + "Explore and learn about various cryptographic and text encoding methods.\n\n" + "Usage:\n" + "Select the method you want to use for encoding / decoding text and fill in the necessary inputs.\n" + "On the output screen, there are up to three options for actions you can do with the output - Save, NFC, and QR. The save button saves the output to a text file in the folder located at /ext/flip_crypt_saved/. The NFC button emulates the output using NTAG215. The QR button generates and displays a QR code of your output. Not all three options will be available on every output screen due to memory limitations - for instance the flipper just can't handle the QR code for a SHA-512 output.\n\n" + "Feel free to leave any issues / PRs on the repo with new feature ideas!\n\n" + "Author: @Tyl3rA\n" + "Source Code: https://github.com/Tyl3rA/FlipCrypt\n\n\n" + "SHA 224-512 LICENSE INFO:" + "FIPS 180-2 SHA-224/256/384/512 implementation\n" + "\n" + "Copyright (C) 2005-2023 Olivier Gay \n" + "All rights reserved.\n" + "\n" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted provided that the following conditions\n" + "are met:\n" + "1. Redistributions of source code must retain the above copyright\n" + "notice, this list of conditions and the following disclaimer.\n" + "2. Redistributions in binary form must reproduce the above copyright\n" + "notice, this list of conditions and the following disclaimer in the\n" + "documentation and/or other materials provided with the distribution.\n" + "3. Neither the name of the project nor the names of its contributors\n" + "may be used to endorse or promote products derived from this software\n" + "without specific prior written permission.\n" + "\n" + "THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND\n" + "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" + "ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE\n" + "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n" + "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n" + "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n" + "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n" + "LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" + "OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" + "SUCH DAMAGE."); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipCryptWidgetView); +} + +// Generic event handlers +bool flip_crypt_generic_event_handler(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void flip_crypt_generic_on_exit(void* context) { + UNUSED(context); +} + +void (*const flip_crypt_scene_on_enter_handlers[])(void*) = { + flip_crypt_main_menu_scene_on_enter, // Main menu + flip_crypt_cipher_submenu_scene_on_enter, // Cipher submenu + flip_crypt_hash_submenu_scene_on_enter, // Hash submenu + flip_crypt_other_submenu_scene_on_enter, // Other submenu + flip_crypt_about_scene_on_enter, // About scene + cipher_submenu_scene_on_enter, // AES128Submenu + cipher_input_scene_on_enter, // AES128Input + cipher_input_scene_on_enter, // AES128KeyInput + cipher_input_scene_on_enter, // AES128DecryptKeyInput + dialog_cipher_output_scene_on_enter, // AES128Output + cipher_input_scene_on_enter, // AES128DecryptInput + dialog_cipher_output_scene_on_enter, // AES128DecryptOutput + cipher_learn_scene_on_enter, // AES128Learn + cipher_submenu_scene_on_enter, // AffineSubmenu + cipher_input_scene_on_enter, // AffineInput + number_input_scene_on_enter, // AffineKeyAInput + number_input_scene_on_enter, // AffineDecryptKeyAInput + number_input_scene_on_enter, // AffineKeyBInput + number_input_scene_on_enter, // AffineDecryptKeyBInput + dialog_cipher_output_scene_on_enter, // AffineOutput + cipher_input_scene_on_enter, // AffineDecryptInput + dialog_cipher_output_scene_on_enter, // AffineDecryptOutput + cipher_learn_scene_on_enter, // AffineLearn + cipher_submenu_scene_on_enter, // AtbashSubmenu + cipher_input_scene_on_enter, // AtbashInput + dialog_cipher_output_scene_on_enter, // AtbashOutput + cipher_input_scene_on_enter, // AtbashDecryptInput + dialog_cipher_output_scene_on_enter, // AtbashDecryptOutput + cipher_learn_scene_on_enter, // AtbashLearn + cipher_submenu_scene_on_enter, // BaconianSubmenu + cipher_input_scene_on_enter, // BaconianInput + dialog_cipher_output_scene_on_enter, // BaconianOutput + cipher_input_scene_on_enter, // BaconianDecryptInput + dialog_cipher_output_scene_on_enter, // BaconianDecryptOutput + cipher_learn_scene_on_enter, // BaconianLearn + cipher_submenu_scene_on_enter, // BeaufortSubmenu + cipher_input_scene_on_enter, // BeaufortInput + cipher_input_scene_on_enter, // BeaufortKeyInput + cipher_input_scene_on_enter, // BeaufortDecryptKeyInput + dialog_cipher_output_scene_on_enter, // BeaufortOutput + cipher_input_scene_on_enter, // BeaufortDecryptInput + dialog_cipher_output_scene_on_enter, // BeaufortDecryptOutput + cipher_learn_scene_on_enter, // BeaufortLearn + cipher_submenu_scene_on_enter, // CaesarSubmenu + cipher_input_scene_on_enter, // CaesarInput + number_input_scene_on_enter, // CaesarKeyInput + number_input_scene_on_enter, // CaesarDecryptKeyInput + dialog_cipher_output_scene_on_enter, // CaesarOutput + cipher_input_scene_on_enter, // CaesarDecryptInput + dialog_cipher_output_scene_on_enter, // CaesarDecryptOutput + cipher_learn_scene_on_enter, // CaesarLearn + cipher_submenu_scene_on_enter, // PlayfairSubmenu + cipher_input_scene_on_enter, // PlayfairInput + cipher_input_scene_on_enter, // PlayfairKeywordInput + cipher_input_scene_on_enter, // PlayfairDecryptKeywordInput + dialog_cipher_output_scene_on_enter, // PlayfairOutput + cipher_input_scene_on_enter, // PlayfairDecryptInput + dialog_cipher_output_scene_on_enter, // PlayfairDecryptOutput + cipher_learn_scene_on_enter, // PlayfairLearn + cipher_submenu_scene_on_enter, // PolybiusSubmenu + cipher_input_scene_on_enter, // PolybiusInput + dialog_cipher_output_scene_on_enter, // PolybiusOutput + cipher_input_scene_on_enter, // PolybiusDecryptInput + dialog_cipher_output_scene_on_enter, // PolybiusDecryptOutput + cipher_learn_scene_on_enter, // PolybiusLearn + cipher_submenu_scene_on_enter, // PortaSubmenu + cipher_input_scene_on_enter, // PortaInput + cipher_input_scene_on_enter, // PortaKeywordInput + cipher_input_scene_on_enter, // PortaDecryptKeywordInput + dialog_cipher_output_scene_on_enter, // PortaOutput + cipher_input_scene_on_enter, // PortaDecryptInput + dialog_cipher_output_scene_on_enter, // PortaDecryptOutput + cipher_learn_scene_on_enter, // PortaLearn + cipher_submenu_scene_on_enter, // RailfenceSubmenu + cipher_input_scene_on_enter, // RailfenceInput + number_input_scene_on_enter, // RailfenceKeyInput + number_input_scene_on_enter, // RailfenceDecryptKeyInput + dialog_cipher_output_scene_on_enter, // RailfenceOutput + cipher_input_scene_on_enter, // RailfenceDecryptInput + dialog_cipher_output_scene_on_enter, // RailfenceDecryptOutput + cipher_learn_scene_on_enter, // RailfenceLearn + cipher_submenu_scene_on_enter, // RC4Submenu + cipher_input_scene_on_enter, // RC4Input + cipher_input_scene_on_enter, // RC4KeywordInput + cipher_input_scene_on_enter, // RC4DecryptKeywordInput + dialog_cipher_output_scene_on_enter, // RC4Output + cipher_input_scene_on_enter, // RC4DecryptInput + dialog_cipher_output_scene_on_enter, // RC4DecryptOutput + cipher_learn_scene_on_enter, // RC4Learn + cipher_submenu_scene_on_enter, // ROT13Submenu + cipher_input_scene_on_enter, // ROT13Input + dialog_cipher_output_scene_on_enter, // ROT13Output + cipher_input_scene_on_enter, // ROT13DecryptInput + dialog_cipher_output_scene_on_enter, // ROT13DecryptOutput + cipher_learn_scene_on_enter, // ROT13Learn + cipher_submenu_scene_on_enter, // ScytaleSubmenu + cipher_input_scene_on_enter, // ScytaleInput + number_input_scene_on_enter, // ScytaleKeywordInput + number_input_scene_on_enter, // ScytaleDecryptKeywordInput + dialog_cipher_output_scene_on_enter, // ScytaleOutput + cipher_input_scene_on_enter, // ScytaleDecryptInput + dialog_cipher_output_scene_on_enter, // ScytaleDecryptOutput + cipher_learn_scene_on_enter, // ScytaleLearn + cipher_submenu_scene_on_enter, // VigenereSubmenu + cipher_input_scene_on_enter, // VigenereInput + cipher_input_scene_on_enter, // VigenereKeywordInput + cipher_input_scene_on_enter, // VigenereDecryptKeywordInput + dialog_cipher_output_scene_on_enter, // VigenereOutput + cipher_input_scene_on_enter, // VigenereDecryptInput + dialog_cipher_output_scene_on_enter, // VigenereDecryptOutput + cipher_learn_scene_on_enter, // VigenereLearn + cipher_submenu_scene_on_enter, // Blake2Submenu + cipher_input_scene_on_enter, // Blake2Input + dialog_cipher_output_scene_on_enter, // Blake2Output + cipher_learn_scene_on_enter, // Blake2Learn + cipher_submenu_scene_on_enter, // FNV1ASubmenu + cipher_input_scene_on_enter, // FNV1AInput + dialog_cipher_output_scene_on_enter, // FNV1AOutput + cipher_learn_scene_on_enter, // FNV1ALearn + cipher_submenu_scene_on_enter, // MD2Submenu + cipher_input_scene_on_enter, // MD2Input + dialog_cipher_output_scene_on_enter, // MD2Output + cipher_learn_scene_on_enter, // MD2Learn + cipher_submenu_scene_on_enter, // MD5Submenu + cipher_input_scene_on_enter, // MD5Input + dialog_cipher_output_scene_on_enter, // MD5Output + cipher_learn_scene_on_enter, // MD5Learn + cipher_submenu_scene_on_enter, // Murmur3Submenu + cipher_input_scene_on_enter, // Murmur3Input + dialog_cipher_output_scene_on_enter, // Murmur3Output + cipher_learn_scene_on_enter, // Murmur3Learn + cipher_submenu_scene_on_enter, // SipSubmenu + cipher_input_scene_on_enter, // SipInput + cipher_input_scene_on_enter, // SipKeywordInput + dialog_cipher_output_scene_on_enter, // SipOutput + cipher_learn_scene_on_enter, // SipLearn + cipher_submenu_scene_on_enter, // SHA1Submenu + cipher_input_scene_on_enter, // SHA1Input + dialog_cipher_output_scene_on_enter, // SHA1Output + cipher_learn_scene_on_enter, // SHA1Learn + cipher_submenu_scene_on_enter, // SHA224Submenu + cipher_input_scene_on_enter, // SHA224Input + dialog_cipher_output_scene_on_enter, // SHA224Output + cipher_learn_scene_on_enter, // SHA224Learn + cipher_submenu_scene_on_enter, // SHA256Submenu + cipher_input_scene_on_enter, // SHA256Input + dialog_cipher_output_scene_on_enter, // SHA256Output + cipher_learn_scene_on_enter, // SHA256Learn + cipher_submenu_scene_on_enter, // SHA384Submenu + cipher_input_scene_on_enter, // SHA384Input + dialog_cipher_output_scene_on_enter, // SHA384Output + cipher_learn_scene_on_enter, // SHA384Learn + cipher_submenu_scene_on_enter, // SHA512Submenu + cipher_input_scene_on_enter, // SHA512Input + dialog_cipher_output_scene_on_enter, // SHA512Output + cipher_learn_scene_on_enter, // SHA512Learn + cipher_submenu_scene_on_enter, // XXSubmenu + cipher_input_scene_on_enter, // XXInput + dialog_cipher_output_scene_on_enter, // XXOutput + cipher_learn_scene_on_enter, // XXLearn + cipher_submenu_scene_on_enter, // Base32Submenu + cipher_input_scene_on_enter, // Base32Input + dialog_cipher_output_scene_on_enter, // Base32Output + cipher_input_scene_on_enter, // Base32DecryptInput + dialog_cipher_output_scene_on_enter, // Base32DecryptOutput + cipher_learn_scene_on_enter, // Base32Learn + cipher_submenu_scene_on_enter, // Base58Submenu + cipher_input_scene_on_enter, // Base58Input + dialog_cipher_output_scene_on_enter, // Base58Output + cipher_input_scene_on_enter, // Base58DecryptInput + dialog_cipher_output_scene_on_enter, // Base58DecryptOutput + cipher_learn_scene_on_enter, // Base58Learn + cipher_submenu_scene_on_enter, // Base64Submenu + cipher_input_scene_on_enter, // Base64Input + dialog_cipher_output_scene_on_enter, // Base64Output + cipher_input_scene_on_enter, // Base64DecryptInput + dialog_cipher_output_scene_on_enter, // Base64DecryptOutput + cipher_learn_scene_on_enter, // Base64Learn + flip_crypt_nfc_scene_on_enter, // NFC + flip_crypt_save_scene_on_enter, // Save + cipher_input_scene_on_enter, // Save Text input for file name + flip_crypt_qr_scene_on_enter, // QR Code +}; + +bool (*const flip_crypt_scene_on_event_handlers[])(void*, SceneManagerEvent) = { + flip_crypt_main_menu_scene_on_event, // Main menu + flip_crypt_main_menu_scene_on_event, // Cipher submenu + flip_crypt_main_menu_scene_on_event, // Hash submenu + flip_crypt_main_menu_scene_on_event, // Other submenu + flip_crypt_generic_event_handler, // About + flip_crypt_generic_event_handler, // AES128Submenu + flip_crypt_generic_event_handler, // AES128Input + flip_crypt_generic_event_handler, // AES128KeyInput + flip_crypt_generic_event_handler, // AES128DecryptKeyInput + flip_crypt_generic_event_handler, // AES128Output + flip_crypt_generic_event_handler, // AES128DecryptInput + flip_crypt_generic_event_handler, // AES128DecryptOutput + flip_crypt_generic_event_handler, // AES128Learn + flip_crypt_generic_event_handler, // AffineSubmenu + flip_crypt_generic_event_handler, // AffineInput + flip_crypt_generic_event_handler, // AffineKeyAInput + flip_crypt_generic_event_handler, // AffineDecryptKeyAInput + flip_crypt_generic_event_handler, // AffineKeyBInput + flip_crypt_generic_event_handler, // AffineDecryptKeyBInput + flip_crypt_generic_event_handler, // AffineOutput + flip_crypt_generic_event_handler, // AffineDecryptInput + flip_crypt_generic_event_handler, // AffineDecryptOutput + flip_crypt_generic_event_handler, // AffineLearn + flip_crypt_generic_event_handler, // AtbashSubmenu + flip_crypt_generic_event_handler, // AtbashInput + flip_crypt_generic_event_handler, // AtbashOutput + flip_crypt_generic_event_handler, // AtbashDecryptInput + flip_crypt_generic_event_handler, // AtbashDecryptOutput + flip_crypt_generic_event_handler, // AtbashLearn + flip_crypt_generic_event_handler, // BaconianSubmenu + flip_crypt_generic_event_handler, // BaconianInput + flip_crypt_generic_event_handler, // BaconianOutput + flip_crypt_generic_event_handler, // BaconianDecryptInput + flip_crypt_generic_event_handler, // BaconianDecryptOutput + flip_crypt_generic_event_handler, // BaconianLearn + flip_crypt_generic_event_handler, // BeaufortSubmenu + flip_crypt_generic_event_handler, // BeaufortInput + flip_crypt_generic_event_handler, // BeaufortKeyInput + flip_crypt_generic_event_handler, // BeaufortDecryptKeyInput + flip_crypt_generic_event_handler, // BeaufortOutput + flip_crypt_generic_event_handler, // BeaufortDecryptInput + flip_crypt_generic_event_handler, // BeaufortDecryptOutput + flip_crypt_generic_event_handler, // BeaufortLearn + flip_crypt_generic_event_handler, // CaesarSubmenu + flip_crypt_generic_event_handler, // CaesarInput + flip_crypt_generic_event_handler, // CaesarKeyInput + flip_crypt_generic_event_handler, // CaesarDecryptKeyInput + flip_crypt_generic_event_handler, // CaesarOutput + flip_crypt_generic_event_handler, // CaesarDecryptInput + flip_crypt_generic_event_handler, // CaesarDecryptOutput + flip_crypt_generic_event_handler, // CaesarLearn + flip_crypt_generic_event_handler, // PlayfairSubmenu + flip_crypt_generic_event_handler, // PlayfairInput + flip_crypt_generic_event_handler, // PlayfairKeywordInput + flip_crypt_generic_event_handler, // PlayfairDecryptKeywordInput + flip_crypt_generic_event_handler, // PlayfairOutput + flip_crypt_generic_event_handler, // PlayfairDecryptInput + flip_crypt_generic_event_handler, // PlayfairDecryptOutput + flip_crypt_generic_event_handler, // PlayfairLearn + flip_crypt_generic_event_handler, // PolybiusSubmenu + flip_crypt_generic_event_handler, // PolybiusInput + flip_crypt_generic_event_handler, // PolybiusOutput + flip_crypt_generic_event_handler, // PolybiusDecryptInput + flip_crypt_generic_event_handler, // PolybiusDecryptOutput + flip_crypt_generic_event_handler, // PolybiusLearn + flip_crypt_generic_event_handler, // PortaSubmenu + flip_crypt_generic_event_handler, // PortaInput + flip_crypt_generic_event_handler, // PortaKeywordInput + flip_crypt_generic_event_handler, // PortaDecryptKeywordInput + flip_crypt_generic_event_handler, // PortaOutput + flip_crypt_generic_event_handler, // PortaDecryptInput + flip_crypt_generic_event_handler, // PortaDecryptOutput + flip_crypt_generic_event_handler, // PortaLearn + flip_crypt_generic_event_handler, // RailfenceSubmenu + flip_crypt_generic_event_handler, // RailfenceInput + flip_crypt_generic_event_handler, // RailfenceKeyInput + flip_crypt_generic_event_handler, // RailfenceDecryptKeyInput + flip_crypt_generic_event_handler, // RailfenceOutput + flip_crypt_generic_event_handler, // RailfenceDecryptInput + flip_crypt_generic_event_handler, // RailfenceDecryptOutput + flip_crypt_generic_event_handler, // RailfenceLearn + flip_crypt_generic_event_handler, // RC4Submenu + flip_crypt_generic_event_handler, // RC4Input + flip_crypt_generic_event_handler, // RC4KeywordInput + flip_crypt_generic_event_handler, // RC4DecryptKeywordInput + flip_crypt_generic_event_handler, // RC4Output + flip_crypt_generic_event_handler, // RC4DecryptInput + flip_crypt_generic_event_handler, // RC4DecryptOutput + flip_crypt_generic_event_handler, // RC4Learn + flip_crypt_generic_event_handler, // ROT13Submenu + flip_crypt_generic_event_handler, // ROT13Input + flip_crypt_generic_event_handler, // ROT13Output + flip_crypt_generic_event_handler, // ROT13DecryptInput + flip_crypt_generic_event_handler, // ROT13DecryptOutput + flip_crypt_generic_event_handler, // ROT13Learn + flip_crypt_generic_event_handler, // ScytaleSubmenu + flip_crypt_generic_event_handler, // ScytaleInput + flip_crypt_generic_event_handler, // ScytaleKeywordInput + flip_crypt_generic_event_handler, // ScytaleDecryptKeywordInput + flip_crypt_generic_event_handler, // ScytaleOutput + flip_crypt_generic_event_handler, // ScytaleDecryptInput + flip_crypt_generic_event_handler, // ScytaleDecryptOutput + flip_crypt_generic_event_handler, // ScytaleLearn + flip_crypt_generic_event_handler, // VigenereSubmenu + flip_crypt_generic_event_handler, // VigenereInput + flip_crypt_generic_event_handler, // VigenereKeywordInput + flip_crypt_generic_event_handler, // VigenereDecryptKeywordInput + flip_crypt_generic_event_handler, // VigenereOutput + flip_crypt_generic_event_handler, // VigenereDecryptInput + flip_crypt_generic_event_handler, // VigenereDecryptOutput + flip_crypt_generic_event_handler, // VigenereLearn + flip_crypt_generic_event_handler, // Blake2Submenu + flip_crypt_generic_event_handler, // Blake2Input + flip_crypt_generic_event_handler, // Blake2Output + flip_crypt_generic_event_handler, // Blake2Learn + flip_crypt_generic_event_handler, // FNV1ASubmenu + flip_crypt_generic_event_handler, // FNV1AInput + flip_crypt_generic_event_handler, // FNV1AOutput + flip_crypt_generic_event_handler, // FNV1ALearn + flip_crypt_generic_event_handler, // MD2Submenu + flip_crypt_generic_event_handler, // MD2Input + flip_crypt_generic_event_handler, // MD2Output + flip_crypt_generic_event_handler, // MD2Learn + flip_crypt_generic_event_handler, // MD5Submenu + flip_crypt_generic_event_handler, // MD5Input + flip_crypt_generic_event_handler, // MD5Output + flip_crypt_generic_event_handler, // MD5Learn + flip_crypt_generic_event_handler, // Murmur3Submenu + flip_crypt_generic_event_handler, // Murmur3Input + flip_crypt_generic_event_handler, // Murmur3Output + flip_crypt_generic_event_handler, // Murmur3Learn + flip_crypt_generic_event_handler, // SipSubmenu + flip_crypt_generic_event_handler, // SipInput + flip_crypt_generic_event_handler, // SipKeywordInput + flip_crypt_generic_event_handler, // SipOutput + flip_crypt_generic_event_handler, // SipLearn + flip_crypt_generic_event_handler, // SHA1Submenu + flip_crypt_generic_event_handler, // SHA1Input + flip_crypt_generic_event_handler, // SHA1Output + flip_crypt_generic_event_handler, // SHA1Learn + flip_crypt_generic_event_handler, // SHA224Submenu + flip_crypt_generic_event_handler, // SHA224Input + flip_crypt_generic_event_handler, // SHA224Output + flip_crypt_generic_event_handler, // SHA224Learn + flip_crypt_generic_event_handler, // SHA256Submenu + flip_crypt_generic_event_handler, // SHA256Input + flip_crypt_generic_event_handler, // SHA256Output + flip_crypt_generic_event_handler, // SHA256Learn + flip_crypt_generic_event_handler, // SHA384Submenu + flip_crypt_generic_event_handler, // SHA384Input + flip_crypt_generic_event_handler, // SHA384Output + flip_crypt_generic_event_handler, // SHA384Learn + flip_crypt_generic_event_handler, // SHA512Submenu + flip_crypt_generic_event_handler, // SHA512Input + flip_crypt_generic_event_handler, // SHA512Output + flip_crypt_generic_event_handler, // SHA512Learn + flip_crypt_generic_event_handler, // XXSubmenu + flip_crypt_generic_event_handler, // XXInput + flip_crypt_generic_event_handler, // XXOutput + flip_crypt_generic_event_handler, // XXLearn + flip_crypt_generic_event_handler, // Base32Submenu + flip_crypt_generic_event_handler, // Base32Input + flip_crypt_generic_event_handler, // Base32Output + flip_crypt_generic_event_handler, // Base32DecryptInput + flip_crypt_generic_event_handler, // Base32DecryptOutput + flip_crypt_generic_event_handler, // Base32Learn + flip_crypt_generic_event_handler, // Base58Submenu + flip_crypt_generic_event_handler, // Base58Input + flip_crypt_generic_event_handler, // Base58Output + flip_crypt_generic_event_handler, // Base58DecryptInput + flip_crypt_generic_event_handler, // Base58DecryptOutput + flip_crypt_generic_event_handler, // Base58Learn + flip_crypt_generic_event_handler, // Base64Submenu + flip_crypt_generic_event_handler, // Base64Input + flip_crypt_generic_event_handler, // Base64Output + flip_crypt_generic_event_handler, // Base64DecryptInput + flip_crypt_generic_event_handler, // Base64DecryptOutput + flip_crypt_generic_event_handler, // Base64Learn + flip_crypt_generic_event_handler, // NFC + flip_crypt_generic_event_handler, // Save + flip_crypt_generic_event_handler, // Save input + flip_crypt_generic_event_handler, // QR +}; + +void (*const flip_crypt_scene_on_exit_handlers[])(void*) = { + flip_crypt_main_menu_scene_on_exit, // Main menu + flip_crypt_main_menu_scene_on_exit, // Cipher submenu + flip_crypt_main_menu_scene_on_exit, // Hash submenu + flip_crypt_main_menu_scene_on_exit, // Other submenu + flip_crypt_generic_on_exit, // About + flip_crypt_generic_on_exit, // AES128Submenu + flip_crypt_generic_on_exit, // AES128Input + flip_crypt_generic_on_exit, // AES128KeyInput + flip_crypt_generic_on_exit, // AES128DecryptKeyInput + flip_crypt_generic_on_exit, // AES128Output + flip_crypt_generic_on_exit, // AES128DecryptInput + flip_crypt_generic_on_exit, // AES128DecryptOutput + flip_crypt_generic_on_exit, // AES128Learn + flip_crypt_generic_on_exit, // AffineSubmenu + flip_crypt_generic_on_exit, // AffineInput + flip_crypt_generic_on_exit, // AffineKeyAInput + flip_crypt_generic_on_exit, // AffineDecryptKeyAInput + flip_crypt_generic_on_exit, // AffineKeyBInput + flip_crypt_generic_on_exit, // AffineDecryptKeyBInput + flip_crypt_generic_on_exit, // AffineOutput + flip_crypt_generic_on_exit, // AffineDecryptInput + flip_crypt_generic_on_exit, // AffineDecryptOutput + flip_crypt_generic_on_exit, // AffineLearn + flip_crypt_generic_on_exit, // AtbashSubmenu + flip_crypt_generic_on_exit, // AtbashInput + flip_crypt_generic_on_exit, // AtbashOutput + flip_crypt_generic_on_exit, // AtbashDecryptInput + flip_crypt_generic_on_exit, // AtbashDecryptOutput + flip_crypt_generic_on_exit, // AtbashLearn + flip_crypt_generic_on_exit, // BaconianSubmenu + flip_crypt_generic_on_exit, // BaconianInput + flip_crypt_generic_on_exit, // BaconianOutput + flip_crypt_generic_on_exit, // BaconianDecryptInput + flip_crypt_generic_on_exit, // BaconianDecryptOutput + flip_crypt_generic_on_exit, // BaconianLearn + flip_crypt_generic_on_exit, // BeaufortSubmenu + flip_crypt_generic_on_exit, // BeaufortInput + flip_crypt_generic_on_exit, // BeaufortKeyInput + flip_crypt_generic_on_exit, // BeaufortDecryptKeyInput + flip_crypt_generic_on_exit, // BeaufortOutput + flip_crypt_generic_on_exit, // BeaufortDecryptInput + flip_crypt_generic_on_exit, // BeaufortDecryptOutput + flip_crypt_generic_on_exit, // BeaufortLearn + flip_crypt_generic_on_exit, // CaesarSubmenu + flip_crypt_generic_on_exit, // CaesarInput + flip_crypt_generic_on_exit, // CaesarKeyInput + flip_crypt_generic_on_exit, // CaesarDecryptKeyInput + flip_crypt_generic_on_exit, // CaesarOutput + flip_crypt_generic_on_exit, // CaesarDecryptInput + flip_crypt_generic_on_exit, // CaesarDecryptOutput + flip_crypt_generic_on_exit, // CaesarLearn + flip_crypt_generic_on_exit, // PlayfairSubmenu + flip_crypt_generic_on_exit, // PlayfairInput + flip_crypt_generic_on_exit, // PlayfairKeywordInput + flip_crypt_generic_on_exit, // PlayfairDecryptKeywordInput + flip_crypt_generic_on_exit, // PlayfairOutput + flip_crypt_generic_on_exit, // PlayfairDecryptInput + flip_crypt_generic_on_exit, // PlayfairDecryptOutput + flip_crypt_generic_on_exit, // PlayfairLearn + flip_crypt_generic_on_exit, // PolybiusSubmenu + flip_crypt_generic_on_exit, // PolybiusInput + flip_crypt_generic_on_exit, // PolybiusOutput + flip_crypt_generic_on_exit, // PolybiusDecryptInput + flip_crypt_generic_on_exit, // PolybiusDecryptOutput + flip_crypt_generic_on_exit, // PolybiusLearn + flip_crypt_generic_on_exit, // PortaSubmenu + flip_crypt_generic_on_exit, // PortaInput + flip_crypt_generic_on_exit, // PortaKeywordInput + flip_crypt_generic_on_exit, // PortaDecryptKeywordInput + flip_crypt_generic_on_exit, // PortaOutput + flip_crypt_generic_on_exit, // PortaDecryptInput + flip_crypt_generic_on_exit, // PortaDecryptOutput + flip_crypt_generic_on_exit, // PortaLearn + flip_crypt_generic_on_exit, // RailfenceSubmenu + flip_crypt_generic_on_exit, // RailfenceInput + flip_crypt_generic_on_exit, // RailfenceKeyInput + flip_crypt_generic_on_exit, // RailfenceDecryptKeyInput + flip_crypt_generic_on_exit, // RailfenceOutput + flip_crypt_generic_on_exit, // RailfenceDecryptInput + flip_crypt_generic_on_exit, // RailfenceDecryptOutput + flip_crypt_generic_on_exit, // RailfenceLearn + flip_crypt_generic_on_exit, // RC4Submenu + flip_crypt_generic_on_exit, // RC4Input + flip_crypt_generic_on_exit, // RC4KeywordInput + flip_crypt_generic_on_exit, // RC4DecryptKeywordInput + flip_crypt_generic_on_exit, // RC4Output + flip_crypt_generic_on_exit, // RC4DecryptInput + flip_crypt_generic_on_exit, // RC4DecryptOutput + flip_crypt_generic_on_exit, // RC4Learn + flip_crypt_generic_on_exit, // ROT13Submenu + flip_crypt_generic_on_exit, // ROT13Input + flip_crypt_generic_on_exit, // ROT13Output + flip_crypt_generic_on_exit, // ROT13DecryptInput + flip_crypt_generic_on_exit, // ROT13DecryptOutput + flip_crypt_generic_on_exit, // ROT13Learn + flip_crypt_generic_on_exit, // ScytaleSubmenu + flip_crypt_generic_on_exit, // ScytaleInput + flip_crypt_generic_on_exit, // ScytaleKeywordInput + flip_crypt_generic_on_exit, // ScytaleDecryptKeywordInput + flip_crypt_generic_on_exit, // ScytaleOutput + flip_crypt_generic_on_exit, // ScytaleDecryptInput + flip_crypt_generic_on_exit, // ScytaleDecryptOutput + flip_crypt_generic_on_exit, // ScytaleLearn + flip_crypt_generic_on_exit, // VigenereSubmenu + flip_crypt_generic_on_exit, // VigenereInput + flip_crypt_generic_on_exit, // VigenereKeywordInput + flip_crypt_generic_on_exit, // VigenereDecryptKeywordInput + flip_crypt_generic_on_exit, // VigenereOutput + flip_crypt_generic_on_exit, // VigenereDecryptInput + flip_crypt_generic_on_exit, // VigenereDecryptOutput + flip_crypt_generic_on_exit, // VigenereLearn + flip_crypt_generic_on_exit, // Blake2Submenu + flip_crypt_generic_on_exit, // Blake2Input + flip_crypt_generic_on_exit, // Blake2Output + flip_crypt_generic_on_exit, // Blake2Learn + flip_crypt_generic_on_exit, // FNV1ASubmenu + flip_crypt_generic_on_exit, // FNV1AInput + flip_crypt_generic_on_exit, // FNV1AOutput + flip_crypt_generic_on_exit, // FNV1ALearn + flip_crypt_generic_on_exit, // MD2Submenu + flip_crypt_generic_on_exit, // MD2Input + flip_crypt_generic_on_exit, // MD2Output + flip_crypt_generic_on_exit, // MD2Learn + flip_crypt_generic_on_exit, // MD5Submenu + flip_crypt_generic_on_exit, // MD5Input + flip_crypt_generic_on_exit, // MD5Output + flip_crypt_generic_on_exit, // MD5Learn + flip_crypt_generic_on_exit, // Murmur3Submenu + flip_crypt_generic_on_exit, // Murmur3Input + flip_crypt_generic_on_exit, // Murmur3Output + flip_crypt_generic_on_exit, // Murmur3Learn + flip_crypt_generic_on_exit, // SipSubmenu + flip_crypt_generic_on_exit, // SipInput + flip_crypt_generic_on_exit, // SipKeywordInput + flip_crypt_generic_on_exit, // SipOutput + flip_crypt_generic_on_exit, // SipLearn + flip_crypt_generic_on_exit, // SHA1Submenu + flip_crypt_generic_on_exit, // SHA1Input + flip_crypt_generic_on_exit, // SHA1Output + flip_crypt_generic_on_exit, // SHA1Learn + flip_crypt_generic_on_exit, // SHA224Submenu + flip_crypt_generic_on_exit, // SHA224Input + flip_crypt_generic_on_exit, // SHA224Output + flip_crypt_generic_on_exit, // SHA224Learn + flip_crypt_generic_on_exit, // SHA256Submenu + flip_crypt_generic_on_exit, // SHA256Input + flip_crypt_generic_on_exit, // SHA256Output + flip_crypt_generic_on_exit, // SHA256Learn + flip_crypt_generic_on_exit, // SHA384Submenu + flip_crypt_generic_on_exit, // SHA384Input + flip_crypt_generic_on_exit, // SHA384Output + flip_crypt_generic_on_exit, // SHA384Learn + flip_crypt_generic_on_exit, // SHA512Submenu + flip_crypt_generic_on_exit, // SHA512Input + flip_crypt_generic_on_exit, // SHA512Output + flip_crypt_generic_on_exit, // SHA512Learn + flip_crypt_generic_on_exit, // XXSubmenu + flip_crypt_generic_on_exit, // XXInput + flip_crypt_generic_on_exit, // XXOutput + flip_crypt_generic_on_exit, // XXLearn + flip_crypt_generic_on_exit, // Base32Submenu + flip_crypt_generic_on_exit, // Base32Input + flip_crypt_generic_on_exit, // Base32Output + flip_crypt_generic_on_exit, // Base32DecryptInput + flip_crypt_generic_on_exit, // Base32DecryptOutput + flip_crypt_generic_on_exit, // Base32Learn + flip_crypt_generic_on_exit, // Base58Submenu + flip_crypt_generic_on_exit, // Base58Input + flip_crypt_generic_on_exit, // Base58Output + flip_crypt_generic_on_exit, // Base58DecryptInput + flip_crypt_generic_on_exit, // Base58DecryptOutput + flip_crypt_generic_on_exit, // Base58Learn + flip_crypt_generic_on_exit, // Base64Submenu + flip_crypt_generic_on_exit, // Base64Input + flip_crypt_generic_on_exit, // Base64Output + flip_crypt_generic_on_exit, // Base64DecryptInput + flip_crypt_generic_on_exit, // Base64DecryptOutput + flip_crypt_generic_on_exit, // Base64Learn + flip_crypt_nfc_scene_on_exit, // NFC + flip_crypt_generic_on_exit, // Save + flip_crypt_generic_on_exit, // Save input + flip_crypt_qr_scene_on_exit, // QR +}; + +static const SceneManagerHandlers flip_crypt_scene_manager_handlers = { + .on_enter_handlers = flip_crypt_scene_on_enter_handlers, + .on_event_handlers = flip_crypt_scene_on_event_handlers, + .on_exit_handlers = flip_crypt_scene_on_exit_handlers, + .scene_num = FlipCryptSceneCount, +}; + +// Custom event callbacks +static bool basic_scene_custom_callback(void* context, uint32_t custom_event) { + furi_assert(context); + App* app = context; + return scene_manager_handle_custom_event(app->scene_manager, custom_event); +} + +bool basic_scene_back_event_callback(void* context) { + furi_assert(context); + App* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +// Memory allocation +static App* app_alloc() { + // App + App* app = malloc(sizeof(App)); + // Vars + app->last_output_scene_size = 64; + app->save_name_input_size = 64; + // universal input + app->universal_input_size = 255; + app->aes_key_input_size = 17; + app->beaufort_key_input_size = 64; + app->playfair_keyword_input_size = 26; + app->porta_keyword_input_size = 64; + app->rc4_keyword_input_size = 64; + app->vigenere_keyword_input_size = 64; + app->universal_input = malloc(app->universal_input_size); + app->save_name_input = malloc(app->save_name_input_size); + app->last_output_scene = malloc(app->last_output_scene_size); + app->aes_key_input = malloc(app->aes_key_input_size); + app->affine_keya_input = 1; + app->affine_keyb_input = 1; + app->beaufort_key_input = malloc(app->beaufort_key_input_size); + app->caesar_key_input = 0; + app->playfair_keyword_input = malloc(app->playfair_keyword_input_size); + app->porta_keyword_input = malloc(app->porta_keyword_input_size); + app->railfence_key_input = 1; + app->rc4_keyword_input = malloc(app->rc4_keyword_input_size); + app->scytale_keyword_input = 0; + app->vigenere_keyword_input = malloc(app->vigenere_keyword_input_size); + // Other + app->scene_manager = scene_manager_alloc(&flip_crypt_scene_manager_handlers, app); + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, basic_scene_custom_callback); + view_dispatcher_set_navigation_event_callback(app->view_dispatcher, basic_scene_back_event_callback); + app->submenu = submenu_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FlipCryptSubmenuView, submenu_get_view(app->submenu)); + app->widget = widget_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FlipCryptWidgetView, widget_get_view(app->widget)); + app->text_input = text_input_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FlipCryptTextInputView, text_input_get_view(app->text_input)); + app->number_input = number_input_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FlipCryptNumberInputView, number_input_get_view(app->number_input)); + app->dialog_ex = dialog_ex_alloc(); + dialog_ex_set_context(app->dialog_ex, app); + dialog_ex_set_result_callback(app->dialog_ex, dialog_ex_callback); + view_dispatcher_add_view(app->view_dispatcher, FlipCryptDialogExView, dialog_ex_get_view(app->dialog_ex)); + app->nfc = nfc_alloc(); + app->nfc_device = nfc_device_alloc(); + app->qr_buffer = malloc(qrcodegen_BUFFER_LEN_MAX); + app->qrcode = malloc(qrcodegen_BUFFER_LEN_MAX); + return app; +} + +// Free memory on app termination +static void app_free(App* app) { + furi_assert(app); + view_dispatcher_remove_view(app->view_dispatcher, FlipCryptSubmenuView); + view_dispatcher_remove_view(app->view_dispatcher, FlipCryptWidgetView); + view_dispatcher_remove_view(app->view_dispatcher, FlipCryptTextInputView); + view_dispatcher_remove_view(app->view_dispatcher, FlipCryptDialogExView); + view_dispatcher_remove_view(app->view_dispatcher, FlipCryptNumberInputView); + dialog_ex_free(app->dialog_ex); + number_input_free(app->number_input); + scene_manager_free(app->scene_manager); + view_dispatcher_free(app->view_dispatcher); + submenu_free(app->submenu); + widget_free(app->widget); + text_input_free(app->text_input); + furi_record_close(RECORD_NOTIFICATION); + nfc_free(app->nfc); + nfc_device_free(app->nfc_device); + free(app->qr_buffer); + free(app->qrcode); + free(app->last_output_scene); + free(app->universal_input); + free(app->aes_key_input); + free(app->beaufort_key_input); + free(app->playfair_keyword_input); + free(app->porta_keyword_input); + free(app->rc4_keyword_input); + free(app->vigenere_keyword_input); + free(app->sip_keyword_input); + free(app); +} + +// Main function +int32_t flip_crypt_app(void* p) { + UNUSED(p); + App* app = app_alloc(); + Gui* gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, FlipCryptMainMenuScene); + view_dispatcher_run(app->view_dispatcher); + app_free(app); + return 0; // return of 0 means success, return of 1 means failure +} \ No newline at end of file diff --git a/lib/FlipCrypt/qrcode/qrcodegen.c b/lib/FlipCrypt/qrcode/qrcodegen.c new file mode 100644 index 0000000..34f1002 --- /dev/null +++ b/lib/FlipCrypt/qrcode/qrcodegen.c @@ -0,0 +1,1027 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include +#include +#include +#include +#include "qrcodegen.h" + +#ifndef QRCODEGEN_TEST + #define testable static // Keep functions private +#else + #define testable // Expose private functions +#endif + + +/*---- Forward declarations for private functions ----*/ + +// Regarding all public and private functions defined in this source file: +// - They require all pointer/array arguments to be not null unless the array length is zero. +// - They only read input scalar/array arguments, write to output pointer/array +// arguments, and return scalar values; they are "pure" functions. +// - They don't read mutable global variables or write to any global variables. +// - They don't perform I/O, read the clock, print to console, etc. +// - They allocate a small and constant amount of stack memory. +// - They don't allocate or free any memory on the heap. +// - They don't recurse or mutually recurse. All the code +// could be inlined into the top-level public functions. +// - They run in at most quadratic time with respect to input arguments. +// Most functions run in linear time, and some in constant time. +// There are no unbounded loops or non-obvious termination conditions. +// - They are completely thread-safe if the caller does not give the +// same writable buffer to concurrent calls to these functions. + +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); + +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); +testable int getNumRawDataModules(int ver); + +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]); +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]); +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); + +testable void initializeFunctionModules(int version, uint8_t qrcode[]); +static void drawLightFunctionModules(uint8_t qrcode[], int version); +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); +testable int getAlignmentPatternPositions(int version, uint8_t result[7]); +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); + +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); +static long getPenaltyScore(const uint8_t qrcode[]); +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize); +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize); +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); + +testable bool getModuleBounded(const uint8_t qrcode[], int x, int y); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); +testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark); +static bool getBit(int x, int i); + +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); +static int numCharCountBits(enum qrcodegen_Mode mode, int version); + + + +/*---- Private tables of constants ----*/ + +// The set of all legal characters in alphanumeric mode, where each character +// value maps to the index in the string. For checking text and encoding segments. +static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +// Sentinel value for use in only some functions. +#define LENGTH_OVERFLOW -1 + +// For generating error correction codes. +testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above + +// For generating error correction codes. +testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + +// For automatic mask pattern selection. +static const int PENALTY_N1 = 3; +static const int PENALTY_N2 = 3; +static const int PENALTY_N3 = 40; +static const int PENALTY_N4 = 10; + + + +/*---- High-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + size_t textLen = strlen(text); + if (textLen == 0) + return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + size_t bufLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + + struct qrcodegen_Segment seg; + if (qrcodegen_isNumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeNumeric(text, tempBuffer); + } else if (qrcodegen_isAlphanumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeAlphanumeric(text, tempBuffer); + } else { + if (textLen > bufLen) + goto fail; + for (size_t i = 0; i < textLen; i++) + tempBuffer[i] = (uint8_t)text[i]; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, textLen); + if (seg.bitLength == LENGTH_OVERFLOW) + goto fail; + seg.numChars = (int)textLen; + seg.data = tempBuffer; + } + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + +fail: + qrcode[0] = 0; // Set size to invalid value for safety + return false; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + struct qrcodegen_Segment seg; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); + if (seg.bitLength == LENGTH_OVERFLOW) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + seg.numChars = (int)dataLen; + seg.data = dataAndTemp; + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); +} + + +// Appends the given number of low-order bits of the given value to the given byte-based +// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { + assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); + for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) + buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); +} + + + +/*---- Low-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = getTotalBits(segs, len, version); + if (dataUsedBits != LENGTH_OVERFLOW && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != LENGTH_OVERFLOW); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Concatenate all segments to create the data bit string + memset(qrcode, 0, (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + appendBitsToBuffer((unsigned int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer((unsigned int)seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) { + int bit = (seg->data[j >> 3] >> (7 - (j & 7))) & 1; + appendBitsToBuffer((unsigned int)bit, 1, qrcode, &bitLen); + } + } + assert(bitLen == dataUsedBits); + + // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + + // Compute ECC, draw modules + addEccAndInterleave(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawLightFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Do masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + applyMask(tempBuffer, qrcode, msk); + drawFormatBits(ecl, msk, qrcode); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = msk; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + applyMask(tempBuffer, qrcode, mask); // Apply the final choice of mask + drawFormatBits(ecl, mask, qrcode); // Overwrite old format bits + return true; +} + + + +/*---- Error correction code generation functions ----*/ + +// Appends error correction bytes to each block of the given data array, then interleaves +// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains +// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will +// be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { + // Calculate parameter numbers + assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int dataLen = getNumDataCodewords(version, ecl); + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; + + // Split data into blocks, calculate ECC, and interleave + // (not concatenate) the bytes into a single sequence + uint8_t rsdiv[qrcodegen_REED_SOLOMON_DEGREE_MAX]; + reedSolomonComputeDivisor(blockEccLen, rsdiv); + const uint8_t *dat = data; + for (int i = 0; i < numBlocks; i++) { + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + uint8_t *ecc = &data[dataLen]; // Temporary storage + reedSolomonComputeRemainder(dat, datLen, rsdiv, blockEccLen, ecc); + for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data + if (j == shortBlockDataLen) + k -= numShortBlocks; + result[k] = dat[j]; + } + for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC + result[k] = ecc[j]; + dat += datLen; + } +} + + +// Returns the number of 8-bit codewords that can be used for storing data (not ECC), +// for the given version number and error correction level. The result is in the range [9, 2956]. +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { + int v = version, e = (int)ecl; + assert(0 <= e && e < 4); + return getNumRawDataModules(v) / 8 + - ECC_CODEWORDS_PER_BLOCK [e][v] + * NUM_ERROR_CORRECTION_BLOCKS[e][v]; +} + + +// Returns the number of data bits that can be stored in a QR Code of the given version number, after +// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. +// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. +testable int getNumRawDataModules(int ver) { + assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + assert(208 <= result && result <= 29648); + return result; +} + + + +/*---- Reed-Solomon ECC generator functions ----*/ + +// Computes a Reed-Solomon ECC generator polynomial for the given degree, storing in result[0 : degree]. +// This could be implemented as a lookup table over all possible parameter values, instead of as an algorithm. +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + memset(result, 0, (size_t)degree * sizeof(result[0])); + result[degree - 1] = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < degree; j++) { + result[j] = reedSolomonMultiply(result[j], root); + if (j + 1 < degree) + result[j] ^= result[j + 1]; + } + root = reedSolomonMultiply(root, 0x02); + } +} + + +// Computes the Reed-Solomon error correction codeword for the given data and divisor polynomials. +// The remainder when data[0 : dataLen] is divided by divisor[0 : degree] is stored in result[0 : degree]. +// All polynomials are in big endian, and the generator has an implicit leading 1 term. +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]) { + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + memset(result, 0, (size_t)degree * sizeof(result[0])); + for (int i = 0; i < dataLen; i++) { // Polynomial division + uint8_t factor = data[i] ^ result[0]; + memmove(&result[0], &result[1], (size_t)(degree - 1) * sizeof(result[0])); + result[degree - 1] = 0; + for (int j = 0; j < degree; j++) + result[j] ^= reedSolomonMultiply(generator[j], factor); + } +} + +#undef qrcodegen_REED_SOLOMON_DEGREE_MAX + + +// Returns the product of the two given field elements modulo GF(2^8/0x11D). +// All inputs are valid. This could be implemented as a 256*256 lookup table. +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + uint8_t z = 0; + for (int i = 7; i >= 0; i--) { + z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D)); + z ^= ((y >> i) & 1) * x; + } + return z; +} + + + +/*---- Drawing function modules ----*/ + +// Clears the given QR Code grid with light modules for the given +// version's size, then marks every function module as dark. +testable void initializeFunctionModules(int version, uint8_t qrcode[]) { + // Initialize QR Code + int qrsize = version * 4 + 17; + memset(qrcode, 0, (size_t)((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); + qrcode[0] = (uint8_t)qrsize; + + // Fill horizontal and vertical timing patterns + fillRectangle(6, 0, 1, qrsize, qrcode); + fillRectangle(0, 6, qrsize, 1, qrcode); + + // Fill 3 finder patterns (all corners except bottom right) and format bits + fillRectangle(0, 0, 9, 9, qrcode); + fillRectangle(qrsize - 8, 0, 8, 9, qrcode); + fillRectangle(0, qrsize - 8, 9, 8, qrcode); + + // Fill numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); + } + } + + // Fill version blocks + if (version >= 7) { + fillRectangle(qrsize - 11, 0, 3, 6, qrcode); + fillRectangle(0, qrsize - 11, 6, 3, qrcode); + } +} + + +// Draws light function modules and possibly some dark modules onto the given QR Code, without changing +// non-function modules. This does not draw the format bits. This requires all function modules to be previously +// marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules. +static void drawLightFunctionModules(uint8_t qrcode[], int version) { + // Draw horizontal and vertical timing patterns + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 7; i < qrsize - 7; i += 2) { + setModuleBounded(qrcode, 6, i, false); + setModuleBounded(qrcode, i, 6, false); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = abs(dx); + if (abs(dy) > dist) + dist = abs(dy); + if (dist == 2 || dist == 4) { + setModuleUnbounded(qrcode, 3 + dx, 3 + dy, false); + setModuleUnbounded(qrcode, qrsize - 4 + dx, 3 + dy, false); + setModuleUnbounded(qrcode, 3 + dx, qrsize - 4 + dy, false); + } + } + } + + // Draw numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Don't draw on the three finder corners + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) + setModuleBounded(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); + } + } + } + + // Draw version blocks + if (version >= 7) { + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = (long)version << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 3; j++) { + int k = qrsize - 11 + j; + setModuleBounded(qrcode, k, i, (bits & 1) != 0); + setModuleBounded(qrcode, i, k, (bits & 1) != 0); + bits >>= 1; + } + } + } +} + + +// Draws two copies of the format bits (with its own error correction code) based +// on the given mask and error correction level. This always draws all modules of +// the format bits, unlike drawLightFunctionModules() which might skip dark modules. +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { + // Calculate error correction code and pack bits + assert(0 <= (int)mask && (int)mask <= 7); + static const int table[] = {1, 0, 3, 2}; + int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setModuleBounded(qrcode, 8, i, getBit(bits, i)); + setModuleBounded(qrcode, 8, 7, getBit(bits, 6)); + setModuleBounded(qrcode, 8, 8, getBit(bits, 7)); + setModuleBounded(qrcode, 7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setModuleBounded(qrcode, 14 - i, 8, getBit(bits, i)); + + // Draw second copy + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 0; i < 8; i++) + setModuleBounded(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setModuleBounded(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); + setModuleBounded(qrcode, 8, qrsize - 8, true); // Always dark +} + + +// Calculates and stores an ascending list of positions of alignment patterns +// for this version number, returning the length of the list (in the range [0,7]). +// Each position is in the range [0,177), and are used on both the x and y axes. +// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. +testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { + if (version == 1) + return 0; + int numAlign = version / 7 + 2; + int step = (version * 8 + numAlign * 3 + 5) / (numAlign * 4 - 4) * 2; + for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) + result[i] = (uint8_t)pos; + result[0] = 6; + return numAlign; +} + + +// Sets every module in the range [left : left + width] * [top : top + height] to dark. +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { + for (int dy = 0; dy < height; dy++) { + for (int dx = 0; dx < width; dx++) + setModuleBounded(qrcode, left + dx, top + dy, true); + } +} + + + +/*---- Drawing data modules and masking ----*/ + +// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of +// the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits). +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + int i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < qrsize; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate + if (!getModuleBounded(qrcode, x, y) && i < dataLen * 8) { + bool dark = getBit(data[i >> 3], 7 - (i & 7)); + setModuleBounded(qrcode, x, y, dark); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i == dataLen * 8); +} + + +// XORs the codeword modules in this QR Code with the given mask pattern +// and given pattern of function modules. The codeword bits must be drawn +// before masking. Due to the arithmetic of XOR, calling applyMask() with +// the same mask value a second time will undo the mask. A final well-formed +// QR Code needs exactly one (not zero, two, etc.) mask applied. +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { + assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO + int qrsize = qrcodegen_getSize(qrcode); + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(functionModules, x, y)) + continue; + bool invert; + switch ((int)mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: assert(false); return; + } + bool val = getModuleBounded(qrcode, x, y); + setModuleBounded(qrcode, x, y, val ^ invert); + } + } +} + + +// Calculates and returns the penalty score based on state of the given QR Code's current modules. +// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +static long getPenaltyScore(const uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < qrsize; y++) { + bool runColor = false; + int runX = 0; + int runHistory[7] = {0}; + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(qrcode, x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory, qrsize); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; + runColor = getModuleBounded(qrcode, x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory, qrsize) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < qrsize; x++) { + bool runColor = false; + int runY = 0; + int runHistory[7] = {0}; + for (int y = 0; y < qrsize; y++) { + if (getModuleBounded(qrcode, x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory, qrsize); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; + runColor = getModuleBounded(qrcode, x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory, qrsize) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < qrsize - 1; y++) { + for (int x = 0; x < qrsize - 1; x++) { + bool color = getModuleBounded(qrcode, x, y); + if ( color == getModuleBounded(qrcode, x + 1, y) && + color == getModuleBounded(qrcode, x, y + 1) && + color == getModuleBounded(qrcode, x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of dark and light modules + int dark = 0; + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(qrcode, x, y)) + dark++; + } + } + int total = qrsize * qrsize; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1; + assert(0 <= k && k <= 9); + result += k * PENALTY_N4; + assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; +} + + +// Can only be called immediately after a light run is added, and +// returns either 0, 1, or 2. A helper function for getPenaltyScore(). +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { + int n = runHistory[1]; + assert(n <= qrsize * 3); (void)qrsize; + bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; + // The maximum QR Code size is 177, hence the dark run length n <= 177. + // Arithmetic is promoted to int, so n*4 will not overflow. + return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); +} + + +// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { + if (currentRunColor) { // Terminate dark run + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); + currentRunLength = 0; + } + currentRunLength += qrsize; // Add light border to final run + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); + return finderPenaltyCountPatterns(runHistory, qrsize); +} + + +// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { + if (runHistory[0] == 0) + currentRunLength += qrsize; // Add light border to initial run + memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); + runHistory[0] = currentRunLength; +} + + + +/*---- Basic QR Code information ----*/ + +// Public function - see documentation comment in header file. +int qrcodegen_getSize(const uint8_t qrcode[]) { + assert(qrcode != NULL); + int result = qrcode[0]; + assert((qrcodegen_VERSION_MIN * 4 + 17) <= result + && result <= (qrcodegen_VERSION_MAX * 4 + 17)); + return result; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { + assert(qrcode != NULL); + int qrsize = qrcode[0]; + return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModuleBounded(qrcode, x, y); +} + + +// Returns the color of the module at the given coordinates, which must be in bounds. +testable bool getModuleBounded(const uint8_t qrcode[], int x, int y) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + return getBit(qrcode[(index >> 3) + 1], index & 7); +} + + +// Sets the color of the module at the given coordinates, which must be in bounds. +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + int bitIndex = index & 7; + int byteIndex = (index >> 3) + 1; + if (isDark) + qrcode[byteIndex] |= 1 << bitIndex; + else + qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; +} + + +// Sets the color of the module at the given coordinates, doing nothing if out of bounds. +testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark) { + int qrsize = qrcode[0]; + if (0 <= x && x < qrsize && 0 <= y && y < qrsize) + setModuleBounded(qrcode, x, y, isDark); +} + + +// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. +static bool getBit(int x, int i) { + return ((x >> i) & 1) != 0; +} + + + +/*---- Segment handling ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_isNumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (*text < '0' || *text > '9') + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_isAlphanumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { + int temp = calcSegmentBitLength(mode, numChars); + if (temp == LENGTH_OVERFLOW) + return SIZE_MAX; + assert(0 <= temp && temp <= INT16_MAX); + return ((size_t)temp + 7) / 8; +} + + +// Returns the number of data bits needed to represent a segment +// containing the given number of characters using the given mode. Notes: +// - Returns LENGTH_OVERFLOW on failure, i.e. numChars > INT16_MAX +// or the number of needed bits exceeds INT16_MAX (i.e. 32767). +// - Otherwise, all valid results are in the range [0, INT16_MAX]. +// - For byte mode, numChars measures the number of bytes, not Unicode code points. +// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. +// An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { + // All calculations are designed to avoid overflow on all platforms + if (numChars > (unsigned int)INT16_MAX) + return LENGTH_OVERFLOW; + long result = (long)numChars; + if (mode == qrcodegen_Mode_NUMERIC) + result = (result * 10 + 2) / 3; // ceil(10/3 * n) + else if (mode == qrcodegen_Mode_ALPHANUMERIC) + result = (result * 11 + 1) / 2; // ceil(11/2 * n) + else if (mode == qrcodegen_Mode_BYTE) + result *= 8; + else if (mode == qrcodegen_Mode_KANJI) + result *= 13; + else if (mode == qrcodegen_Mode_ECI && numChars == 0) + result = 3 * 8; + else { // Invalid argument + assert(false); + return LENGTH_OVERFLOW; + } + assert(result >= 0); + if (result > INT16_MAX) + return LENGTH_OVERFLOW; + return (int)result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { + assert(data != NULL || len == 0); + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_BYTE; + result.bitLength = calcSegmentBitLength(result.mode, len); + assert(result.bitLength != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (len > 0) + memcpy(buf, data, len * sizeof(buf[0])); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { + assert(digits != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(digits); + result.mode = qrcodegen_Mode_NUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *digits != '\0'; digits++) { + char c = *digits; + assert('0' <= c && c <= '9'); + accumData = accumData * 10 + (unsigned int)(c - '0'); + accumCount++; + if (accumCount == 3) { + appendBitsToBuffer(accumData, 10, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { + assert(text != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(text); + result.mode = qrcodegen_Mode_ALPHANUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *text != '\0'; text++) { + const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); + assert(temp != NULL); + accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + appendBitsToBuffer(accumData, 11, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + appendBitsToBuffer(accumData, 6, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_ECI; + result.numChars = 0; + result.bitLength = 0; + if (assignVal < 0) + assert(false); + else if (assignVal < (1 << 7)) { + memset(buf, 0, 1 * sizeof(buf[0])); + appendBitsToBuffer((unsigned int)assignVal, 8, buf, &result.bitLength); + } else if (assignVal < (1 << 14)) { + memset(buf, 0, 2 * sizeof(buf[0])); + appendBitsToBuffer(2, 2, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)assignVal, 14, buf, &result.bitLength); + } else if (assignVal < 1000000L) { + memset(buf, 0, 3 * sizeof(buf[0])); + appendBitsToBuffer(6, 3, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal >> 10), 11, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal & 0x3FF), 10, buf, &result.bitLength); + } else + assert(false); + result.data = buf; + return result; +} + + +// Calculates the number of bits needed to encode the given segments at the given version. +// Returns a non-negative number if successful. Otherwise returns LENGTH_OVERFLOW if a segment +// has too many characters to fit its length field, or the total bits exceeds INT16_MAX. +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { + assert(segs != NULL || len == 0); + long result = 0; + for (size_t i = 0; i < len; i++) { + int numChars = segs[i].numChars; + int bitLength = segs[i].bitLength; + assert(0 <= numChars && numChars <= INT16_MAX); + assert(0 <= bitLength && bitLength <= INT16_MAX); + int ccbits = numCharCountBits(segs[i].mode, version); + assert(0 <= ccbits && ccbits <= 16); + if (numChars >= (1L << ccbits)) + return LENGTH_OVERFLOW; // The segment's length doesn't fit the field's bit width + result += 4L + ccbits + bitLength; + if (result > INT16_MAX) + return LENGTH_OVERFLOW; // The sum might overflow an int type + } + assert(0 <= result && result <= INT16_MAX); + return (int)result; +} + + +// Returns the bit width of the character count field for a segment in the given mode +// in a QR Code at the given version number. The result is in the range [0, 16]. +static int numCharCountBits(enum qrcodegen_Mode mode, int version) { + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int i = (version + 7) / 17; + switch (mode) { + case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } + case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } + case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } + case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } + case qrcodegen_Mode_ECI : return 0; + default: assert(false); return -1; // Dummy value + } +} + + +#undef LENGTH_OVERFLOW diff --git a/lib/FlipCrypt/qrcode/qrcodegen.h b/lib/FlipCrypt/qrcode/qrcodegen.h new file mode 100644 index 0000000..6bbc157 --- /dev/null +++ b/lib/FlipCrypt/qrcode/qrcodegen.h @@ -0,0 +1,385 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * This library creates QR Code symbols, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * A QR Code structure is an immutable square grid of dark and light cells. + * The library provides functions to create a QR Code from text or binary data. + * The library covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). + * - Low level: Custom-make the list of segments and call + * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). + * (Note that all ways require supplying the desired error correction level and various byte buffers.) + */ + + +/*---- Enum and struct types----*/ + +/* + * The error correction level in a QR Code symbol. + */ +enum qrcodegen_Ecc { + // Must be declared in ascending order of error protection + // so that an internal qrcodegen function works properly + qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords +}; + + +/* + * The mask pattern used in a QR Code symbol. + */ +enum qrcodegen_Mask { + // A special value to tell the QR Code encoder to + // automatically select an appropriate mask pattern + qrcodegen_Mask_AUTO = -1, + // The eight actual mask patterns + qrcodegen_Mask_0 = 0, + qrcodegen_Mask_1, + qrcodegen_Mask_2, + qrcodegen_Mask_3, + qrcodegen_Mask_4, + qrcodegen_Mask_5, + qrcodegen_Mask_6, + qrcodegen_Mask_7, +}; + + +/* + * Describes how a segment's data bits are interpreted. + */ +enum qrcodegen_Mode { + qrcodegen_Mode_NUMERIC = 0x1, + qrcodegen_Mode_ALPHANUMERIC = 0x2, + qrcodegen_Mode_BYTE = 0x4, + qrcodegen_Mode_KANJI = 0x8, + qrcodegen_Mode_ECI = 0x7, +}; + + +/* + * A segment of character/binary/control data in a QR Code symbol. + * The mid-level way to create a segment is to take the payload data + * and call a factory function such as qrcodegen_makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and initialize a qrcodegen_Segment struct with appropriate values. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * Moreover, the maximum allowed bit length is 32767 because + * the largest QR Code (version 40) has 31329 modules. + */ +struct qrcodegen_Segment { + // The mode indicator of this segment. + enum qrcodegen_Mode mode; + + // The length of this segment's unencoded data. Measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. Not the same as the data's bit length. + int numChars; + + // The data bits of this segment, packed in bitwise big endian. + // Can be null if the bit length is zero. + uint8_t *data; + + // The number of valid data bits used in the buffer. Requires + // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. + // The character count (numChars) must agree with the mode and the bit buffer length. + int bitLength; +}; + + + +/*---- Macro constants and functions ----*/ + +#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard +#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard + +// Calculates the number of bytes needed to store any QR Code up to and including the given version number, +// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' +// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). +// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. +#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) + +// The worst-case number of bytes needed to store one QR Code, up to and including +// version 40. This value equals 3918, which is just under 4 kilobytes. +// Use this more convenient value to avoid calculating tighter memory bounds for buffers. +#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) + + + +/*---- Functions (high level) to generate QR Codes ----*/ + +/* + * Encodes the given text string to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * The input text must be encoded in UTF-8 and contain no NULs. + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * If successful, the resulting QR Code may use numeric, + * alphanumeric, or byte mode to encode the text. + * + * In the most optimistic case, a QR Code at version 40 with low ECC + * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string + * up to 4296 characters, or any digit string up to 7089 characters. + * These numbers represent the hard upper limit of the QR Code standard. + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/* + * Encodes the given binary data to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): + * - Before calling the function: + * - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The input array range dataAndTemp[0 : dataLen] should normally be + * valid UTF-8 text, but is not required by the QR Code standard. + * - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len] + * can be uninitialized because the function always writes before reading. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - dataAndTemp contains no useful data and should be treated as entirely uninitialized. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * If successful, the resulting QR Code will use byte mode to encode the data. + * + * In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte + * sequence up to length 2953. This is the hard upper limit of the QR Code standard. + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/*---- Functions (low level) to generate QR Codes ----*/ + +/* + * Encodes the given segments to a QR Code, returning true if successful. + * If the data is too long to fit in any version at the given ECC level, + * then false is returned. + * + * The smallest possible QR Code version is automatically chosen for + * the output. The ECC level of the result may be higher than the + * ecl argument if it can be done without increasing the version. + * + * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - The input array segs can contain segments whose data buffers overlap with tempBuffer. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - Any segment whose data buffer overlaps with tempBuffer[0 : len] + * must be treated as having invalid values in that array. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + * + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Encodes the given segments to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - The input array segs can contain segments whose data buffers overlap with tempBuffer. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - Any segment whose data buffer overlaps with tempBuffer[0 : len] + * must be treated as having invalid values in that array. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + * + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ +bool qrcodegen_isNumeric(const char *text); + + +/* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +bool qrcodegen_isAlphanumeric(const char *text); + + +/* + * Returns the number of bytes (uint8_t) needed for the data buffer of a segment + * containing the given number of characters using the given mode. Notes: + * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal + * calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767). + * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. + * - It is okay for the user to allocate more bytes for the buffer than needed. + * - For byte mode, numChars measures the number of bytes, not Unicode code points. + * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. + * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. + */ +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); + + +/* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte arrays are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); + + +/* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); + + +/* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); + + +/* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); + + +/*---- Functions to extract raw data from QR Codes ----*/ + +/* + * Returns the side length of the given QR Code, assuming that encoding succeeded. + * The result is in the range [21, 177]. Note that the length of the array buffer + * is related to the side length - every 'uint8_t qrcode[]' must have length at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). + */ +int qrcodegen_getSize(const uint8_t qrcode[]); + + +/* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. + */ +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); + + +#ifdef __cplusplus +} +#endif diff --git a/lib/FlipCrypt/screenshots/screenshot1.png b/lib/FlipCrypt/screenshots/screenshot1.png new file mode 100644 index 0000000..e364310 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot1.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot10.png b/lib/FlipCrypt/screenshots/screenshot10.png new file mode 100644 index 0000000..e4d2a40 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot10.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot11.png b/lib/FlipCrypt/screenshots/screenshot11.png new file mode 100644 index 0000000..c2ee774 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot11.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot2.png b/lib/FlipCrypt/screenshots/screenshot2.png new file mode 100644 index 0000000..5ae11fa Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot2.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot3.png b/lib/FlipCrypt/screenshots/screenshot3.png new file mode 100644 index 0000000..ab73a16 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot3.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot4.png b/lib/FlipCrypt/screenshots/screenshot4.png new file mode 100644 index 0000000..7c5ed62 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot4.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot5.png b/lib/FlipCrypt/screenshots/screenshot5.png new file mode 100644 index 0000000..9921c7b Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot5.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot6.png b/lib/FlipCrypt/screenshots/screenshot6.png new file mode 100644 index 0000000..aa57734 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot6.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot7.png b/lib/FlipCrypt/screenshots/screenshot7.png new file mode 100644 index 0000000..7121c4e Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot7.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot8.png b/lib/FlipCrypt/screenshots/screenshot8.png new file mode 100644 index 0000000..bd52f63 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot8.png differ diff --git a/lib/FlipCrypt/screenshots/screenshot9.png b/lib/FlipCrypt/screenshots/screenshot9.png new file mode 100644 index 0000000..63a58b3 Binary files /dev/null and b/lib/FlipCrypt/screenshots/screenshot9.png differ diff --git a/lib/FlipCrypt/storage.c b/lib/FlipCrypt/storage.c new file mode 100644 index 0000000..f3ea1ec --- /dev/null +++ b/lib/FlipCrypt/storage.c @@ -0,0 +1,53 @@ +#include + +// callback for saving from output screen +void save_result(const char* text, char* file_name) { + File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + + char buffer[128]; + snprintf(buffer, sizeof(buffer), "/ext/flip_crypt_saved/%s.txt", file_name); + + if(storage_simply_mkdir(furi_record_open(RECORD_STORAGE), "/ext/flip_crypt_saved")) { + if (storage_file_open(file, buffer, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, text, strlen(text)); + } + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +// back end saving and loading for QR and NFC functionality + +void save_result_generic(const char* filename, const char* text) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + if(storage_file_open(file, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, text, strlen(text)); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +char* load_result_generic(const char* filename) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + char* result = NULL; + + if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { + size_t size = storage_file_size(file); + if(size > 0) { + result = malloc(size + 1); + storage_file_read(file, result, size); + result[size] = '\0'; + } else { + result = strdup("FAILURE"); + } + storage_file_close(file); + } + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return result; +} \ No newline at end of file diff --git a/lib/FlipCrypt/storage.h b/lib/FlipCrypt/storage.h new file mode 100644 index 0000000..1269336 --- /dev/null +++ b/lib/FlipCrypt/storage.h @@ -0,0 +1,3 @@ +void save_result(const char* text, char* file_name); +void save_result_generic(const char* filename, const char* text); +char* load_result_generic(const char* filename); \ No newline at end of file diff --git a/main.c b/main.c index 0bb08aa..525559f 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,43 @@ +// Enable BLE HID transport +#define PASSWORD_MANAGER_TRANSPORT_BLE + #include "main.h" + +#include "views/passcode.h" #include "views/saved_passwords.h" #include "views/delete_password.h" + +#include "views/password_generator.h" +#include "views/options.h" +#include "badusb/badusb.h" + +// HID keyboard constants for BLE HID typing +#define HID_KEYBOARD_LEFT_SHIFT 0xE1 +#define HID_KEYBOARD_NONE 0x00 + +// Include furi_hal for delay functions +#include + #include "textInput/textInput.h" #include "passwordStorage/passwordStorage.h" +#include + + +// Forward declaration of PasswordGeneratorState +typedef struct { + bool enabled[4]; + uint8_t length; + char generated_password[33]; + uint8_t selected_option; + bool show_password; + uint8_t scroll_offset; + bool display_mode; +} PasswordGeneratorState; + +// File path for the encrypted password database +#define DATABASE_FILE "/ext/apps_data/PasswordManager/passwords.enc" +// File path for configuration +#define CONFIG_FILE "/ext/apps_data/PasswordManager/config.conf" static void render_main_menu(Canvas* canvas, void* model) { @@ -14,22 +49,44 @@ static void render_main_menu(Canvas* canvas, void* model) { AppContext** model_ = model; AppContext* app = *model_; - FURI_LOG_I("Password Manager", "Context in render: %p", app); + canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); // Draw header canvas_set_color(canvas, ColorBlack); canvas_draw_str(canvas, 20, 10, "Password Manager"); + + + // Draw BLE status indicator + if(app->bluetooth_enabled && app->ble_hid_profile) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, 2, 10, "BT"); + } + canvas_draw_line(canvas, 0, 12, 128, 12); - for(size_t i = 0; i < MENU_ITEMS; i++) { - int y = 25 + i * 12; + + // Calculate visible items based on scroll offset + size_t visible_items = 4; // Can show 4 items on screen (25, 37, 49, 61) + size_t start_item = app->scroll_offset; + size_t end_item = start_item + visible_items; + + // Ensure we don't go out of bounds + if(end_item > MENU_ITEMS) { + end_item = MENU_ITEMS; + start_item = (end_item > visible_items) ? end_item - visible_items : 0; + } + + // Draw visible menu items + for(size_t i = start_item; i < end_item; i++) { + int display_index = i - start_item; + int y = 25 + display_index * 12; if(i == app->selected) { - // Highlight selected item with black box + // Highlight selected item with black box (leave space for scrollbar) canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, y - 10, 128, 12); + canvas_draw_box(canvas, 0, y - 10, 122, 12); canvas_set_color(canvas, ColorWhite); } else { canvas_set_color(canvas, ColorBlack); @@ -38,6 +95,28 @@ static void render_main_menu(Canvas* canvas, void* model) { canvas_draw_str(canvas, 5, y, app->items[i]); } + // Draw vertical scrollbar on the right + { + const size_t total_items = MENU_ITEMS; + if(total_items > visible_items) { + int track_x = 124; + int track_y = 14; // below header line + int track_h = 64 - track_y; // to bottom + int track_w = 3; + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, track_x, track_y, track_w, track_h); + int thumb_h = (track_h * (int)visible_items) / (int)total_items; + if(thumb_h < 6) thumb_h = 6; + int max_offset = (int)total_items - (int)visible_items; + int thumb_max = track_h - thumb_h; + int thumb_y = track_y; + if(max_offset > 0) { + thumb_y = track_y + (thumb_max * (int)start_item) / max_offset; + } + canvas_draw_box(canvas, track_x + 1, thumb_y + 1, track_w - 2, thumb_h - 2); + } + } + } static bool handle_main_menu_input(InputEvent* event, void* context) { @@ -49,47 +128,307 @@ static bool handle_main_menu_input(InputEvent* event, void* context) { return false; } - if(event->type == InputTypeShort) { + + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { if(event->key == InputKeyUp) { - if(app->selected > 0) app->selected--; + + // Circular navigation: wrap from top to bottom + if(app->selected == 0) { + app->selected = MENU_ITEMS - 1; // Jump to last item + // Adjust scroll to show last items + app->scroll_offset = (MENU_ITEMS > 4) ? MENU_ITEMS - 4 : 0; + } else { + app->selected--; + // Adjust scroll offset to keep selected item visible + if(app->selected < app->scroll_offset) { + app->scroll_offset = app->selected; + } + } return true; } else if(event->key == InputKeyDown) { - if(app->selected + 1 < MENU_ITEMS) app->selected++; - return true; - } else if(event->key == InputKeyBack) { - app->running = false; - view_dispatcher_stop(app->view_dispatcher); + + // Circular navigation: wrap from bottom to top + if(app->selected == MENU_ITEMS - 1) { + app->selected = 0; // Jump to first item + app->scroll_offset = 0; // Reset scroll to top + } else { + app->selected++; + // Adjust scroll offset to keep selected item visible + if(app->selected >= app->scroll_offset + 4) { + app->scroll_offset = app->selected - 3; + } + } return true; } else if(event->key == InputKeyOk) { if(app->selected == 0) { // Show stored credentials - app->credentials_number = read_passwords_from_file("/ext/passwordManager.txt", app->credentials); + + app->credentials_number = read_passwords_from_file(DATABASE_FILE, app->credentials); view_dispatcher_switch_to_view(app->view_dispatcher, ViewSavedPasswords); return true; } else if(app->selected == 1) { - app->credentials_number = read_passwords_from_file("/ext/passwordManager.txt", app->credentials); + + app->credentials_number = read_passwords_from_file(DATABASE_FILE, app->credentials); // Add password flow view_dispatcher_switch_to_view(app->view_dispatcher, ViewTextInputCredentialName); - app->selected = 0; - app->scroll_offset = 0; return true; } else if(app->selected == 2) { - app->credentials_number = read_passwords_from_file("/ext/passwordManager.txt", app->credentials); - // Delete password view + + // Reload credentials to get the latest data + app->credentials_number = read_passwords_from_file(DATABASE_FILE, app->credentials); + + // Only switch to delete view if we have credentials to delete + if(app->credentials_number > 0) { + // Reset delete view state properly + app->confirm_delete = false; view_dispatcher_switch_to_view(app->view_dispatcher, ViewDeletePassword); - app->selected = 0; - app->scroll_offset = 0; + + return true; + } + // If no credentials, stay on main menu (could show a message) + return true; + } else if(app->selected == 3) { + // Password Generator + view_dispatcher_switch_to_view(app->view_dispatcher, ViewPasswordGenerator); + return true; + } else if(app->selected == 4) { + // Options + view_dispatcher_switch_to_view(app->view_dispatcher, ViewOptions); return true; } } + + } else if(event->type == InputTypeLong && event->key == InputKeyBack) { + // Hold back button to exit immediately (faster than default) + app->running = false; + view_dispatcher_stop(app->view_dispatcher); + return true; } return false; } // Custom callback for the view dispatcher -static bool view_dispatcher_navigation_event_callback(void* context) { + +// BLE connection status callback +static void ble_led_timer_cb(void* context) { + AppContext* app = context; + if(!app->ble_led_blinking) return; + static bool on = false; + on = !on; + furi_hal_light_set(LightBlue, on ? 255 : 0); +} + +static void ble_led_start_blink(AppContext* app) { + app->ble_led_blinking = true; + if(!app->ble_led_timer) { + app->ble_led_timer = furi_timer_alloc(ble_led_timer_cb, FuriTimerTypePeriodic, app); + } + // Slightly faster blink (~0.6s period) + furi_timer_start(app->ble_led_timer, furi_ms_to_ticks(600)); +} + +static void ble_led_stop(AppContext* app) { + app->ble_led_blinking = false; + if(app->ble_led_timer) furi_timer_stop(app->ble_led_timer); + furi_hal_light_set(LightBlue, 0); +} + +void ble_connection_status_changed_callback(BtStatus status, void* context) { AppContext* app = context; + if(status == BtStatusConnected) { + app->is_ble_connected = true; + app->ble_led_blinking = false; + if(app->ble_led_timer) furi_timer_stop(app->ble_led_timer); + furi_hal_light_set(LightBlue, 255); + } else if(status < BtStatusConnected) { + app->is_ble_connected = false; + // If BLE profile still active, resume discovery blink; otherwise turn off + if(app->ble_hid_profile) { + ble_led_start_blink(app); + } else { + ble_led_stop(app); + } + } +} + +// BLE HID management functions +void password_manager_ble_start_hid(AppContext* app) { + if(!app->bluetooth_enabled || app->ble_hid_profile) { + return; // Already running or disabled + } + + // Disable USB HID before starting BLE HID + deinitialize_hid(); + + // Disconnect any existing BT connection + bt_disconnect(app->bt); + furi_delay_ms(200); + + // Configure BLE HID profile parameters + BleProfileHidParams ble_params = { + .device_name_prefix = "Control", + .mac_xor = 0x00 + }; + + // Start BLE HID profile + app->ble_hid_profile = bt_profile_start(app->bt, ble_profile_hid, &ble_params); + + if(app->ble_hid_profile) { + // Start advertising + furi_hal_bt_start_advertising(); + // Start blue blink (discovery) + ble_led_start_blink(app); + + // Set connection status callback + bt_set_status_changed_callback(app->bt, ble_connection_status_changed_callback, app); + + // BLE HID profile started successfully + } else { + FURI_LOG_E("Password Manager", "Failed to start BLE HID profile"); + // Don't re-enable USB HID - let Flipper stay in default state + } +} + +void password_manager_ble_stop_hid(AppContext* app) { + if(!app->ble_hid_profile) { + return; // Not running + } + + + + // Remove status callback + bt_set_status_changed_callback(app->bt, NULL, NULL); + + // Stop advertising + furi_hal_bt_stop_advertising(); + + // Disconnect + bt_disconnect(app->bt); + furi_delay_ms(200); + + // Restore default profile + if(!bt_profile_restore_default(app->bt)) { + FURI_LOG_E("Password Manager", "Failed to restore default BT profile"); + } + + app->ble_hid_profile = NULL; + app->is_ble_connected = false; + ble_led_stop(app); + + // Don't re-enable USB HID - let Flipper stay in default state + // USB HID will be initialized only when needed (typing passwords) +} + + + +// Load keyboard layout from saved configuration +void password_manager_load_keyboard_layout(AppContext* app) { + UNUSED(app); + // Load configuration to get saved keyboard layout + Storage* config_storage = furi_record_open(RECORD_STORAGE); + if(config_storage) { + if(storage_file_exists(config_storage, CONFIG_FILE)) { + File* config_file = storage_file_alloc(config_storage); + if(storage_file_open(config_file, CONFIG_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { + char buffer[512]; + size_t bytes_read = storage_file_read(config_file, buffer, sizeof(buffer) - 1); + buffer[bytes_read] = '\0'; + + + + // Look for the keyboard layout line (using same format as Options menu) + char* layout_line = strstr(buffer, "LAYOUT:"); + if(layout_line) { + layout_line += 7; // Skip "LAYOUT:" + char* newline = strchr(layout_line, '\n'); + if(newline) { + *newline = '\0'; // Null-terminate the layout filename + } + + // Build the full path and load the saved keyboard layout + char layout_path[256]; + snprintf(layout_path, sizeof(layout_path), "/ext/badusb/assets/layouts/%s", layout_line); + load_keyboard_layout(layout_path); + } else { + // No saved layout, load default + load_keyboard_layout(""); + } + + storage_file_close(config_file); + } + storage_file_free(config_file); + } else { + // No config file, load default layout + load_keyboard_layout(""); + } + furi_record_close(RECORD_STORAGE); + } +} + + + +// BLE HID keyboard functions +void password_manager_hal_keyboard_press(AppContext* instance, uint16_t keycode) { + if(instance && instance->ble_hid_profile && instance->is_ble_connected) { + ble_profile_hid_kb_press(instance->ble_hid_profile, keycode); + } +} + +void password_manager_hal_keyboard_release(AppContext* instance, uint16_t keycode) { + if(instance && instance->ble_hid_profile && instance->is_ble_connected) { + ble_profile_hid_kb_release(instance->ble_hid_profile, keycode); + } +} + +void password_manager_hal_keyboard_release_all(AppContext* instance) { + if(instance && instance->ble_hid_profile && instance->is_ble_connected) { + ble_profile_hid_kb_release_all(instance->ble_hid_profile); + } +} + +void password_manager_hal_type_string(AppContext* instance, const char* text) { + if(!instance || !instance->ble_hid_profile || !instance->is_ble_connected || !text) { + return; + } + + // Use the same keyboard layout system as badUSB for consistency + for(int i = 0; text[i] != '\0'; i++) { + char c = text[i]; + uint16_t keycode = ascii_to_key(c); // Use the same function as badUSB + + if(keycode != HID_KEYBOARD_NONE) { + // Check if we need Shift modifier for this character + bool need_shift = (c >= 'A' && c <= 'Z') || + (c == '!' || c == '@' || c == '#' || c == '$' || c == '%' || + c == '^' || c == '&' || c == '*' || c == '(' || c == ')' || + c == '_' || c == '+' || c == '{' || c == '}' || c == '|' || + c == ':' || c == '"' || c == '<' || c == '>' || c == '?' || + c == '~'); + + if(need_shift) { + password_manager_hal_keyboard_press(instance, HID_KEYBOARD_LEFT_SHIFT); + } + + password_manager_hal_keyboard_press(instance, keycode); + furi_delay_ms(50); // Small delay + password_manager_hal_keyboard_release(instance, keycode); + + if(need_shift) { + password_manager_hal_keyboard_release(instance, HID_KEYBOARD_LEFT_SHIFT); + } + + furi_delay_ms(50); // Small delay between characters + } + } +} + +bool view_dispatcher_navigation_event_callback(void* context) { + AppContext* app = context; + + // Reset selection and scroll when returning to main menu + app->selected = 0; + app->scroll_offset = 0; view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); return true; } @@ -113,14 +452,58 @@ int32_t password_manager_app(void* p) { app->running = true; app->confirm_delete = false; + + // Initialize options + app->bluetooth_enabled = false; + app->keyboard_layout = 0; // US QWERTY by default + app->passcode_change_requested = false; + + // Keyboard layout will be loaded after passcode verification + + // Initialize password generator state + app->password_generator_state = malloc(sizeof(PasswordGeneratorState)); + if(app->password_generator_state) { + // Initialize with default values + PasswordGeneratorState* state = (PasswordGeneratorState*)app->password_generator_state; + state->length = 12; // DEFAULT_PASSWORD_LENGTH + state->selected_option = 0; + state->show_password = false; + + // Enable common character sets by default + state->enabled[0] = true; // Uppercase + state->enabled[1] = true; // Lowercase + state->enabled[2] = true; // Numbers + state->enabled[3] = false; // Symbols (disabled by default) + + // Clear generated password + memset(state->generated_password, 0, sizeof(state->generated_password)); + + // Initialize scroll offset + state->scroll_offset = 0; + + // Initialize display mode + state->display_mode = false; + } + app->items[0] = "Saved passwords"; app->items[1] = "Add new password"; app->items[2] = "Delete password"; - app->credentials_number = read_passwords_from_file("/ext/passwordManager.txt", app->credentials); + app->items[3] = "Password Generator"; + app->items[4] = "Options"; + + + app->credentials_number = read_passwords_from_file(DATABASE_FILE, app->credentials); + + + // Configuration will be loaded after passcode verification // Initialize GUI and View Dispatcher app->gui = furi_record_open(RECORD_GUI); + // Notification app for LED + app->notification = furi_record_open(RECORD_NOTIFICATION); + app->ble_led_timer = NULL; + app->ble_led_blinking = false; if (!app->gui) { free(app); return -2; @@ -163,6 +546,10 @@ int32_t password_manager_app(void* p) { app->delete_password_view = delete_password_view_alloc(app); view_dispatcher_add_view(app->view_dispatcher, ViewDeletePassword, app->delete_password_view); + + app->passcode_view = passcode_view_alloc(app); + view_dispatcher_add_view(app->view_dispatcher, ViewPasscode, app->passcode_view); + app->textInput_credential_name = credential_name_TextInput_alloc(app); view_dispatcher_add_view(app->view_dispatcher, ViewTextInputCredentialName, text_input_get_view(app->textInput_credential_name)); @@ -172,8 +559,55 @@ int32_t password_manager_app(void* p) { app->textInput_password = credential_password_TextInput_alloc(app); view_dispatcher_add_view(app->view_dispatcher, ViewTextInputPassword, text_input_get_view(app->textInput_password)); - // Start with main menu - view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); + + app->password_generator_view = password_generator_view_alloc(app); + view_dispatcher_add_view(app->view_dispatcher, ViewPasswordGenerator, app->password_generator_view); + + app->options_view = options_view_alloc(app); + view_dispatcher_add_view(app->view_dispatcher, ViewOptions, app->options_view); + + // Initialize BLE HID support + app->bt = furi_record_open(RECORD_BT); + app->ble_hid_profile = NULL; + app->is_ble_connected = false; + app->usb_hid_active = false; + + // Check if key file exists to determine initial state + Storage* storage = furi_record_open(RECORD_STORAGE); + bool key_exists = storage_file_exists(storage, "/ext/apps_data/PasswordManager/key.enc"); + + // Load fail count if it exists + app->fail_count = 0; + if(storage_file_exists(storage, "/ext/apps_data/PasswordManager/fail_count.enc")) { + File* fail_file = storage_file_alloc(storage); + if(storage_file_open(fail_file, "/ext/apps_data/PasswordManager/fail_count.enc", FSAM_READ, FSOM_OPEN_EXISTING)) { + if(storage_file_read(fail_file, &app->fail_count, sizeof(app->fail_count)) == sizeof(app->fail_count)) { + // Validate fail count + if(app->fail_count >= 10) { + // If fail count is at max, reset everything + storage_simply_remove(storage, "/ext/apps_data/PasswordManager/key.enc"); + storage_simply_remove(storage, "/ext/apps_data/PasswordManager/passwords.enc"); + storage_simply_remove(storage, "/ext/apps_data/PasswordManager/fail_count.enc"); + app->fail_count = 0; + key_exists = false; + } + } + storage_file_close(fail_file); + } + storage_file_free(fail_file); + } + + furi_record_close(RECORD_STORAGE); + + if(key_exists) { + // Key file exists - go to unlock mode + app->passcode_state = DpadPasscodeState_Unlock; + } else { + // No key file - go to setup mode + app->passcode_state = DpadPasscodeState_Setup; + } + + view_dispatcher_switch_to_view(app->view_dispatcher, ViewPasscode); // Main loop - just run the view dispatcher view_dispatcher_run(app->view_dispatcher); @@ -185,6 +619,9 @@ int32_t password_manager_app(void* p) { view_free(app->saved_passwords_view); view_dispatcher_remove_view(app->view_dispatcher, ViewDeletePassword); view_free(app->delete_password_view); + + view_dispatcher_remove_view(app->view_dispatcher, ViewPasscode); + view_free(app->passcode_view); view_dispatcher_remove_view(app->view_dispatcher, ViewTextInputCredentialName); view_free(text_input_get_view(app->textInput_credential_name)); view_dispatcher_remove_view(app->view_dispatcher, ViewTextInputUsername); @@ -192,10 +629,43 @@ int32_t password_manager_app(void* p) { view_dispatcher_remove_view(app->view_dispatcher, ViewTextInputPassword); view_free(text_input_get_view(app->textInput_password)); + + view_dispatcher_remove_view(app->view_dispatcher, ViewPasswordGenerator); + view_free(app->password_generator_view); + + view_dispatcher_remove_view(app->view_dispatcher, ViewOptions); + view_free(app->options_view); + + + + // Cleanup BLE HID profile + if(app->ble_hid_profile) { + password_manager_ble_stop_hid(app); + } + + // Cleanup USB HID if active + if(app->usb_hid_active) { + deinitialize_hid(); + app->usb_hid_active = false; + } + + // Close BT service + if(app->bt) { + furi_record_close(RECORD_BT); + app->bt = NULL; + } + view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_GUI); + + + // Free password generator state + if(app->password_generator_state) { + free(app->password_generator_state); + } free(app); return 0; + } \ No newline at end of file diff --git a/main.h b/main.h index eec7003..ed0dfd3 100644 --- a/main.h +++ b/main.h @@ -12,20 +12,41 @@ #include #include #include +#include +#include +#include +#include "ble/pm_ble_hid_ext_profile.h" +#include +#include +#include +#include // TODO: VOID HARDCODED STUFF -#define MENU_ITEMS 3 +#define MENU_ITEMS 5 // Define view IDs typedef enum { + ViewPasscode, ViewMainMenu, ViewSavedPasswords, ViewDeletePassword, ViewTextInputCredentialName, ViewTextInputUsername, - ViewTextInputPassword + ViewTextInputPassword, + ViewPasswordGenerator, + ViewOptions } ViewID; +typedef enum { + DpadPasscodeState_None, + DpadPasscodeState_Setup, + DpadPasscodeState_Confirm, + DpadPasscodeState_Unlock, + DpadPasscodeState_Unlocked, + DpadPasscodeState_Reset, + DpadPasscodeState_ResetConfirm +} DpadPasscodeState; + typedef struct { char name[100]; char username[100]; @@ -41,9 +62,12 @@ typedef struct { // For view management Gui* gui; ViewDispatcher* view_dispatcher; + View* passcode_view; View* main_menu_view; View* saved_passwords_view; View* delete_password_view; + View* password_generator_view; + View* options_view; Credential credentials[100]; size_t credentials_number; @@ -56,5 +80,44 @@ typedef struct { TextInput* textInput_password; bool confirm_delete; + DpadPasscodeState passcode_state; + uint8_t passcode[16]; // stores the passcode sequence (max 16 steps) + uint8_t passcode_len; + uint8_t passcode_input[16]; // current input + uint8_t passcode_input_len; + bool passcode_set; + bool passcode_unlocked; + uint8_t fail_count; + + // Options settings + bool bluetooth_enabled; + uint8_t keyboard_layout; + bool passcode_change_requested; + + // BLE HID support + Bt* bt; + FuriHalBleProfileBase* ble_hid_profile; + bool is_ble_connected; + bool usb_hid_active; + FuriHalUsbInterface* previous_usb_config; + NotificationApp* notification; + FuriTimer* ble_led_timer; + bool ble_led_blinking; + + // Password generator state + void* password_generator_state; + +} AppContext; + +// BLE HID management functions (placeholder for future implementation) +void password_manager_ble_start_hid(AppContext* app); +void password_manager_ble_stop_hid(AppContext* app); + +void password_manager_load_keyboard_layout(AppContext* app); + -} AppContext; \ No newline at end of file +// BLE HID transport functions +void password_manager_hal_keyboard_press(AppContext* instance, uint16_t keycode); +void password_manager_hal_keyboard_release(AppContext* instance, uint16_t keycode); +void password_manager_hal_keyboard_release_all(AppContext* instance); +void password_manager_hal_type_string(AppContext* instance, const char* text); diff --git a/passwordStorage/passwordStorage.c b/passwordStorage/passwordStorage.c index e4b07b3..c357fde 100644 --- a/passwordStorage/passwordStorage.c +++ b/passwordStorage/passwordStorage.c @@ -2,68 +2,263 @@ #include "../main.h" #include #include - -#include -#include #include +#include "../lib/FlipCrypt/ciphers/aes.h" +#include "../lib/FlipCrypt/hashes/sha2.h" -#include -#include -#include +// Default AES key and IV (legacy fallback for already-encrypted DB rows) +static const uint8_t default_aes_key[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F}; +static const uint8_t default_aes_iv[16] = {0x0F,0x0E,0x0D,0x0C,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00}; -#include -#include -#include +// Runtime AES key/iv derived at unlock from passcode (or other secret) +static uint8_t runtime_aes_key[16]; +static uint8_t runtime_aes_iv[16]; +static bool runtime_key_set = false; + +void password_storage_set_key(const uint8_t* key16, const uint8_t* iv16) { + if(key16 && iv16) { + memcpy(runtime_aes_key, key16, 16); + memcpy(runtime_aes_iv, iv16, 16); + runtime_key_set = true; + } +} + +void password_storage_clear_key(void) { + memset(runtime_aes_key, 0, sizeof(runtime_aes_key)); + memset(runtime_aes_iv, 0, sizeof(runtime_aes_iv)); + runtime_key_set = false; +} + +// Derive AES-128 key and IV from passcode bytes using SHA-256 (no DB format change) +// key = SHA256(passcode || 0x01)[0..15], iv = SHA256(passcode || 0x02)[0..15] +static void derive_key_iv_from_passcode(const uint8_t* passcode, size_t passcode_len, uint8_t* out_key16, uint8_t* out_iv16) { + uint8_t buf[16]; + uint8_t hash[32]; + sha256_ctx ctx; + + // Key derivation + sha256_init(&ctx); + sha256_update(&ctx, passcode, (uint64)passcode_len); + buf[0] = 0x01; // domain separation + sha256_update(&ctx, buf, 1); + sha256_final(&ctx, hash); + memcpy(out_key16, hash, 16); + + // IV derivation + sha256_init(&ctx); + sha256_update(&ctx, passcode, (uint64)passcode_len); + buf[0] = 0x02; // domain separation + sha256_update(&ctx, buf, 1); + sha256_final(&ctx, hash); + memcpy(out_iv16, hash, 16); + + // Wipe + memset(hash, 0, sizeof(hash)); +} + +void password_storage_set_key_from_passcode(const uint8_t* passcode, size_t passcode_len) { + uint8_t key16[16]; + uint8_t iv16[16]; + derive_key_iv_from_passcode(passcode, passcode_len, key16, iv16); + password_storage_set_key(key16, iv16); + memset(key16, 0, sizeof(key16)); + memset(iv16, 0, sizeof(iv16)); +} + +bool password_storage_get_runtime_key(uint8_t* key16_out, uint8_t* iv16_out, bool* is_set_out) { + if(is_set_out) *is_set_out = runtime_key_set; + if(!runtime_key_set) return false; + if(key16_out) memcpy(key16_out, runtime_aes_key, 16); + if(iv16_out) memcpy(iv16_out, runtime_aes_iv, 16); + return true; +} + +// Helper function to convert bytes to hex string +void bytes_to_hex(const uint8_t* data, size_t len, char* hex_str) { + const char hex_chars[] = "0123456789ABCDEF"; + for(size_t i = 0; i < len; i++) { + hex_str[i * 2] = hex_chars[(data[i] >> 4) & 0x0F]; + hex_str[i * 2 + 1] = hex_chars[data[i] & 0x0F]; + } + hex_str[len * 2] = '\0'; +} + +// Helper function to convert hex string to bytes +bool hex_to_bytes(const char* hex_str, uint8_t* data, size_t len) { + if(strlen(hex_str) != len * 2) return false; + + for(size_t i = 0; i < len; i++) { + char hex_byte[3] = {hex_str[i * 2], hex_str[i * 2 + 1], '\0'}; + int value; + if(sscanf(hex_byte, "%2x", &value) != 1) return false; + data[i] = (uint8_t)value; + } + return true; +} + +// Encrypt a credential and return hex string +void encrypt_credential(const char* service, const char* username, const char* password, char* encrypted_hex) { + // Create a buffer with all three fields concatenated + char plaintext[64]; // STM32 WB55 optimized - 64 bytes max + + // Check if concatenated string will fit and truncate if necessary + size_t service_len = strlen(service); + size_t username_len = strlen(username); + size_t password_len = strlen(password); + + + // Calculate total length needed (including 2 delimiters 0x1E) + const char field_delim = '\x1E'; + size_t total_len = service_len + username_len + password_len + 2; // +2 for delimiters + + // Build plaintext with non-printable delimiter to avoid conflicts with ',' in fields + if(total_len >= sizeof(plaintext)) { + // Truncate fields to fit in buffer + size_t max_field_len = (sizeof(plaintext) - 2 /*delims*/ - 1 /*null*/) / 3; + size_t offset = 0; + // Service + size_t copy = (service_len > max_field_len) ? max_field_len : service_len; + memcpy(&plaintext[offset], service, copy); offset += copy; + plaintext[offset++] = field_delim; + // Username + copy = (username_len > max_field_len) ? max_field_len : username_len; + memcpy(&plaintext[offset], username, copy); offset += copy; + plaintext[offset++] = field_delim; + // Password + copy = (password_len > max_field_len) ? max_field_len : password_len; + memcpy(&plaintext[offset], password, copy); offset += copy; + plaintext[offset] = '\0'; + } else { + size_t offset = 0; + memcpy(&plaintext[offset], service, service_len); offset += service_len; + plaintext[offset++] = field_delim; + memcpy(&plaintext[offset], username, username_len); offset += username_len; + plaintext[offset++] = field_delim; + memcpy(&plaintext[offset], password, password_len); offset += password_len; + plaintext[offset] = '\0'; + } + + + // Pad to 16-byte boundary for AES + size_t plaintext_len = strlen(plaintext); + size_t padded_len = ((plaintext_len + 15) / 16) * 16; + + uint8_t padded_data[64]; // STM32 WB55 optimized + memset(padded_data, 0, padded_len); + memcpy(padded_data, plaintext, plaintext_len); + + // Encrypt the padded data + uint8_t encrypted_data[64]; // STM32 WB55 optimized + struct AES_ctx ctx; + if(runtime_key_set) { + AES_init_ctx_iv(&ctx, runtime_aes_key, runtime_aes_iv); + } else { + AES_init_ctx_iv(&ctx, default_aes_key, default_aes_iv); + } + memcpy(encrypted_data, padded_data, padded_len); + AES_CBC_encrypt_buffer(&ctx, encrypted_data, padded_len); + + // Convert to hex string + bytes_to_hex(encrypted_data, padded_len, encrypted_hex); +} -bool parse_escaped_line(const char *line, char *field1, char *field2, char *field3) { - char *fields[3] = { field1, field2, field3 }; - int current_field = 0; - int field_letter = 0; - bool escape = false; +// Decrypt a hex string back to credential fields +static bool try_decrypt_parse_with_key( + const uint8_t* encrypted_data, + size_t encrypted_len, + const uint8_t* key, + const uint8_t* iv, + char* service, + char* username, + char* password) { + uint8_t decrypted_data[64]; + struct AES_ctx lctx; + AES_init_ctx_iv(&lctx, key, iv); + memcpy(decrypted_data, encrypted_data, encrypted_len); + AES_CBC_decrypt_buffer(&lctx, decrypted_data, encrypted_len); - for (int i = 0; line[i] != '\0'; ++i) { - char c = line[i]; + char* decrypted_str = (char*)decrypted_data; - if (escape) { - // Copy the next character - if (field_letter < 99) { - fields[current_field][field_letter++] = c; + size_t actual_len = 0; + for(size_t i = 0; i < encrypted_len && i < sizeof(decrypted_data); i++) { + if(decrypted_data[i] == 0 && i > 0) { + bool is_padding = true; + for(size_t j = i; j < encrypted_len && j < sizeof(decrypted_data); j++) { + if(decrypted_data[j] != 0) { + is_padding = false; + break; + } } - escape = false; - } else if (c == '\\') { - escape = true; - } else if (c == ',' && current_field < 2) { - // End of current field, move to next - fields[current_field][field_letter] = '\0'; - current_field++; - field_letter = 0; - } else { - if (field_letter < 99) { - fields[current_field][field_letter++] = c; + if(is_padding) { + actual_len = i; + break; } } + actual_len = i + 1; } + if(actual_len < sizeof(decrypted_data)) { + decrypted_data[actual_len] = '\0'; + } + + const char field_delim = '\x1E'; + char* first = strchr(decrypted_str, field_delim); + if(!first) return false; + char* second = strchr(first + 1, field_delim); + if(!second) return false; + + size_t service_len = first - decrypted_str; + if(service_len >= 100) service_len = 99; + strncpy(service, decrypted_str, service_len); + service[service_len] = '\0'; + + size_t username_len = second - (first + 1); + if(username_len >= 100) username_len = 99; + strncpy(username, first + 1, username_len); + username[username_len] = '\0'; - if (escape) { - // Dangling backslash at end of input + size_t password_len = strlen(second + 1); + if(password_len >= 100) password_len = 99; + strncpy(password, second + 1, password_len); + password[password_len] = '\0'; + + return true; +} + +bool decrypt_credential(const char* encrypted_hex, char* service, char* username, char* password) { + // Calculate the encrypted data length from hex string + size_t hex_len = strlen(encrypted_hex); + if(hex_len % 2 != 0) return false; + size_t encrypted_len = hex_len / 2; + + // Convert hex back to bytes + uint8_t encrypted_data[64]; // STM32 WB55 optimized + if(!hex_to_bytes(encrypted_hex, encrypted_data, encrypted_len)) return false; + + // Try runtime key first, then legacy default key as fallback + if(runtime_key_set) { + if(try_decrypt_parse_with_key(encrypted_data, encrypted_len, runtime_aes_key, runtime_aes_iv, service, username, password)) return true; + if(try_decrypt_parse_with_key(encrypted_data, encrypted_len, default_aes_key, default_aes_iv, service, username, password)) return true; + return false; + } else { + if(try_decrypt_parse_with_key(encrypted_data, encrypted_len, default_aes_key, default_aes_iv, service, username, password)) return true; return false; } +} - // Terminate last field - if (current_field > 2) return false; // Too many commas - fields[current_field][field_letter] = '\0'; +// File paths for the password manager +#define PASSWORD_MANAGER_DIR "/ext/apps_data/PasswordManager" +#define DATABASE_FILE PASSWORD_MANAGER_DIR "/passwords.enc" - // Require exactly 3 fields - return current_field == 2; +// Ensure directory exists +static bool ensure_directory_exists(Storage* storage) { + return storage_common_mkdir(storage, PASSWORD_MANAGER_DIR); } // Add entry to password list bool password_list_add(Credential* credentials, size_t i, const char* service, const char* username, const char* password) { - strcpy(credentials[i].name, service); strcpy(credentials[i].username, username); strcpy(credentials[i].password, password); - return true; } @@ -95,6 +290,9 @@ size_t read_passwords_from_file(const char* filename, Credential* credentials) { Storage* storage = furi_record_open(RECORD_STORAGE); if(!storage) return 0; + // Ensure directory exists + ensure_directory_exists(storage); + File* file = storage_file_alloc(storage); if(!storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E("PasswordManager", "Failed to open file: %s", filename); @@ -103,18 +301,22 @@ size_t read_passwords_from_file(const char* filename, Credential* credentials) { return 0; } - char line[300]; + char encrypted_line[128]; // STM32 WB55 optimized - 128 bytes for hex string char service[100], username[100], password[100]; size_t i = 0; while(true) { - // Read a line - memset(line, 0, sizeof(line)); - if(storage_file_read_line(file, line, sizeof(line)) <= 0) { + // Read a line (encrypted hex string) + memset(encrypted_line, 0, sizeof(encrypted_line)); + if(storage_file_read_line(file, encrypted_line, sizeof(encrypted_line)) <= 0) { break; // End of file or error } - // Parse the line (expected format: "service,username,password") - if(parse_escaped_line(line, service, username, password)) { + + // Skip empty lines + if(strlen(encrypted_line) == 0) continue; + + // Decrypt the line + if(decrypt_credential(encrypted_line, service, username, password)) { // Add to our list password_list_add(credentials, i, service, username, password); i++; @@ -133,6 +335,9 @@ bool write_password_to_file(const char* filename, const char* service, const cha Storage* storage = furi_record_open(RECORD_STORAGE); if(!storage) return false; + // Ensure directory exists + ensure_directory_exists(storage); + File* file = storage_file_alloc(storage); if(!storage_file_open(file, filename, FSAM_WRITE, FSOM_OPEN_APPEND)) { // If file doesn't exist, create it @@ -144,12 +349,13 @@ bool write_password_to_file(const char* filename, const char* service, const cha } } - // Format the line - char line[100]; - snprintf(line, sizeof(line), "%s,%s,%s\n", service, username, password); + // Encrypt the credential + char encrypted_hex[128]; // STM32 WB55 optimized - 128 bytes for hex string + encrypt_credential(service, username, password, encrypted_hex); - // Write to file - bool success = storage_file_write(file, line, strlen(line)) == strlen(line); + // Add newline and write encrypted hex string + strcat(encrypted_hex, "\n"); + bool success = storage_file_write(file, encrypted_hex, strlen(encrypted_hex)) == strlen(encrypted_hex); storage_file_close(file); storage_file_free(file); @@ -158,48 +364,224 @@ bool write_password_to_file(const char* filename, const char* service, const cha return success; } -bool delete_line_from_file(const char* path, size_t line_to_delete) { +// Delete a specific credential by its content (more reliable than line number) +bool delete_credential_from_file(const char* path, const char* service, const char* username, const char* password) { // Open original file for reading Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + // Ensure directory exists + ensure_directory_exists(storage); + File* source = storage_file_alloc(storage); + if(!storage_file_open(source, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E("FileEdit", "Failed to open source file"); + storage_file_free(source); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Use a much smaller buffer to avoid stack overflow + char encrypted_line[128]; // Reduced from 256 to 128 bytes + bool credential_found = false; + + // First pass: count how many lines we'll keep + size_t lines_to_keep = 0; + while(true) { + memset(encrypted_line, 0, sizeof(encrypted_line)); + ssize_t len = storage_file_read_line(source, encrypted_line, sizeof(encrypted_line) - 1); + if(len <= 0) break; + // Skip empty lines + if(strlen(encrypted_line) == 0) continue; + + // Instead of decrypting, encrypt the target credential and compare hex strings + char target_encrypted[128]; + encrypt_credential(service, username, password, target_encrypted); + + // Compare the encrypted hex strings directly + if(strcmp(encrypted_line, target_encrypted) == 0) { + // This is the credential to delete, skip it + credential_found = true; + continue; + } + lines_to_keep++; + } + + // Close the source file + storage_file_close(source); + storage_file_free(source); + + if(!credential_found) { + furi_record_close(RECORD_STORAGE); + return false; // Credential not found + } + + // Simple approach: read all lines, filter out the one to delete, then rewrite + // This avoids file corruption issues + char temp_buffer[512]; // Small buffer for temporary storage + memset(temp_buffer, 0, sizeof(temp_buffer)); + size_t temp_pos = 0; + + // Second pass: read from original and collect lines to keep + File* source2 = storage_file_alloc(storage); + if(!storage_file_open(source2, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_file_free(source2); + furi_record_close(RECORD_STORAGE); + return false; + } + + while(true) { + memset(encrypted_line, 0, sizeof(encrypted_line)); + ssize_t len = storage_file_read_line(source2, encrypted_line, sizeof(encrypted_line) - 1); + if(len <= 0) break; + + // Skip empty lines + if(strlen(encrypted_line) == 0) continue; + + // Instead of decrypting, encrypt the target credential and compare hex strings + char target_encrypted2[128]; + encrypt_credential(service, username, password, target_encrypted2); + + // Compare the encrypted hex strings directly + if(strcmp(encrypted_line, target_encrypted2) == 0) { + // This is the credential to delete, skip it + continue; + } + + // Also try to decrypt the line and compare the plaintext fields + char decrypted_service[100], decrypted_username[100], decrypted_password[100]; + if(decrypt_credential(encrypted_line, decrypted_service, decrypted_username, decrypted_password)) { + // Compare plaintext fields (more flexible matching) + if(strcmp(decrypted_service, service) == 0 && + strcmp(decrypted_username, username) == 0 && + strcmp(decrypted_password, password) == 0) { + // This is the credential to delete, skip it + continue; + } + } + + // Add this line to our temp buffer (if we have space) + size_t line_len = strlen(encrypted_line); + if(temp_pos + line_len + 1 < sizeof(temp_buffer)) { + strcpy(temp_buffer + temp_pos, encrypted_line); + temp_pos += line_len; + temp_buffer[temp_pos] = '\n'; + temp_pos++; + } + } + + storage_file_close(source2); + storage_file_free(source2); + + // Now rewrite the original file with the filtered content + File* dest = storage_file_alloc(storage); + if(!storage_file_open(dest, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + FURI_LOG_E("FileEdit", "Failed to rewrite file"); + storage_file_free(dest); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Write the filtered content back to the file + if(temp_pos > 0) { + storage_file_write(dest, temp_buffer, temp_pos); + } + + storage_file_close(dest); + storage_file_free(dest); + furi_record_close(RECORD_STORAGE); + + return true; +} + +bool delete_line_from_file(const char* path, size_t line_to_delete) { + // Open original file for reading + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + // Ensure directory exists + ensure_directory_exists(storage); + + // Create a temporary file path + char temp_path[256]; + snprintf(temp_path, sizeof(temp_path), "%s.tmp", path); + + File* source = storage_file_alloc(storage); if(!storage_file_open(source, path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E("FileEdit", "Failed to open source file"); + storage_file_free(source); + furi_record_close(RECORD_STORAGE); return false; } - // Open a temporary file for writing - File* tmp = storage_file_alloc(storage); - if(!storage_file_open(tmp, "/ext/passowordManager_tmp.txt", FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - FURI_LOG_E("FileEdit", "Failed to open temporary file"); + // Create a temporary file for writing + File* temp_file = storage_file_alloc(storage); + if(!storage_file_open(temp_file, temp_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + FURI_LOG_E("FileEdit", "Failed to create temp file"); storage_file_close(source); + storage_file_free(source); + storage_file_free(temp_file); + furi_record_close(RECORD_STORAGE); return false; } - // Line read buffer - char line[300]; + // Line read buffer with extra space for newline + char encrypted_line[256]; // Increased buffer size for safety size_t current_line = 0; + bool success = true; + bool line_deleted = false; while(true) { - ssize_t len = storage_file_read_line(source, line, sizeof(line)); + // Clear buffer before reading + memset(encrypted_line, 0, sizeof(encrypted_line)); + + ssize_t len = storage_file_read_line(source, encrypted_line, sizeof(encrypted_line) - 1); if(len <= 0) break; + // Skip empty lines but count them + if(strlen(encrypted_line) == 0) { + current_line++; + continue; + } + // Write all lines except the one to delete if(current_line != line_to_delete) { - storage_file_write(tmp, line, strlen(line)); - storage_file_write(tmp, "\n", 1); // Add newline back + // Ensure we have space for newline + if(strlen(encrypted_line) + 1 < sizeof(encrypted_line)) { + strcat(encrypted_line, "\n"); + if(storage_file_write(temp_file, encrypted_line, strlen(encrypted_line)) != strlen(encrypted_line)) { + success = false; + break; + } + } + } else { + line_deleted = true; } current_line++; } + // Close both files storage_file_close(source); - storage_file_close(tmp); + storage_file_close(temp_file); + storage_file_free(source); + storage_file_free(temp_file); - // Delete original and rename temp - storage_simply_remove(storage, path); - storage_common_rename(storage, "/ext/passowordManager_tmp.txt", path); + if(success && line_deleted) { + // Remove original file and rename temp to original + if(storage_simply_remove(storage, path) == FSE_OK) { + if(storage_common_rename(storage, temp_path, path) == FSE_OK) { + furi_record_close(RECORD_STORAGE); + return true; + } + } + // If rename failed, try to clean up temp file + storage_simply_remove(storage, temp_path); + } else { + // Clean up temp file on failure + storage_simply_remove(storage, temp_path); + } furi_record_close(RECORD_STORAGE); - - return true; + return false; } \ No newline at end of file diff --git a/passwordStorage/passwordStorage.h b/passwordStorage/passwordStorage.h index 1cbf1bc..a7ad91a 100644 --- a/passwordStorage/passwordStorage.h +++ b/passwordStorage/passwordStorage.h @@ -1,11 +1,20 @@ +#pragma once + #include #include -#include -#include +#include "../lib/FlipCrypt/ciphers/aes.h" #include #include #include "../main.h" +// Function declarations size_t read_passwords_from_file(const char* filename, Credential* credentials); bool write_password_to_file(const char* filename, const char* service, const char* username, const char* password); -bool delete_line_from_file(const char* path, size_t line_to_delete); \ No newline at end of file +bool delete_credential_from_file(const char* path, const char* service, const char* username, const char* password); +bool delete_line_from_file(const char* path, size_t line_to_delete); + +// Runtime AES key management (derived from passcode on unlock) +void password_storage_set_key(const uint8_t* key16, const uint8_t* iv16); +void password_storage_clear_key(void); +void password_storage_set_key_from_passcode(const uint8_t* passcode, size_t passcode_len); +bool password_storage_get_runtime_key(uint8_t* key16_out, uint8_t* iv16_out, bool* is_set_out); diff --git a/textInput/textInput.c b/textInput/textInput.c index 2f87846..6ce1767 100644 --- a/textInput/textInput.c +++ b/textInput/textInput.c @@ -24,11 +24,19 @@ static void password_callback(void* context) { strcpy(app->credentials[app->credentials_number].password, app->tmp_password); - // Add to file!!! - write_password_to_file("/ext/passwordManager.txt", app->tmp_credential_name, app->tmp_username, app->tmp_password); + // Auto-save when password is complete + write_password_to_file("/ext/apps_data/PasswordManager/passwords.enc", + app->tmp_credential_name, + app->tmp_username, + app->tmp_password); + + // Clear temporary buffers strcpy(app->tmp_credential_name, ""); strcpy(app->tmp_username, ""); strcpy(app->tmp_password, ""); + + // Increment credential count + app->credentials_number++; view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); } @@ -41,11 +49,11 @@ TextInput* credential_name_TextInput_alloc(void* context) { text_input_set_header_text(textInput, "Insert Website:"); text_input_set_result_callback( textInput, - credential_name_callback, // Callback when user submits + credential_name_callback, // Callback when user presses Tab app, // Context passed to callback app->tmp_credential_name, // Buffer where text will be stored sizeof(char)*99, - true + false // Changed to false - don't auto-enter ); return textInput; @@ -60,11 +68,11 @@ TextInput* credential_username_TextInput_alloc(void* context) { text_input_set_header_text(textInput, "Insert Username:"); text_input_set_result_callback( textInput, - username_callback, // Callback when user submits + username_callback, // Callback when user presses Tab app, // Context passed to callback app->tmp_username, // Buffer where text will be stored sizeof(char)*99, - true + false // Changed to false - don't auto-enter ); return textInput; @@ -78,11 +86,11 @@ TextInput* credential_password_TextInput_alloc(void* context) { text_input_set_header_text(textInput, "Insert Password:"); text_input_set_result_callback( textInput, - password_callback, // Callback when user submits + password_callback, // Callback when user presses Tab app, // Context passed to callback app->tmp_password, // Buffer where text will be stored sizeof(char)*99, - true + false // Changed to false - don't auto-enter ); return textInput; diff --git a/textInput/textInput.h b/textInput/textInput.h index bd3f663..a147fd6 100644 --- a/textInput/textInput.h +++ b/textInput/textInput.h @@ -1,6 +1,8 @@ -#include -#include +#pragma once + #include +#include +#include "../main.h" TextInput* credential_name_TextInput_alloc(void* context); TextInput* credential_username_TextInput_alloc(void* context); diff --git a/views/delete_password.c b/views/delete_password.c index 73b506e..3c04a2f 100644 --- a/views/delete_password.c +++ b/views/delete_password.c @@ -33,7 +33,7 @@ static void delete_passwords_draw_callback(Canvas* canvas, void* model) { int y = 25 + (i - start) * 12; if(i == app->selected) { canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, y - 10, 120, 12); + canvas_draw_box(canvas, 0, y - 10, 122, 12); canvas_set_color(canvas, ColorWhite); } else { canvas_set_color(canvas, ColorBlack); @@ -59,14 +59,23 @@ static void delete_passwords_draw_callback(Canvas* canvas, void* model) { // Delete confirm box if(app->confirm_delete) { + // Draw a semi-transparent overlay + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 128, 64); + + // Draw the confirmation box with better styling canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 6, 20, 114, 36); + canvas_draw_box(canvas, 8, 16, 112, 32); canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 6, 20, 114, 36); + canvas_draw_frame(canvas, 8, 16, 112, 32); + + // Draw the confirmation text char confirm_text[64]; snprintf(confirm_text, sizeof(confirm_text), "Delete \"%s\"?", app->credentials[app->selected].name); - canvas_draw_str(canvas, 10, 34, confirm_text); - canvas_draw_str(canvas, 10, 48, "[OK] Yes [Back] No"); + canvas_draw_str(canvas, 12, 28, confirm_text); + + // Draw the action buttons + canvas_draw_str(canvas, 12, 40, "[OK] Yes [Back] No"); return; } @@ -79,8 +88,11 @@ static bool delete_passwords_input_callback(InputEvent* event, void* context) { if(app->confirm_delete) { if(event->key == InputKeyOk) { - // Confirm deletion - delete_line_from_file("/ext/passwordManager.txt", app->selected); + // Confirm deletion - use the new function that deletes by credential content + delete_credential_from_file("/ext/apps_data/PasswordManager/passwords.enc", + app->credentials[app->selected].name, + app->credentials[app->selected].username, + app->credentials[app->selected].password); app->selected = 0; app->scroll_offset = 0; app->confirm_delete = false; @@ -111,9 +123,6 @@ static bool delete_passwords_input_callback(InputEvent* event, void* context) { app->scroll_offset++; } - return true; - } else if(event->key == InputKeyDown) { - if(app->selected + 1 < app->credentials_number) app->selected++; return true; } else if(event->key == InputKeyBack) { app->selected = 0; @@ -122,8 +131,6 @@ static bool delete_passwords_input_callback(InputEvent* event, void* context) { } else if(event->key == InputKeyOk && app->credentials_number > 0) { // Ask for confirmation app->confirm_delete = true; - // delete_line_from_file("/ext/passwordManager.txt", app->selected); - // view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); return true; } } @@ -131,6 +138,24 @@ static bool delete_passwords_input_callback(InputEvent* event, void* context) { return false; } +// Callback when entering the delete password view +static void delete_password_view_enter_callback(void* context) { + AppContext* app = context; + // Ensure we have fresh credential data and proper state + app->credentials_number = read_passwords_from_file("/ext/apps_data/PasswordManager/passwords.enc", app->credentials); + + // Ensure selection is within bounds + if(app->selected >= app->credentials_number && app->credentials_number > 0) { + app->selected = app->credentials_number - 1; + } else if(app->credentials_number == 0) { + app->selected = 0; + } + + // Reset other state + app->scroll_offset = 0; + app->confirm_delete = false; +} + View* delete_password_view_alloc(AppContext* app_context) { View* view = view_alloc(); @@ -142,6 +167,7 @@ View* delete_password_view_alloc(AppContext* app_context) { *app_view = app; view_set_draw_callback(view, delete_passwords_draw_callback); view_set_input_callback(view, delete_passwords_input_callback); + view_set_enter_callback(view, delete_password_view_enter_callback); return view; } \ No newline at end of file diff --git a/views/options.c b/views/options.c new file mode 100644 index 0000000..68caed4 --- /dev/null +++ b/views/options.c @@ -0,0 +1,546 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../main.h" +#include "../badusb/badusb.h" +#include "../passwordStorage/passwordStorage.h" + +// File path for unified configuration storage +#define CONFIG_FILE "/ext/apps_data/PasswordManager/config.conf" + +// Options menu constants +#define OPTIONS_MENU_ITEMS 4 +#define MAX_LAYOUT_FILES 50 +#define LAYOUTS_PATH "/ext/badusb/assets/layouts" + +// Keyboard layout file structure +typedef struct { + char filename[64]; + char display_name[64]; + bool is_selected; +} LayoutFile; + +// Global layout files array +static LayoutFile layout_files[MAX_LAYOUT_FILES]; +static size_t layout_files_count = 0; + +// Save configuration to file (layout + Bluetooth state) +static bool save_config(AppContext* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + // Ensure directory exists + storage_common_mkdir(storage, "/ext/apps_data/PasswordManager"); + + File* file = storage_file_alloc(storage); + if(!storage_file_open(file, CONFIG_FILE, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Write configuration in a simple format: + // LAYOUT:filename.kl + // BLUETOOTH:0/1 + + // Write layout preference + const char* layout_filename = (layout_files_count > 0 && app->keyboard_layout < layout_files_count) + ? layout_files[app->keyboard_layout].filename : "ba-ba.kl"; + char layout_line[128]; + snprintf(layout_line, sizeof(layout_line), "LAYOUT:%s\n", layout_filename); + storage_file_write(file, layout_line, strlen(layout_line)); + + // Write Bluetooth state + char bluetooth_line[32]; + snprintf(bluetooth_line, sizeof(bluetooth_line), "BLUETOOTH:%d\n", app->bluetooth_enabled ? 1 : 0); + storage_file_write(file, bluetooth_line, strlen(bluetooth_line)); + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return true; +} + +// Load configuration from file (layout + Bluetooth state) +static bool load_config(AppContext* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + if(!storage_file_exists(storage, CONFIG_FILE)) { + furi_record_close(RECORD_STORAGE); + return false; + } + + File* file = storage_file_alloc(storage); + if(!storage_file_open(file, CONFIG_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Read the entire file to find the configuration values + char buffer[512]; + size_t bytes_read = storage_file_read(file, buffer, sizeof(buffer) - 1); + buffer[bytes_read] = '\0'; + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + bool config_loaded = false; + + // Look for the layout preference line + char* layout_line = strstr(buffer, "LAYOUT:"); + if(layout_line) { + layout_line += 7; // Skip "LAYOUT:" + char* newline = strchr(layout_line, '\n'); + if(newline) *newline = '\0'; // Null-terminate the layout filename + + // Find the layout index in our list + for(size_t i = 0; i < layout_files_count; i++) { + if(strcmp(layout_line, layout_files[i].filename) == 0) { + app->keyboard_layout = i; + config_loaded = true; + break; + } + } + } + + // Look for the Bluetooth state line + char* bluetooth_line = strstr(buffer, "BLUETOOTH:"); + if(bluetooth_line) { + bluetooth_line += 11; // Skip "BLUETOOTH:" + char* newline = strchr(bluetooth_line, '\n'); + if(newline) *newline = '\0'; // Null-terminate the value + + if(strcmp(bluetooth_line, "1") == 0) { + app->bluetooth_enabled = true; + } else { + app->bluetooth_enabled = false; + } + config_loaded = true; + } + + return config_loaded; +} + +// Function to scan for keyboard layout files and load saved preference +static void scan_layout_files(AppContext* app) { + layout_files_count = 0; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* dir = storage_file_alloc(storage); + + if(storage_dir_open(dir, LAYOUTS_PATH)) { + FileInfo file_info; + char temp_name[64]; + + while(storage_dir_read(dir, &file_info, temp_name, sizeof(temp_name))) { + if(file_info.flags & FSF_DIRECTORY) continue; // Skip directories + + // Check if it's a .kl file + size_t name_len = strlen(temp_name); + if(name_len > 3 && strcmp(temp_name + name_len - 3, ".kl") == 0) { + if(layout_files_count < MAX_LAYOUT_FILES) { + strncpy(layout_files[layout_files_count].filename, temp_name, sizeof(layout_files[layout_files_count].filename) - 1); + + // Create display name (remove .kl extension) + strncpy(layout_files[layout_files_count].display_name, temp_name, sizeof(layout_files[layout_files_count].display_name) - 1); + layout_files[layout_files_count].display_name[name_len - 3] = '\0'; + + layout_files[layout_files_count].is_selected = false; + layout_files_count++; + } + } + } + storage_dir_close(dir); + } + + storage_file_free(dir); + furi_record_close(RECORD_STORAGE); + + // Load saved configuration (layout + Bluetooth state) + if(load_config(app)) { + // Load the saved layout into BadUSB system + if(layout_files_count > 0 && app->keyboard_layout < layout_files_count) { + char layout_path[256]; + snprintf(layout_path, sizeof(layout_path), "%s/%s", LAYOUTS_PATH, layout_files[app->keyboard_layout].filename); + load_keyboard_layout(layout_path); + + // Loaded saved layout preference + } + } +} + +static void options_draw_callback(Canvas* canvas, void* model) { + AppContext** model_ = model; + AppContext* app = *model_; + + if(!app) return; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + + // Draw header + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, 20, 10, "Options"); + canvas_draw_line(canvas, 0, 12, 128, 12); + + // Static strings for menu items that need dynamic content + static char bluetooth_text[32] = "Bluetooth: OFF"; + static char layout_text[64] = "Select Keyboard Layout"; + static const char* export_key_text = "Export AES key"; + + // Update Bluetooth status + if(app->bluetooth_enabled) { + strcpy(bluetooth_text, "Bluetooth: ON"); + } else { + strcpy(bluetooth_text, "Bluetooth: OFF"); + } + + // Update keyboard layout display + if(layout_files_count > 0 && app->keyboard_layout < layout_files_count) { + snprintf(layout_text, sizeof(layout_text), "Layout: %s (L/R)", layout_files[app->keyboard_layout].display_name); + } else { + strcpy(layout_text, "Select Keyboard Layout"); + } + + // Draw menu items + const char* menu_items[OPTIONS_MENU_ITEMS] = { + bluetooth_text, + layout_text, + "Change Passcode", + export_key_text + }; + + // Calculate visible items based on scroll offset + size_t visible_items = 3; // Can show 3 items on screen (25, 37, 49) + size_t start_item = app->scroll_offset; + size_t end_item = start_item + visible_items; + + // Ensure we don't go out of bounds + if(end_item > OPTIONS_MENU_ITEMS) { + end_item = OPTIONS_MENU_ITEMS; + start_item = (end_item > visible_items) ? end_item - visible_items : 0; + } + + // Draw visible menu items + for(size_t i = start_item; i < end_item; i++) { + int display_index = i - start_item; + int y = 25 + display_index * 12; + + if(i == app->selected) { + // Highlight selected item (leave space for scrollbar) + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, y - 10, 122, 12); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + // Truncate long text to fit screen + char display_text[32]; + strncpy(display_text, menu_items[i], sizeof(display_text) - 1); + display_text[sizeof(display_text) - 1] = '\0'; + + canvas_draw_str(canvas, 5, y, display_text); + } + // Draw vertical scrollbar on the right + { + const size_t total_items = OPTIONS_MENU_ITEMS; + if(total_items > visible_items) { + int track_x = 124; + int track_y = 14; // below header line + int track_h = 62 - track_y; // up to bottom + int track_w = 3; + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, track_x, track_y, track_w, track_h); + // Thumb size proportional to visible/total; minimum height 6 + int thumb_h = (track_h * (int)visible_items) / (int)total_items; + if(thumb_h < 6) thumb_h = 6; + int max_offset = (int)total_items - (int)visible_items; + int thumb_max = track_h - thumb_h; + int thumb_y = track_y; + if(max_offset > 0) { + thumb_y = track_y + (thumb_max * (int)start_item) / max_offset; + } + // Inset by 1 px + canvas_draw_box(canvas, track_x + 1, thumb_y + 1, track_w - 2, thumb_h - 2); + } + } + + // Show hint for layout selection when selected + if(app->selected == 1) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 61, "< >=Cycle Hold OK=Browser"); + canvas_set_font(canvas, FontPrimary); + } +} + +static bool options_input_callback(InputEvent* event, void* context) { + AppContext* app = context; + + if(!app) return false; + + if(event->type == InputTypeShort || event->type == InputTypeLong) { + if(event->key == InputKeyUp) { + if(app->selected > 0) { + app->selected--; + // Adjust scroll offset to keep selected item visible + if(app->selected < app->scroll_offset) { + app->scroll_offset = app->selected; + } + } + return true; + } else if(event->key == InputKeyDown) { + if(app->selected + 1 < OPTIONS_MENU_ITEMS) { + app->selected++; + // Adjust scroll offset to keep selected item visible + if(app->selected >= app->scroll_offset + 3) { + app->scroll_offset = app->selected - 2; + } + } + return true; + } else if(event->key == InputKeyBack) { + // Go back to main menu + app->selected = 0; + view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); + return true; + } else if(event->key == InputKeyOk) { + switch(app->selected) { + case 0: // Bluetooth toggle + app->bluetooth_enabled = !app->bluetooth_enabled; + // Save configuration when Bluetooth state changes + save_config(app); + + // Implement actual BLE HID toggle + if(app->bluetooth_enabled) { + // Start BLE HID profile + password_manager_ble_start_hid(app); + } else { + // Stop BLE HID profile + password_manager_ble_stop_hid(app); + } + + // Bluetooth toggled + break; + + case 1: // Keyboard layout selection + if(event->type == InputTypeShort) { + if(layout_files_count > 0) { + // Short press: cycle to next layout + app->keyboard_layout = (app->keyboard_layout + 1) % layout_files_count; + // Save the configuration when layout changes + save_config(app); + // Keyboard layout changed + } + } else if(event->type == InputTypeLong) { + // Long press: open file browser for layout selection + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".kl", NULL); + browser_options.base_path = LAYOUTS_PATH; + browser_options.skip_assets = false; + + FuriString* selected_path = furi_string_alloc(); + FuriString* current_path = furi_string_alloc(); + + // Set current path to layouts folder + furi_string_set(current_path, LAYOUTS_PATH); + + // Get the dialogs record properly + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + + // Show file browser + bool result = dialog_file_browser_show( + dialogs, + selected_path, + current_path, + &browser_options); + + furi_record_close(RECORD_DIALOGS); + + if(result) { + // Load the selected layout + const char* layout_path = furi_string_get_cstr(selected_path); + load_keyboard_layout(layout_path); + + // Extract filename for display and update the keyboard_layout index + const char* filename = strrchr(layout_path, '/'); + if(filename) { + filename++; // Skip the '/' + // Find the layout index in our list for display purposes + for(size_t i = 0; i < layout_files_count; i++) { + if(strcmp(filename, layout_files[i].filename) == 0) { + app->keyboard_layout = i; + // Save the configuration when layout changes + save_config(app); + break; + } + } + // Selected layout from file browser + } + } + + furi_string_free(selected_path); + furi_string_free(current_path); + } + break; + + case 2: // Change passcode + app->passcode_change_requested = true; + // Switch to passcode view for reset + app->passcode_state = DpadPasscodeState_Reset; + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + view_dispatcher_switch_to_view(app->view_dispatcher, ViewPasscode); + // Passcode change requested - switching to reset view + break; + case 3: { // Export AES key + // Show confirmation dialog: press OK to reveal + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Reveal AES key?", 64, 5, AlignCenter, AlignTop); + dialog_message_set_text(message, "Press OK to view key\nBack to cancel", 64, 32, AlignCenter, AlignCenter); + dialog_message_set_buttons(message, "Back", NULL, "OK"); + DialogMessageButton btn = dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + + if(btn == DialogMessageButtonRight) { + // Obtain runtime key/iv + uint8_t key[16] = {0}; + uint8_t iv[16] = {0}; + bool is_set = false; + password_storage_get_runtime_key(key, iv, &is_set); + + // Build grouped hex strings (avoid hyphenation; show 2-byte groups) + char key_hex[33]; + char iv_hex[33]; + const char hex_chars[] = "0123456789ABCDEF"; + for(int i = 0; i < 16; i++) { + key_hex[i*2] = hex_chars[(key[i] >> 4) & 0xF]; + key_hex[i*2+1] = hex_chars[key[i] & 0xF]; + iv_hex[i*2] = hex_chars[(iv[i] >> 4) & 0xF]; + iv_hex[i*2+1] = hex_chars[iv[i] & 0xF]; + } + key_hex[32] = '\0'; + iv_hex[32] = '\0'; + + // key_hex and iv_hex are used directly below, split into two 16-char lines + + if(!is_set) { + dialogs = furi_record_open(RECORD_DIALOGS); + message = dialog_message_alloc(); + dialog_message_set_header(message, "AES Key", 64, 5, AlignCenter, AlignTop); + char buf[160]; + snprintf(buf, sizeof(buf), "Key not set (legacy).\nUnlock to derive from passcode."); + dialog_message_set_text(message, buf, 64, 36, AlignCenter, AlignCenter); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } else { + int page = 0; // 0 = Key, 1 = IV + bool running = true; + while(running) { + dialogs = furi_record_open(RECORD_DIALOGS); + message = dialog_message_alloc(); + char buf[200]; + if(page == 0) { + dialog_message_set_header(message, "AES Key", 64, 5, AlignCenter, AlignTop); + char l1[17]; char l2[17]; + memcpy(l1, key_hex, 16); l1[16] = '\0'; + memcpy(l2, key_hex + 16, 16); l2[16] = '\0'; + snprintf(buf, sizeof(buf), "Key: %s\n%s", l1, l2); + } else { + dialog_message_set_header(message, "AES IV", 64, 5, AlignCenter, AlignTop); + char l1[17]; char l2[17]; + memcpy(l1, iv_hex, 16); l1[16] = '\0'; + memcpy(l2, iv_hex + 16, 16); l2[16] = '\0'; + snprintf(buf, sizeof(buf), "IV: %s\n%s", l1, l2); + } + dialog_message_set_text(message, buf, 64, 36, AlignCenter, AlignCenter); + dialog_message_set_buttons(message, "<", "Close", ">"); + DialogMessageButton b = dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + + if(b == DialogMessageButtonRight) { + page = 1; + } else if(b == DialogMessageButtonLeft) { + page = 0; + } else { + running = false; // Close or Back + } + } + } + } + break; + } + + + } + return true; + } else if(event->key == InputKeyLeft || event->key == InputKeyRight) { + // Handle left/right for layout selection when on keyboard layout option + if(app->selected == 1 && layout_files_count > 0) { + if(event->key == InputKeyLeft) { + // Go to previous layout + if(app->keyboard_layout == 0) { + app->keyboard_layout = layout_files_count - 1; + } else { + app->keyboard_layout--; + } + } else if(event->key == InputKeyRight) { + // Go to next layout + app->keyboard_layout = (app->keyboard_layout + 1) % layout_files_count; + } + + // Load the selected keyboard layout file + char layout_path[256]; + snprintf(layout_path, sizeof(layout_path), "%s/%s", LAYOUTS_PATH, layout_files[app->keyboard_layout].filename); + load_keyboard_layout(layout_path); + + // Save the configuration when layout changes + save_config(app); + + // Keyboard layout changed + return true; + } + } + } + + return false; +} + +static void options_view_enter_callback(void* context) { + AppContext* app = context; + if(app) { + // Reset selection and scroll when entering options + app->selected = 0; + app->scroll_offset = 0; + + // Scan for available keyboard layout files + scan_layout_files(app); + } +} + +View* options_view_alloc(void* context) { + View* view = view_alloc(); + + AppContext* app = context; + view_set_context(view, app); + view_allocate_model(view, ViewModelTypeLockFree, sizeof(AppContext*)); + AppContext** app_view = view_get_model(view); + *app_view = app; + view_set_draw_callback(view, options_draw_callback); + view_set_input_callback(view, options_input_callback); + view_set_enter_callback(view, options_view_enter_callback); + + return view; +} diff --git a/views/options.h b/views/options.h new file mode 100644 index 0000000..cc0c431 --- /dev/null +++ b/views/options.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include + +View* options_view_alloc(void* context); diff --git a/views/passcode.c b/views/passcode.c new file mode 100644 index 0000000..6498c45 --- /dev/null +++ b/views/passcode.c @@ -0,0 +1,441 @@ +#include +#include +#include +#include +#include +#include + +#include "../main.h" +#include "../passwordStorage/passwordStorage.h" + +// File paths +#define KEY_FILE "/ext/apps_data/PasswordManager/key.enc" +#define FAIL_COUNT_FILE "/ext/apps_data/PasswordManager/fail_count.enc" + +// Passcode length constants +static const size_t MIN_PASSCODE_LENGTH = 4; +static const size_t MAX_PASSCODE_LENGTH = 8; +static const uint8_t MAX_FAIL_ATTEMPTS = 10; + +// Save passcode to encrypted file +static bool save_passcode(const uint8_t* passcode, size_t length) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + // Ensure directory exists + storage_common_mkdir(storage, "/ext/apps_data/PasswordManager"); + + File* file = storage_file_alloc(storage); + if(!storage_file_open(file, KEY_FILE, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Write length first, then passcode data + storage_file_write(file, &length, sizeof(length)); + storage_file_write(file, passcode, length); + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return true; +} + +// Load passcode from encrypted file +static bool load_passcode(uint8_t* passcode, size_t* length) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + File* file = storage_file_alloc(storage); + if(!storage_file_open(file, KEY_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Read length first + if(storage_file_read(file, length, sizeof(*length)) != sizeof(*length)) { + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Validate length + if(*length < MIN_PASSCODE_LENGTH || *length > MAX_PASSCODE_LENGTH) { + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + // Read passcode data + if(storage_file_read(file, passcode, *length) != *length) { + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return true; +} + +// Save fail count to file +static bool save_fail_count(uint8_t count) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return false; + + // Ensure directory exists + storage_common_mkdir(storage, "/ext/apps_data/PasswordManager"); + + File* file = storage_file_alloc(storage); + if(!storage_file_open(file, FAIL_COUNT_FILE, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return false; + } + + storage_file_write(file, &count, sizeof(count)); + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return true; +} + + + +// Nuclear option: Delete all encrypted files +static void nuclear_reset(void) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage) return; + + // Delete key.enc + if(storage_file_exists(storage, KEY_FILE)) { + storage_simply_remove(storage, KEY_FILE); + } + + // Delete passwords.enc + if(storage_file_exists(storage, "/ext/apps_data/PasswordManager/passwords.enc")) { + storage_simply_remove(storage, "/ext/apps_data/PasswordManager/passwords.enc"); + } + + // Delete fail count file + if(storage_file_exists(storage, FAIL_COUNT_FILE)) { + storage_simply_remove(storage, FAIL_COUNT_FILE); + } + + furi_record_close(RECORD_STORAGE); +} + +static void passcode_draw_callback(Canvas* canvas, void* model) { + AppContext** model_ = model; + AppContext* app = *model_; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + + // Draw header based on state + canvas_set_color(canvas, ColorBlack); + + const char* header_text = "Enter Passcode"; + const char* instruction_text = "Back=Clear Hold=Exit OK=Go"; + + if(app->passcode_state == DpadPasscodeState_Setup) { + header_text = "Create Passcode"; + instruction_text = "4-8 buttons OK=Next"; + } else if(app->passcode_state == DpadPasscodeState_Confirm) { + header_text = "Confirm Passcode"; + instruction_text = "Re-enter OK=Save"; + } else if(app->passcode_state == DpadPasscodeState_Reset) { + header_text = "Reset Passcode"; + instruction_text = "Enter current OK=Next"; + } else if(app->passcode_state == DpadPasscodeState_ResetConfirm) { + header_text = "New Passcode"; + instruction_text = "4-8 buttons OK=Next"; + } + + canvas_draw_str(canvas, 20, 10, header_text); + canvas_draw_line(canvas, 0, 12, 128, 12); + + // Draw fail count if in unlock mode (centered above dots) + if(app->passcode_state == DpadPasscodeState_Unlock && app->fail_count > 0) { + char fail_text[32]; + snprintf(fail_text, sizeof(fail_text), "Fails:%d/%d", app->fail_count, MAX_FAIL_ATTEMPTS); + // Center the fail text + int text_width = strlen(fail_text) * 6; // Approximate character width + int fail_x = (128 - text_width) / 2; + canvas_draw_str(canvas, fail_x, 30, fail_text); + } + + // Draw input dots (up to 8 max) - properly centered, bigger and fancier + size_t max_display = MAX_PASSCODE_LENGTH; + int dot_spacing = 14; // Spacing between dots + int total_width = (max_display - 1) * dot_spacing; // Total width of all gaps + int start_x = (128 - total_width) / 2; // Center the dots properly + + for(size_t i = 0; i < max_display; i++) { + int x = start_x + (i * dot_spacing); + if(i < app->passcode_input_len) { + // Filled circle for entered inputs (bigger) + canvas_draw_circle(canvas, x, 40, 4); + // Add inner highlight for filled dots + canvas_draw_circle(canvas, x, 40, 2); + } else { + // Empty circle for remaining inputs (bigger) + canvas_draw_frame(canvas, x - 4, 36, 8, 8); + } + } + + // Draw instructions - positioned at bottom with smaller font + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 60, instruction_text); +} + +static bool passcode_input_callback(InputEvent* event, void* context) { + AppContext* app = context; + static uint32_t back_hold_start = 0; + static bool back_held = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + // Clear current input + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + back_held = false; + + // If in reset mode and user hits back, return to options + if(app->passcode_state == DpadPasscodeState_Reset || + app->passcode_state == DpadPasscodeState_ResetConfirm) { + app->passcode_change_requested = false; + view_dispatcher_switch_to_view(app->view_dispatcher, ViewOptions); + return true; + } + + return true; + } else if(event->key == InputKeyOk) { + // Handle OK based on current state + if(app->passcode_state == DpadPasscodeState_Setup) { + // Setting up new passcode + if(app->passcode_input_len >= MIN_PASSCODE_LENGTH) { + // Save the setup passcode and move to confirm + memcpy(app->passcode, app->passcode_input, app->passcode_input_len); + app->passcode_len = app->passcode_input_len; + + // Clear input for confirmation + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + + // Move to confirm state + app->passcode_state = DpadPasscodeState_Confirm; + } + } else if(app->passcode_state == DpadPasscodeState_Confirm) { + // Confirming the passcode + if(app->passcode_input_len == app->passcode_len) { + // Check if confirmation matches + bool matches = true; + for(size_t i = 0; i < app->passcode_len; i++) { + if(app->passcode_input[i] != app->passcode[i]) { + matches = false; + break; + } + } + + if(matches) { + // Save to file and unlock + if(save_passcode(app->passcode, app->passcode_len)) { + // Derive and set runtime AES key from new passcode + password_storage_set_key_from_passcode(app->passcode, app->passcode_len); + app->passcode_state = DpadPasscodeState_Unlocked; + + // Load keyboard layout from saved configuration + password_manager_load_keyboard_layout(app); + + // If this was a reset, go back to options, otherwise to main menu + if(app->passcode_change_requested) { + app->passcode_change_requested = false; + view_dispatcher_switch_to_view(app->view_dispatcher, ViewOptions); + } else { + view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); + } + } + } else { + // Confirmation failed - go back to setup + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + if(app->passcode_change_requested) { + app->passcode_state = DpadPasscodeState_Reset; + } else { + app->passcode_state = DpadPasscodeState_Setup; + } + } + } + } else if(app->passcode_state == DpadPasscodeState_Reset) { + // Reset mode: verify current passcode first + if(app->passcode_input_len >= MIN_PASSCODE_LENGTH) { + // Load saved passcode and compare + uint8_t saved_passcode[MAX_PASSCODE_LENGTH]; + size_t saved_length; + + if(load_passcode(saved_passcode, &saved_length)) { + bool correct = true; + for(size_t i = 0; i < saved_length; i++) { + if(app->passcode_input[i] != saved_passcode[i]) { + correct = false; + break; + } + } + + if(correct) { + // Current passcode verified - move to new passcode entry + app->passcode_state = DpadPasscodeState_ResetConfirm; + // Clear input for new passcode + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } else { + // Wrong current passcode - increment fail count + app->fail_count++; + save_fail_count(app->fail_count); + + // Check if we've hit the limit + if(app->fail_count >= MAX_FAIL_ATTEMPTS) { + // NUCLEAR RESET! Delete everything + nuclear_reset(); + // Reset app state and go back to setup + app->passcode_state = DpadPasscodeState_Setup; + app->fail_count = 0; + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } else { + // Clear input and try again + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } + } + } else { + // Could not load passcode - clear input + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } + } + } else if(app->passcode_state == DpadPasscodeState_ResetConfirm) { + // Setting up new passcode during reset + if(app->passcode_input_len >= MIN_PASSCODE_LENGTH) { + // Save the new passcode and move to confirm + memcpy(app->passcode, app->passcode_input, app->passcode_input_len); + app->passcode_len = app->passcode_input_len; + + // Clear input for confirmation + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + + // Move to confirm state + app->passcode_state = DpadPasscodeState_Confirm; + } + } else if(app->passcode_state == DpadPasscodeState_Unlock) { + // Normal unlock mode + if(app->passcode_input_len >= MIN_PASSCODE_LENGTH) { + // Load saved passcode and compare + uint8_t saved_passcode[MAX_PASSCODE_LENGTH]; + size_t saved_length; + + if(load_passcode(saved_passcode, &saved_length)) { + + bool correct = true; + for(size_t i = 0; i < saved_length; i++) { + if(app->passcode_input[i] != saved_passcode[i]) { + correct = false; + break; + } + } + + if(correct) { + // Correct passcode - unlock the app + // Derive and set runtime AES key from passcode + password_storage_set_key_from_passcode(app->passcode_input, app->passcode_input_len); + app->passcode_state = DpadPasscodeState_Unlocked; + // Reset fail count on success + app->fail_count = 0; + save_fail_count(app->fail_count); + + // Load keyboard layout from saved configuration + password_manager_load_keyboard_layout(app); + + view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); + } else { + // Wrong passcode - increment fail count + app->fail_count++; + save_fail_count(app->fail_count); + + // Check if we've hit the limit + if(app->fail_count >= MAX_FAIL_ATTEMPTS) { + // NUCLEAR RESET! Delete everything + nuclear_reset(); + // Reset app state and go back to setup + app->passcode_state = DpadPasscodeState_Setup; + app->fail_count = 0; + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } else { + // Clear input and try again + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } + } + } else { + // Could not load passcode - clear input + app->passcode_input_len = 0; + memset(app->passcode_input, 0, sizeof(app->passcode_input)); + } + } + } + return true; + } else if(event->key == InputKeyUp || event->key == InputKeyDown || + event->key == InputKeyLeft || event->key == InputKeyRight) { + // Add to passcode input if we have space (4-8 buttons) + if(app->passcode_input_len < MAX_PASSCODE_LENGTH) { + app->passcode_input[app->passcode_input_len] = event->key; + app->passcode_input_len++; + } + return true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + // Start timing the hold + back_hold_start = furi_get_tick(); + back_held = true; + return true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyBack && back_held) { + // Check if back was held long enough to exit + uint32_t hold_duration = furi_get_tick() - back_hold_start; + if(hold_duration >= 1000) { // 1 second hold + app->running = false; + view_dispatcher_stop(app->view_dispatcher); + } + back_held = false; + return true; + } + } + + return false; +} + +View* passcode_view_alloc(AppContext* app_context) { + View* view = view_alloc(); + + AppContext* app = app_context; + view_set_context(view, app); + view_allocate_model(view, ViewModelTypeLockFree, sizeof(AppContext*)); + AppContext** app_view = view_get_model(view); + *app_view = app; + view_set_draw_callback(view, passcode_draw_callback); + view_set_input_callback(view, passcode_input_callback); + + return view; +} diff --git a/views/passcode.h b/views/passcode.h new file mode 100644 index 0000000..a403808 --- /dev/null +++ b/views/passcode.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include + +View* passcode_view_alloc(void* context); diff --git a/views/password_generator.c b/views/password_generator.c new file mode 100644 index 0000000..7fb3e5d --- /dev/null +++ b/views/password_generator.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../main.h" + +// Password generator constants - optimized for Flipper Zero RAM +#define MAX_PASSWORD_LENGTH 32 // Reduced from 64 to save RAM +#define MIN_PASSWORD_LENGTH 4 // Reduced from 8 for flexibility +#define DEFAULT_PASSWORD_LENGTH 12 + +// Character sets - static to save RAM +static const char* const charsets[] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", // Uppercase + "abcdefghijklmnopqrstuvwxyz", // Lowercase + "0123456789", // Numbers + "!@#$%^&*()_+-=[]{}|;:,.<>?" // Symbols +}; + +static const char* const charset_names[] = { + "Uppercase", + "Lowercase", + "Numbers", + "Symbols" +}; + +#define CHARSET_COUNT 4 + +typedef struct { + bool enabled[CHARSET_COUNT]; + uint8_t length; + char generated_password[MAX_PASSWORD_LENGTH + 1]; + uint8_t selected_option; + bool show_password; + uint8_t scroll_offset; + bool display_mode; // New: true when showing generated password +} PasswordGeneratorState; + +// Forward declaration +static void generate_password(PasswordGeneratorState* state); + +static void password_generator_draw_callback(Canvas* canvas, void* model) { + AppContext** model_ = model; + AppContext* app = *model_; + + if(!app) return; + + // Get the password generator state from app context + PasswordGeneratorState* state = (PasswordGeneratorState*)app->password_generator_state; + if(!state) return; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + + if(state->display_mode) { + // Display mode: show only the generated password + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, 15, 10, "Generated Password"); + canvas_draw_line(canvas, 0, 12, 128, 12); + + // Display the password in large font, centered + canvas_set_font(canvas, FontPrimary); + int password_len = strlen(state->generated_password); + + if(password_len > 0) { + // If password is too long, truncate and show with ellipsis + if(password_len > 16) { // Max characters that fit on screen + char display_pass[17]; + strncpy(display_pass, state->generated_password, 16); + display_pass[16] = '\0'; + + // Calculate center position for truncated password + int text_width = 16 * 6; // 16 characters * 6 pixels + int x = (128 - text_width - 18) / 2; // Account for "..." width + canvas_draw_str(canvas, x, 35, display_pass); + canvas_draw_str(canvas, x + text_width, 35, "..."); + } else { + // Calculate center position for full password + int text_width = password_len * 6; // Approximate character width + int x = (128 - text_width) / 2; + if(x < 2) x = 2; // Ensure minimum margin + canvas_draw_str(canvas, x, 35, state->generated_password); + } + } + + // Draw instructions + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 58, "Back=Menu Right=Add"); + + } else { + // Normal mode: show password generator options + // Draw header + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, 15, 10, "Password Generator"); + canvas_draw_line(canvas, 0, 12, 128, 12); + + // Total options: Length + 4 character sets + Generate = 6 options + #define TOTAL_OPTIONS 6 + #define VISIBLE_OPTIONS 4 + + // Calculate visible range based on scroll offset + int start_option = state->scroll_offset; + int end_option = start_option + VISIBLE_OPTIONS; + if(end_option > TOTAL_OPTIONS) { + end_option = TOTAL_OPTIONS; + } + + // Draw visible options + for(int i = start_option; i < end_option; i++) { + int display_index = i - start_option; + int y = 25 + display_index * 12; + + // Highlight selected option + bool is_selected = (i == state->selected_option); + if(is_selected) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, y - 10, 122, 12); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + // Draw option based on index + if(i == 0) { + // Length selector + canvas_draw_str(canvas, 2, y, "Length:"); + char length_text[16]; + snprintf(length_text, sizeof(length_text), "%d", state->length); + canvas_draw_str(canvas, 50, y, length_text); + } else if(i >= 1 && i <= 4) { + // Character set toggles + int charset_idx = i - 1; + const char* status = state->enabled[charset_idx] ? "[X]" : "[ ]"; + canvas_draw_str(canvas, 2, y, status); + canvas_draw_str(canvas, 20, y, charset_names[charset_idx]); + } else if(i == 5) { + // Generate option + canvas_draw_str(canvas, 2, y, ">> Generate Password"); + } + } + // Draw vertical scrollbar on the right (match main/options) + { + const int total_items = TOTAL_OPTIONS; + const int visible_items = VISIBLE_OPTIONS; + if(total_items > visible_items) { + int track_x = 124; + int track_y = 14; // below header line + int track_h = 64 - track_y; + int track_w = 3; + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, track_x, track_y, track_w, track_h); + int thumb_h = (track_h * visible_items) / total_items; + if(thumb_h < 6) thumb_h = 6; + int max_offset = total_items - visible_items; + int thumb_max = track_h - thumb_h; + int thumb_y = track_y; + if(max_offset > 0) { + thumb_y = track_y + (thumb_max * start_option) / max_offset; + } + canvas_draw_box(canvas, track_x + 1, thumb_y + 1, track_w - 2, thumb_h - 2); + } + } + + } +} + +static bool password_generator_input_callback(InputEvent* event, void* context) { + AppContext* app = context; + + if(!app) return false; + + // Get the password generator state from app context + PasswordGeneratorState* state = (PasswordGeneratorState*)app->password_generator_state; + if(!state) return false; + + #define TOTAL_OPTIONS 6 + #define VISIBLE_OPTIONS 4 + + if(event->type == InputTypeShort) { + if(event->key == InputKeyUp) { + if(!state->display_mode && state->selected_option > 0) { + state->selected_option--; + + // Adjust scroll offset to keep selection visible + if(state->selected_option < state->scroll_offset) { + state->scroll_offset = state->selected_option; + } + } + return true; + } else if(event->key == InputKeyDown) { + if(!state->display_mode && state->selected_option < TOTAL_OPTIONS - 1) { + state->selected_option++; + + // Adjust scroll offset to keep selection visible + if(state->selected_option >= state->scroll_offset + VISIBLE_OPTIONS) { + state->scroll_offset = state->selected_option - VISIBLE_OPTIONS + 1; + } + } + return true; + } else if(event->key == InputKeyBack) { + if(state->display_mode) { + // In display mode, Back returns to generator options + state->display_mode = false; + return true; + } else { + // In generator mode, Back goes to main menu + view_dispatcher_switch_to_view(app->view_dispatcher, ViewMainMenu); + return true; + } + } else if(event->key == InputKeyOk) { + if(state->display_mode) { + // In display mode, OK generates a new password + generate_password(state); + return true; + } else { + // Normal mode + if(state->selected_option == 0) { + // Length selector - don't do anything on OK, use L/R + return true; + } else if(state->selected_option >= 1 && state->selected_option <= 4) { + // Toggle character set + int charset_idx = state->selected_option - 1; + state->enabled[charset_idx] = !state->enabled[charset_idx]; + return true; + } else if(state->selected_option == 5) { + // Generate password and switch to display mode + generate_password(state); + state->display_mode = true; + return true; + } + } + } else if(event->key == InputKeyLeft || event->key == InputKeyRight) { + if(state->display_mode) { + // In display mode, Right button goes to Add New Password + if(event->key == InputKeyRight) { + // Copy generated password to temp password field + strncpy(app->tmp_password, state->generated_password, sizeof(app->tmp_password) - 1); + app->tmp_password[sizeof(app->tmp_password) - 1] = '\0'; + + // Clear other fields for user to fill + memset(app->tmp_credential_name, 0, sizeof(app->tmp_credential_name)); + memset(app->tmp_username, 0, sizeof(app->tmp_username)); + + // Switch to credential name input + view_dispatcher_switch_to_view(app->view_dispatcher, ViewTextInputCredentialName); + return true; + } + } else if(state->selected_option == 0) { + // Adjust length + if(event->key == InputKeyLeft && state->length > MIN_PASSWORD_LENGTH) { + state->length--; + } else if(event->key == InputKeyRight && state->length < MAX_PASSWORD_LENGTH) { + state->length++; + } + return true; + } + } + } + + return false; +} + +// Generate password based on current settings - optimized for RAM +static void generate_password(PasswordGeneratorState* state) { + // Check if at least one charset is enabled + bool has_charset = false; + for(int i = 0; i < CHARSET_COUNT; i++) { + if(state->enabled[i]) { + has_charset = true; + break; + } + } + + if(!has_charset) { + // Enable lowercase by default if nothing is selected + state->enabled[1] = true; + } + + // Build combined character set - use stack efficiently + char combined_charset[128] = ""; // Reduced from 256 to save RAM + int total_len = 0; + + for(int i = 0; i < CHARSET_COUNT && total_len < 127; i++) { + if(state->enabled[i]) { + int charset_len = strlen(charsets[i]); + if(total_len + charset_len < 127) { + strcat(combined_charset, charsets[i]); + total_len += charset_len; + } + } + } + + int charset_len = strlen(combined_charset); + if(charset_len == 0) return; + + // Generate password using efficient random generation + for(int i = 0; i < state->length; i++) { + int random_idx = rand() % charset_len; + state->generated_password[i] = combined_charset[random_idx]; + } + state->generated_password[state->length] = '\0'; +} + + + +// Reset password generator state when entering the view +static void password_generator_enter_callback(void* context) { + AppContext* app = context; + if(app && app->password_generator_state) { + PasswordGeneratorState* state = (PasswordGeneratorState*)app->password_generator_state; + // Only reset to generator mode when entering, but preserve settings + state->display_mode = false; + // Don't reset selected_option and scroll_offset to preserve user position + } +} + +View* password_generator_view_alloc(void* context) { + View* view = view_alloc(); + + AppContext* app = context; + view_set_context(view, app); + view_allocate_model(view, ViewModelTypeLockFree, sizeof(AppContext*)); + AppContext** app_view = view_get_model(view); + *app_view = app; + + // Password generator state is already initialized in main app + + view_set_draw_callback(view, password_generator_draw_callback); + view_set_input_callback(view, password_generator_input_callback); + view_set_enter_callback(view, password_generator_enter_callback); + + return view; +} diff --git a/views/password_generator.h b/views/password_generator.h new file mode 100644 index 0000000..8084d31 --- /dev/null +++ b/views/password_generator.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include + +View* password_generator_view_alloc(void* context); diff --git a/views/saved_passwords.c b/views/saved_passwords.c index 8b508d1..85e03e2 100644 --- a/views/saved_passwords.c +++ b/views/saved_passwords.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,6 +20,12 @@ static void saved_passwords_draw_callback(Canvas* canvas, void* model) { // Draw header canvas_set_color(canvas, ColorBlack); canvas_draw_str(canvas, 20, 10, "Saved Passwords"); + + // Draw BLE HID status indicator + if(app->bluetooth_enabled && app->ble_hid_profile) { + canvas_draw_str(canvas, 2, 10, "BT"); + } + canvas_draw_line(canvas, 0, 12, 128, 12); size_t max_visible = 4; @@ -30,7 +37,7 @@ static void saved_passwords_draw_callback(Canvas* canvas, void* model) { int y = 25 + (i - start) * 12; if(i == app->selected) { canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, y - 10, 120, 12); + canvas_draw_box(canvas, 0, y - 10, 122, 12); canvas_set_color(canvas, ColorWhite); } else { canvas_set_color(canvas, ColorBlack); @@ -82,14 +89,63 @@ static bool saved_passwords_input_callback(InputEvent* event, void* context) { app->scroll_offset = 0; return false; } else if(event->key == InputKeyOk) { - initialize_hid(); - if (strlen(app->credentials[app->selected].username)){ - type_string(app->credentials[app->selected].username); - press_key(HID_KEYBOARD_TAB); + // Check if BLE HID is available and connected + if(app->bluetooth_enabled && app->ble_hid_profile && app->is_ble_connected) { + // Use BLE HID for typing + // Typing via BLE HID + + if (strlen(app->credentials[app->selected].username)){ + password_manager_hal_type_string(app, app->credentials[app->selected].username); + furi_delay_ms(100); // Small delay + password_manager_hal_keyboard_press(app, 0x2B); // Tab key + furi_delay_ms(50); + password_manager_hal_keyboard_release(app, 0x2B); + furi_delay_ms(100); // Small delay + } + password_manager_hal_type_string(app, app->credentials[app->selected].password); + furi_delay_ms(100); // Small delay + password_manager_hal_keyboard_press(app, 0x28); // Enter key + furi_delay_ms(50); + password_manager_hal_keyboard_release(app, 0x28); + + // BLE HID typing completed + } else { + // Use USB HID (default mode) + // Typing via USB HID + + // Save current USB configuration before switching to HID mode + FuriHalUsbInterface* previous_usb_config = furi_hal_usb_get_config(); + + // Only initialize USB HID if not already active + if(!app->usb_hid_active) { + initialize_hid(); + app->usb_hid_active = true; + // USB HID initialized for typing + // Allow host to enumerate before sending keystrokes + furi_delay_ms(500); + } + + if (strlen(app->credentials[app->selected].username)){ + // Final settle before first characters to avoid losing leading chars + furi_delay_ms(100); + type_string(app->credentials[app->selected].username); + release_all_keys(); + furi_delay_ms(50); + press_key(HID_KEYBOARD_TAB); + release_all_keys(); + furi_delay_ms(80); + } + type_string(app->credentials[app->selected].password); + press_key(HID_KEYBOARD_RETURN); + release_all_keys(); + + // Give host time to process final key releases + furi_delay_ms(150); + // Restore previous USB configuration after typing to return to default state + deinitialize_hid_with_restore(previous_usb_config); + app->usb_hid_active = false; + // USB HID deinitialized - returned to default state } - type_string(app->credentials[app->selected].password); - press_key(HID_KEYBOARD_RETURN); - release_all_keys(); return true; } }