Add C implementation of Master Password.
This commit is contained in:
parent
989cafda71
commit
d43c19a749
5
.gitignore
vendored
5
.gitignore
vendored
@ -27,3 +27,8 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
|
||||
|
||||
# Java
|
||||
MasterPassword/Java/**/target
|
||||
|
||||
# C
|
||||
MasterPassword/C/mpw
|
||||
MasterPassword/C/lib/*/*
|
||||
!MasterPassword/C/lib/*/.source
|
||||
|
2
MasterPassword/C/build
Executable file
2
MasterPassword/C/build
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
gcc -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" -I"lib/proplib/include" -l "crypto_aesctr.o" -l "sha256.o" -l "crypto_scrypt-nosse.o" -l "memlimit.o" -l "scryptenc_cpuperf.o" -l"scryptenc.o" -l"crypto" -l"prop" -L"lib/scrypt" -L"lib/proplib/src/.libs" mpw.c -o mpw
|
15
MasterPassword/C/install
Executable file
15
MasterPassword/C/install
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
BINDIR=${BINDIR:+${PREFIX:+$PREFIX/bin}}
|
||||
[[ $BINDIR ]] && mkdir -p "$BINDIR"
|
||||
if [[ ! -w $BINDIR ]]; then
|
||||
for dir in /usr/local/bin ~/.bin ~/bin /usr/bin; do
|
||||
[[ -w $dir ]] && BINDIR=$dir && break
|
||||
done
|
||||
if [[ ! -w $BINDIR ]]; then
|
||||
echo >&2 "Could not find directory to install to."
|
||||
echo >&2 "You can specify a prefix to install to, eg. PREFIX=/usr/local ./install"
|
||||
echo >&2 "You can specify a bin directory to install to, eg. BINDIR=~/bin ./install"
|
||||
echo >&2 "Make sure you have write permission to the bin directory."
|
||||
fi
|
||||
fi
|
||||
install -v -s mpw "$BINDIR"
|
1
MasterPassword/C/lib/proplib/.source
Normal file
1
MasterPassword/C/lib/proplib/.source
Normal file
@ -0,0 +1 @@
|
||||
https://code.google.com/p/portableproplib/
|
38
MasterPassword/C/lib/scrypt-osx.patch
Normal file
38
MasterPassword/C/lib/scrypt-osx.patch
Normal file
@ -0,0 +1,38 @@
|
||||
diff -ruN /Users/lhunath/.src/scrypt/Makefile ./Makefile
|
||||
--- /Users/lhunath/.src/scrypt/Makefile 2014-05-02 11:28:58.000000000 -0400
|
||||
+++ ./Makefile 2014-05-02 12:07:27.000000000 -0400
|
||||
@@ -2,11 +2,11 @@
|
||||
VER?= nosse
|
||||
SRCS= main.c
|
||||
LDADD+= -lcrypto
|
||||
-WARNS?= 6
|
||||
+WARNS?= 0
|
||||
|
||||
# We have a config file for FreeBSD
|
||||
CFLAGS += -I .
|
||||
-CFLAGS += -DCONFIG_H_FILE=\"config_freebsd.h\"
|
||||
+CFLAGS += -DCONFIG_H_FILE=\"config_osx.h\"
|
||||
|
||||
# Include all possible object files containing built scrypt code.
|
||||
CLEANFILES += crypto_scrypt-ref.o
|
||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
||||
--- /Users/lhunath/.src/scrypt/lib/util/memlimit.c 2014-05-02 11:28:58.000000000 -0400
|
||||
+++ ./lib/util/memlimit.c 2014-05-02 11:52:42.000000000 -0400
|
||||
@@ -75,7 +75,7 @@
|
||||
* have returned to us.
|
||||
*/
|
||||
if (usermemlen == sizeof(uint64_t))
|
||||
- usermem = *(uint64_t *)usermembuf;
|
||||
+ usermem = *(uint64_t *)(void *)usermembuf;
|
||||
else if (usermemlen == sizeof(uint32_t))
|
||||
usermem = SIZE_MAX;
|
||||
else
|
||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
||||
--- /Users/lhunath/.src/scrypt/config_osx.h 1969-12-31 19:00:00.000000000 -0500
|
||||
+++ config_osx.h 2014-05-02 12:06:55.000000000 -0400
|
||||
@@ -0,0 +1,5 @@
|
||||
+/* A default configuration for FreeBSD, used if there is no config.h. */
|
||||
+
|
||||
+#define HAVE_POSIX_MEMALIGN 1
|
||||
+#define HAVE_SYSCTL_HW_USERMEM 1
|
||||
+#define HAVE_SYS_PARAM_H 1
|
1
MasterPassword/C/lib/scrypt/.source
Normal file
1
MasterPassword/C/lib/scrypt/.source
Normal file
@ -0,0 +1 @@
|
||||
https://code.google.com/p/scrypt/
|
220
MasterPassword/C/mpw.c
Normal file
220
MasterPassword/C/mpw.c
Normal file
@ -0,0 +1,220 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/fs.h>
|
||||
#elif defined(__CYGWIN__)
|
||||
#include <cygwin/fs.h>
|
||||
#else
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <pwd.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <alg/sha256.h>
|
||||
#include <crypto/crypto_scrypt.h>
|
||||
#include <prop/proplib.h>
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_dkLen 64
|
||||
#define MP_hash PearlHashSHA256
|
||||
|
||||
#define MP_env_username "MP_USERNAME"
|
||||
#define MP_env_sitetype "MP_SITETYPE"
|
||||
#define MP_env_sitecounter "MP_SITECOUNTER"
|
||||
|
||||
char *homedir(const char *filename) {
|
||||
char *homedir = NULL;
|
||||
#if defined(__CYGWIN__)
|
||||
homedir = getenv("USERPROFILE");
|
||||
if (!homedir) {
|
||||
const char *homeDrive = getenv("HOMEDRIVE");
|
||||
const char *homePath = getenv("HOMEPATH");
|
||||
homedir = char[strlen(homeDrive) + strlen(homePath) + 1];
|
||||
sprintf(homedir, "%s/%s", homeDrive, homePath);
|
||||
}
|
||||
#else
|
||||
struct passwd* passwd = getpwuid(getuid());
|
||||
if (passwd)
|
||||
homedir = passwd->pw_dir;
|
||||
if (!homedir)
|
||||
homedir = getenv("HOME");
|
||||
#endif
|
||||
if (!homedir)
|
||||
homedir = getcwd(NULL, 0);
|
||||
|
||||
char *homefile = NULL;
|
||||
asprintf(&homefile, "%s/%s", homedir, filename);
|
||||
return homefile;
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
|
||||
// Read the environment.
|
||||
const char *userName = getenv( MP_env_username );
|
||||
const char *masterPassword = NULL;
|
||||
const char *siteName = NULL;
|
||||
const char *siteTypeString = getenv( MP_env_sitetype );
|
||||
uint32_t siteCounter = 1;
|
||||
const char *siteCounterString = getenv( MP_env_sitecounter );
|
||||
|
||||
// Read the options.
|
||||
char opt;
|
||||
while ((opt = getopt(argc, argv, "u:t:c:")) != -1)
|
||||
switch (opt) {
|
||||
case 'u':
|
||||
userName = optarg;
|
||||
break;
|
||||
case 't':
|
||||
siteTypeString = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
siteCounterString = optarg;
|
||||
break;
|
||||
case '?':
|
||||
switch (optopt) {
|
||||
case 'u':
|
||||
fprintf (stderr, "Missing user name to option: -%c\n", optopt);
|
||||
break;
|
||||
case 't':
|
||||
fprintf (stderr, "Missing type name to option: -%c\n", optopt);
|
||||
break;
|
||||
case 'c':
|
||||
fprintf (stderr, "Missing counter value to option: -%c\n", optopt);
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr, "Unknown option: -%c\n", optopt);
|
||||
}
|
||||
return 1;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (optind < argc)
|
||||
siteName = argv[optind];
|
||||
|
||||
// Convert and validate input.
|
||||
if (!userName) {
|
||||
fprintf (stderr, "Missing user name.\n");
|
||||
return 1;
|
||||
}
|
||||
if (!siteName) {
|
||||
fprintf (stderr, "Missing site name.\n");
|
||||
return 1;
|
||||
}
|
||||
if (siteCounterString)
|
||||
siteCounter = atoi( siteCounterString );
|
||||
if (siteCounter < 1) {
|
||||
fprintf (stderr, "Invalid site counter: %d\n", siteCounter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read the master password.
|
||||
char *mpwConfigPath = homedir(".mpw");
|
||||
if (!mpwConfigPath) {
|
||||
fprintf (stderr, "Couldn't resolve path for configuration file: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
FILE *mpwConfig = fopen(mpwConfigPath, "r");
|
||||
if (!mpwConfig) {
|
||||
fprintf (stderr, "Couldn't open configuration file: %s: %d\n", mpwConfigPath, errno);
|
||||
return 1;
|
||||
}
|
||||
free(mpwConfigPath);
|
||||
char *line = NULL;
|
||||
size_t linecap = 0;
|
||||
ssize_t linelen;
|
||||
while ((linelen = getline(&line, &linecap, mpwConfig)) > 0) {
|
||||
char *configUserName = strsep(&line, "\t: ");
|
||||
if (configUserName == userName) {
|
||||
while (line[0] && strlen(masterPassword = strsep(&line, "\t: ")) == 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!masterPassword) {
|
||||
fprintf (stderr, "Missing master password for user: %s\n", userName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Calculate the master key.
|
||||
uint8_t *masterKey = malloc( MP_dkLen );
|
||||
if (!masterKey) {
|
||||
fprintf (stderr, "Could not allocate master key: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
const uint32_t n_userNameLength = htonl(strlen(userName));
|
||||
char *masterKeySalt = NULL;
|
||||
size_t masterKeySaltLength = asprintf(&masterKeySalt, "com.lyndir.masterpassword%s%s", (const char *) &n_userNameLength, userName);
|
||||
if (!masterKeySalt) {
|
||||
fprintf (stderr, "Could not allocate master key salt: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
if (crypto_scrypt( (const uint8_t *)masterPassword, strlen(masterPassword), (const uint8_t *)masterKeySalt, masterKeySaltLength, MP_N, MP_r, MP_p, masterKey, MP_dkLen ) < 0) {
|
||||
fprintf (stderr, "Could not generate master key: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
memset(masterKeySalt, 0, masterKeySaltLength);
|
||||
free(masterKeySalt);
|
||||
|
||||
// Calculate the site seed.
|
||||
const uint32_t n_siteCounter = htonl(siteCounter), n_siteNameLength = htonl(strlen(siteName));
|
||||
char *sitePasswordInfo = NULL;
|
||||
size_t sitePasswordInfoLength = asprintf(&sitePasswordInfo, "com.lyndir.masterpassword%s%s%s", (const char *) &n_siteNameLength, siteName, (const char *) &n_siteCounter);
|
||||
if (!sitePasswordInfo) {
|
||||
fprintf (stderr, "Could not allocate site seed: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
uint8_t sitePasswordSeed[32];
|
||||
HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed);
|
||||
memset(masterKey, 0, MP_dkLen);
|
||||
memset(sitePasswordInfo, 0, sitePasswordInfoLength);
|
||||
free(masterKey);
|
||||
free(sitePasswordInfo);
|
||||
|
||||
// Determine the cipher.
|
||||
prop_dictionary_t MPTypes_ciphers = prop_dictionary_internalize_from_file("ciphers.plist");
|
||||
if (!MPTypes_ciphers) {
|
||||
fprintf (stderr, "Could not read cipher definitions: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
prop_array_t typeCiphers = prop_dictionary_get(prop_dictionary_get(MPTypes_ciphers, "[self classNameOfType:type]"), "[self nameOfType:type]");
|
||||
if (!typeCiphers) {
|
||||
fprintf (stderr, "Could not find cipher definition for type: %s\n", siteTypeString);
|
||||
return 1;
|
||||
}
|
||||
prop_string_t cipher = prop_array_get(typeCiphers, sitePasswordSeed[0] % prop_array_count(typeCiphers));
|
||||
if (!typeCiphers) {
|
||||
fprintf (stderr, "Missing cipher definitions for type: %s\n", siteTypeString);
|
||||
return 1;
|
||||
}
|
||||
//trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher);
|
||||
|
||||
// Encode the password from the seed using the cipher.
|
||||
//NSAssert([seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher.");
|
||||
const prop_dictionary_t characterClasses = prop_dictionary_get(MPTypes_ciphers, "MPCharacterClasses");
|
||||
char *sitePassword = calloc(prop_string_size(cipher) + 1, sizeof(char));
|
||||
char cipherClass[2] = {0, 0};
|
||||
for (int c = 0; c < prop_string_size(cipher); ++c) {
|
||||
|
||||
const uint16_t keyByte = sitePasswordSeed[c + 1];
|
||||
cipherClass[0] = prop_string_cstring_nocopy(cipher)[c];
|
||||
const prop_string_t cipherClassCharacters = prop_dictionary_get(characterClasses, cipherClass);
|
||||
const char character = prop_string_cstring_nocopy(cipherClassCharacters)[ keyByte % prop_string_size(cipherClassCharacters) ];
|
||||
|
||||
//trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character);
|
||||
sitePassword[c] = character;
|
||||
}
|
||||
memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed));
|
||||
|
||||
// Output the password.
|
||||
fprintf( stdout, "%s\n", sitePassword );
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user