diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index 8bc794ec..cf0b6b8e 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -96,6 +96,10 @@ set(EXTCPPSRC #sstvtx external/sstvtx/main.cpp external/sstvtx/ui_sstvtx.cpp + + #random + external/random_password/main.cpp + external/random_password/ui_random_password.cpp ) set(EXTAPPLIST @@ -122,4 +126,5 @@ set(EXTAPPLIST adsbtx morse_tx sstvtx + random_password ) diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index 932c76a6..f93a617e 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -46,6 +46,7 @@ MEMORY ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k ram_external_app_morse_tx(rwx) : org = 0xADC60000, len = 32k ram_external_app_sstvtx(rwx) : org = 0xADC70000, len = 32k + ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k } SECTIONS @@ -190,6 +191,10 @@ SECTIONS *(*ui*external_app*sstvtx*); } > ram_external_app_sstvtx - + .external_app_random_password : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_random_password.application_information)); + *(*ui*external_app*random_password*); + } > ram_external_app_random_password } diff --git a/firmware/application/external/random_password/main.cpp b/firmware/application/external/random_password/main.cpp new file mode 100644 index 00000000..0d1ddc75 --- /dev/null +++ b/firmware/application/external/random_password/main.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "ui_random_password.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::random_password { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::random_password + +extern "C" { + +__attribute__((section(".external_app.app_random_password.application_information"), used)) application_information_t _application_information_random_password = { + /*.memory_location = */ (uint8_t*)0x00000000, + /*.externalAppEntry = */ ui::external_app::random_password::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "random_password", + /*.bitmap_data = */ { + 0xC0, + 0x03, + 0xE0, + 0x07, + 0x30, + 0x0C, + 0x30, + 0x0C, + 0x30, + 0x0C, + 0x30, + 0x0C, + 0xE0, + 0x07, + 0xC0, + 0x03, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x07, + 0x80, + 0x03, + 0x80, + 0x07, + 0x80, + 0x01, + }, + /*.icon_color = */ ui::Color::yellow().v, + /*.menu_location = */ app_location_t::UTILITIES, + + /*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'}, + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} diff --git a/firmware/application/external/random_password/ui_random_password.cpp b/firmware/application/external/random_password/ui_random_password.cpp new file mode 100644 index 00000000..e36b0f8d --- /dev/null +++ b/firmware/application/external/random_password/ui_random_password.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * copyleft zxkmm + * Copyright (C) 2024 HToToo + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_random_password.hpp" +#include "ui_modemsetup.hpp" + +#include "modems.hpp" +#include "rtc_time.hpp" +#include "baseband_api.hpp" +#include "string_format.hpp" +#include "portapack_persistent_memory.hpp" +#include "file_path.hpp" + +using namespace portapack; +using namespace modems; +using namespace ui; + +namespace ui::external_app::random_password { + +void RandomPasswordLogger::log_raw_data(const std::string& data) { + log_file.write_entry(data); +} + +void RandomPasswordView::focus() { + field_digits.focus(); +} + +RandomPasswordView::RandomPasswordView(NavigationView& nav) + : nav_{nav} { + baseband::run_prepared_image(portapack::memory::map::m4_code.base()); + + add_children({&rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &check_log, + &button_modem_setup, + &labels, + &text_generated_passwd, + &text_char_type_hints, + &check_digits, + &check_latin_lower, + &check_latin_upper, + &check_punctuation, + &check_show_seeds, + &check_auto_send, + &button_refresh, + &button_show_qr, + &button_pause, + &button_send, + &field_digits, + &check_allow_confusable_chars, + &text_seed, + &progressbar}); + + // no idea what's these, i copied from afsk rx app and they seems needed' + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); + + progressbar.set_max(30); + + check_log.set_value(logging); + check_log.on_select = [this](Checkbox&, bool v) { + if (v) { + nav_.display_modal( + "Warning", + "Sure?\n" + "this will save all generated\n" + "password to sdcard\n" + "in plain text\n" + "those which generated before\n" + "you check me, will lost", + YESNO, + [this, v](bool c) { + if (c) { + logging = v; + } else { + check_log.set_value(false); + // this is needed to check back to false cuz when trigger by human, the check to true already happened + // this blocked interface so won't accidently saved even if user checked but selected no later here, + // but take care of here if in the future implemented ticking/auto/batch save etc + } + }); + } else { + logging = v; + } + }; + + button_modem_setup.on_select = [&nav](Button&) { // copied from afsk rx app + nav.push(); + }; + + check_digits.on_select = [this](Checkbox&, bool) { + this->new_password(); + }; + + check_latin_lower.on_select = [this](Checkbox&, bool) { + this->new_password(); + }; + + check_latin_upper.on_select = [this](Checkbox&, bool) { + this->new_password(); + }; + + check_punctuation.on_select = [this](Checkbox&, bool) { + this->new_password(); + }; + + check_allow_confusable_chars.on_select = [this](Checkbox&, bool) { + this->new_password(); + }; + + button_refresh.on_select = [this](Button&) { + this->set_random_freq(); + this->new_password(); + }; + + button_show_qr.on_select = [this, &nav](Button&) { + nav.push(password.data()); + }; + + button_pause.on_select = [this](Button&) { + if (paused) { + paused = false; + button_pause.set_text("pause"); + } else { + paused = true; + button_pause.set_text("resume"); + } + }; + button_send.on_select = [this, &nav](Button&) { + portapack::async_tx_enabled = true; + UsbSerialAsyncmsg::asyncmsg(password); + portapack::async_tx_enabled = false; + }; + + field_digits.on_change = [this](int32_t) { + clean_buffer(); + this->new_password(); + }; + + /// v check defauly val init + check_digits.set_value(true); + check_latin_lower.set_value(true); + check_latin_upper.set_value(true); + check_punctuation.set_value(true); + check_show_seeds.set_value(true); + field_digits.set_value(8); + ///^ check defauly val init + + logger = std::make_unique(); + if (logger) + logger->append(logs_dir / u"random.TXT"); + + // Auto-configure modem for LCR RX (will be removed later), copied from afsk rx app + baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false); + + receiver_model.enable(); + receiver_model.set_rf_amp(false); + set_random_freq(); + new_password(); +} + +void RandomPasswordView::on_data(uint32_t value, bool is_data) { + if (paused) + return; + if (is_data) { + seed = static_cast(value); + text_seed.set(to_string_dec_uint(check_show_seeds.value() ? seed : 0)); + + /// v feed deque + seeds_deque.push_back(value); + if (seeds_deque.size() > MAX_DIGITS) { + seeds_deque.pop_front(); + } + ///^ feed deque + + progressbar.set_value(seeds_deque.size()); + + } else { + text_generated_passwd.set("Baudrate estimation: ~"); + text_char_type_hints.set(to_string_dec_uint(value)); + } +} + +void RandomPasswordView::clean_buffer() { + seeds_deque = {0}; + char_deque = {""}; +} + +void RandomPasswordView::on_freqchg(int64_t freq) { + field_frequency.set_value(freq); +} + +void RandomPasswordView::set_random_freq() { + std::srand(LPC_RTC->CTIME0); + // this is only for seed to visit random freq, the radio is still real random + + auto random_freq = 100000000 + (std::rand() % 900000000); // 100mhz to 1ghz + receiver_model.set_target_frequency(random_freq); + field_frequency.set_value(random_freq); +} + +void RandomPasswordView::new_password() { + password = ""; + std::string charset = ""; + std::string char_type_hints = ""; + + if (check_digits.value()) + charset += "0123456789"; + if (check_latin_lower.value()) + charset += "abcdefghijklmnopqrstuvwxyz"; + if (check_latin_upper.value()) + charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (check_punctuation.value()) + charset += ".,-!?"; + + if (!check_allow_confusable_chars.value()) { + charset.erase(std::remove_if(charset.begin(), charset.end(), + [](char c) { return c == '0' || c == 'O' || c == 'o' || c == '1' || c == 'l'; }), + charset.end()); + } + + if (charset.empty()) { + text_generated_passwd.set("generate failed,"); + text_char_type_hints.set("select at least 1 type"); + return; + } + + if (seeds_deque.size() < MAX_DIGITS) { + seeds_buffer_not_full = true; + text_generated_passwd.set("wait seeds buffer full"); + text_char_type_hints.set("then press generate"); + return; + } + + int password_length = field_digits.value(); + + /*the seeds_buffer were feed streaming by AFSK, + * and when generate, it use each seed for each char, and uint seeds totally can generate UINT_MAX result, + * which already cover the 10+26+25+4 (123+abc+abc+.!) + * so total possible password would be PW_LENGTH ^ (10+26+25+4), which already covered all the possible solution + * (assume AFSK data is averaged in chaotic space, which maybe no one can garentee but I hope so) + * */ + + for (int i = 0; i < password_length; i++) { + unsigned int seed = seeds_deque[i]; + std::srand(seed); + char c = charset[std::rand() % charset.length()]; + password += c; + char_deque.push_back(std::string(1, c)); + + if (std::isdigit(c)) { + char_type_hints += "1"; + } else if (std::islower(c)) { + char_type_hints += "a"; + } else if (std::isupper(c)) { + char_type_hints += "A"; + } else { + char_type_hints += ","; + } + } + + text_generated_passwd.set(password); + text_char_type_hints.set(char_type_hints); + + paint_password_hints(); // TODO: why flash and disappeared + + if (logger && logging) { + str_log += generate_log_line(); + logger->log_raw_data(str_log); + str_log = ""; + } + + if (check_auto_send.value()) { + portapack::async_tx_enabled = true; + UsbSerialAsyncmsg::asyncmsg(password); + portapack::async_tx_enabled = false; + } +} + +// TODO: why flash and disappeared +// tried: +// 1. paint inline in new_password func +// 2. paint in a seperate func and call from new_password +// 3. override nav's paint func (i think it can tried to capture same obj) and paint, hoping set_dirty handle it correctly +// 4. override nav's paint func (i think it can tried to capture same obj) and paint in a seperate func, hoping set_dirty handle it correctly +// all these methods failed, and all of them only flash and disappeared. only when set_dirty in on_data (which seems incorrect), and it keep flashing never stop. but see painted content (flashing too) +// btw this is not caused by the seed text set thing, cuz commented it out not helping. +void RandomPasswordView::paint_password_hints() { + Painter painter; + const int char_width = 8; + const int char_height = 16; + const int start_y = 6 * char_height + 5; + const int rect_height = 4; + + for (size_t i = 0; i < password.length(); i++) { + char c = password[i]; + Color color; + if (std::isdigit(c)) { + color = Color::red(); + } else if (std::islower(c)) { + color = Color::green(); + } else if (std::isupper(c)) { + color = Color::blue(); + } else { + color = Color::white(); + } + + painter.fill_rectangle( + {{static_cast(i) * char_width, start_y}, + {char_width, rect_height}}, + color); + } +} + +std::string RandomPasswordView::generate_log_line() { + std::string line = "\npassword=" + password + + "\nseed=" + std::to_string(seed) + + "\n"; + return line; +} + +RandomPasswordView::~RandomPasswordView() { + receiver_model.disable(); + baseband::shutdown(); +} + +} // namespace ui::external_app::random_password diff --git a/firmware/application/external/random_password/ui_random_password.hpp b/firmware/application/external/random_password/ui_random_password.hpp new file mode 100644 index 00000000..fb0e3dd2 --- /dev/null +++ b/firmware/application/external/random_password/ui_random_password.hpp @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * copyleft zxkmm + * Copyright (C) 2024 HToToo + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __UI_RANDOM_PASSWORD_H__ +#define __UI_RANDOM_PASSWORD_H__ + +#define MAX_DIGITS 30 + +#include "ui.hpp" +#include "ui_language.hpp" +#include "ui_navigation.hpp" +#include "ui_receiver.hpp" +#include "ui_freq_field.hpp" +#include "ui_record_view.hpp" +#include "app_settings.hpp" +#include "radio_state.hpp" +#include "log_file.hpp" +#include "utility.hpp" +#include "ui_qrcode.hpp" +#include "usb_serial_asyncmsg.hpp" +#include + +using namespace ui; + +namespace ui::external_app::random_password { + +class RandomPasswordLogger { // TODO: log is broken after introduced the buffer thing + public: + Optional append(const std::filesystem::path& filename) { + return log_file.append(filename); + } + + void log_raw_data(const std::string& data); + + private: + LogFile log_file{}; +}; + +class RandomPasswordView : public View { + public: + RandomPasswordView(NavigationView& nav); + ~RandomPasswordView(); + + void focus() override; + + std::string title() const override { return "r.passwd"; }; + + private: + unsigned int seed = 0; // extern void srand (unsigned int __seed) __THROW; + std::string password = ""; + std::deque seeds_deque = {0}; + std::deque char_deque = {""}; + bool seeds_buffer_not_full = true; + + void on_data(uint32_t value, bool is_data); + void clean_buffer(); + void new_password(); + std::string generate_log_line(); + void paint_password_hints(); + + NavigationView& nav_; + RxRadioState radio_state_{}; + app_settings::SettingsManager settings_{ + "rx_afsk", app_settings::Mode::RX}; + + std::string str_log{""}; + bool logging{false}; + bool paused{false}; + + Labels labels{ + {{0 * 8, 0 * 16}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground}, + {{0 * 8, 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground}, + {{5 * 8, 7 * 16}, "digits:", Theme::getInstance()->fg_light->foreground}, + }; + + RFAmpField field_rf_amp{ + {13 * 8, 1 * 16}}; + LNAGainField field_lna{ + {15 * 8, 1 * 16}}; + VGAGainField field_vga{ + {18 * 8, 1 * 16}}; + + RSSI rssi{ + {21 * 8, 1 * 16 + 0, 6 * 8, 4}}; + Channel channel{ + {21 * 8, 1 * 16 + 5, 6 * 8, 4}}; + + RxFrequencyField field_frequency{ + {0 * 8, 1 * 16}, + nav_}; + + Button button_modem_setup{ + {screen_width - 12 * 8, 2 * 16, 96, 16}, + "AFSK modem"}; + + Text text_seed{ + {0, 2 * 16, screen_width / 2, 16}, + "0000000000"}; + + ProgressBar progressbar{ + {screen_width / 2 + 1, 2 * 16, screen_width - 96 - (0 * 8 + screen_width / 2 + 1) - 1, 16}}; + + Text text_generated_passwd{ + {0, 4 * 16, screen_width, 28}, + "000000000000000000000000000000"}; + + Text text_char_type_hints{ + {0, 5 * 16, screen_width, 28}, + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"}; + + Checkbox check_show_seeds{ + {17 * 8, 8 * 16}, + 6, + "show seed"}; + + Checkbox check_auto_send{ + {1 * 8, 8 * 16}, + 20, + "auto send"}; + + Checkbox check_punctuation{ + {17 * 8, 12 * 16}, + 6, + ".,-!?"}; + + Checkbox check_allow_confusable_chars{ + {1 * 8, 10 * 16}, + 20, + "0 O o 1 l"}; + + Checkbox check_digits{ + {1 * 8, 12 * 16}, + 3, + "123"}; + + Checkbox check_latin_lower{ + {1 * 8, 14 * 16}, + 3, + "abc"}; + + Checkbox check_latin_upper{ + {17 * 8, 14 * 16}, + 3, + "ABC"}; + + Checkbox check_log{ + {17 * 8, 10 * 16}, + 3, + "savin", + false}; + + Button button_pause{ + {0 * 8, 15 * 16 + 20, screen_width / 2, 24}, + "pause"}; + + Button button_send{ + {screen_width / 2, 15 * 16 + 20, screen_width / 2, 24}, + "send pwd"}; + + Button button_refresh{ + {0 * 8, 17 * 16 + 10, screen_width / 2, 24}, + "generate"}; + + Button button_show_qr{ + {screen_width / 2, 17 * 16 + 10, screen_width / 2, 24}, + "show QR"}; + + NumberField field_digits{ + {16 * 8, 7 * 16}, + 2, + {1, 30}, + 1, + ' '}; + + void on_data_afsk(const AFSKDataMessage& message); + + std::unique_ptr logger{}; + + MessageHandlerRegistration message_handler_packet{ + Message::ID::AFSKData, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_data(message->value, message->is_data); + }}; + + MessageHandlerRegistration message_handler_freqchg{ + Message::ID::FreqChangeCommand, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_freqchg(message->freq); + }}; + + void on_freqchg(int64_t freq); + void set_random_freq(); +}; + +} // namespace ui::external_app::random_password + +#endif /*__UI_RANDOM_PASSWORD_H__*/ diff --git a/firmware/graphics/key.png b/firmware/graphics/key.png new file mode 100644 index 00000000..4328e0d9 Binary files /dev/null and b/firmware/graphics/key.png differ