2
0

Finished the C implementation & CLI tool. Providing an OS X binary.

This commit is contained in:
Maarten Billemont 2014-06-07 15:51:11 -04:00
parent cf9dabcc82
commit d732b03828
11 changed files with 2112 additions and 131 deletions

1
.gitignore vendored
View File

@ -29,6 +29,7 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
MasterPassword/Java/**/target MasterPassword/Java/**/target
# C # C
MasterPassword/C/*.o
MasterPassword/C/mpw MasterPassword/C/mpw
MasterPassword/C/lib/*/* MasterPassword/C/lib/*/*
!MasterPassword/C/lib/*/.source !MasterPassword/C/lib/*/.source

2
External/LoveLyndir vendored

@ -1 +1 @@
Subproject commit ceed9e20009f2cf3679445e2c60b0f206aaef383 Subproject commit 77e8fa376e3b28224ca26e08146242b71269567c

1920
MasterPassword/C/bashlib Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,9 @@
#!/usr/bin/env bash -e #!/usr/bin/env bash -e
# Run with -DDEBUG to enable trace-level output. # Run with -DDEBUG to enable trace-level output.
gcc -c types.c -o types.o "$@" [[ -e lib/scrypt/scryptenc.o ]] || { echo >&2 "Missing scrypt. First get and build the scrypt source in lib/scrypt from <$(<lib/scrypt/.source)>.\n"; exit 1; }
gcc -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" -l "crypto_aesctr.o" -l "sha256.o" -l "crypto_scrypt-nosse.o" -l "memlimit.o" -l "scryptenc_cpuperf.o" -l"scryptenc.o" -l"types.o" -l"crypto" -L"." -L"lib/scrypt" mpw.c -o mpw "$@"
deps=( -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" -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"." -L"lib/scrypt" )
gcc "${deps[@]}" -Qunused-arguments -c types.c -o types.o "$@"
gcc "${deps[@]}" -Qunused-arguments -l"types.o" mpw.c -o mpw "$@"

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MPElementGeneratedEntity</key>
<dict>
<key>Maximum Security Password</key>
<array>
<string>anoxxxxxxxxxxxxxxxxx</string>
<string>axxxxxxxxxxxxxxxxxno</string>
</array>
<key>Long Password</key>
<array>
<string>CvcvnoCvcvCvcv</string>
<string>CvcvCvcvnoCvcv</string>
<string>CvcvCvcvCvcvno</string>
<string>CvccnoCvcvCvcv</string>
<string>CvccCvcvnoCvcv</string>
<string>CvccCvcvCvcvno</string>
<string>CvcvnoCvccCvcv</string>
<string>CvcvCvccnoCvcv</string>
<string>CvcvCvccCvcvno</string>
<string>CvcvnoCvcvCvcc</string>
<string>CvcvCvcvnoCvcc</string>
<string>CvcvCvcvCvccno</string>
<string>CvccnoCvccCvcv</string>
<string>CvccCvccnoCvcv</string>
<string>CvccCvccCvcvno</string>
<string>CvcvnoCvccCvcc</string>
<string>CvcvCvccnoCvcc</string>
<string>CvcvCvccCvccno</string>
<string>CvccnoCvcvCvcc</string>
<string>CvccCvcvnoCvcc</string>
<string>CvccCvcvCvccno</string>
</array>
<key>Medium Password</key>
<array>
<string>CvcnoCvc</string>
<string>CvcCvcno</string>
</array>
<key>Basic Password</key>
<array>
<string>aaanaaan</string>
<string>aannaaan</string>
<string>aaannaaa</string>
</array>
<key>Short Password</key>
<array>
<string>Cvcn</string>
</array>
<key>PIN</key>
<array>
<string>nnnn</string>
</array>
</dict>
<key>MPCharacterClasses</key>
<dict>
<key>V</key>
<string>AEIOU</string>
<key>C</key>
<string>BCDFGHJKLMNPQRSTVWXYZ</string>
<key>v</key>
<string>aeiou</string>
<key>c</key>
<string>bcdfghjklmnpqrstvwxyz</string>
<key>A</key>
<string>AEIOUBCDFGHJKLMNPQRSTVWXYZ</string>
<key>a</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</string>
<key>n</key>
<string>0123456789</string>
<key>o</key>
<string>@&amp;%?,=[]_:-+*$#!'^~;()/.</string>
<key>x</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string>
</dict>
</dict>
</plist>

View File

@ -1,15 +1,53 @@
#!/usr/bin/env bash #!/usr/bin/env bash
BINDIR=${BINDIR:+${PREFIX:+$PREFIX/bin}} #
[[ $BINDIR ]] && mkdir -p "$BINDIR" # Install the Master Password CLI tool.
if [[ ! -w $BINDIR ]]; then set -e
for dir in /usr/local/bin ~/.bin ~/bin /usr/bin; do cd "${BASH_SOURCE%/*}"
[[ -w $dir ]] && BINDIR=$dir && break source bashlib
done
if [[ ! -w $BINDIR ]]; then inf "This will install the mpw tool."
echo >&2 "Could not find directory to install to."
echo >&2 "You can specify a prefix to install to, eg. PREFIX=/usr/local ./install" # Try to guess then ask for the bin dir to install to.
echo >&2 "You can specify a bin directory to install to, eg. BINDIR=~/bin ./install" IFS=: read -a paths <<< "$PATH"
echo >&2 "Make sure you have write permission to the bin directory." if inArray ~/bin "${paths[@]}"; then
fi bindir=~/bin
elif inArray ~/.bin "${paths[@]}"; then
bindir=~/.bin
elif inArray /usr/local/bin "${paths[@]}"; then
bindir=/usr/local/bin
else
bindir=~/bin
fi fi
install -v -s mpw "$BINDIR" bindir=$(ask -d "$bindir" "What bin directory should I install to?")
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
# Install Master Password.
install mpw "$bindir"
[[ ! -e "$bindir/bashlib" ]] && install bashlib "$bindir" ||:
# Convenience bash function.
inf "Installation successful!"
echo
inf "To improve usability, you can install an mpw function in your bash shell."
inf "This function adds the following features:"
inf " - Automatically remember your user name in the shell if not set."
inf " - Automatically put the password in the clipboard (some platforms)."
echo
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<mpw.bashrc)"
echo
inf "We can do this for you automatically now."
if ask -c Y!n "Append the mpw function to your .bashrc?"; then
cat mpw.bashrc >> ~/.bashrc
inf "Done! Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
fi
echo
inf "You can also save your user name in ~/.bashrc. Leave blank to skip this step."
if MP_USERNAME=$(ask "Your full name:") && [[ $MP_USERNAME ]] ; then
printf 'export MP_USERNAME=%q\n' "$MP_USERNAME" >> ~/.bashrc
fi
echo
inf "To begin using Master Password, type: mpw [site name]"

View File

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

View File

@ -0,0 +1,24 @@
## Added by Master Password
source bashlib
mpw() {
_copy() {
if hash pbcopy 2>/dev/null; then
pbcopy
elif hash xclip 2>/dev/null; then
xclip
else
cat
return
fi
echo >&2 "Copied!"
}
# Empty the clipboard
:| _copy 2>/dev/null
# Ask for the user's name and password if not yet known.
MP_USERNAME=${MP_USERNAME:-$(ask -s 'Your Full Name:')}
# Start Master Password and copy the output.
printf %s "$(MP_USERNAME=$MP_USERNAME command mpw "$@")" | _copy
}

View File

@ -32,6 +32,23 @@
#define MP_env_sitetype "MP_SITETYPE" #define MP_env_sitetype "MP_SITETYPE"
#define MP_env_sitecounter "MP_SITECOUNTER" #define MP_env_sitecounter "MP_SITECOUNTER"
void usage() {
fprintf(stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n");
fprintf(stderr, " -u name Specify the full name of the user.\n"
" Defaults to %s in env.\n\n", MP_env_username);
fprintf(stderr, " -t type Specify the password's template.\n"
" Defaults to %s in env or 'long'.\n"
" x, max, maximum | 20 characters, contains symbols.\n"
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
" b, basic | 8 characters, no symbols.\n"
" s, short | Copy-friendly, 4 characters, no symbols.\n"
" p, pin | 4 numbers.\n\n", MP_env_sitetype);
fprintf(stderr, " -c counter The value of the counter.\n"
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
exit(0);
}
char *homedir(const char *filename) { char *homedir(const char *filename) {
char *homedir = NULL; char *homedir = NULL;
#if defined(__CYGWIN__) #if defined(__CYGWIN__)
@ -59,6 +76,9 @@ char *homedir(const char *filename) {
int main(int argc, char *const argv[]) { int main(int argc, char *const argv[]) {
if (argc < 2)
usage();
// Read the environment. // Read the environment.
const char *userName = getenv( MP_env_username ); const char *userName = getenv( MP_env_username );
const char *masterPassword = NULL; const char *masterPassword = NULL;
@ -70,8 +90,11 @@ int main(int argc, char *const argv[]) {
// Read the options. // Read the options.
char opt; char opt;
while ((opt = getopt(argc, argv, "u:t:c:")) != -1) while ((opt = getopt(argc, argv, "u:t:c:h")) != -1)
switch (opt) { switch (opt) {
case 'h':
usage();
break;
case 'u': case 'u':
userName = optarg; userName = optarg;
break; break;
@ -142,7 +165,7 @@ int main(int argc, char *const argv[]) {
ssize_t linelen; ssize_t linelen;
while ((linelen = getline(&line, &linecap, mpwConfig)) > 0) while ((linelen = getline(&line, &linecap, mpwConfig)) > 0)
if (strcmp(strsep(&line, ":"), userName) == 0) { if (strcmp(strsep(&line, ":"), userName) == 0) {
masterPassword = line; masterPassword = strsep(&line, "\n");
break; break;
} }
if (!masterPassword) { if (!masterPassword) {
@ -151,47 +174,75 @@ int main(int argc, char *const argv[]) {
} }
trc("masterPassword: %s\n", masterPassword); trc("masterPassword: %s\n", masterPassword);
// Calculate the master key salt.
char *mpNameSpace = "com.lyndir.masterpassword";
const uint32_t n_userNameLength = htonl(strlen(userName));
size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_userNameLength) + strlen(userName);
char *masterKeySalt = malloc( masterKeySaltLength );
if (!masterKeySalt) {
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
return 1;
}
char *mKS = masterKeySalt;
memcpy(mKS, mpNameSpace, strlen(mpNameSpace)); mKS += strlen(mpNameSpace);
memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength);
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
if (mKS - masterKeySalt != masterKeySaltLength)
abort();
trc("masterKeySalt ID: %s\n", IDForBuf(masterKeySalt, masterKeySaltLength));
// Calculate the master key. // Calculate the master key.
uint8_t *masterKey = malloc( MP_dkLen ); uint8_t *masterKey = malloc( MP_dkLen );
if (!masterKey) { if (!masterKey) {
fprintf(stderr, "Could not allocate master key: %d\n", errno); fprintf(stderr, "Could not allocate master key: %d\n", errno);
return 1; 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) { 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); fprintf(stderr, "Could not generate master key: %d\n", errno);
return 1; return 1;
} }
memset(masterKeySalt, 0, masterKeySaltLength); memset(masterKeySalt, 0, masterKeySaltLength);
free(masterKeySalt); free(masterKeySalt);
trc("masterPassword Hex: %s\n", Hex(masterPassword, strlen(masterPassword)));
trc("masterPassword ID: %s\n", IDForBuf(masterPassword, strlen(masterPassword)));
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
// Calculate the site seed. // Calculate the site seed.
const uint32_t n_siteCounter = htonl(siteCounter), n_siteNameLength = htonl(strlen(siteName)); const uint32_t n_siteNameLength = htonl(strlen(siteName));
char *sitePasswordInfo = NULL; const uint32_t n_siteCounter = htonl(siteCounter);
size_t sitePasswordInfoLength = asprintf(&sitePasswordInfo, "com.lyndir.masterpassword%s%s%s", (const char *) &n_siteNameLength, siteName, (const char *) &n_siteCounter); size_t sitePasswordInfoLength = strlen(mpNameSpace) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
char *sitePasswordInfo = malloc( sitePasswordInfoLength );
if (!sitePasswordInfo) { if (!sitePasswordInfo) {
fprintf(stderr, "Could not allocate site seed: %d\n", errno); fprintf(stderr, "Could not allocate site seed: %d\n", errno);
return 1; return 1;
} }
char *sPI = sitePasswordInfo;
memcpy(sPI, mpNameSpace, strlen(mpNameSpace)); sPI += strlen(mpNameSpace);
memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
abort();
trc("seed from: hmac-sha256(masterKey, 'com.lyndir.masterpassword' | %s | %s | %s)\n", Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)));
trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength));
uint8_t sitePasswordSeed[32]; uint8_t sitePasswordSeed[32];
HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed); HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed);
memset(masterKey, 0, MP_dkLen); memset(masterKey, 0, MP_dkLen);
memset(sitePasswordInfo, 0, sitePasswordInfoLength); memset(sitePasswordInfo, 0, sitePasswordInfoLength);
free(masterKey); free(masterKey);
free(sitePasswordInfo); free(sitePasswordInfo);
trc("sitePasswordSeed ID: %s\n", IDForBuf(sitePasswordSeed, 32));
// Determine the cipher. // Determine the cipher.
const char *cipher = CipherForType(siteType, sitePasswordSeed[0]); const char *cipher = CipherForType(siteType, sitePasswordSeed[0]);
trc("type %s, cipher: %s\n", siteTypeString, cipher); trc("type %s, cipher: %s\n", siteTypeString, cipher);
if (strlen(cipher) > 32)
abort();
// Encode the password from the seed using the cipher. // Encode the password from the seed using the cipher.
//NSAssert([seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher.");
char *sitePassword = calloc(strlen(cipher) + 1, sizeof(char)); char *sitePassword = calloc(strlen(cipher) + 1, sizeof(char));
for (int c = 0; c < strlen(cipher); ++c) { for (int c = 0; c < strlen(cipher); ++c) {
sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]); sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]);

View File

@ -10,6 +10,9 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <alg/sha256.h>
#include "types.h" #include "types.h"
const MPElementType TypeWithName(const char *typeName) { const MPElementType TypeWithName(const char *typeName) {
@ -118,4 +121,20 @@ const char CharacterFromClass(char characterClass, uint8_t seedByte) {
return classCharacters[seedByte % strlen(classCharacters)]; return classCharacters[seedByte % strlen(classCharacters)];
} }
const char *IDForBuf(const void *buf, size_t length) {
uint8_t hash[32];
SHA256_Buf(buf, length, hash);
char *id = calloc(65, sizeof(char));
for (int kH = 0; kH < 32; kH++)
sprintf(&(id[kH * 2]), "%02X", hash[kH]);
return id;
}
const char *Hex(const void *buf, size_t length) {
char *id = calloc(length*2+1, sizeof(char));
for (int kH = 0; kH < length; kH++)
sprintf(&(id[kH * 2]), "%02X", ((const uint8_t*)buf)[kH]);
return id;
}

View File

@ -47,3 +47,6 @@ typedef enum {
const MPElementType TypeWithName(const char *typeName); const MPElementType TypeWithName(const char *typeName);
const char *CipherForType(MPElementType type, uint8_t seedByte); const char *CipherForType(MPElementType type, uint8_t seedByte);
const char CharacterFromClass(char characterClass, uint8_t seedByte); const char CharacterFromClass(char characterClass, uint8_t seedByte);
const char *IDForBuf(const void *buf, size_t length);
const char *Hex(const void *buf, size_t length);