TinyPasswordManager est un gestionnaire de mot de passe Open Source et (bientôt) multiplate-forme. Basé sur le chiffrement AES avec clé de 256bits, je l’avais initialement développé pour un usage personnel. L’objectif initial est d’obtenir un gestionnaire simple, c’est-à-dire n’utilisant pas de bibliothèques, et sécurisé. Pour l’instant, seule la version pour Windows est dévelopée entièrement, avec GUI. En ce qui concerne la version pour les systèmes dérivés d’UNIX, je prévois de poster d’ici peu de temps la version ligne de commande, puis, par la suite, envisager le développement d’une GUI avec QT ou GTK.
Pour les spécifications “techniques”, la passphrase choisie par l’utilisateur est hashée avec SHA256. Le hash obtenu est utilisé comme clé AES. En ce qui concerne la méthode de padding, j’ai opté pour la technique décrite dans la norme ISO 10126, à savoir le remplissage avec des octets aléatoires, en spécifiant la taille du bourrage dans le dernier.
Enfin, le mode d’opération d’AES utilisé est CBC. Pour rappel, avec un chiffrement en mode ECB, tous les blocs sont indépendants : chaque bloc est simplement chiffré avec la clé. Cela pose quelques soucis en matière de confidentialité car deux mêmes blocs auront un résultat chiffré identique, ce qui n’est pas le cas avec CBC, mode utilisé en général dans lequel chaque bloc est XORé avec le bloc chiffré précédent, puis chiffré.
Je mets à disposition le binaire ainsi que les sources :
- Executable win32 : TinyPasswordManager.exe
- Sources (projet Visual Studio) : TinyPasswordManager.rar
- Screenshot
Voici le code source de la classe principale :
-
-
// TinyPasswordManager.h
-
-
#ifndef TINY_PASSWORD_MANAGER_H
-
#define TINY_PASSWORD_MANAGER_H
-
-
#ifdef WIN32
-
#undef UNICODE
-
#define _CRT_SECURE_NO_DEPRECATE 1
-
#define _CRT_NONSTDC_NO_DEPRECATE 1
-
#define _WIN32_WINNT 0×0502
-
#endif
-
-
#include "identifier.h"
-
#include "exceptions.h"
-
-
#include <string>
-
#include <vector>
-
-
#ifdef WIN32
-
#include <windows.h>
-
#endif
-
-
class TinyPasswordManager {
-
private:
-
TinyPasswordManager();
-
~TinyPasswordManager();
-
-
public:
-
static TinyPasswordManager *GetInstance();
-
static void Kill();
-
-
bool IsBeingUsed() const;
-
-
void CreateEmptyDatabase(const std::string &filename = std::string("pm.db"));
-
void OpenDatabase(const std::string &filename = std::string("pm.db"));
-
void GetIdentifiers();
-
void AddIdentifier(const Identifier &identifier);
-
-
unsigned int GetSize() const;
-
unsigned int GetIndex(const std::string &description) const;
-
-
std::string GetLogin(unsigned int index) const;
-
std::string GetPassword(unsigned int index) const;
-
std::string GetDescription(unsigned int index) const;
-
-
void SetPrivateKey(const unsigned char key[32]);
-
-
static std::string GeneratePassword(unsigned int minSize, unsigned int maxSize, const std::string &charset);
-
-
private:
-
static bool CompareIdentifier(Identifier *ident1, Identifier *ident2);
-
static void AddPadding(char *message, unsigned int length);
-
-
void Reinitialize();
-
-
std::vector<Identifier *> m_idents;
-
unsigned char *m_cipheredBuff;
-
unsigned int m_filesize;
-
std::string m_filename;
-
unsigned char m_key[32];
-
-
static TinyPasswordManager *m_instance;
-
};
-
#endif
-
-
-
// TinyPasswordManager.cpp
-
-
#include "tinypasswordmanager.h"
-
#include "aes.h"
-
#include "sha256.h"
-
-
#include <fstream>
-
#include <cstring>
-
#include <algorithm>
-
#include <sstream>
-
-
TinyPasswordManager *TinyPasswordManager::m_instance = NULL;
-
-
TinyPasswordManager::TinyPasswordManager()
-
: m_cipheredBuff(NULL), m_filesize(0), m_filename("") {
-
memset(m_key, 0, 32);
-
}
-
-
TinyPasswordManager::~TinyPasswordManager() {
-
Identifier *tmp = NULL;
-
-
while(!m_idents.empty()) {
-
tmp = m_idents.back();
-
m_idents.pop_back();
-
delete tmp;
-
tmp = NULL;
-
}
-
-
if(m_cipheredBuff) {
-
delete[] m_cipheredBuff;
-
m_cipheredBuff = NULL;
-
}
-
}
-
-
TinyPasswordManager *TinyPasswordManager::GetInstance() {
-
if(!m_instance)
-
m_instance = new TinyPasswordManager();
-
-
return m_instance;
-
}
-
-
void TinyPasswordManager::Kill() {
-
if(m_instance) {
-
delete m_instance;
-
m_instance = NULL;
-
}
-
}
-
-
bool TinyPasswordManager::IsBeingUsed() const {
-
return !m_filename.empty();
-
}
-
-
void TinyPasswordManager::CreateEmptyDatabase(const std::string &filename) {
-
if(IsBeingUsed())
-
Reinitialize();
-
-
AES aes;
-
aes.SetParameters(256);
-
aes.StartEncryption(m_key);
-
-
m_filesize = 32;
-
char plaintext[32] = "PM1";
-
-
#ifdef WIN32
-
srand(GetTickCount());
-
#endif
-
-
for(int i = 0; i < 13; ++i)
-
plaintext[3 + i] = static_cast<char>((rand() * 255) / RAND_MAX);
-
-
AddPadding(plaintext + 16, 0);
-
-
m_cipheredBuff = new unsigned char[m_filesize];
-
if(!m_cipheredBuff)
-
throw ExMemoryAllocationFailed(__FUNCTION__);
-
-
aes.Encrypt(reinterpret_cast<unsigned char *>(plaintext), m_cipheredBuff, m_filesize >> 4, AES::CBC);
-
-
std::ofstream ofs(filename.c_str(), std::ios::out | std::ios::binary);
-
-
ofs.seekp(0, std::ios::beg);
-
ofs.write(reinterpret_cast<char *>(m_cipheredBuff), m_filesize);
-
-
if(!ofs.good())
-
throw ExFileErrorWriting(__FUNCTION__, filename);
-
-
ofs.close();
-
-
m_filename = std::string(filename);
-
}
-
-
void TinyPasswordManager::OpenDatabase(const std::string &filename) {
-
if(IsBeingUsed() && !filename.compare(m_filename))
-
return;
-
else if(IsBeingUsed() && filename.compare(m_filename))
-
Reinitialize();
-
-
std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary);
-
-
if(!ifs.good())
-
throw ExFileNotFound(__FUNCTION__, filename);
-
-
ifs.seekg(0, std::ios::end);
-
m_filesize = ifs.tellg();
-
ifs.seekg(0, std::ios::beg);
-
-
m_cipheredBuff = new unsigned char[m_filesize];
-
-
if(!m_cipheredBuff)
-
throw ExMemoryAllocationFailed(__FUNCTION__);
-
-
ifs.read(reinterpret_cast<char *>(m_cipheredBuff), m_filesize);
-
-
if(!ifs.good())
-
throw ExFileErrorReading(__FUNCTION__, filename);
-
-
m_filename = std::string(filename);
-
ifs.close();
-
}
-
-
void TinyPasswordManager::GetIdentifiers() {
-
if(!IsBeingUsed())
-
return;
-
-
AES aes;
-
aes.SetParameters(256, 128);
-
aes.StartDecryption(m_key);
-
-
char *plaintext = new char[m_filesize];
-
-
if(!plaintext)
-
throw ExMemoryAllocationFailed(__FUNCTION__);
-
-
aes.Decrypt(m_cipheredBuff, reinterpret_cast<unsigned char *>(plaintext), m_filesize >> 4, AES::CBC);
-
-
unsigned int sizePadding = plaintext[m_filesize – 1];
-
if(sizePadding > 16)
-
throw ExBadFileFormat(__FUNCTION__, m_filename);
-
-
plaintext[m_filesize – sizePadding] = 0;
-
-
if(plaintext[0] != ‘P’ || plaintext[1] != ‘M’ || plaintext[2] != ’1′)
-
throw ExBadFileFormat(__FUNCTION__, m_filename);
-
-
char *tmp = strtok(plaintext + 16, "\n");
-
-
if(!tmp)
-
return;
-
-
#define STAGE_LOGIN 0
-
#define STAGE_PASSWORD 1
-
#define STAGE_DESCRIPTION 2
-
-
unsigned int stage = STAGE_LOGIN;
-
Identifier *identifier = NULL;
-
-
do {
-
switch(stage % 3) {
-
case STAGE_LOGIN:
-
identifier = new Identifier();
-
identifier->SetLogin(tmp);
-
break;
-
-
case STAGE_PASSWORD:
-
identifier->SetPassword(tmp);
-
break;
-
-
case STAGE_DESCRIPTION:
-
identifier->SetDescription(tmp);
-
m_idents.push_back(identifier);
-
break;
-
}
-
-
stage++;
-
} while(tmp = strtok(NULL, "\n"));
-
-
memset(plaintext, 0, m_filesize);
-
delete[] plaintext;
-
-
std::sort(m_idents.begin(), m_idents.end(), TinyPasswordManager::CompareIdentifier);
-
}
-
-
void TinyPasswordManager::AddIdentifier(const Identifier &identifier) {
-
if(!IsBeingUsed())
-
return;
-
-
AES aes;
-
aes.SetParameters(256);
-
aes.StartDecryption(m_key);
-
-
std::ostringstream oss;
-
oss << identifier.GetLogin() << "\n" << identifier.GetPassword() << "\n" << identifier.GetDescription() << "\n";
-
-
unsigned newFilesize = m_filesize + oss.str().size();
-
newFilesize += 16 – (newFilesize % 16);
-
-
char *plaintext = new char[newFilesize];
-
-
if(!plaintext)
-
throw ExMemoryAllocationFailed(__FUNCTION__);
-
-
aes.Decrypt(m_cipheredBuff, reinterpret_cast<unsigned char *>(plaintext), m_filesize >> 4, AES::CBC);
-
-
unsigned int sizePadding = plaintext[m_filesize – 1];
-
if(sizePadding > 16)
-
throw ExBadFileFormat(__FUNCTION__, m_filename);
-
-
if(plaintext[0] != ‘P’ || plaintext[1] != ‘M’ || plaintext[2] != ’1′)
-
throw ExBadFileFormat(__FUNCTION__, m_filename);
-
-
strncpy(plaintext + m_filesize – sizePadding, oss.str().c_str(), oss.str().size());
-
-
newFilesize = m_filesize – sizePadding + oss.str().size();
-
AddPadding(plaintext, newFilesize);
-
newFilesize += 16 – (newFilesize % 16);
-
-
unsigned char *cipheredtext = new unsigned char[newFilesize];
-
-
if(!cipheredtext)
-
throw ExMemoryAllocationFailed(__FUNCTION__);
-
-
aes.StartEncryption(m_key);
-
aes.Encrypt(reinterpret_cast<unsigned char *>(plaintext), cipheredtext, newFilesize >> 4, AES::CBC);
-
std::ofstream ofs(m_filename.c_str(), std::ios::out | std::ios::binary);
-
-
ofs.seekp(std::ios_base::beg);
-
ofs.write(reinterpret_cast<char *>(cipheredtext), newFilesize);
-
-
if(!ofs.good())
-
throw ExFileErrorWriting(__FUNCTION__, m_filename);
-
-
ofs.close();
-
-
m_idents.push_back(new Identifier(identifier));
-
-
delete[] m_cipheredBuff;
-
m_cipheredBuff = cipheredtext;
-
m_filesize = newFilesize;
-
}
-
-
unsigned int TinyPasswordManager::GetSize() const {
-
return static_cast<unsigned int>(m_idents.size());
-
}
-
-
unsigned int TinyPasswordManager::GetIndex(const std::string &description) const {
-
std::vector<Identifier *>::const_iterator it;
-
unsigned int index = 0;
-
bool found = false;
-
-
for(it = m_idents.begin(); it != m_idents.end(); ++it, ++index)
-
if((*it)->GetDescription().compare(description) == 0) {
-
found = true;
-
break;
-
}
-
-
if(!found)
-
index = static_cast<unsigned int>(-1);
-
-
return index;
-
}
-
-
std::string TinyPasswordManager::GetLogin(unsigned int index) const {
-
return m_idents.at(index)->GetLogin();
-
}
-
-
std::string TinyPasswordManager::GetPassword(unsigned int index) const {
-
return m_idents.at(index)->GetPassword();
-
}
-
-
std::string TinyPasswordManager::GetDescription(unsigned int index) const {
-
return m_idents.at(index)->GetDescription();
-
}
-
-
void TinyPasswordManager::SetPrivateKey(const unsigned char key[32]) {
-
memcpy(m_key, key, 32);
-
}
-
-
std::string TinyPasswordManager::GeneratePassword(unsigned int minSize, unsigned int maxSize, const std::string &charset) {
-
#ifdef WIN32
-
srand(GetTickCount());
-
#endif
-
-
unsigned int passwordSize = (static_cast<unsigned int>(rand()) % (maxSize – minSize)) + minSize;
-
std::string password;
-
unsigned int charsetIndex;
-
-
for(unsigned int i = 0; i < passwordSize; ++i) {
-
charsetIndex = static_cast<unsigned int>(rand()) % charset.length();
-
password += charset.at(charsetIndex);
-
}
-
-
return password;
-
}
-
-
bool TinyPasswordManager::CompareIdentifier(Identifier *ident1, Identifier *ident2) {
-
return (ident1->GetDescription().compare(ident2->GetDescription())) > 0 ? false : true;
-
}
-
-
void TinyPasswordManager::AddPadding(char *message, unsigned int length) {
-
unsigned int i;
-
unsigned int sizePadding = 16 – (length % 16);
-
-
#ifdef WIN32
-
srand(GetTickCount());
-
#endif
-
-
for(i = 0; i < sizePadding – 1; ++i)
-
message[length + i] = static_cast<char>((rand() * 255) / RAND_MAX);
-
-
message[length + i] = static_cast<char>(sizePadding);
-
}
-
-
void TinyPasswordManager::Reinitialize() {
-
m_filename = "";
-
m_filesize = 0;
-
-
if(m_cipheredBuff) {
-
delete[] m_cipheredBuff;
-
m_cipheredBuff = NULL;
-
}
-
}
-
Entries (RSS)
Euh… Super !
C’est génial, je cherchais ça à l’instant, coup de chance.
Bonne continuation.
Une petite remarque quand même: ça serait bien de pouvoir changer des infos de connexion. On pourrait par exemple vouloir changer nous-mêmes notre mot de passe et le re-changer dans TinyPasswordManager. Ainsi que la description.
Merci encore.
Merci. Effectivement, ça peut être utile, j’y ai pensé rapidement. Lorsque j’aurai d’avantage de temps (i.e. à partir de juillet) je pense créer un repo svn où sera récupérable la dernière version du logiciel, et ajouter doucement des fonctionnalités au fil du temps.
Ça fait maintenant depuis le… 31 mai ! que j’utilise ton logiciel. Et je ne peux m’empêcher de te féliciter à nouveau ! Avec le temps, j’ai une autre remarque (je dis ça comme ça), tu pourrais faire plusieurs sessions: pour que plusieurs personnes puisse utiliser le logiciel sans mélanger les site sur le même ordinateur. Je sais pas si je suis bien clair…?
Thanks for the useful info. It’s so interesting