Add wxSecretStore

Add a new class allowing to store passwords and other sensitive information
using the OS-provided facilities.

Add implementations for all the main platforms, documentation and a new sample
(which contains an ad hoc unit test as the real unit test for this class would
probably be a bad idea as it wouldn't run in non-interactive contexts and
could show OS level dialog boxes if it did).
This commit is contained in:
Vadim Zeitlin
2016-05-29 01:13:44 +02:00
parent 21d90d48ba
commit 675d9d779d
48 changed files with 5288 additions and 68 deletions

View File

@@ -0,0 +1,235 @@
/////////////////////////////////////////////////////////////////////////////
// Name: secretstore.cpp
// Purpose: wxWidgets sample showing the use of wxSecretStore class
// Author: Vadim Zeitlin
// Created: 2016-05-27
// Copyright: (c) 2016 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/secretstore.h"
#include "wx/init.h"
#include "wx/crt.h"
#include "wx/log.h"
bool Save(wxSecretStore& store, const wxString& service, const wxString& user)
{
char password[4096];
wxPrintf("Enter the password for %s/%s (echoing NOT disabled): ",
service, user);
if ( !wxFgets(password, WXSIZEOF(password), stdin) )
{
wxFprintf(stderr, "Password not stored.\n");
return false;
}
const size_t size = wxStrlen(password) - 1;
password[size] = 0; // Strip trailing new line.
wxSecretValue secret(size, password);
// The password data was copied into wxSecretValue, don't leave it lying
// around in the stack unnecessarily.
wxSecretValue::Wipe(size, password);
if ( !store.Save(service, user, secret) )
{
wxFprintf(stderr,
"Failed to save the password for %s/%s.\n",
service, user);
return false;
}
wxPrintf("Password for %s/%s saved.\n",
service, user);
return true;
}
bool Load(wxSecretStore& store, const wxString& service, const wxString& user)
{
wxSecretValue secret = store.Load(service, user);
if ( !secret.IsOk() )
{
wxFprintf(stderr,
"Failed to load the password for %s/%s.\n",
service, user);
return false;
}
const size_t size = secret.GetSize();
wxPrintf("Password for %s/%s is %zu bytes long: \"",
service, user, size);
// We can't easily print a non-NUL-terminated string and copying it into a
// wxString or std::string would leave the password in memory, so do it the
// hard way.
const char* p = static_cast<const char *>(secret.GetData());
for ( size_t n = 0; n < size; n++ )
wxPutc(*p++, stdout);
wxPrintf("\"\n");
return true;
}
bool Delete(wxSecretStore& store, const wxString& service, const wxString& user)
{
if ( !store.Delete(service, user) )
{
wxFprintf(stderr,
"Password for %s/%s not deleted.\n",
service, user);
return false;
}
wxPrintf("Stored password for %s/%s deleted.\n",
service, user);
return true;
}
static bool PrintResult(bool ok)
{
wxPuts(ok ? "ok" : "ERROR");
return ok;
}
bool SelfTest(wxSecretStore& store, const wxString& service, const wxString& user)
{
wxPrintf("Running the tests...\n");
const wxSecretValue secret1(6, "secret");
wxPrintf("Storing the password:\t");
bool ok = store.Save(service, user, secret1);
if ( !PrintResult(ok) )
{
// The rest of the tests will probably fail too, no need to continue.
wxPrintf("Bailing out.\n");
return false;
}
wxPrintf("Loading the password:\t");
wxSecretValue secret = store.Load(service, user);
ok = secret.IsOk() &&
secret.GetSize() == secret1.GetSize() &&
memcmp(secret.GetData(), secret1.GetData(), secret1.GetSize()) == 0;
if ( !PrintResult(secret == secret1) )
ok = false;
// Overwriting the password should work.
const wxSecretValue secret2(6, "privet");
wxPrintf("Changing the password:\t");
if ( PrintResult(store.Save(service, user, secret2)) )
{
wxPrintf("Reloading the password:\t");
secret = store.Load(service, user);
if ( !PrintResult(secret == secret2) )
ok = false;
}
else
ok = false;
wxPrintf("Deleting the password:\t");
if ( !PrintResult(store.Delete(service, user)) )
ok = false;
// This is supposed to fail now.
wxPrintf("Deleting it again:\t");
if ( !PrintResult(!store.Delete(service, user)) )
ok = false;
// And loading should fail too.
wxPrintf("Loading after deleting:\t");
if ( !PrintResult(!store.Load(service, user).IsOk()) )
ok = false;
if ( ok )
wxPrintf("All tests passed!\n");
return ok;
}
int main(int argc, char **argv)
{
// To complement the standard EXIT_{SUCCESS,FAILURE}.
const int EXIT_SYNTAX = 2;
wxInitializer initializer;
if ( !initializer )
{
fprintf(stderr, "Failed to initialize the wxWidgets library, aborting.");
return EXIT_FAILURE;
}
if ( argc != 4 )
{
wxFprintf(stderr,
"Usage: %s {save|load|delete|selftest} <service> <user>\n"
"\n"
"Sample showing wxSecretStore class functionality.\n"
"Specify one of the commands to perform the corresponding\n"
"function call. The \"service\" and \"user\" arguments are\n"
"mandatory, \"save\" will also prompt for password.\n",
argv[0]);
return EXIT_SYNTAX;
}
wxSecretStore store = wxSecretStore::GetDefault();
if ( !store.IsOk() )
{
wxFprintf(stderr, "Failed to create default secret store.\n");
return EXIT_FAILURE;
}
const wxString operation = argv[1];
const wxString service = argv[2];
const wxString user = argv[3];
bool ok;
if ( operation == "save" )
{
ok = Save(store, service, user);
}
else if ( operation == "load" )
{
ok = Load(store, service, user);
}
else if ( operation == "delete" )
{
ok = Delete(store, service, user);
}
else if ( operation == "selftest" )
{
ok = SelfTest(store, service, user);
}
else
{
wxFprintf(stderr,
"Unknown operation \"%s\", expected \"save\", \"load\" or "
"\"delete\".\n",
operation);
return EXIT_SYNTAX;
}
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}