我编写了一个工具,它接受域名和密码,将它们连接起来,使用sha512对结果进行散列,并返回被截断为32个字符的base64编码的结果。这背后的主要想法是防止滥用的服务器所有者通过将我的密码存储在纯文本中,并在其他帐户上使用该密码,或者如果开发人员忘记从日志中删除密码,然后黑客进入日志,从而防止服务器所有者获取我的密码。
我的许多代码已经编写并在另一个应用程序中使用了。base64编码器取自https://github.com/superwills/NibbleAndAHalf,稍作修改。makeSyscallError过去常常抛出一个std::system_error,但是为了保持简单,我只是在代码中调用std::perror并退出。
我的主要问题是,这是一个安全的方式来保护我的密码吗?我知道我真的应该保留一个由随机生成的密码组成的数据库,然后用我的主密码加密它,但是我不想丢失这个数据库,我也不想把加密的数据库放在云中。
这里是mpass.cpp:
#include
#include
#include
#include
#include
#include
#include
#include "hash.hpp"
#include "base64.h"
struct termios newTerm, oldTerm;
void setEcho(bool echo, bool icanon){
if(echo) newTerm.c_lflag |= ECHO;
else newTerm.c_lflag &= ~ECHO;
if(icanon) newTerm.c_lflag |= ICANON;
else newTerm.c_lflag &= ~ICANON;
if(tcsetattr(0, TCSANOW, &newTerm) == -1){
std::perror("tcsetattr");
std::exit(1);
}
}
//I hate it so much when my program crashes on a signal and my terminal gets screwed up, so
void cleanUp(int sn, siginfo_t *info, void *ctx){
tcsetattr(0, TCSANOW, &oldTerm);
if(sn != SIGINT) psiginfo(info, NULL);
signal(sn, SIG_DFL);
raise(sn);
std::exit(-1); //if raise some reason didn't kill
}
//A list of signals to clean up on
const int signalsToCatch[] = {
SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM,
SIGTERM, SIGUSR1, SIGUSR2, SIGBUS, SIGIO, SIGPROF, SIGSYS, SIGTRAP,
SIGVTALRM, SIGXCPU, SIGXFSZ, SIGPWR, 0
}
/*This struct is defined in an external C file as
struct sigaction sa = {
.sa_flags = SA_SIGINFO
};
Now sa is already filled with 0s, except for sa_flags. I wish C++ included this feature.
*/
extern struct sigaction sa;
int main(){
if(tcgetattr(0, &oldTerm) == -1){
std::perror("tcgetattr");
return 1;
}
std::memcpy(&newTerm, &oldTerm, sizeof(struct termios));
sa.sa_sigaction = cleanUp;
for(int i = 0; signalsToCatch[i] != 0; ++i){
if(sigaction(signalsToCatch[i], &sa, NULL) == -1){
std::perror("sigaction");
return 1;
}
}
std::string dom, pass;
(std::cout << "Enter domain: ").flush();
std::getline(std::cin, dom);
(std::cout << "Enter password: ").flush();
setEcho(false, true);
std::getline(std::cin, pass);
setEcho(true, true);
(std::cout << "\nPassword for " << dom << ": ").flush();
dom += pass;
char buf[64];
sha512sum(dom.c_str(), dom.length(), buf);
int useless;
char *ret = base64(buf, 64, &useless);
if(ret == NULL){ //Almost forgot to include this so if someone posts about this while I make this edit, don't look at them like they're stupid.
perror("malloc");
return 1;
}
ret[32] = '\n'; //I'm just gonna put the newline here
if(write(1, ret, 33) == -1){
std::perror("write");
return 1;
}
//I could std::free(ret), but it will get freed anyway by the program exit.
return 0;
}
//Must be defined for hash.cpp, but I wont be catching exceptions for sha512sum
//I don't want to edit hash.cpp either, as it is the same file used in another application
void makeSyscallError(const char *what){
std::perror(what);
std::exit(1);
}这里是hash.hpp:
#ifndef HASH_HPP
#define HASH_HPP
#include
//Args: input buffer, input buffer length, output buffer (output buffer must always be 64 bytes or more)
void sha512sum(const void *, size_t, void *);
#endif这里是hash.cpp:
#include
#include
#include
#include
#include
#include
#include
//I won't show misc.hpp, it's just a definition for makeSyscallError(const char *msg);
#include "misc.hpp"
static int cryptoFd = -1;
extern "C"{
extern const struct sockaddr_alg sha512addr;
/*This is also defined in the C file:
const struct sockaddr_alg sha512addr = {
.salg_family = AF_ALG,
.salg_type = "hash",
.salg_name = "sha512"
};
*/
}
//This function checks if cryptoFd is equal to -1, and if it is, it will create it
static void checkCryptoFd(){
if(cryptoFd != -1) return;
int bindFd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if(bindFd == -1)
makeSyscallError("Failed to create AF_ALG socket");
if(bind(bindFd, (struct sockaddr *) &sha512addr, sizeof(struct sockaddr_alg))){
close(bindFd);
makeSyscallError("Failed to bind AF_ALG socket");
}
cryptoFd = accept(bindFd, 0, 0);
close(bindFd);
if(cryptoFd == -1)
makeSyscallError("Failed to create sha512 socket");
}
//Now, I am using linux AF_ALG not for speed (I believe this usage of it would actually be slower due to syscall overhead,
//but simply because it's there and its the only interface I actually learned how to use. I'm not looking at portability in any way, and if I were, I'd rewrite this as a browser extension
void sha512sum(const void *toHash, size_t len, void *result){
checkCryptoFd();
for(;;){
if(len < 128){ //Last 128 bytes to write
if(write(cryptoFd, toHash, len) == -1)
makeSyscallError("(odd) Failed to write to sha512 socket");
if(read(cryptoFd, result, 64) == -1) //Get result
makeSyscallError("(odd) Failed to read from sha512 socket");
return; //All done!
}
if(send(cryptoFd, toHash, 128, MSG_MORE)){
makeSyscallError("(odd) Failed to write to sha512 socket");
}
toHash += 128;
len -= 128;
}
}基础64.c:
#include
const static char* b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
// Converts binary data of length=len to base64 characters.
// Length of the resultant string is stored in flen
// (you must pass pointer flen).
//I did modify this
char* base64( const void* binaryData, int len, int *flen )
{
const unsigned char* bin = (const unsigned char*) binaryData ;
char* res ;
int rc = 0 ; // result counter
int byteNo ; // I need this after the loop
int modulusLen = len % 3 ;
int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1) ; // 2 gives 1 and 1 gives 2, but 0 gives 0.
*flen = 4*(len + pad)/3 ;
res = (char*) malloc( *flen + 1 ) ; // and one for the null
//Here's the modfifcations I made
/* if( !res )
{
puts( "ERROR: base64 could not allocate enough memory." ) ;
puts( "I must stop because I could not get enough" ) ;
return 0;
}*/
if(!res) return NULL; //Much better
for( byteNo = 0 ; byteNo <= len-3 ; byteNo+=3 )
{
unsigned char BYTE0=bin[byteNo];
unsigned char BYTE1=bin[byteNo+1];
unsigned char BYTE2=bin[byteNo+2];
res[rc++] = b64[ BYTE0 >> 2 ] ;
res[rc++] = b64[ ((0x3&BYTE0)<<4) + (BYTE1 >> 4) ] ;
res[rc++] = b64[ ((0x0f&BYTE1)<<2) + (BYTE2>>6) ] ;
res[rc++] = b64[ 0x3f&BYTE2 ] ;
}
if( pad==2 )
{
res[rc++] = b64[ bin[byteNo] >> 2 ] ;
res[rc++] = b64[ (0x3&bin[byteNo])<<4 ] ;
res[rc++] = '=';
res[rc++] = '=';
}
else if( pad==1 )
{
res[rc++] = b64[ bin[byteNo] >> 2 ] ;
res[rc++] = b64[ ((0x3&bin[byteNo])<<4) + (bin[byteNo+1] >> 4) ] ;
res[rc++] = b64[ (0x0f&bin[byteNo+1])<<2 ] ;
res[rc++] = '=';
}
res[rc]=0; // NULL TERMINATOR! ;)
return res ;
}
//I removed the decoderbase64编码器以头库的形式出现。我将它移到一个.c文件中,并添加了这个.h文件( file 64.h):
#ifndef BASE64_H
#define BASE64_H
#ifdef __cplusplus
#define MYEXTERN extern "C"
#else
#define MYEXTERN
#endif
//To encode, length, returned length
MYEXTERN char *base64(const void *, int, int *);
#undef MYEXTERN
#endif我相信许多人会问,这是makeSyscallError的最初定义(const *):
void makeSyscallError(const char *what){
throw std::system_error(std::make_error_code(std::errc(errno)), what);
}编辑:忘了提到,这段代码主要是供我个人使用的。我会重写它,如果事情看起来很好的话,我会释放它。
发布于 2018-10-11 21:05:05
看上去很不错。我没有使用AF_ALG套接字的经验,所以不能对它的使用发表评论(但是不用审查家庭实现的密码是一件令人欣慰的事情,所以可以为避免这个陷阱而赞叹!)
我的大部分建议都是以风格为导向的,所以不要觉得这里有任何“必须做”的动作。
这种模式是不寻常的:
(std::cout << string).flush();虽然在功能上没有什么不正确的地方,但大多数C++作者都会包含,然后更有诗意地写出来:
std::cout << string << std::flush;最后一次刷新(打印检索的密码之前)是不需要的。
当使用std::memcpy时,如果使用sizeof expression而不是sizeof (type),则更容易看到size参数是正确的:
std::memcpy(&newTerm, &oldTerm, sizeof newTerm);假设base64.c是C代码而不是C++,那么不应该对来自malloc()的返回进行强制转换,当分配给bin时也不应该使用binaryData。不要对变量使用全大写--惯例是它们应该用于宏,以提醒读者要特别小心。
MYEXTERN宏是另一个值得怀疑的样式点。按照惯例,只需将头封装在一个extern "C"块中,它不再是代码:
#ifdef __cplusplus
extern "C" {
#endif
/* definitions */
#ifdef __cplusplus
}
#endif在sha512sum()中,对于循环的最后一次迭代有一个特例--它能被重新组织以便在循环之后出现吗?有点像
for (; len >= 128; len -= 128, toHash += 128) {
if(send(cryptoFd, toHash, 128, MSG_MORE)){
makeSyscallError("(odd) Failed to write to sha512 socket");
}
}
if (len > 0) { //Last few bytes to write
if(write(cryptoFd, toHash, len) == -1)
makeSyscallError("(odd) Failed to write to sha512 socket");
}
if (read(cryptoFd, result, 64) == -1) //Get result
makeSyscallError("(odd) Failed to read from sha512 socket");
return; //All done!如果hash.hpp是C++头,那么更愿意包含来定义std::size_t。
std::perror("malloc")可能做不到您期望的事情-- malloc()不会在失败时设置errno。您可能可以使用ulimit来测试分配失败,以减少进程可用的虚拟内存(这将需要一些尝试和错误),或者您可以找到一个调试malloc(),该调试D23可以在正确的位置准备失败。
代码有点不一致--在一些地方,我们有if (!value),还有一些地方我们显式地使用if (value == nullptr)。如果我们选择一种风格并坚持下去,那就更容易阅读了。
释放分配的内存可能是个好主意--这样您就可以在Val差尔下运行代码,而不必过滤仍在使用的内存中的假阳性。
这就是现在的一切,我也许可以稍后再谈这个问题。
https://codereview.stackexchange.com/questions/205404
复制相似问题