summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crypto.cpp16
-rw-r--r--totpgen.h58
-rw-r--r--wxgui.cpp253
3 files changed, 227 insertions, 100 deletions
diff --git a/crypto.cpp b/crypto.cpp
index 818f68a..cd5f5f4 100644
--- a/crypto.cpp
+++ b/crypto.cpp
@@ -20,7 +20,7 @@ extern "C" {
sizeof(unsigned char)));
bool
-TOTPGen::GenTOTP(std::string& secret_str)
+TOTPGen::GenTOTP(const std::string& secret_str)
{
uint32_t bits, code, count, hmacsize;
uint64_t clock;
@@ -87,7 +87,7 @@ TOTPGen::InitAES(const char *passphrase, unsigned char *salt,
{
char *key_data = strdup(passphrase);
int key_text_len = strlen(passphrase);
- int nrounds = 0xFF;
+ int nrounds = 1 << 20;
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt,
reinterpret_cast<unsigned char *>(key_data),
@@ -97,7 +97,7 @@ TOTPGen::InitAES(const char *passphrase, unsigned char *salt,
}
bool
-TOTPGen::EncryptSecret(std::string& secret_str)
+TOTPGen::EncryptDB(const std::string& secret_str)
{
unsigned char salt[SALT_BYTES], key[KEY_BYTES], iv[IV_BYTES];
@@ -115,7 +115,7 @@ TOTPGen::EncryptSecret(std::string& secret_str)
reinterpret_cast<unsigned char *>(&key),
reinterpret_cast<unsigned char *>(&iv));
- size_t plaintext_len = strlen(secret_str.c_str()) ;
+ size_t plaintext_len = strlen(secret_str.c_str());
unsigned char *plaintext = AES_BUFFER(plaintext_len);
strncpy(reinterpret_cast<char *>(plaintext),
@@ -129,7 +129,7 @@ TOTPGen::EncryptSecret(std::string& secret_str)
EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
ciphertext_len += len;
- FILE *secret_file = fopen(SecretFile.c_str(), "wb");
+ FILE *secret_file = fopen(SecretDB_File.c_str(), "wb");
if (!secret_file) {
return false;
}
@@ -148,14 +148,14 @@ TOTPGen::EncryptSecret(std::string& secret_str)
bool
-TOTPGen::DecryptSecret(void)
+TOTPGen::DecryptDB(void)
{
unsigned char salt[SALT_BYTES], key[KEY_BYTES], iv[IV_BYTES];
memset(&key, 0, KEY_BYTES);
memset(&iv, 0, IV_BYTES);
- FILE *secret_file = fopen(SecretFile.c_str(), "rb");
+ FILE *secret_file = fopen(SecretDB_File.c_str(), "rb");
if (!secret_file) {
return false;
}
@@ -190,7 +190,7 @@ TOTPGen::DecryptSecret(void)
return false;
}
- Secret = reinterpret_cast<const char *>(plaintext);
+ KeyDB = json::parse(reinterpret_cast<const char *>(plaintext));
EVP_CIPHER_CTX_free(ctx);
free(ciphertext);
diff --git a/totpgen.h b/totpgen.h
index 8feee9a..e9283d8 100644
--- a/totpgen.h
+++ b/totpgen.h
@@ -18,6 +18,9 @@
#include <wx/regex.h>
#include <wx/font.h>
+#include <nlohmann/json.hpp>
+using json = nlohmann::json;
+
static const std::string base32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
class TOTPGenGUI : public wxApp {
@@ -33,15 +36,30 @@ class TOTPGen : public wxFrame {
wxPanel *MainWindow;
wxBoxSizer *MainSizer;
+ wxBoxSizer *SelectSizer;
+ wxBoxSizer *TextSizer;
+
wxBoxSizer *ButtonSizer_Upper;
+ wxBoxSizer *ButtonSizer_Middle;
wxBoxSizer *ButtonSizer_Lower;
- wxButton *UpdateSecretButton;
- void UpdateSecret(wxCommandEvent& event);
+ wxComboBox *SecretChooser;
+ void ReloadSelections(void);
+ void SelectSecret(wxCommandEvent& event);
- bool Locked;
- wxButton *UnlockButton;
- void Unlock(wxCommandEvent& event);
+ wxStaticText *TOTPField;
+ void ResizeTextToFit(wxStaticText *text_field);
+
+ wxTimer *CountdownTimer;
+ wxGauge *CountdownGauge;
+ void UpdateStatus(wxTimerEvent& event);
+
+ wxButton *AddButton;
+ void AddSecret(wxCommandEvent& event);
+ bool IsBase32(std::string& str);
+
+ wxButton *DeleteButton;
+ void DeleteSecret(wxCommandEvent& event);
wxButton *CopyButton;
void CopyText(wxCommandEvent& event);
@@ -49,30 +67,22 @@ class TOTPGen : public wxFrame {
wxButton *QuitButton;
void OnExit(wxCommandEvent& event);
- wxBoxSizer *TextSizer;
- wxStaticText *TOTPField;
- void ResizeTextToFit(wxStaticText *text_field);
-
- wxString SecretFile;
+ std::string Passphrase;
+ wxButton *SecurityButton;
+ void InitDB(wxCommandEvent& event);
+ void SetPassword(wxCommandEvent& event);
+ void Unlock(wxCommandEvent& event);
+ json KeyDB;
+ wxString SecretDB_File;
void InitAES(const char *passphrase,
unsigned char *salt,
unsigned char *key,
unsigned char *iv);
-
- wxTimer *CountdownTimer;
- wxGauge *CountdownGauge;
- void UpdateStatus(wxTimerEvent& event);
+ bool DecryptDB(void);
+ bool EncryptDB(const std::string& secret_str);
std::string Secret;
- std::string Passphrase;
-
- bool IsBase32(std::string& str);
-
- bool DecryptSecret(void);
- bool EncryptSecret(std::string& secret_str);
-
std::string TOTP;
-
- bool GenTOTP(std::string& secret_str);
-}; \ No newline at end of file
+ bool GenTOTP(const std::string& secret_str);
+};
diff --git a/wxgui.cpp b/wxgui.cpp
index 846c042..d85ad2a 100644
--- a/wxgui.cpp
+++ b/wxgui.cpp
@@ -10,7 +10,8 @@ static const std::string config_dir_name = ".totpgen";
IMPLEMENT_APP(TOTPGenGUI)
-bool TOTPGenGUI::OnInit()
+bool
+TOTPGenGUI::OnInit()
{
TOTPGen *MainAppFrame = new TOTPGen();
MainAppFrame->Show(true);
@@ -18,13 +19,13 @@ bool TOTPGenGUI::OnInit()
}
TOTPGen::TOTPGen() : wxFrame(nullptr, wxID_ANY, wxT("TOTP Generator"),
- wxDefaultPosition, wxSize(320,240),
+ wxDefaultPosition, wxSize(320,320),
wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX))
{
MainWindow = new wxPanel(this, wxID_ANY);
MainSizer = new wxBoxSizer(wxVERTICAL);
- Locked = true;
+ KeyDB = {};
wxString UserConfigDir = wxStandardPaths::Get().GetUserConfigDir();
@@ -33,7 +34,16 @@ TOTPGen::TOTPGen() : wxFrame(nullptr, wxID_ANY, wxT("TOTP Generator"),
wxMkdir(AppConfigDir);
}
- SecretFile = AppConfigDir + path_separator + "secret.dat";
+ SecretDB_File = AppConfigDir + path_separator + "secret.db";
+
+ SecretChooser = new wxComboBox(MainWindow, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize, 0, NULL,
+ wxCB_READONLY, wxDefaultValidator);
+ SecretChooser->Enable(false);
+ SecretChooser->Bind(wxEVT_COMBOBOX, &TOTPGen::SelectSecret, this);
+
+ SelectSizer = new wxBoxSizer(wxHORIZONTAL);
+ SelectSizer->Add(SecretChooser, 2, wxEXPAND | wxALL);
TOTPField = new wxStaticText(MainWindow, wxID_ANY,
wxEmptyString, wxDefaultPosition,
@@ -50,48 +60,61 @@ TOTPGen::TOTPGen() : wxFrame(nullptr, wxID_ANY, wxT("TOTP Generator"),
CountdownGauge = new wxGauge(MainWindow, wxID_ANY, 30, wxPoint(50, 50));
CountdownTimer = new wxTimer(MainWindow, wxID_ANY);
- CountdownTimer->Start(1000);
- UpdateSecretButton = new wxButton(MainWindow, wxID_OK, wxT("Set Key"));
- UpdateSecretButton->Bind(wxEVT_BUTTON, &TOTPGen::UpdateSecret, this);
+ MainWindow->Connect(CountdownTimer->GetId(), wxEVT_TIMER,
+ wxTimerEventHandler(TOTPGen::UpdateStatus), NULL, this);
- UnlockButton = new wxButton(MainWindow, wxID_CLEAR, wxT("Unlock"));
- UnlockButton->Bind(wxEVT_BUTTON, &TOTPGen::Unlock, this);
- if (!wxFileExists(SecretFile)) {
- UnlockButton->Enable(false);
- }
+ AddButton = new wxButton(MainWindow, wxID_OK, wxT("Add"));
+ AddButton->Bind(wxEVT_BUTTON, &TOTPGen::AddSecret, this);
+ AddButton->Enable(false);
+
+ DeleteButton = new wxButton(MainWindow, wxID_OK, wxT("Delete"));
+ DeleteButton->Bind(wxEVT_BUTTON, &TOTPGen::DeleteSecret, this);
+ DeleteButton->Enable(false);
ButtonSizer_Upper = new wxBoxSizer(wxHORIZONTAL);
- ButtonSizer_Upper->Add(UpdateSecretButton, 2, wxEXPAND | wxALL);
- ButtonSizer_Upper->Add(UnlockButton, 2, wxEXPAND | wxALL);
+ ButtonSizer_Upper->Add(AddButton, 2, wxEXPAND | wxALL);
+ ButtonSizer_Upper->Add(DeleteButton, 2, wxEXPAND | wxALL);
CopyButton = new wxButton(MainWindow, wxID_COPY, wxT("Copy"));
CopyButton->Bind(wxEVT_BUTTON, &TOTPGen::CopyText, this);
- if (Locked) {
- CopyButton->Enable(false);
- }
+ CopyButton->Enable(false);
QuitButton = new wxButton(MainWindow, wxID_EXIT, wxT("Exit"));
QuitButton->Bind(wxEVT_BUTTON, &TOTPGen::OnExit, this);
+ ButtonSizer_Middle = new wxBoxSizer(wxHORIZONTAL);
+ ButtonSizer_Middle->Add(CopyButton, 2, wxEXPAND | wxALL);
+ ButtonSizer_Middle->Add(QuitButton, 2, wxEXPAND | wxALL);
+
+ SecurityButton = new wxButton(MainWindow, wxID_OK, wxT("Unlock Database"));
+ SecurityButton->Bind(wxEVT_BUTTON, &TOTPGen::Unlock, this);
+ if (!wxFileExists(SecretDB_File)) {
+ SecurityButton->SetLabel(wxT("Create Database"));
+ SecurityButton->Bind(wxEVT_BUTTON, &TOTPGen::InitDB, this);
+ }
+
ButtonSizer_Lower = new wxBoxSizer(wxHORIZONTAL);
- ButtonSizer_Lower->Add(CopyButton, 2, wxEXPAND | wxALL);
- ButtonSizer_Lower->Add(QuitButton, 2, wxEXPAND | wxALL);
+ ButtonSizer_Lower->Add(SecurityButton, 2, wxEXPAND | wxALL);
+ MainSizer->Add(SelectSizer, 2, wxEXPAND | wxALL);
MainSizer->Add(TextSizer, 2, wxCENTER | wxALIGN_CENTER);
MainSizer->Add(CountdownGauge, 2, wxEXPAND | wxALL);
MainSizer->Add(ButtonSizer_Upper, 2, wxEXPAND | wxALL);
+ MainSizer->Add(ButtonSizer_Middle, 2, wxEXPAND | wxALL);
MainSizer->Add(ButtonSizer_Lower, 2, wxEXPAND | wxALL);
MainWindow->SetSizer(MainSizer);
}
-void TOTPGen::OnExit(wxCommandEvent& WXUNUSED(event))
+void
+TOTPGen::OnExit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
-bool TOTPGen::IsBase32(std::string& str)
+bool
+TOTPGen::IsBase32(std::string& str)
{
std::unordered_set<char> Base32Chars(base32.begin(), base32.end());
@@ -104,32 +127,143 @@ bool TOTPGen::IsBase32(std::string& str)
return true;
}
-void TOTPGen::UpdateSecret(wxCommandEvent& WXUNUSED(event))
+void
+TOTPGen::ReloadSelections(void)
+{
+ SecretChooser->Clear();
+
+ int pos = 0;
+ for (auto Entry : KeyDB.items()) {
+ SecretChooser->Insert(Entry.key(), pos);
+ pos++;
+ }
+
+ if (pos) {
+ DeleteButton->Enable(true);
+ CopyButton->Enable(true);
+
+ SecretChooser->Enable(true);
+ SecretChooser->SetSelection(0);
+
+ wxCommandEvent DummyComboEvent(wxEVT_COMBOBOX);
+ SelectSecret(DummyComboEvent);
+
+ CountdownTimer->Start(1000);
+ } else {
+ DeleteButton->Enable(false);
+ CopyButton->Enable(false);
+ SecretChooser->Enable(false);
+
+ CountdownTimer->Stop();
+ CountdownGauge->SetValue(0);
+
+ TOTPField->SetLabel(" ");
+ }
+}
+
+void
+TOTPGen::SelectSecret(wxCommandEvent& WXUNUSED(event))
+{
+ Secret = KeyDB[SecretChooser->GetStringSelection().ToStdString()];
+ GenTOTP(Secret);
+}
+
+void
+TOTPGen::DeleteSecret(wxCommandEvent& WXUNUSED(event))
+{
+ wxString SecretName_Selected = SecretChooser->GetStringSelection();
+
+ wxMessageDialog *ConfirmDialog = new wxMessageDialog(this,
+ wxT("Are you want to delete the token '" + SecretName_Selected + "'?"),
+ wxT("Confirm Deletion"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
+ int ConfirmResponse = ConfirmDialog->ShowModal();
+
+ if (ConfirmResponse != wxID_YES) {
+ return;
+ }
+
+ KeyDB.erase(SecretName_Selected.ToStdString());
+
+ EncryptDB(KeyDB.dump().c_str());
+
+ ReloadSelections();
+}
+
+void
+TOTPGen::AddSecret(wxCommandEvent& WXUNUSED(event))
{
+ struct {
+ std::string Name;
+ std::string Base32Str;
+ } NewSecret;
+
wxTextEntryDialog SecretPrompt(this, wxT("Please enter the base 32 token."),
wxT("Secret Entry"), wxEmptyString);
- std::string Secret_New;
if (SecretPrompt.ShowModal() == wxID_OK) {
- Secret_New = SecretPrompt.GetValue().MakeUpper().ToStdString();
- if (!IsBase32(Secret_New)) {
+ NewSecret.Base32Str = SecretPrompt.GetValue().ToStdString();
+ if (!IsBase32(NewSecret.Base32Str) || NewSecret.Base32Str.empty()) {
wxMessageBox(wxT("Base 32 token contains invalid characters.\n"
"Only the following are permissible: \n" + base32),
wxT("Error"), wxOK | wxICON_ERROR);
return;
}
+ } else {
+ return;
}
+ wxTextEntryDialog NamePrompt(this, wxT("Please enter a name for the secret."),
+ wxT("Secret Entry"), wxEmptyString);
+
+ if (NamePrompt.ShowModal() == wxID_OK) {
+ NewSecret.Name = NamePrompt.GetValue().ToStdString();
+ } else {
+ return;
+ }
+
+ if (NewSecret.Base32Str.empty() || NewSecret.Name.empty()) {
+ wxMessageBox(wxT("Empty input - please try again."),
+ wxT("Error"), wxOK | wxICON_ERROR);
+ return;
+ }
+
+ if (!GenTOTP(NewSecret.Base32Str)) {
+ wxMessageBox(wxT("Could not generate TOTP - invalid token?"),
+ wxT("Error"), wxOK | wxICON_ERROR);
+ return;
+ }
+
+ KeyDB[NewSecret.Name] = NewSecret.Base32Str;
+
+ EncryptDB(KeyDB.dump().c_str());
+
+ ReloadSelections();
+}
+
+void
+TOTPGen::InitDB(wxCommandEvent& WXUNUSED(event))
+{
+ wxCommandEvent DummyBtnEvent(wxEVT_BUTTON);
+ SetPassword(DummyBtnEvent);
+
+ AddButton->Enable(true);
+
+ SecurityButton->SetLabel(wxT("Set Password"));
+ SecurityButton->Bind(wxEVT_BUTTON, &TOTPGen::SetPassword, this);
+}
+
+void
+TOTPGen::SetPassword(wxCommandEvent& WXUNUSED(event))
+{
wxPasswordEntryDialog PasswordPrompt(this, wxT("Please enter a new passphrase\n"
- "to encrypt the secret token."),
+ "to encrypt the token database."),
wxT("Passphrase Entry"), wxEmptyString);
wxPasswordEntryDialog ConfirmPrompt(this, wxT("Please confirm the passphrase."),
wxT("Passphrase Entry"), wxEmptyString);
-
if ((PasswordPrompt.ShowModal() == wxID_OK)
- && (ConfirmPrompt.ShowModal() == wxID_OK)) {
+ && (ConfirmPrompt.ShowModal() == wxID_OK)) {
if (PasswordPrompt.GetValue() == ConfirmPrompt.GetValue()) {
Passphrase = PasswordPrompt.GetValue();
}
@@ -138,36 +272,20 @@ void TOTPGen::UpdateSecret(wxCommandEvent& WXUNUSED(event))
wxT("Error"), wxOK | wxICON_ERROR);
return;
}
- }
- else {
- return;
- }
-
-
- if (!GenTOTP(Secret_New)) {
- wxMessageBox(wxT("Could not generate TOTP - bad token?"),
- wxT("Error"), wxOK | wxICON_ERROR);
+ if (Passphrase.empty()) {
+ wxMessageBox(wxT("Passphrase may not be empty."),
+ wxT("Error"), wxOK | wxICON_ERROR);
+ return;
+ }
+ } else {
return;
}
- Secret = Secret_New;
- EncryptSecret(Secret);
-
- if (Locked == true) {
- Locked = false;
-
- MainWindow->Connect(CountdownTimer->GetId(), wxEVT_TIMER,
- wxTimerEventHandler(TOTPGen::UpdateStatus),
- NULL, this);
-
- CopyButton->Enable(true);
- UnlockButton->Enable(false);
- }
-
- return;
+ EncryptDB(KeyDB.dump().c_str());
}
-void TOTPGen::Unlock(wxCommandEvent& WXUNUSED(event))
+void
+TOTPGen::Unlock(wxCommandEvent& WXUNUSED(event))
{
wxPasswordEntryDialog PasswordPrompt(this, wxT("Please enter your passphrase."),
wxT("Passphrase Entry"), wxEmptyString);
@@ -176,26 +294,23 @@ void TOTPGen::Unlock(wxCommandEvent& WXUNUSED(event))
Passphrase = PasswordPrompt.GetValue();
}
- if (!DecryptSecret()) {
- wxMessageBox(wxT("Could not unlock base 32 token,\n"
- "bad passphrase or corrupt file?"),
+ if (!DecryptDB()) {
+ wxMessageBox(wxT("Could not unlock database,\n"
+ "bad passphrase or corrupt file?"),
wxT("Error"), wxOK | wxICON_ERROR);
return;
}
- MainWindow->Connect(CountdownTimer->GetId(), wxEVT_TIMER,
- wxTimerEventHandler(TOTPGen::UpdateStatus),
- NULL, this);
-
- Locked = false;
-
- CopyButton->Enable(true);
- UnlockButton->Enable(false);
+ AddButton->Enable(true);
+ SecurityButton->SetLabel(wxT("Set Password"));
+ SecurityButton->Bind(wxEVT_BUTTON, &TOTPGen::SetPassword, this);
+ ReloadSelections();
GenTOTP(Secret);
}
-void TOTPGen::UpdateStatus(wxTimerEvent& WXUNUSED(event))
+void
+TOTPGen::UpdateStatus(wxTimerEvent& WXUNUSED(event))
{
unsigned char CounterValue = 29 - ((time(NULL) - 1) % 30);
@@ -206,17 +321,19 @@ void TOTPGen::UpdateStatus(wxTimerEvent& WXUNUSED(event))
}
}
-void TOTPGen::CopyText(wxCommandEvent& WXUNUSED(event))
+void
+TOTPGen::CopyText(wxCommandEvent& WXUNUSED(event))
{
wxString ToCopy = TOTPField->GetLabel();
- if (wxTheClipboard->Open() && !Locked) {
+ if (wxTheClipboard->Open()) {
wxTheClipboard->SetData(new wxTextDataObject(ToCopy));
wxTheClipboard->Close();
}
}
-void TOTPGen::ResizeTextToFit(wxStaticText* text_field)
+void
+TOTPGen::ResizeTextToFit(wxStaticText* text_field)
{
wxString text = text_field->GetLabel();
wxSize text_size = text_field->GetClientSize();