Remove MPW_COLOR from core, safer decryption, more standard password input & curses dialog.
This commit is contained in:
parent
877eba66be
commit
9443d93500
@ -262,3 +262,40 @@ const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
|
||||
|
||||
return classCharacters[seedByte % strlen( classCharacters )];
|
||||
}
|
||||
|
||||
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword) {
|
||||
|
||||
const char *leftArm[] = { "╔", "╚", "╰", "═" };
|
||||
const char *rightArm[] = { "╗", "╝", "╯", "═" };
|
||||
const char *body[] = { "█", "░", "▒", "▓", "☺", "☻" };
|
||||
const char *accessory[] = {
|
||||
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
||||
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
||||
};
|
||||
|
||||
const uint8_t *identiconSeed = NULL;
|
||||
if (fullName && strlen( fullName ) && masterPassword && strlen( masterPassword ))
|
||||
identiconSeed = mpw_hash_hmac_sha256(
|
||||
(const uint8_t *)masterPassword, strlen( masterPassword ),
|
||||
(const uint8_t *)fullName, strlen( fullName ) );
|
||||
if (!identiconSeed)
|
||||
return (MPIdenticon){
|
||||
.leftArm = "",
|
||||
.body = "",
|
||||
.rightArm = "",
|
||||
.accessory = "",
|
||||
.color=0,
|
||||
};
|
||||
|
||||
MPIdenticon identicon = {
|
||||
.leftArm = leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
|
||||
.body = body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
|
||||
.rightArm = rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
|
||||
.accessory = accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
|
||||
.color = (uint8_t)(identiconSeed[4] % 7 + 1),
|
||||
};
|
||||
mpw_free( &identiconSeed, 32 );
|
||||
|
||||
return identicon;
|
||||
}
|
||||
|
@ -111,6 +111,14 @@ typedef mpw_enum ( uint32_t, MPCounterValue ) {
|
||||
MPCounterValueLast = UINT32_MAX,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char *leftArm;
|
||||
const char *body;
|
||||
const char *rightArm;
|
||||
const char *accessory;
|
||||
uint8_t color;
|
||||
} MPIdenticon;
|
||||
|
||||
//// Type utilities.
|
||||
|
||||
/**
|
||||
@ -157,4 +165,7 @@ const char *mpw_charactersInClass(char characterClass);
|
||||
*/
|
||||
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
|
||||
|
||||
/** @return A fingerprint for a user. */
|
||||
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword);
|
||||
|
||||
#endif // _MPW_TYPES_H
|
||||
|
@ -20,12 +20,6 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if MPW_COLOR
|
||||
#include <unistd.h>
|
||||
#include <curses.h>
|
||||
#include <term.h>
|
||||
#endif
|
||||
|
||||
#if MPW_CPERCIVA
|
||||
#include <scrypt/crypto_scrypt.h>
|
||||
#include <scrypt/sha256.h>
|
||||
@ -274,9 +268,10 @@ uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, co
|
||||
return mac;
|
||||
}
|
||||
|
||||
// We do our best to not fail on odd buf's, eg. non-padded cipher texts.
|
||||
static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, size_t *bufSize) {
|
||||
|
||||
if (!key || keySize < 16)
|
||||
if (!key || keySize < 16 || !*bufSize)
|
||||
return NULL;
|
||||
|
||||
// IV = zero
|
||||
@ -284,9 +279,9 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
|
||||
mpw_zero( iv, sizeof iv );
|
||||
|
||||
// Add PKCS#7 padding
|
||||
uint32_t aesSize = (uint32_t)*bufSize;
|
||||
if (encrypt)
|
||||
aesSize = (aesSize / 16) * 16 + 16;
|
||||
uint32_t aesSize = ((uint32_t)*bufSize + 15 / 16) * 16; // round up to block size.
|
||||
if (encrypt && !(*bufSize % 16)) // add pad block if plain text fits block size.
|
||||
encrypt += 16;
|
||||
uint8_t aesBuf[aesSize];
|
||||
memcpy( aesBuf, buf, *bufSize );
|
||||
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
|
||||
@ -302,7 +297,7 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
|
||||
// Truncate PKCS#7 padding
|
||||
if (encrypt)
|
||||
*bufSize = aesSize;
|
||||
else
|
||||
else if (*bufSize % 16 == 0 && resultBuf[aesSize - 1] < 16)
|
||||
*bufSize -= resultBuf[aesSize - 1];
|
||||
|
||||
return resultBuf;
|
||||
@ -443,99 +438,6 @@ const char *mpw_hex_l(uint32_t number) {
|
||||
return mpw_hex( &buf, sizeof( buf ) );
|
||||
}
|
||||
|
||||
#if MPW_COLOR
|
||||
static char *str_tputs;
|
||||
static int str_tputs_cursor;
|
||||
static const int str_tputs_max = 256;
|
||||
|
||||
static bool mpw_setupterm() {
|
||||
|
||||
if (!isatty( STDERR_FILENO ))
|
||||
return false;
|
||||
|
||||
static bool termsetup;
|
||||
if (!termsetup) {
|
||||
int errret;
|
||||
if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
|
||||
wrn( "Terminal doesn't support color (setupterm errret %d).\n", errret );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mpw_tputc(int c) {
|
||||
|
||||
if (++str_tputs_cursor < str_tputs_max) {
|
||||
str_tputs[str_tputs_cursor] = (char)c;
|
||||
return OK;
|
||||
}
|
||||
|
||||
return ERR;
|
||||
}
|
||||
|
||||
static char *mpw_tputs(const char *str, int affcnt) {
|
||||
|
||||
if (str_tputs)
|
||||
mpw_free( &str_tputs, str_tputs_max );
|
||||
str_tputs = calloc( str_tputs_max, sizeof( char ) );
|
||||
str_tputs_cursor = -1;
|
||||
|
||||
char *result = tputs( str, affcnt, mpw_tputc ) == ERR? NULL: mpw_strndup( str_tputs, str_tputs_max );
|
||||
if (str_tputs)
|
||||
mpw_free( &str_tputs, str_tputs_max );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
||||
|
||||
const char *leftArm[] = { "╔", "╚", "╰", "═" };
|
||||
const char *rightArm[] = { "╗", "╝", "╯", "═" };
|
||||
const char *body[] = { "█", "░", "▒", "▓", "☺", "☻" };
|
||||
const char *accessory[] = {
|
||||
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
||||
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
||||
};
|
||||
|
||||
const uint8_t *identiconSeed = mpw_hash_hmac_sha256(
|
||||
(const uint8_t *)masterPassword, strlen( masterPassword ),
|
||||
(const uint8_t *)fullName, strlen( fullName ) );
|
||||
if (!identiconSeed)
|
||||
return NULL;
|
||||
|
||||
char *colorString, *resetString;
|
||||
#ifdef MPW_COLOR
|
||||
if (mpw_setupterm()) {
|
||||
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
|
||||
colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), colorIdentifier ), 1 );
|
||||
resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
colorString = calloc( 1, sizeof( char ) );
|
||||
resetString = calloc( 1, sizeof( char ) );
|
||||
}
|
||||
|
||||
char *identicon = (char *)calloc( 256, sizeof( char ) );
|
||||
snprintf( identicon, 256, "%s%s%s%s%s%s",
|
||||
colorString,
|
||||
leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
|
||||
body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
|
||||
rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
|
||||
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
|
||||
resetString );
|
||||
|
||||
mpw_free( &identiconSeed, 32 );
|
||||
mpw_free_strings( &colorString, &resetString, NULL );
|
||||
return identicon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
|
||||
*/
|
||||
@ -568,6 +470,10 @@ const size_t mpw_utf8_strlen(const char *utf8String) {
|
||||
}
|
||||
|
||||
char *mpw_strdup(const char *src) {
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
|
||||
size_t len = strlen( src );
|
||||
char *dst = malloc( len + 1 );
|
||||
memcpy( dst, src, len );
|
||||
@ -577,6 +483,10 @@ char *mpw_strdup(const char *src) {
|
||||
}
|
||||
|
||||
char *mpw_strndup(const char *src, size_t max) {
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
|
||||
size_t len = 0;
|
||||
for (; len < max && src[len] != '\0'; ++len);
|
||||
|
||||
|
@ -203,17 +203,14 @@ MPKeyID mpw_id_buf(const void *buf, size_t length);
|
||||
/** Compare two fingerprints for equality.
|
||||
* @return true if the buffers represent identical fingerprints. */
|
||||
bool mpw_id_buf_equals(const char *id1, const char *id2);
|
||||
/** Encode a visual fingerprint for a user.
|
||||
* @return A newly allocated string. */
|
||||
const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
||||
|
||||
//// String utilities.
|
||||
|
||||
/** @return The amount of display characters in the given UTF-8 string. */
|
||||
const size_t mpw_utf8_strlen(const char *utf8String);
|
||||
/** Drop-in for non-standard strdup(3). */
|
||||
/** Drop-in for POSIX strdup(3). */
|
||||
char *mpw_strdup(const char *src);
|
||||
/** Drop-in for non-standard strndup(3). */
|
||||
/** Drop-in for POSIX strndup(3). */
|
||||
char *mpw_strndup(const char *src, size_t max);
|
||||
|
||||
#endif // _MPW_UTIL_H
|
||||
|
@ -28,18 +28,21 @@
|
||||
#include <errno.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#define MPW_MAX_INPUT 60
|
||||
|
||||
#if MPW_COLOR
|
||||
#include <curses.h>
|
||||
#include <term.h>
|
||||
#endif
|
||||
|
||||
#include "mpw-util.h"
|
||||
|
||||
/** Read the value of an environment variable.
|
||||
* @return A newly allocated string or NULL if the variable doesn't exist. */
|
||||
const char *mpw_getenv(const char *variableName) {
|
||||
|
||||
char *envBuf = getenv( variableName );
|
||||
return envBuf? mpw_strdup( envBuf ): NULL;
|
||||
}
|
||||
|
||||
/** Use the askpass program to prompt the user.
|
||||
* @return A newly allocated string or NULL if askpass is not supported or an error occurred. */
|
||||
char *mpw_askpass(const char *prompt) {
|
||||
|
||||
const char *askpass = mpw_getenv( MP_ENV_askpass );
|
||||
@ -91,15 +94,59 @@ char *mpw_askpass(const char *prompt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Ask the user a question.
|
||||
* @return A newly allocated string or NULL if an error occurred trying to read from the user. */
|
||||
const char *mpw_getline(const char *prompt) {
|
||||
static const char *_mpw_getline(const char *prompt, bool silent) {
|
||||
|
||||
// Get answer from askpass.
|
||||
char *answer = mpw_askpass( prompt );
|
||||
if (answer)
|
||||
return answer;
|
||||
|
||||
#if MPW_COLOR
|
||||
// Initialize a curses screen.
|
||||
initscr();
|
||||
start_color();
|
||||
init_pair( 1, COLOR_WHITE, COLOR_BLUE );
|
||||
init_pair( 2, COLOR_BLACK, COLOR_WHITE );
|
||||
int rows, cols;
|
||||
getmaxyx( stdscr, rows, cols );
|
||||
|
||||
// Display a dialog box.
|
||||
int width = max( prompt? (int)strlen( prompt ): 0, MPW_MAX_INPUT ) + 6;
|
||||
char *version = "mpw v" stringify_def( MP_VERSION );
|
||||
mvprintw( rows - 1, (cols - (int)strlen( version )) / 2, "%s", version );
|
||||
attron( A_BOLD );
|
||||
color_set( 2, NULL );
|
||||
mvprintw( rows / 2 - 1, (cols - width) / 2, "%s%*s%s", "*", width - 2, "", "*" );
|
||||
mvprintw( rows / 2 - 1, (cols - (int)strlen( prompt )) / 2, "%s", prompt );
|
||||
color_set( 1, NULL );
|
||||
mvprintw( rows / 2 + 0, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
|
||||
mvprintw( rows / 2 + 1, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
|
||||
mvprintw( rows / 2 + 2, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
|
||||
|
||||
// Read response.
|
||||
color_set( 2, NULL );
|
||||
attron( A_STANDOUT );
|
||||
int result = ERR;
|
||||
char str[MPW_MAX_INPUT + 1];
|
||||
if (silent) {
|
||||
mvprintw( rows / 2 + 1, (cols - 5) / 2, "[ * ]" );
|
||||
refresh();
|
||||
|
||||
noecho();
|
||||
result = mvgetnstr( rows / 2 + 1, (cols - 1) / 2, str, MPW_MAX_INPUT );
|
||||
echo();
|
||||
} else {
|
||||
mvprintw( rows / 2 + 1, (cols - (MPW_MAX_INPUT + 2)) / 2, "%*s", MPW_MAX_INPUT + 2, "" );
|
||||
refresh();
|
||||
|
||||
echo();
|
||||
result = mvgetnstr( rows / 2 + 1, (cols - MPW_MAX_INPUT) / 2, str, MPW_MAX_INPUT );
|
||||
}
|
||||
attrset( 0 );
|
||||
endwin();
|
||||
|
||||
return result == ERR? NULL: mpw_strndup( str, MPW_MAX_INPUT );
|
||||
#else
|
||||
// Get password from terminal.
|
||||
fprintf( stderr, "%s ", prompt );
|
||||
|
||||
@ -113,31 +160,19 @@ const char *mpw_getline(const char *prompt) {
|
||||
// Remove trailing newline.
|
||||
answer[lineSize - 1] = '\0';
|
||||
return answer;
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *mpw_getline(const char *prompt) {
|
||||
|
||||
return _mpw_getline( prompt, false );
|
||||
}
|
||||
|
||||
/** Ask the user for a password.
|
||||
* @return A newly allocated string or NULL if an error occurred trying to read from the user. */
|
||||
const char *mpw_getpass(const char *prompt) {
|
||||
|
||||
// Get password from askpass.
|
||||
const char *password = mpw_askpass( prompt );
|
||||
if (password)
|
||||
return password;
|
||||
|
||||
// Get password from terminal.
|
||||
char *answer = getpass( prompt );
|
||||
if (!answer)
|
||||
return NULL;
|
||||
|
||||
password = mpw_strdup( answer );
|
||||
mpw_zero( answer, strlen( answer ) );
|
||||
return password;
|
||||
return _mpw_getline( prompt, true );
|
||||
}
|
||||
|
||||
/** Get the absolute path to the mpw configuration file with the given prefix name and file extension.
|
||||
* Resolves the file <prefix.extension> as located in the <.mpw.d> directory inside the user's home directory
|
||||
* or current directory if it couldn't be resolved.
|
||||
* @return A newly allocated string. */
|
||||
const char *mpw_path(const char *prefix, const char *extension) {
|
||||
|
||||
// Resolve user's home directory.
|
||||
@ -180,8 +215,6 @@ const char *mpw_path(const char *prefix, const char *extension) {
|
||||
return path;
|
||||
}
|
||||
|
||||
/** mkdir all the directories up to the directory of the given file path.
|
||||
* @return true if the file's path exists. */
|
||||
bool mpw_mkdirs(const char *filePath) {
|
||||
|
||||
if (!filePath)
|
||||
@ -215,8 +248,6 @@ bool mpw_mkdirs(const char *filePath) {
|
||||
return success;
|
||||
}
|
||||
|
||||
/** Read until EOF from the given file descriptor.
|
||||
* @return A newly allocated string or NULL if the read buffer couldn't be allocated or an error occurred. */
|
||||
char *mpw_read_fd(int fd) {
|
||||
|
||||
char *buf = NULL;
|
||||
@ -230,8 +261,6 @@ char *mpw_read_fd(int fd) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Read the file contents of a given file.
|
||||
* @return A newly allocated string or NULL if the read buffer couldn't be allocated. */
|
||||
char *mpw_read_file(FILE *file) {
|
||||
|
||||
if (!file)
|
||||
@ -245,3 +274,73 @@ char *mpw_read_file(FILE *file) {
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#if MPW_COLOR
|
||||
static char *str_tputs;
|
||||
static int str_tputs_cursor;
|
||||
static const int str_tputs_max = 256;
|
||||
|
||||
static bool mpw_setupterm() {
|
||||
|
||||
if (!isatty( STDERR_FILENO ))
|
||||
return false;
|
||||
|
||||
static bool termsetup;
|
||||
if (!termsetup) {
|
||||
int errret;
|
||||
if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
|
||||
wrn( "Terminal doesn't support color (setupterm errret %d).\n", errret );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mpw_tputc(int c) {
|
||||
|
||||
if (++str_tputs_cursor < str_tputs_max) {
|
||||
str_tputs[str_tputs_cursor] = (char)c;
|
||||
return OK;
|
||||
}
|
||||
|
||||
return ERR;
|
||||
}
|
||||
|
||||
static char *mpw_tputs(const char *str, int affcnt) {
|
||||
|
||||
if (str_tputs)
|
||||
mpw_free( &str_tputs, str_tputs_max );
|
||||
str_tputs = calloc( str_tputs_max, sizeof( char ) );
|
||||
str_tputs_cursor = -1;
|
||||
|
||||
char *result = tputs( str, affcnt, mpw_tputc ) == ERR? NULL: mpw_strndup( str_tputs, str_tputs_max );
|
||||
if (str_tputs)
|
||||
mpw_free( &str_tputs, str_tputs_max );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const char *mpw_identicon_str(MPIdenticon identicon) {
|
||||
|
||||
char *colorString, *resetString;
|
||||
#ifdef MPW_COLOR
|
||||
if (mpw_setupterm()) {
|
||||
colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), identicon.color ), 1 );
|
||||
resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
colorString = calloc( 1, sizeof( char ) );
|
||||
resetString = calloc( 1, sizeof( char ) );
|
||||
}
|
||||
|
||||
const char *str = mpw_str( "%s%s%s%s%s%s",
|
||||
colorString, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory, resetString );
|
||||
mpw_free_strings( &colorString, &resetString, NULL );
|
||||
|
||||
return mpw_strdup( str );
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "mpw-types.h"
|
||||
|
||||
#ifndef MP_VERSION
|
||||
#define MP_VERSION ?
|
||||
@ -62,3 +63,7 @@ char *mpw_read_fd(int fd);
|
||||
/** Read the file contents of a given file.
|
||||
* @return A newly allocated string or NULL the read buffer couldn't be allocated. */
|
||||
char *mpw_read_file(FILE *file);
|
||||
|
||||
/** Encode a visual fingerprint for a user.
|
||||
* @return A newly allocated string. */
|
||||
const char *mpw_identicon_str(MPIdenticon identicon);
|
||||
|
@ -564,7 +564,7 @@ void cli_question(Arguments __unused *args, Operation *operation) {
|
||||
|
||||
void cli_operation(Arguments __unused *args, Operation *operation) {
|
||||
|
||||
operation->identicon = mpw_identicon( operation->user->fullName, operation->user->masterPassword );
|
||||
operation->identicon = mpw_identicon_str( mpw_identicon( operation->user->fullName, operation->user->masterPassword ) );
|
||||
|
||||
if (!operation->site)
|
||||
abort();
|
||||
|
Loading…
Reference in New Issue
Block a user