2
0

More restructuring and rewriting of the C code.

This commit is contained in:
Maarten Billemont 2014-12-20 14:30:34 -05:00
parent 49da0b47c7
commit 0ccd545dd4
12 changed files with 392 additions and 359 deletions

View File

@ -2,6 +2,7 @@
<profile version="1.0" is_locked="false"> <profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<option name="myLocal" value="false" /> <option name="myLocal" value="false" />
<inspection_tool class="Convert to string" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
@ -9,6 +10,7 @@
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Replace with subshell" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" /> <inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />

View File

@ -216,9 +216,11 @@ mpw() {
# optional features # optional features
(( mpw_color )) && CFLAGS+=( -DCOLOR ) LDFLAGS+=( -l"curses" ) (( mpw_color )) && CFLAGS+=( -DCOLOR ) LDFLAGS+=( -l"curses" )
cc "${CFLAGS[@]}" -c types.c -o types.o "$@" cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
cc "${CFLAGS[@]}" -c mpw.c -o mpw.o "$@" cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "types.o" "mpw.o" mpw-cli.c -o mpw "$@" cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
mpw-cli.c -o mpw
echo "done! Now run ./install or use ./mpw" echo "done! Now run ./install or use ./mpw"
} }
@ -254,8 +256,11 @@ mpw-bench() {
-l"crypto" -l"crypto"
) )
cc "${CFLAGS[@]}" -c types.c -o types.o "$@" cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "types.o" mpw-bench.c -o mpw-bench "$@" cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
mpw-bench.c -o mpw-bench
echo "done! Now use ./mpw-bench" echo "done! Now use ./mpw-bench"
} }

View File

@ -1,14 +1,18 @@
#define _GNU_SOURCE //
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h> #include <stdio.h>
#include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <scrypt/sha256.h> #include "mpw-types.h"
#include <scrypt/crypto_scrypt.h> #include "mpw-util.h"
#include "types.h"
#define MP_N 32768 #define MP_N 32768
#define MP_r 8 #define MP_r 8
@ -16,33 +20,9 @@
#define MP_dkLen 64 #define MP_dkLen 64
#define MP_hash PearlHashSHA256 #define MP_hash PearlHashSHA256
static void mpw_pushBuf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) {
*bufferSize += pushSize;
*buffer = realloc( *buffer, *bufferSize );
uint8_t *pushDst = *buffer + *bufferSize - pushSize;
memcpy( pushDst, pushBuffer, pushSize );
}
static void mpw_pushString(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
mpw_pushBuf( buffer, bufferSize, pushString, strlen( pushString ) );
}
static void mpw_pushInt(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
mpw_pushBuf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
}
static void mpw_free(void *const buffer, const size_t bufferSize) {
memset( buffer, 0, bufferSize );
free( buffer );
}
const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword) { const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = ScopeForVariant( MPSiteVariantPassword ); const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
trc( "fullName: %s\n", fullName ); trc( "fullName: %s\n", fullName );
trc( "masterPassword: %s\n", masterPassword ); trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope ); trc( "key scope: %s\n", mpKeyScope );
@ -56,18 +36,15 @@ const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPass
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName ); mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) if (!masterKeySalt)
ftl( "Could not allocate master key salt: %d\n", errno ); ftl( "Could not allocate master key salt: %d\n", errno );
trc( "masterKeySalt ID: %s\n", IDForBuf( masterKeySalt, masterKeySaltSize ) ); trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key. // Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt ) // masterKey = scrypt( masterPassword, masterKeySalt )
uint8_t *masterKey = (uint8_t *)malloc( MP_dkLen ); const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) if (!masterKey)
ftl( "Could not allocate master key: %d\n", errno ); ftl( "Could not allocate master key: %d\n", errno );
if (crypto_scrypt( (const uint8_t *)masterPassword, strlen( masterPassword ), trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p, masterKey, MP_dkLen ) < 0)
ftl( "Could not generate master key: %d\n", errno );
mpw_free( masterKeySalt, masterKeySaltSize );
trc( "masterKey ID: %s\n", IDForBuf( masterKey, MP_dkLen ) );
return masterKey; return masterKey;
} }
@ -75,7 +52,7 @@ const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPass
const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) { const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = ScopeForVariant( siteVariant ); const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "siteName: %s\n", siteName ); trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter ); trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant ); trc( "siteVariant: %d\n", siteVariant );
@ -95,28 +72,28 @@ const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName,
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext ); mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
} }
if (!sitePasswordInfo) if (!sitePasswordInfo)
ftl( "Could not allocate site seed: %d\n", errno ); ftl( "Could not allocate site seed info: %d\n", errno );
trc( "sitePasswordInfo ID: %s\n", IDForBuf( sitePasswordInfo, sitePasswordInfoSize ) ); trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
uint8_t sitePasswordSeed[32]; const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
HMAC_SHA256_Buf( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize, sitePasswordSeed ); if (!sitePasswordSeed)
mpw_free( sitePasswordInfo, sitePasswordInfoSize ); ftl( "Could not allocate site seed: %d\n", errno );
trc( "sitePasswordSeed ID: %s\n", IDForBuf( sitePasswordSeed, 32 ) ); trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template. // Determine the template.
const char *template = TemplateForType( siteType, sitePasswordSeed[0] ); const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template ); trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) if (strlen( template ) > 32)
ftl( "Template too long for password seed: %d", strlen( template ) ); ftl( "Template too long for password seed: %lu", strlen( template ) );
// Encode the password from the seed using the template. // Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (int c = 0; c < strlen( template ); ++c) { for (int c = 0; c < strlen( template ); ++c) {
sitePassword[c] = CharacterFromClass( template[c], sitePasswordSeed[c + 1] ); sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1], trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] ); sitePassword[c] );
} }
memset( sitePasswordSeed, 0, sizeof( sitePasswordSeed ) ); mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword; return sitePassword;
} }

View File

@ -0,0 +1,18 @@
//
// mpw-algorithm.h
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
/** Derive the master key for a user based on their name and master password.
* @return A new 64-byte allocated buffer. */
const uint8_t *mpw_masterKeyForUser(
const char *fullName, const char *masterPassword);
/** Encode a password for the site from the given master key and site parameters.
* @return A newly allocated string. */
const char *mpw_passwordForSite(
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext);

View File

@ -1,20 +1,22 @@
#include <sys/time.h> //
// mpw-bench.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h> #include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <pwd.h>
#include <netinet/in.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/time.h>
#include <scrypt/sha256.h> #include <scrypt/sha256.h>
#include <scrypt/crypto_scrypt.h>
#include <bcrypt/ow-crypt.h> #include <bcrypt/ow-crypt.h>
#include "types.h"
#include "mpw-types.h"
#include "mpw-algorithm.h"
#define MP_N 32768 #define MP_N 32768
#define MP_r 8 #define MP_r 8
@ -22,166 +24,82 @@
#define MP_dkLen 64 #define MP_dkLen 64
#define MP_hash PearlHashSHA256 #define MP_hash PearlHashSHA256
static void mpw_getTime(struct timeval *time) {
if (gettimeofday( time, NULL ) != 0)
ftl( "Could not get time: %d\n", errno );
}
static const double mpw_showSpeed(struct timeval startTime, const unsigned int iterations, const char *operation) {
struct timeval endTime;
mpw_getTime( &endTime );
const time_t dsec = (endTime.tv_sec - startTime.tv_sec);
const suseconds_t dusec = (endTime.tv_usec - startTime.tv_usec);
const double elapsed = dsec + dusec / 1000000.;
const double speed = iterations / elapsed;
fprintf( stderr, " done. " );
fprintf( stdout, "%d %s iterations in %llds %lldµs -> %.2f/s\n", iterations, operation, (long long)dsec, (long long)dusec, speed );
return speed;
}
int main(int argc, char *const argv[]) { int main(int argc, char *const argv[]) {
char *fullName = "Robert Lee Mitchel"; const char *fullName = "Robert Lee Mitchel";
char *masterPassword = "banana colored duckling"; const char *masterPassword = "banana colored duckling";
char *siteName = "masterpasswordapp.com"; const char *siteName = "masterpasswordapp.com";
uint32_t siteCounter = 1; const uint32_t siteCounter = 1;
MPSiteType siteType = MPSiteTypeGeneratedLong; const MPSiteType siteType = MPSiteTypeGeneratedLong;
const MPSiteVariant siteVariant = MPSiteVariantPassword;
// Start MP const char *siteContext = NULL;
struct timeval startTime; struct timeval startTime;
if (gettimeofday(&startTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
int iterations = 100; // Start MPW
unsigned int iterations = 100;
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) { for (int i = 0; i < iterations; ++i) {
// Calculate the master key salt. const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword );
char *mpNameSpace = "com.lyndir.masterpassword"; if (!masterKey)
const uint32_t n_fullNameLength = htonl(strlen(fullName)); ftl( "Could not allocate master key: %d\n", errno );
const size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_fullNameLength) + strlen(fullName); free( (void *)mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ) );
char *masterKeySalt = malloc( masterKeySaltLength ); free( (void *)masterKey );
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_fullNameLength, sizeof(n_fullNameLength)); mKS += sizeof(n_fullNameLength);
memcpy(mKS, fullName, strlen(fullName)); mKS += strlen(fullName);
if (mKS - masterKeySalt != masterKeySaltLength)
abort();
trc("masterKeySalt ID: %s\n", IDForBuf(masterKeySalt, masterKeySaltLength));
// Calculate the master key.
uint8_t *masterKey = malloc( MP_dkLen );
if (!masterKey) {
fprintf(stderr, "Could not allocate master key: %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_siteNameLength = htonl(strlen(siteName));
const uint32_t n_siteCounter = htonl(siteCounter);
const size_t sitePasswordInfoLength = strlen(mpNameSpace) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
char *sitePasswordInfo = malloc( sitePasswordInfoLength );
if (!sitePasswordInfo) {
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
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();
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 template.
const char *template = TemplateForType(siteType, sitePasswordSeed[0]);
trc("type %d, template: %s\n", siteType, template);
if (strlen(template) > 32)
abort();
// Encode the password from the seed using the template.
char *sitePassword = calloc(strlen(template) + 1, sizeof(char));
for (int c = 0; c < strlen(template); ++c) {
sitePassword[c] = CharacterFromClass(template[c], sitePasswordSeed[c + 1]);
trc("class %c, character: %c\n", template[c], sitePassword[c]);
}
memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed));
if (i % 1 == 0) if (i % 1 == 0)
fprintf( stderr, "\rmpw: iteration %d / %d..", i, iterations ); fprintf( stderr, "\rmpw: iteration %d / %d..", i, iterations );
} }
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
// Output timing results.
struct timeval endTime;
if (gettimeofday(&endTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
long long secs = (endTime.tv_sec - startTime.tv_sec);
long long usecs = (endTime.tv_usec - startTime.tv_usec);
double elapsed = secs + usecs / 1000000.0;
double mpwSpeed = iterations / elapsed;
fprintf( stdout, " done. %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, mpwSpeed );
// Start SHA-256 // Start SHA-256
if (gettimeofday(&startTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
iterations = 50000000; iterations = 50000000;
uint8_t hash[32]; uint8_t hash[32];
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) { for (int i = 0; i < iterations; ++i) {
SHA256_Buf(masterPassword, strlen(masterPassword), hash); SHA256_Buf( masterPassword, strlen( masterPassword ), hash );
if (i % 1000 == 0) if (i % 1000 == 0)
fprintf( stderr, "\rsha256: iteration %d / %d..", i, iterations ); fprintf( stderr, "\rsha256: iteration %d / %d..", i, iterations );
} }
const double sha256Speed = mpw_showSpeed( startTime, iterations, "sha256" );
// Output timing results.
if (gettimeofday(&endTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
secs = (endTime.tv_sec - startTime.tv_sec);
usecs = (endTime.tv_usec - startTime.tv_usec);
elapsed = secs + usecs / 1000000.0;
double sha256Speed = iterations / elapsed;
fprintf( stdout, " done. %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, sha256Speed );
// Start BCrypt // Start BCrypt
if (gettimeofday(&startTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
int bcrypt_cost = 9; int bcrypt_cost = 9;
iterations = 600; iterations = 600;
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) { for (int i = 0; i < iterations; ++i) {
crypt(masterPassword, crypt_gensalt("$2b$", bcrypt_cost, fullName, strlen(fullName))); crypt( masterPassword, crypt_gensalt( "$2b$", bcrypt_cost, fullName, strlen( fullName ) ) );
if (i % 10 == 0) if (i % 10 == 0)
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d..", bcrypt_cost, i, iterations ); fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d..", bcrypt_cost, i, iterations );
} }
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt9" );
// Output timing results.
if (gettimeofday(&endTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
secs = (endTime.tv_sec - startTime.tv_sec);
usecs = (endTime.tv_usec - startTime.tv_usec);
elapsed = secs + usecs / 1000000.0;
double bcrypt9Speed = iterations / elapsed;
fprintf( stdout, " done. %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, bcrypt9Speed );
// Summarize. // Summarize.
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" ); fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
fprintf( stdout, "mpw is %f times slower than sha256\n", sha256Speed / mpwSpeed ); fprintf( stdout, " - mpw is %f times slower than sha256.\n", sha256Speed / mpwSpeed );
fprintf( stdout, "mpw is %f times slower than bcrypt (cost 9)\n", bcrypt9Speed / mpwSpeed ); fprintf( stdout, " - mpw is %f times slower than bcrypt (cost 9).\n", bcrypt9Speed / mpwSpeed );
return 0; return 0;
} }

View File

@ -1,22 +1,24 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <stdio.h> #include <stdio.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <pwd.h> #include <pwd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include "types.h"
#include "mpw.h"
#if defined(READLINE) #if defined(READLINE)
#include <readline/readline.h> #include <readline/readline.h>
#elif defined(EDITLINE) #elif defined(EDITLINE)
#include <histedit.h> #include <histedit.h>
#endif #endif
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
#include "mpw-types.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
#define MP_env_fullname "MP_FULLNAME" #define MP_env_fullname "MP_FULLNAME"
#define MP_env_sitetype "MP_SITETYPE" #define MP_env_sitetype "MP_SITETYPE"
#define MP_env_sitecounter "MP_SITECOUNTER" #define MP_env_sitecounter "MP_SITECOUNTER"
@ -125,18 +127,17 @@ int main(int argc, char *const argv[]) {
case '?': case '?':
switch (optopt) { switch (optopt) {
case 'u': case 'u':
fprintf( stderr, "Missing full name to option: -%c\n", optopt ); ftl( "Missing full name to option: -%c\n", optopt );
break; break;
case 't': case 't':
fprintf( stderr, "Missing type name to option: -%c\n", optopt ); ftl( "Missing type name to option: -%c\n", optopt );
break; break;
case 'c': case 'c':
fprintf( stderr, "Missing counter value to option: -%c\n", optopt ); ftl( "Missing counter value to option: -%c\n", optopt );
break; break;
default: default:
fprintf( stderr, "Unknown option: -%c\n", optopt ); ftl( "Unknown option: -%c\n", optopt );
} }
return 1;
default: default:
ftl("Unexpected option: %c", opt); ftl("Unexpected option: %c", opt);
} }
@ -144,39 +145,27 @@ int main(int argc, char *const argv[]) {
siteName = argv[optind]; siteName = argv[optind];
// Convert and validate input. // Convert and validate input.
if (!fullName) { if (!fullName && !(fullName = getlinep( "Your full name:" )))
if (!(fullName = getlinep( "Your full name:" ))) { ftl( "Missing full name.\n" );
fprintf( stderr, "Missing full name.\n" ); if (!siteName && !(siteName = getlinep( "Site name:" )))
return 1; ftl( "Missing site name.\n" );
}
}
if (!siteName) {
if (!(siteName = getlinep( "Site name:" ))) {
fprintf( stderr, "Missing site name.\n" );
return 1;
}
}
if (siteCounterString) if (siteCounterString)
siteCounter = atoi( siteCounterString ); siteCounter = atoi( siteCounterString );
if (siteCounter < 1) { if (siteCounter < 1)
fprintf( stderr, "Invalid site counter: %d\n", siteCounter ); ftl( "Invalid site counter: %d\n", siteCounter );
return 1;
}
if (siteVariantString) if (siteVariantString)
siteVariant = VariantWithName( siteVariantString ); siteVariant = mpw_variantWithName( siteVariantString );
if (siteVariant == MPSiteVariantLogin) if (siteVariant == MPSiteVariantLogin)
siteType = MPSiteTypeGeneratedName; siteType = MPSiteTypeGeneratedName;
if (siteVariant == MPSiteVariantAnswer) if (siteVariant == MPSiteVariantAnswer)
siteType = MPSiteTypeGeneratedPhrase; siteType = MPSiteTypeGeneratedPhrase;
if (siteTypeString) if (siteTypeString)
siteType = TypeWithName( siteTypeString ); siteType = mpw_typeWithName( siteTypeString );
// Read the master password. // Read the master password.
char *mpwConfigPath = homedir( ".mpw" ); char *mpwConfigPath = homedir( ".mpw" );
if (!mpwConfigPath) { if (!mpwConfigPath)
fprintf( stderr, "Couldn't resolve path for configuration file: %d\n", errno ); ftl( "Couldn't resolve path for configuration file: %d\n", errno );
return 1;
}
trc( "mpwConfigPath: %s\n", mpwConfigPath ); trc( "mpwConfigPath: %s\n", mpwConfigPath );
FILE *mpwConfig = fopen( mpwConfigPath, "r" ); FILE *mpwConfig = fopen( mpwConfigPath, "r" );
free( mpwConfigPath ); free( mpwConfigPath );
@ -190,16 +179,18 @@ int main(int argc, char *const argv[]) {
break; break;
} }
} }
free( line ); mpw_free( line, linecap );
} }
while (!masterPassword) while (!masterPassword || !strlen(masterPassword))
masterPassword = getpass( "Your master password: " ); masterPassword = getpass( "Your master password: " );
// Summarize operation. // Summarize operation.
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, Identicon( fullName, masterPassword ) ); fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, mpw_identicon( fullName, masterPassword ) );
// Output the password. // Output the password.
const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword ); const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword );
mpw_free( masterPassword, strlen( masterPassword ) );
const char *sitePassword = mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString ); const char *sitePassword = mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString );
fprintf( stdout, "%s\n", sitePassword ); fprintf( stdout, "%s\n", sitePassword );

View File

@ -1,29 +1,24 @@
// //
// MPTypes.h // mpw-types.c
// MasterPassword // MasterPassword
// //
// Created by Maarten Billemont on 02/01/12. // Created by Maarten Billemont on 2012-02-01.
// Copyright (c) 2012 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <scrypt/sha256.h>
#ifdef COLOR #ifdef COLOR
#include <curses.h> #include <curses.h>
#include <term.h> #include <term.h>
#endif #endif
#include "types.h" #include "mpw-types.h"
const MPSiteType TypeWithName(const char *typeName) { const MPSiteType mpw_typeWithName(const char *typeName) {
char lowerTypeName[strlen(typeName)]; char lowerTypeName[strlen(typeName)];
strcpy(lowerTypeName, typeName); strcpy(lowerTypeName, typeName);
for (char *tN = lowerTypeName; *tN; ++tN) for (char *tN = lowerTypeName; *tN; ++tN)
@ -50,7 +45,7 @@ const MPSiteType TypeWithName(const char *typeName) {
abort(); abort();
} }
const char *TemplateForType(MPSiteType type, uint8_t seedByte) { const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
if (!(type & MPSiteTypeClassGenerated)) { if (!(type & MPSiteTypeClassGenerated)) {
fprintf(stderr, "Not a generated type: %d", type); fprintf(stderr, "Not a generated type: %d", type);
abort(); abort();
@ -93,7 +88,7 @@ const char *TemplateForType(MPSiteType type, uint8_t seedByte) {
} }
} }
const MPSiteVariant VariantWithName(const char *variantName) { const MPSiteVariant mpw_variantWithName(const char *variantName) {
char lowerVariantName[strlen(variantName)]; char lowerVariantName[strlen(variantName)];
strcpy(lowerVariantName, variantName); strcpy(lowerVariantName, variantName);
for (char *vN = lowerVariantName; *vN; ++vN) for (char *vN = lowerVariantName; *vN; ++vN)
@ -110,7 +105,7 @@ const MPSiteVariant VariantWithName(const char *variantName) {
abort(); abort();
} }
const char *ScopeForVariant(MPSiteVariant variant) { const char *mpw_scopeForVariant(MPSiteVariant variant) {
switch (variant) { switch (variant) {
case MPSiteVariantPassword: { case MPSiteVariantPassword: {
return "com.lyndir.masterpassword"; return "com.lyndir.masterpassword";
@ -128,7 +123,7 @@ const char *ScopeForVariant(MPSiteVariant variant) {
} }
} }
const char CharacterFromClass(char characterClass, uint8_t seedByte) { const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
const char *classCharacters; const char *classCharacters;
switch (characterClass) { switch (characterClass) {
case 'V': { case 'V': {
@ -179,82 +174,3 @@ 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 = (char *)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 = (char *)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;
}
#ifdef COLOR
int putvari;
char *putvarc = NULL;
bool istermsetup = false;
static void initputvar() {
if (putvarc)
free(putvarc);
putvarc=(char *)calloc(256, sizeof(char));
putvari=0;
if (!istermsetup)
istermsetup = (OK == setupterm(NULL, STDERR_FILENO, NULL));
}
static int putvar(int c) {
putvarc[putvari++]=c;
return 0;
}
#endif
const char *Identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" };
uint8_t identiconSeed[32];
HMAC_SHA256_Buf(masterPassword, strlen(masterPassword), fullName, strlen(fullName), identiconSeed);
char *colorString, *resetString;
#ifdef COLOR
if (isatty( STDERR_FILENO )) {
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
initputvar();
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar);
colorString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(colorString, putvarc);
tputs(tgetstr("me", NULL), 1, putvar);
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(resetString, putvarc);
} 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);
free(colorString);
free(resetString);
return identicon;
}

View File

@ -1,11 +1,24 @@
// //
// MPTypes.h // mpw-types.h
// MasterPassword // MasterPassword
// //
// Created by Maarten Billemont on 02/01/12. // Created by Maarten Billemont on 2012-02-01.
// Copyright (c) 2012 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
//// Logging.
#ifdef DEBUG
#define trc(...) fprintf( stderr, __VA_ARGS__ )
#else
#define trc(...) do {} while (0)
#endif
#ifndef ftl
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); abort(); } while (0)
#endif
//// Types.
typedef enum { typedef enum {
/** Generate the password to log in with. */ /** Generate the password to log in with. */
MPSiteVariantPassword, MPSiteVariantPassword,
@ -43,19 +56,11 @@ typedef enum {
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate, MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
} MPSiteType; } MPSiteType;
#ifdef DEBUG //// Type utilities.
#define trc(...) fprintf(stderr, __VA_ARGS__)
#else
#define trc(...) do {} while (0)
#endif
#define ftl(...) do { fprintf( stderr, "Could not allocate master key salt: %d\n", errno ); abort(); } while (0)
const MPSiteVariant VariantWithName(const char *variantName); const MPSiteVariant mpw_variantWithName(const char *variantName);
const char *ScopeForVariant(MPSiteVariant variant); const char *mpw_scopeForVariant(MPSiteVariant variant);
const MPSiteType TypeWithName(const char *typeName); const MPSiteType mpw_typeWithName(const char *typeName);
const char *TemplateForType(MPSiteType type, uint8_t seedByte); const char *mpw_templateForType(MPSiteType type, uint8_t seedByte);
const char CharacterFromClass(char characterClass, uint8_t seedByte); const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
const char *IDForBuf(const void *buf, size_t length);
const char *Hex(const void *buf, size_t length);
const char *Identicon(const char *fullName, const char *masterPassword);

155
MasterPassword/C/mpw-util.c Normal file
View File

@ -0,0 +1,155 @@
//
// mpw-util.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <scrypt/sha256.h>
#include <scrypt/crypto_scrypt.h>
#include "mpw-util.h"
void mpw_pushBuf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) {
if (*bufferSize == (size_t)-1)
return;
*bufferSize += pushSize;
*buffer = realloc( *buffer, *bufferSize );
if (!*buffer) {
*bufferSize = -1;
return;
}
uint8_t *pushDst = *buffer + *bufferSize - pushSize;
memcpy( pushDst, pushBuffer, pushSize );
}
void mpw_pushString(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
mpw_pushBuf( buffer, bufferSize, pushString, strlen( pushString ) );
}
void mpw_pushInt(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
mpw_pushBuf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
}
void mpw_free(const void *buffer, const size_t bufferSize) {
memset( (void *)buffer, 0, bufferSize );
free( (void *)buffer );
}
uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p) {
uint8_t *key = malloc( keySize );
if (!key)
return NULL;
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
mpw_free( key, keySize );
return NULL;
}
return key;
}
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
uint8_t *const buffer = malloc(32);
if (!buffer)
return NULL;
HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer );
return buffer;
}
const char *mpw_idForBuf(const void *buf, size_t length) {
uint8_t hash[32];
SHA256_Buf( buf, length, hash );
return mpw_hex( hash, 32 );
}
static char *mpw_hex_buf = NULL;
const char *mpw_hex(const void *buf, size_t length) {
mpw_hex_buf = realloc( mpw_hex_buf, length * 2 + 1 );
for (int kH = 0; kH < length; kH++)
sprintf( &(mpw_hex_buf[kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
return mpw_hex_buf;
}
#ifdef COLOR
static int putvari;
static char *putvarc = NULL;
static bool istermsetup = false;
static void initputvar() {
if (putvarc)
free(putvarc);
putvarc=(char *)calloc(256, sizeof(char));
putvari=0;
if (!istermsetup)
istermsetup = (OK == setupterm(NULL, STDERR_FILENO, NULL));
}
static int putvar(int c) {
putvarc[putvari++]=c;
return 0;
}
#endif
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" };
uint8_t identiconSeed[32];
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
char *colorString, *resetString;
#ifdef COLOR
if (isatty( STDERR_FILENO )) {
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
initputvar();
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar);
colorString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(colorString, putvarc);
tputs(tgetstr("me", NULL), 1, putvar);
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(resetString, putvarc);
} 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 );
free( colorString );
free( resetString );
return identicon;
}

View File

@ -0,0 +1,46 @@
//
// mpw-util.h
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
//// Buffers and memory.
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
void mpw_pushBuf(
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
void mpw_pushString(
uint8_t **buffer, size_t *const bufferSize, const char *pushString);
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
void mpw_pushInt(
uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt);
/** Free a buffer after zero'ing its contents. */
void mpw_free(
const void *buffer, const size_t bufferSize);
//// Cryptographic functions.
/** Perform a scrypt-based key derivation on the given key using the given salt and scrypt parameters.
* @return A new keySize-size allocated buffer. */
uint8_t const *mpw_scrypt(
const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p);
/** Calculate a SHA256-based HMAC by encrypting the given salt with the given key.
* @return A new 32-byte allocated buffer. */
uint8_t const *mpw_hmac_sha256(
const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize);
//// Visualizers.
/** Encode a buffer as a string of hexadecimal characters.
* @return A reused buffer, do not free or store it. */
const char *mpw_hex(const void *buf, size_t length);
/** Encode a fingerprint for a buffer.
* @return A reused buffer, do not free or store it. */
const char *mpw_idForBuf(const void *buf, size_t length);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string. */
const char *mpw_identicon(const char *fullName, const char *masterPassword);

View File

@ -1,6 +0,0 @@
const uint8_t *mpw_masterKeyForUser(
const char *fullName, const char *masterPassword);
const char *mpw_passwordForSite(
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext);

View File

@ -27,7 +27,7 @@
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; }; 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; }; 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; };
93D3958C13B557F60F63C72B /* distribute in Resources */ = {isa = PBXBuildFile; fileRef = 93D3979016BF0C5B29D1340D /* distribute */; }; 93D3958C13B557F60F63C72B /* distribute in Resources */ = {isa = PBXBuildFile; fileRef = 93D3979016BF0C5B29D1340D /* distribute */; };
93D395B715D15F2B56F2A2EE /* types.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D392C5A6572DB0EB5B82C8 /* types.c */; }; 93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */; }; 93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */; };
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3956915634581E737B38C /* PearlNavigationController.m */; }; 93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3956915634581E737B38C /* PearlNavigationController.m */; };
@ -40,6 +40,7 @@
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; }; 93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; }; 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; };
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; }; 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; }; 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; };
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; }; 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; };
@ -477,7 +478,7 @@
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; }; 93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; }; 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; }; 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
93D392C5A6572DB0EB5B82C8 /* types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = types.c; sourceTree = "<group>"; }; 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; }; 93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; }; 93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
@ -490,7 +491,8 @@
93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = "<group>"; }; 93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = "<group>"; };
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; }; 93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = "<group>"; }; 93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = "<group>"; };
93D3969393A3A46BD27D7078 /* mpw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpw.c; sourceTree = "<group>"; }; 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm.c"; sourceTree = "<group>"; };
93D396C311C3725870343EE0 /* mpw-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-util.c"; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = "<group>"; }; 93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = "<group>"; };
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; }; 93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; };
@ -502,7 +504,7 @@
93D398121C8F063A3637144E /* mpw-cli.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli.c"; sourceTree = "<group>"; }; 93D398121C8F063A3637144E /* mpw-cli.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli.c"; sourceTree = "<group>"; };
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; }; 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; }; 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D3990D850D76A94C6B7A4D /* mpw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpw.h; sourceTree = "<group>"; }; 93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-algorithm.h"; sourceTree = "<group>"; };
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; }; 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; };
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRootSegue.m; sourceTree = "<group>"; }; 93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRootSegue.m; sourceTree = "<group>"; };
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; }; 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; };
@ -521,7 +523,7 @@
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencyViewController.m; sourceTree = "<group>"; }; 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencyViewController.m; sourceTree = "<group>"; };
93D39ACD33E79386E6F33601 /* install */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = install; sourceTree = "<group>"; }; 93D39ACD33E79386E6F33601 /* install */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = install; sourceTree = "<group>"; };
93D39AFD17CBE324D745DAB0 /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; }; 93D39AFD17CBE324D745DAB0 /* mpw-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-types.h"; sourceTree = "<group>"; };
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPopdownSegue.m; sourceTree = "<group>"; }; 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPopdownSegue.m; sourceTree = "<group>"; };
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; }; 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; };
93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; }; 93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; };
@ -536,6 +538,7 @@
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; }; 93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; };
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = "<group>"; }; 93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = "<group>"; };
93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; }; 93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; };
93D39CF7DB942C69D1C5D6BE /* mpw-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-util.h"; sourceTree = "<group>"; };
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = "<group>"; }; 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = "<group>"; };
93D39D6604447D7708039155 /* MPAnswersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAnswersViewController.h; sourceTree = "<group>"; }; 93D39D6604447D7708039155 /* MPAnswersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAnswersViewController.h; sourceTree = "<group>"; };
93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = "<group>"; }; 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = "<group>"; };
@ -1606,17 +1609,19 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
93D39B573E4DE98BAE518215 /* build */, 93D39B573E4DE98BAE518215 /* build */,
93D3969393A3A46BD27D7078 /* mpw.c */, 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */,
93D39E71D6BAECEC4CD886F4 /* bashlib */, 93D39E71D6BAECEC4CD886F4 /* bashlib */,
93D39ACD33E79386E6F33601 /* install */, 93D39ACD33E79386E6F33601 /* install */,
93D392C5A6572DB0EB5B82C8 /* types.c */, 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */,
93D39AFD17CBE324D745DAB0 /* types.h */, 93D39AFD17CBE324D745DAB0 /* mpw-types.h */,
93D3944F4D55D2A37EF6021B /* VERSION */, 93D3944F4D55D2A37EF6021B /* VERSION */,
93D3979016BF0C5B29D1340D /* distribute */, 93D3979016BF0C5B29D1340D /* distribute */,
93D39245A478883C672818F3 /* mpw.bashrc */, 93D39245A478883C672818F3 /* mpw.bashrc */,
93D39B70138D0E28F7D5E86B /* mpw-bench.c */, 93D39B70138D0E28F7D5E86B /* mpw-bench.c */,
93D398121C8F063A3637144E /* mpw-cli.c */, 93D398121C8F063A3637144E /* mpw-cli.c */,
93D3990D850D76A94C6B7A4D /* mpw.h */, 93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */,
93D39CF7DB942C69D1C5D6BE /* mpw-util.h */,
93D396C311C3725870343EE0 /* mpw-util.c */,
); );
name = C; name = C;
path = ../../C; path = ../../C;
@ -3630,8 +3635,9 @@
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */, 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */, 93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */,
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */, 93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */,
93D395B715D15F2B56F2A2EE /* types.c in Sources */, 93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */,
93D39B35D4B8E87ADEC05246 /* mpw-cli.c in Sources */, 93D39B35D4B8E87ADEC05246 /* mpw-cli.c in Sources */,
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };