2
0

Add C implementation of Master Password.

This commit is contained in:
Maarten Billemont 2014-05-05 11:16:59 -04:00
parent 989cafda71
commit d43c19a749
7 changed files with 282 additions and 0 deletions

5
.gitignore vendored
View File

@ -27,3 +27,8 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
# Java # Java
MasterPassword/Java/**/target MasterPassword/Java/**/target
# C
MasterPassword/C/mpw
MasterPassword/C/lib/*/*
!MasterPassword/C/lib/*/.source

2
MasterPassword/C/build Executable file
View 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
View 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"

View File

@ -0,0 +1 @@
https://code.google.com/p/portableproplib/

View 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

View File

@ -0,0 +1 @@
https://code.google.com/p/scrypt/

220
MasterPassword/C/mpw.c Normal file
View 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;
}