From 57769ba1997dd4bb2f24a220dbc2b057eb3ac71b Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Thu, 15 Jan 2015 17:43:41 -0500 Subject: [PATCH] Algorithm versions in C and wire ObjC into C, remove ObjC algorithm implementation. --- MasterPassword/C/mpw-algorithm.c | 118 ++++--------- MasterPassword/C/mpw-algorithm.h | 22 ++- MasterPassword/C/mpw-algorithm_v0.c | 109 ++++++++++++ MasterPassword/C/mpw-algorithm_v1.c | 109 ++++++++++++ MasterPassword/C/mpw-algorithm_v2.c | 109 ++++++++++++ MasterPassword/C/mpw-algorithm_v3.c | 109 ++++++++++++ MasterPassword/C/mpw-bench.c | 6 +- MasterPassword/C/mpw-cli.c | 6 +- MasterPassword/C/mpw-tests.c | 4 +- MasterPassword/C/mpw-types.c | 118 +++++++------ MasterPassword/C/mpw-types.h | 55 +++++- MasterPassword/C/mpw-util.c | 7 + MasterPassword/C/mpw-util.h | 10 +- MasterPassword/ObjC/MPAlgorithm.h | 6 +- MasterPassword/ObjC/MPAlgorithmV0.h | 7 - MasterPassword/ObjC/MPAlgorithmV0.m | 161 ++++-------------- MasterPassword/ObjC/MPAlgorithmV1.m | 49 +----- MasterPassword/ObjC/MPAlgorithmV2.h | 2 +- MasterPassword/ObjC/MPAlgorithmV2.m | 54 +----- MasterPassword/ObjC/MPAlgorithmV3.h | 21 +++ MasterPassword/ObjC/MPAlgorithmV3.m | 48 ++++++ MasterPassword/ObjC/MPTypes.h | 39 ----- MasterPassword/ObjC/iOS/MPPasswordCell.m | 4 +- .../ObjC/iOS/MPPreferencesViewController.m | 2 +- .../project.pbxproj | 24 +++ 25 files changed, 750 insertions(+), 449 deletions(-) create mode 100644 MasterPassword/C/mpw-algorithm_v0.c create mode 100644 MasterPassword/C/mpw-algorithm_v1.c create mode 100644 MasterPassword/C/mpw-algorithm_v2.c create mode 100644 MasterPassword/C/mpw-algorithm_v3.c create mode 100644 MasterPassword/ObjC/MPAlgorithmV3.h create mode 100644 MasterPassword/ObjC/MPAlgorithmV3.m diff --git a/MasterPassword/C/mpw-algorithm.c b/MasterPassword/C/mpw-algorithm.c index 0210e789..89dbf422 100644 --- a/MasterPassword/C/mpw-algorithm.c +++ b/MasterPassword/C/mpw-algorithm.c @@ -6,106 +6,48 @@ // Copyright (c) 2014 Lyndir. All rights reserved. // -#include -#include -#include -#include - -#include "mpw-types.h" -#include "mpw-util.h" #include "mpw-algorithm.h" +#include "mpw-algorithm_v0.c" +#include "mpw-algorithm_v1.c" +#include "mpw-algorithm_v2.c" +#include "mpw-algorithm_v3.c" #define MP_N 32768 #define MP_r 8 #define MP_p 2 #define MP_hash PearlHashSHA256 -const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword) { +const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) { - const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); - trc( "fullName: %s\n", fullName ); - trc( "masterPassword: %s\n", masterPassword ); - trc( "key scope: %s\n", mpKeyScope ); - - // Calculate the master key salt. - // masterKeySalt = mpKeyScope . #fullName . fullName - size_t masterKeySaltSize = 0; - uint8_t *masterKeySalt = NULL; - mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope ); - mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) ); - mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName ); - if (!masterKeySalt) { - ftl( "Could not allocate master key salt: %d\n", errno ); - return NULL; + switch (algorithmVersion) { + case MPAlgorithmVersion0: + return mpw_masterKeyForUser_v0( fullName, masterPassword ); + case MPAlgorithmVersion1: + return mpw_masterKeyForUser_v1( fullName, masterPassword ); + case MPAlgorithmVersion2: + return mpw_masterKeyForUser_v2( fullName, masterPassword ); + case MPAlgorithmVersion3: + return mpw_masterKeyForUser_v3( fullName, masterPassword ); + default: + ftl( "Unsupported version: %d", algorithmVersion ); + return NULL; } - trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) ); - - // Calculate the master key. - // masterKey = scrypt( masterPassword, masterKeySalt ) - const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); - mpw_free( masterKeySalt, masterKeySaltSize ); - if (!masterKey) { - ftl( "Could not allocate master key: %d\n", errno ); - return NULL; - } - trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) ); - - return masterKey; } 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 MPAlgorithmVersion algorithmVersion) { - const char *siteScope = mpw_scopeForVariant( siteVariant ); - trc( "siteName: %s\n", siteName ); - trc( "siteCounter: %d\n", siteCounter ); - trc( "siteVariant: %d\n", siteVariant ); - trc( "siteType: %d\n", siteType ); - trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext ); - - // Calculate the site seed. - // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) - size_t sitePasswordInfoSize = 0; - uint8_t *sitePasswordInfo = NULL; - mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope ); - mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) ); - mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName ); - mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) ); - if (siteContext) { - mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) ); - mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext ); + switch (algorithmVersion) { + case MPAlgorithmVersion0: + return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ); + case MPAlgorithmVersion1: + return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ); + case MPAlgorithmVersion2: + return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ); + case MPAlgorithmVersion3: + return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ); + default: + ftl( "Unsupported version: %d", algorithmVersion ); + return NULL; } - if (!sitePasswordInfo) { - ftl( "Could not allocate site seed info: %d\n", errno ); - return NULL; - } - trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) ); - - const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); - mpw_free( sitePasswordInfo, sitePasswordInfoSize ); - if (!sitePasswordSeed) { - ftl( "Could not allocate site seed: %d\n", errno ); - return NULL; - } - trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) ); - - // Determine the template. - const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] ); - trc( "type %d, template: %s\n", siteType, template ); - if (strlen( template ) > 32) { - ftl( "Template too long for password seed: %lu", strlen( template ) ); - mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); - return NULL; - } - - // Encode the password from the seed using the template. - char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); - for (size_t c = 0; c < strlen( template ); ++c) { - 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], - sitePassword[c] ); - } - mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); - - return sitePassword; } diff --git a/MasterPassword/C/mpw-algorithm.h b/MasterPassword/C/mpw-algorithm.h index ac080a93..2a541ef4 100644 --- a/MasterPassword/C/mpw-algorithm.h +++ b/MasterPassword/C/mpw-algorithm.h @@ -6,15 +6,27 @@ // Copyright (c) 2014 Lyndir. All rights reserved. // -#define MP_dkLen 64 +#import "mpw-types.h" + +typedef enum(unsigned int, MPAlgorithmVersion) { + /** V0 did math with chars whose signedness was platform-dependent. */ + MPAlgorithmVersion0, + /** V1 miscounted the byte-length of multi-byte site names. */ + MPAlgorithmVersion1, + /** V2 miscounted the byte-length of multi-byte user names. */ + MPAlgorithmVersion2, + /** V3 is the current version. */ + MPAlgorithmVersion3, +}; +#define MPAlgorithmVersionCurrent MPAlgorithmVersion3 /** Derive the master key for a user based on their name and master password. - * @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */ + * @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */ const uint8_t *mpw_masterKeyForUser( - const char *fullName, const char *masterPassword); + const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion); /** Encode a password for the site from the given master key and site parameters. - * @return A newly allocated string or NULL if an allocation error occurred. */ + * @return A newly allocated string or NULL if an allocation error occurred. */ 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 MPAlgorithmVersion algorithmVersion); diff --git a/MasterPassword/C/mpw-algorithm_v0.c b/MasterPassword/C/mpw-algorithm_v0.c new file mode 100644 index 00000000..78dae9f7 --- /dev/null +++ b/MasterPassword/C/mpw-algorithm_v0.c @@ -0,0 +1,109 @@ +// +// mpw-algorithm.c +// MasterPassword +// +// Created by Maarten Billemont on 2014-12-20. +// Copyright (c) 2014 Lyndir. All rights reserved. +// + +#include +#include +#include + +#include "mpw-types.h" +#include "mpw-util.h" + +#define MP_N 32768 +#define MP_r 8 +#define MP_p 2 +#define MP_hash PearlHashSHA256 + +static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) { + + const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); + trc( "fullName: %s\n", fullName ); + trc( "masterPassword: %s\n", masterPassword ); + trc( "key scope: %s\n", mpKeyScope ); + + // Calculate the master key salt. + // masterKeySalt = mpKeyScope . #fullName . fullName + size_t masterKeySaltSize = 0; + uint8_t *masterKeySalt = NULL; + mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope ); + mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) ); + mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName ); + if (!masterKeySalt) { + ftl( "Could not allocate master key salt: %d\n", errno ); + return NULL; + } + trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) ); + + // Calculate the master key. + // masterKey = scrypt( masterPassword, masterKeySalt ) + const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); + mpw_free( masterKeySalt, masterKeySaltSize ); + if (!masterKey) { + ftl( "Could not allocate master key: %d\n", errno ); + return NULL; + } + trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) ); + + return masterKey; +} + +static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, + const MPSiteVariant siteVariant, const char *siteContext) { + + const char *siteScope = mpw_scopeForVariant( siteVariant ); + trc( "siteName: %s\n", siteName ); + trc( "siteCounter: %d\n", siteCounter ); + trc( "siteVariant: %d\n", siteVariant ); + trc( "siteType: %d\n", siteType ); + trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext ); + + // Calculate the site seed. + // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) + size_t sitePasswordInfoSize = 0; + uint8_t *sitePasswordInfo = NULL; + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) ); + if (siteContext) { + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext ); + } + if (!sitePasswordInfo) { + ftl( "Could not allocate site seed info: %d\n", errno ); + return NULL; + } + trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) ); + + const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + mpw_free( sitePasswordInfo, sitePasswordInfoSize ); + if (!sitePasswordSeed) { + ftl( "Could not allocate site seed: %d\n", errno ); + return NULL; + } + trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) ); + + // Determine the template. + const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] ); + trc( "type %d, template: %s\n", siteType, template ); + if (strlen( template ) > 32) { + ftl( "Template too long for password seed: %lu", strlen( template ) ); + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + return NULL; + } + + // Encode the password from the seed using the template. + char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); + for (size_t c = 0; c < strlen( template ); ++c) { + 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], + sitePassword[c] ); + } + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + + return sitePassword; +} diff --git a/MasterPassword/C/mpw-algorithm_v1.c b/MasterPassword/C/mpw-algorithm_v1.c new file mode 100644 index 00000000..ad80eaff --- /dev/null +++ b/MasterPassword/C/mpw-algorithm_v1.c @@ -0,0 +1,109 @@ +// +// mpw-algorithm.c +// MasterPassword +// +// Created by Maarten Billemont on 2014-12-20. +// Copyright (c) 2014 Lyndir. All rights reserved. +// + +#include +#include +#include + +#include "mpw-types.h" +#include "mpw-util.h" + +#define MP_N 32768 +#define MP_r 8 +#define MP_p 2 +#define MP_hash PearlHashSHA256 + +static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) { + + const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); + trc( "fullName: %s\n", fullName ); + trc( "masterPassword: %s\n", masterPassword ); + trc( "key scope: %s\n", mpKeyScope ); + + // Calculate the master key salt. + // masterKeySalt = mpKeyScope . #fullName . fullName + size_t masterKeySaltSize = 0; + uint8_t *masterKeySalt = NULL; + mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope ); + mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) ); + mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName ); + if (!masterKeySalt) { + ftl( "Could not allocate master key salt: %d\n", errno ); + return NULL; + } + trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) ); + + // Calculate the master key. + // masterKey = scrypt( masterPassword, masterKeySalt ) + const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); + mpw_free( masterKeySalt, masterKeySaltSize ); + if (!masterKey) { + ftl( "Could not allocate master key: %d\n", errno ); + return NULL; + } + trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) ); + + return masterKey; +} + +static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, + const MPSiteVariant siteVariant, const char *siteContext) { + + const char *siteScope = mpw_scopeForVariant( siteVariant ); + trc( "siteName: %s\n", siteName ); + trc( "siteCounter: %d\n", siteCounter ); + trc( "siteVariant: %d\n", siteVariant ); + trc( "siteType: %d\n", siteType ); + trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext ); + + // Calculate the site seed. + // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) + size_t sitePasswordInfoSize = 0; + uint8_t *sitePasswordInfo = NULL; + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) ); + if (siteContext) { + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext ); + } + if (!sitePasswordInfo) { + ftl( "Could not allocate site seed info: %d\n", errno ); + return NULL; + } + trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) ); + + const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + mpw_free( sitePasswordInfo, sitePasswordInfoSize ); + if (!sitePasswordSeed) { + ftl( "Could not allocate site seed: %d\n", errno ); + return NULL; + } + trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) ); + + // Determine the template. + const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] ); + trc( "type %d, template: %s\n", siteType, template ); + if (strlen( template ) > 32) { + ftl( "Template too long for password seed: %lu", strlen( template ) ); + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + return NULL; + } + + // Encode the password from the seed using the template. + char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); + for (size_t c = 0; c < strlen( template ); ++c) { + 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], + sitePassword[c] ); + } + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + + return sitePassword; +} diff --git a/MasterPassword/C/mpw-algorithm_v2.c b/MasterPassword/C/mpw-algorithm_v2.c new file mode 100644 index 00000000..823c72d9 --- /dev/null +++ b/MasterPassword/C/mpw-algorithm_v2.c @@ -0,0 +1,109 @@ +// +// mpw-algorithm.c +// MasterPassword +// +// Created by Maarten Billemont on 2014-12-20. +// Copyright (c) 2014 Lyndir. All rights reserved. +// + +#include +#include +#include + +#include "mpw-types.h" +#include "mpw-util.h" + +#define MP_N 32768 +#define MP_r 8 +#define MP_p 2 +#define MP_hash PearlHashSHA256 + +static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) { + + const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); + trc( "fullName: %s\n", fullName ); + trc( "masterPassword: %s\n", masterPassword ); + trc( "key scope: %s\n", mpKeyScope ); + + // Calculate the master key salt. + // masterKeySalt = mpKeyScope . #fullName . fullName + size_t masterKeySaltSize = 0; + uint8_t *masterKeySalt = NULL; + mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope ); + mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) ); + mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName ); + if (!masterKeySalt) { + ftl( "Could not allocate master key salt: %d\n", errno ); + return NULL; + } + trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) ); + + // Calculate the master key. + // masterKey = scrypt( masterPassword, masterKeySalt ) + const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); + mpw_free( masterKeySalt, masterKeySaltSize ); + if (!masterKey) { + ftl( "Could not allocate master key: %d\n", errno ); + return NULL; + } + trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) ); + + return masterKey; +} + +static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, + const MPSiteVariant siteVariant, const char *siteContext) { + + const char *siteScope = mpw_scopeForVariant( siteVariant ); + trc( "siteName: %s\n", siteName ); + trc( "siteCounter: %d\n", siteCounter ); + trc( "siteVariant: %d\n", siteVariant ); + trc( "siteType: %d\n", siteType ); + trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext ); + + // Calculate the site seed. + // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) + size_t sitePasswordInfoSize = 0; + uint8_t *sitePasswordInfo = NULL; + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) ); + if (siteContext) { + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext ); + } + if (!sitePasswordInfo) { + ftl( "Could not allocate site seed info: %d\n", errno ); + return NULL; + } + trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) ); + + const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + mpw_free( sitePasswordInfo, sitePasswordInfoSize ); + if (!sitePasswordSeed) { + ftl( "Could not allocate site seed: %d\n", errno ); + return NULL; + } + trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) ); + + // Determine the template. + const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] ); + trc( "type %d, template: %s\n", siteType, template ); + if (strlen( template ) > 32) { + ftl( "Template too long for password seed: %lu", strlen( template ) ); + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + return NULL; + } + + // Encode the password from the seed using the template. + char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); + for (size_t c = 0; c < strlen( template ); ++c) { + 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], + sitePassword[c] ); + } + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + + return sitePassword; +} diff --git a/MasterPassword/C/mpw-algorithm_v3.c b/MasterPassword/C/mpw-algorithm_v3.c new file mode 100644 index 00000000..cc396ca7 --- /dev/null +++ b/MasterPassword/C/mpw-algorithm_v3.c @@ -0,0 +1,109 @@ +// +// mpw-algorithm.c +// MasterPassword +// +// Created by Maarten Billemont on 2014-12-20. +// Copyright (c) 2014 Lyndir. All rights reserved. +// + +#include +#include +#include + +#include "mpw-types.h" +#include "mpw-util.h" + +#define MP_N 32768 +#define MP_r 8 +#define MP_p 2 +#define MP_hash PearlHashSHA256 + +static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) { + + const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); + trc( "fullName: %s\n", fullName ); + trc( "masterPassword: %s\n", masterPassword ); + trc( "key scope: %s\n", mpKeyScope ); + + // Calculate the master key salt. + // masterKeySalt = mpKeyScope . #fullName . fullName + size_t masterKeySaltSize = 0; + uint8_t *masterKeySalt = NULL; + mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope ); + mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) ); + mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName ); + if (!masterKeySalt) { + ftl( "Could not allocate master key salt: %d\n", errno ); + return NULL; + } + trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) ); + + // Calculate the master key. + // masterKey = scrypt( masterPassword, masterKeySalt ) + const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); + mpw_free( masterKeySalt, masterKeySaltSize ); + if (!masterKey) { + ftl( "Could not allocate master key: %d\n", errno ); + return NULL; + } + trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) ); + + return masterKey; +} + +static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, + const MPSiteVariant siteVariant, const char *siteContext) { + + const char *siteScope = mpw_scopeForVariant( siteVariant ); + trc( "siteName: %s\n", siteName ); + trc( "siteCounter: %d\n", siteCounter ); + trc( "siteVariant: %d\n", siteVariant ); + trc( "siteType: %d\n", siteType ); + trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext ); + + // Calculate the site seed. + // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) + size_t sitePasswordInfoSize = 0; + uint8_t *sitePasswordInfo = NULL; + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName ); + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) ); + if (siteContext) { + mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) ); + mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext ); + } + if (!sitePasswordInfo) { + ftl( "Could not allocate site seed info: %d\n", errno ); + return NULL; + } + trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) ); + + const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + mpw_free( sitePasswordInfo, sitePasswordInfoSize ); + if (!sitePasswordSeed) { + ftl( "Could not allocate site seed: %d\n", errno ); + return NULL; + } + trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) ); + + // Determine the template. + const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] ); + trc( "type %d, template: %s\n", siteType, template ); + if (strlen( template ) > 32) { + ftl( "Template too long for password seed: %lu", strlen( template ) ); + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + return NULL; + } + + // Encode the password from the seed using the template. + char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); + for (size_t c = 0; c < strlen( template ); ++c) { + 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], + sitePassword[c] ); + } + mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) ); + + return sitePassword; +} diff --git a/MasterPassword/C/mpw-bench.c b/MasterPassword/C/mpw-bench.c index 5f817c63..a12d7387 100644 --- a/MasterPassword/C/mpw-bench.c +++ b/MasterPassword/C/mpw-bench.c @@ -62,10 +62,12 @@ int main(int argc, char *const argv[]) { unsigned int iterations = 100; mpw_getTime( &startTime ); for (int i = 0; i < iterations; ++i) { - const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword ); + const uint8_t *masterKey = mpw_masterKeyForUser( + fullName, masterPassword, MPAlgorithmVersionCurrent ); if (!masterKey) ftl( "Could not allocate master key: %d\n", errno ); - free( (void *)mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ) ); + free( (void *)mpw_passwordForSite( + masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) ); free( (void *)masterKey ); if (i % 1 == 0) diff --git a/MasterPassword/C/mpw-cli.c b/MasterPassword/C/mpw-cli.c index 44752099..7b5935d9 100644 --- a/MasterPassword/C/mpw-cli.c +++ b/MasterPassword/C/mpw-cli.c @@ -188,12 +188,14 @@ int main(int argc, char *const argv[]) { fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, mpw_identicon( fullName, masterPassword ) ); // Output the password. - const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword ); + const uint8_t *masterKey = mpw_masterKeyForUser( + fullName, masterPassword, MPAlgorithmVersionCurrent ); mpw_freeString( masterPassword ); if (!masterKey) ftl( "Couldn't derive master key." ); - const char *sitePassword = mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString ); + const char *sitePassword = mpw_passwordForSite( + masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString, MPAlgorithmVersionCurrent ); mpw_free( masterKey, MP_dkLen ); if (!sitePassword) ftl( "Couldn't derive site password." ); diff --git a/MasterPassword/C/mpw-tests.c b/MasterPassword/C/mpw-tests.c index 0ce00f6a..4fbfa6ad 100644 --- a/MasterPassword/C/mpw-tests.c +++ b/MasterPassword/C/mpw-tests.c @@ -40,13 +40,13 @@ int main(int argc, char *const argv[]) { // 1. calculate the master key. const uint8_t *masterKey = mpw_masterKeyForUser( - (char *)fullName, (char *)masterPassword ); + (char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent ); if (!masterKey) ftl( "Couldn't derive master key." ); // 2. calculate the site password. const char *sitePassword = mpw_passwordForSite( - masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext ); + masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, MPAlgorithmVersionCurrent ); mpw_free( masterKey, MP_dkLen ); if (!sitePassword) ftl( "Couldn't derive site password." ); diff --git a/MasterPassword/C/mpw-types.c b/MasterPassword/C/mpw-types.c index b86fedd6..92d5da5f 100644 --- a/MasterPassword/C/mpw-types.c +++ b/MasterPassword/C/mpw-types.c @@ -51,56 +51,71 @@ const MPSiteType mpw_typeWithName(const char *typeName) { abort(); } -const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) { +inline const char **mpw_templatesForType(MPSiteType type, size_t *count) { if (!(type & MPSiteTypeClassGenerated)) { - fprintf( stderr, "Not a generated type: %d", type ); - abort(); + ftl( "Not a generated type: %d", type ); + *count = 0; + return NULL; } switch (type) { case MPSiteTypeGeneratedMaximum: { - const char *templates[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" }; - return templates[seedByte % 2]; + *count = 2; + return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" }; } case MPSiteTypeGeneratedLong: { - const char *templates[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", + *count = 21; + return (const char *[]){ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" }; - return templates[seedByte % 21]; } case MPSiteTypeGeneratedMedium: { - const char *templates[] = { "CvcnoCvc", "CvcCvcno" }; - return templates[seedByte % 2]; + *count = 2; + return (const char *[]){ "CvcnoCvc", "CvcCvcno" }; } case MPSiteTypeGeneratedBasic: { - const char *templates[] = { "aaanaaan", "aannaaan", "aaannaaa" }; - return templates[seedByte % 3]; + *count = 3; + return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" }; } case MPSiteTypeGeneratedShort: { - return "Cvcn"; + *count = 1; + return (const char *[]){"Cvcn"}; } case MPSiteTypeGeneratedPIN: { - return "nnnn"; + *count = 1; + return (const char *[]){ "nnnn" }; } case MPSiteTypeGeneratedName: { - return "cvccvcvcv"; + *count = 1; + return (const char *[]) {"cvccvcvcv"}; } case MPSiteTypeGeneratedPhrase: { - const char *templates[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" }; - return templates[seedByte % 3]; + *count = 3; + return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" }; } default: { - fprintf( stderr, "Unknown generated type: %d", type ); - abort(); + ftl( "Unknown generated type: %d", type ); + *count = 0; + return NULL; } } } +const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) { + + size_t count = 0; + const char **templates = mpw_templatesForType( type, &count ); + if (!count) + return NULL; + + return templates[seedByte % count]; +} + const MPSiteVariant mpw_variantWithName(const char *variantName) { char stdVariantName[strlen( variantName )]; @@ -138,55 +153,38 @@ const char *mpw_scopeForVariant(MPSiteVariant variant) { } } -const char mpw_characterFromClass(char characterClass, uint8_t seedByte) { +const char *mpw_charactersInClass(char characterClass) { - const char *classCharacters; switch (characterClass) { - case 'V': { - classCharacters = "AEIOU"; - break; - } - case 'C': { - classCharacters = "BCDFGHJKLMNPQRSTVWXYZ"; - break; - } - case 'v': { - classCharacters = "aeiou"; - break; - } - case 'c': { - classCharacters = "bcdfghjklmnpqrstvwxyz"; - break; - } - case 'A': { - classCharacters = "AEIOUBCDFGHJKLMNPQRSTVWXYZ"; - break; - } - case 'a': { - classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"; - break; - } - case 'n': { - classCharacters = "0123456789"; - break; - } - case 'o': { - classCharacters = "@&%?,=[]_:-+*$#!'^~;()/."; - break; - } - case 'x': { - classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()"; - break; - } - case ' ': { - classCharacters = " "; - break; - } + case 'V': + return "AEIOU"; + case 'C': + return "BCDFGHJKLMNPQRSTVWXYZ"; + case 'v': + return "aeiou"; + case 'c': + return "bcdfghjklmnpqrstvwxyz"; + case 'A': + return "AEIOUBCDFGHJKLMNPQRSTVWXYZ"; + case 'a': + return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"; + case 'n': + return "0123456789"; + case 'o': + return "@&%?,=[]_:-+*$#!'^~;()/."; + case 'x': + return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()"; + case ' ': + return " "; default: { fprintf( stderr, "Unknown character class: %c", characterClass ); abort(); } } +} +const char mpw_characterFromClass(char characterClass, uint8_t seedByte) { + + const char *classCharacters = mpw_charactersInClass( characterClass ); return classCharacters[seedByte % strlen( classCharacters )]; } diff --git a/MasterPassword/C/mpw-types.h b/MasterPassword/C/mpw-types.h index 95017074..9f376bbb 100644 --- a/MasterPassword/C/mpw-types.h +++ b/MasterPassword/C/mpw-types.h @@ -6,32 +6,42 @@ // Copyright (c) 2014 Lyndir. All rights reserved. // +#include + +#ifdef NS_ENUM +#define enum(_type, _name) NS_ENUM(_type, _name) +#else +#define enum(_type, _name) _type _name; enum +#endif + +#define MP_dkLen 64 + //// Types. -typedef enum { +typedef enum( unsigned int, MPSiteVariant ) { /** Generate the password to log in with. */ MPSiteVariantPassword, /** Generate the login name to log in as. */ MPSiteVariantLogin, /** Generate the answer to a security question. */ MPSiteVariantAnswer, -} MPSiteVariant; +}; -typedef enum { +typedef enum( unsigned int, MPSiteTypeClass ) { /** Generate the password. */ MPSiteTypeClassGenerated = 1 << 4, /** Store the password. */ MPSiteTypeClassStored = 1 << 5, -} MPSiteTypeClass; +}; -typedef enum { +typedef enum( unsigned int, MPSiteFeature ) { /** Export the key-protected content data. */ MPSiteFeatureExportContent = 1 << 10, /** Never export content. */ MPSiteFeatureDevicePrivate = 1 << 11, -} MPSiteFeature; +}; -typedef enum { +typedef enum( unsigned int, MPSiteType) { MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0, MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0, MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0, @@ -43,13 +53,42 @@ typedef enum { MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent, MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate, -} MPSiteType; +}; //// Type utilities. +/** + * @return The variant represented by the given name. + */ const MPSiteVariant mpw_variantWithName(const char *variantName); +/** + * @return An internal string containing the scope identifier to apply when encoding for the given variant. + */ const char *mpw_scopeForVariant(MPSiteVariant variant); + +/** + * @return The type represented by the given name. + */ const MPSiteType mpw_typeWithName(const char *typeName); + +/** + * @return An array of internal strings that express the templates to use for the given type. + * The amount of elements in the array is stored in count. + * If an unsupported type is given, count will be 0 and will return NULL. + */ +const char **mpw_templatesForType(MPSiteType type, size_t *count); +/** + * @return An internal string that contains the password encoding template of the given type + * for a seed that starts with the given byte. + */ const char *mpw_templateForType(MPSiteType type, uint8_t seedByte); + +/** + * @return An internal string that contains all the characters that occur in the given character class. + */ +const char *mpw_charactersInClass(char characterClass); +/** + * @return A character from given character class that encodes the given byte. + */ const char mpw_characterFromClass(char characterClass, uint8_t seedByte); diff --git a/MasterPassword/C/mpw-util.c b/MasterPassword/C/mpw-util.c index 7610fc79..864371b3 100644 --- a/MasterPassword/C/mpw-util.c +++ b/MasterPassword/C/mpw-util.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -163,3 +164,9 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) { free( resetString ); return identicon; } + +const size_t mpw_charlen(const char *string) { + + setlocale( LC_ALL, "en_US.UTF-8" ); + return mbstowcs( NULL, string, strlen( string ) ); +} diff --git a/MasterPassword/C/mpw-util.h b/MasterPassword/C/mpw-util.h index 701af92c..8a9bc210 100644 --- a/MasterPassword/C/mpw-util.h +++ b/MasterPassword/C/mpw-util.h @@ -9,7 +9,9 @@ //// Logging. #ifdef DEBUG +#ifndef trc #define trc(...) fprintf( stderr, __VA_ARGS__ ) +#endif #else #define trc(...) do {} while (0) #endif @@ -50,11 +52,15 @@ uint8_t const *mpw_hmac_sha256( //// Visualizers. /** Encode a buffer as a string of hexadecimal characters. - * @return A reused buffer, do not free or store it. */ + * @return A C-string in 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. */ + * @return A C-string in 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); + +//// String utilities. + +const size_t mpw_charlen(const char *string); diff --git a/MasterPassword/ObjC/MPAlgorithm.h b/MasterPassword/ObjC/MPAlgorithm.h index ab251671..958b9ba4 100644 --- a/MasterPassword/ObjC/MPAlgorithm.h +++ b/MasterPassword/ObjC/MPAlgorithm.h @@ -19,8 +19,9 @@ #import "MPStoredSiteEntity.h" #import "MPGeneratedSiteEntity.h" #import "MPSiteQuestionEntity.h" +#import "mpw-algorithm.h" -#define MPAlgorithmDefaultVersion 2 +#define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent #define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion) id MPAlgorithmForVersion(NSUInteger version); @@ -43,7 +44,7 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack); @protocol MPAlgorithm @required -- (NSUInteger)version; +- (MPAlgorithmVersion)version; - (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc; - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit; @@ -51,7 +52,6 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack); - (MPKey *)keyFromKeyData:(NSData *)keyData; - (NSData *)keyIDForKeyData:(NSData *)keyData; -- (NSString *)scopeForVariant:(MPSiteVariant)variant; - (NSString *)nameOfType:(MPSiteType)type; - (NSString *)shortNameOfType:(MPSiteType)type; - (NSString *)classNameOfType:(MPSiteType)type; diff --git a/MasterPassword/ObjC/MPAlgorithmV0.h b/MasterPassword/ObjC/MPAlgorithmV0.h index 3e7c902a..9204e0e4 100644 --- a/MasterPassword/ObjC/MPAlgorithmV0.h +++ b/MasterPassword/ObjC/MPAlgorithmV0.h @@ -18,11 +18,4 @@ #import "MPAlgorithm.h" @interface MPAlgorithmV0 : NSObject - -- (NSDictionary *)allCiphers; -- (NSArray *)ciphersForType:(MPSiteType)type; -- (NSArray *)cipherClasses; -- (NSArray *)cipherClassCharacters; -- (NSString *)charactersForCipherClass:(NSString *)cipherClass; - @end diff --git a/MasterPassword/ObjC/MPAlgorithmV0.m b/MasterPassword/ObjC/MPAlgorithmV0.m index 6207f81e..7942522a 100644 --- a/MasterPassword/ObjC/MPAlgorithmV0.m +++ b/MasterPassword/ObjC/MPAlgorithmV0.m @@ -19,16 +19,11 @@ #import "MPEntities.h" #import "MPAppDelegate_Shared.h" #import "MPAppDelegate_InApp.h" -#import "MPSiteQuestionEntity.h" +#import "mpw-util.h" +#import "mpw-types.h" #include #include -#define MP_N 32768 -#define MP_r 8 -#define MP_p 2 -#define MP_dkLen 64 -#define MP_hash PearlHashSHA256 - /* An AMD HD 7970 calculates 2495M SHA-1 hashes per second at a cost of ~350$ per GPU */ #define CRACKING_PER_SECOND 2495000000UL #define CRACKING_PRICE 350 @@ -53,9 +48,9 @@ ctx = NULL; } -- (NSUInteger)version { +- (MPAlgorithmVersion)version { - return 0; + return MPAlgorithmVersion0; } - (NSString *)description { @@ -112,21 +107,13 @@ - (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName { - uint32_t nuserNameLength = htonl( userName.length ); NSDate *start = [NSDate date]; - NSData *keyData = [PearlSCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding] - usingSalt:[NSData dataByConcatenatingDatas: - [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], - [NSData dataWithBytes:&nuserNameLength - length:sizeof( nuserNameLength )], - [userName dataUsingEncoding:NSUTF8StringEncoding], - nil] N:MP_N r:MP_r p:MP_p]; - - MPKey *key = [self keyFromKeyData:keyData]; - trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex], + uint8_t const *masterKeyBytes = mpw_masterKeyForUser( userName.UTF8String, password.UTF8String, [self version] ); + MPKey *masterKey = [self keyFromKeyData:[NSData dataWithBytes:masterKeyBytes length:MP_dkLen]]; + mpw_free( masterKeyBytes, MP_dkLen ); + trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [masterKey.keyID encodeHex], -[start timeIntervalSinceNow] ); - - return key; + return masterKey; } - (MPKey *)keyFromKeyData:(NSData *)keyData { @@ -136,21 +123,7 @@ - (NSData *)keyIDForKeyData:(NSData *)keyData { - return [keyData hashWith:MP_hash]; -} - -- (NSString *)scopeForVariant:(MPSiteVariant)variant { - - switch (variant) { - case MPSiteVariantPassword: - return @"com.lyndir.masterpassword"; - case MPSiteVariantLogin: - return @"com.lyndir.masterpassword.login"; - case MPSiteVariantAnswer: - return @"com.lyndir.masterpassword.answer"; - } - - Throw( @"Unsupported variant: %ld", (long)variant ); + return [keyData hashWith:PearlHashSHA256]; } - (NSString *)nameOfType:(MPSiteType)type { @@ -327,40 +300,6 @@ return previousType; } -- (NSDictionary *)allCiphers { - - static NSDictionary *ciphers = nil; - static dispatch_once_t once = 0; - dispatch_once( &once, ^{ - ciphers = [NSDictionary dictionaryWithContentsOfURL: - [[NSBundle mainBundle] URLForResource:@"ciphers" withExtension:@"plist"]]; - } ); - - return ciphers; -} - -- (NSArray *)ciphersForType:(MPSiteType)type { - - NSString *typeClass = [self classNameOfType:type]; - NSString *typeName = [self nameOfType:type]; - return [[[self allCiphers] valueForKey:typeClass] valueForKey:typeName]; -} - -- (NSArray *)cipherClasses { - - return [[[self allCiphers] valueForKey:@"MPCharacterClasses"] allKeys]; -} - -- (NSArray *)cipherClassCharacters { - - return [[[self allCiphers] valueForKey:@"MPCharacterClasses"] allValues]; -} - -- (NSString *)charactersForCipherClass:(NSString *)cipherClass { - - return [NSNullToNil( [NSNullToNil( [[self allCiphers] valueForKey:@"MPCharacterClasses"] ) valueForKey:cipherClass] ) copy]; -} - - (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key { return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1 @@ -383,44 +322,10 @@ - (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key { - // Determine the seed whose bytes will be used for calculating a password - uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ), ncontextLength = htonl( context.length ); - NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; - NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; - NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )]; - NSString *scope = [self scopeForVariant:variant]; - trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@ | %@)", - [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex], context ); - NSData *seed = [[NSData dataByConcatenatingDatas: - [scope dataUsingEncoding:NSUTF8StringEncoding], - nameLengthBytes, - [name dataUsingEncoding:NSUTF8StringEncoding], - counterBytes, - context? contextLengthBytes: nil, - [context dataUsingEncoding:NSUTF8StringEncoding], - nil] - hmacWith:PearlHashSHA256 key:key.keyData]; - trc( @"seed is: %@", [seed encodeHex] ); - const char *seedBytes = seed.bytes; - - // Determine the cipher from the first seed byte. - NSAssert( [seed length], @"Missing seed." ); - NSArray *typeCiphers = [self ciphersForType:type]; - NSString *cipher = typeCiphers[htons( seedBytes[0] ) % [typeCiphers count]]; - trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher ); - - // Encode the content, character by character, using subsequent seed bytes and the cipher. - NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." ); - NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; - for (NSUInteger c = 0; c < [cipher length]; ++c) { - uint16_t keyByte = htons( seedBytes[c + 1] ); - NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )]; - NSString *cipherClassCharacters = [self charactersForCipherClass:cipherClass]; - NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )]; - - trc( @"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character ); - [content appendString:character]; - } + char const *contentBytes = mpw_passwordForSite( key.keyData.bytes, name.UTF8String, type, (uint32_t)counter, + variant, context.UTF8String, [self version] ); + NSString *content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding]; + mpw_freeString( contentBytes ); return content; } @@ -808,21 +713,23 @@ if (!type) return NO; - NSArray *ciphers = [self ciphersForType:type]; - if (!ciphers) + size_t count = 0; + const char **templates = mpw_templatesForType( type, &count ); + if (!templates) return NO; - BIGNUM *permutations = BN_new(), *cipherPermutations = BN_new(); - for (NSString *cipher in ciphers) { - BN_one( cipherPermutations ); + BIGNUM *permutations = BN_new(), *templatePermutations = BN_new(); + for (int t = 0; t < count; ++t) { + const char *template = templates[t]; + BN_one( templatePermutations ); - for (NSUInteger c = 0; c < [cipher length]; ++c) - BN_mul_word( cipherPermutations, - (BN_ULONG)[[self charactersForCipherClass:[cipher substringWithRange:NSMakeRange( c, 1 )]] length] ); + for (NSUInteger c = 0; c < strlen( template ); ++c) + BN_mul_word( templatePermutations, + (BN_ULONG)strlen( mpw_charactersInClass( template[c] ) ) ); - BN_add( permutations, permutations, cipherPermutations ); + BN_add( permutations, permutations, templatePermutations ); } - BN_free( cipherPermutations ); + BN_free( templatePermutations ); return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker]; } @@ -832,25 +739,21 @@ BIGNUM *permutations = BN_new(); BN_one( permutations ); - NSMutableString *cipher = [NSMutableString new]; for (NSUInteger c = 0; c < [password length]; ++c) { - NSString *passwordCharacter = [password substringWithRange:NSMakeRange( c, 1 )]; + const char passwordCharacter = [password substringWithRange:NSMakeRange( c, 1 )].UTF8String[0]; unsigned int characterEntropy = 0; - for (NSString *cipherClass in @[ @"v", @"c", @"a", @"x" ]) { - NSString *charactersForClass = [self charactersForCipherClass:cipherClass]; + for (NSString *characterClass in @[ @"v", @"c", @"a", @"x" ]) { + char const *charactersForClass = mpw_charactersInClass( characterClass.UTF8String[0] ); - if ([charactersForClass rangeOfString:passwordCharacter].location != NSNotFound) { + if (strchr( charactersForClass, passwordCharacter )) { // Found class for password character. - characterEntropy = (BN_ULONG)[charactersForClass length]; - [cipher appendString:cipherClass]; + characterEntropy = (BN_ULONG)strlen(charactersForClass); break; } } - if (!characterEntropy) { - [cipher appendString:@"b"]; + if (!characterEntropy) characterEntropy = 256 /* a byte */; - } BN_mul_word( permutations, characterEntropy ); } diff --git a/MasterPassword/ObjC/MPAlgorithmV1.m b/MasterPassword/ObjC/MPAlgorithmV1.m index 22e2ef0f..44be3caa 100644 --- a/MasterPassword/ObjC/MPAlgorithmV1.m +++ b/MasterPassword/ObjC/MPAlgorithmV1.m @@ -20,9 +20,9 @@ @implementation MPAlgorithmV1 -- (NSUInteger)version { +- (MPAlgorithmVersion)version { - return 1; + return MPAlgorithmVersion1; } - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit { @@ -45,49 +45,4 @@ return YES; } -- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter - variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key { - - // Determine the seed whose bytes will be used for calculating a password - uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ), ncontextLength = htonl( context.length ); - NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; - NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; - NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )]; - NSString *scope = [self scopeForVariant:variant]; - trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)", - [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); - NSData *seed = [[NSData dataByConcatenatingDatas: - [scope dataUsingEncoding:NSUTF8StringEncoding], - nameLengthBytes, - [name dataUsingEncoding:NSUTF8StringEncoding], - counterBytes, - context? contextLengthBytes: nil, - [context dataUsingEncoding:NSUTF8StringEncoding], - nil] - hmacWith:PearlHashSHA256 key:key.keyData]; - trc( @"seed is: %@", [seed encodeHex] ); - const unsigned char *seedBytes = seed.bytes; - - // Determine the cipher from the first seed byte. - NSAssert( [seed length], @"Missing seed." ); - NSArray *typeCiphers = [self ciphersForType:type]; - NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]]; - trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher ); - - // Encode the content, character by character, using subsequent seed bytes and the cipher. - NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." ); - NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; - for (NSUInteger c = 0; c < [cipher length]; ++c) { - uint16_t keyByte = seedBytes[c + 1]; - NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )]; - NSString *cipherClassCharacters = [self charactersForCipherClass:cipherClass]; - NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )]; - - trc( @"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character ); - [content appendString:character]; - } - - return content; -} - @end diff --git a/MasterPassword/ObjC/MPAlgorithmV2.h b/MasterPassword/ObjC/MPAlgorithmV2.h index ec76634a..ea7c600b 100644 --- a/MasterPassword/ObjC/MPAlgorithmV2.h +++ b/MasterPassword/ObjC/MPAlgorithmV2.h @@ -9,7 +9,7 @@ */ // -// MPAlgorithmV1 +// MPAlgorithmV2 // // Created by Maarten Billemont on 17/07/12. // Copyright 2012 lhunath (Maarten Billemont). All rights reserved. diff --git a/MasterPassword/ObjC/MPAlgorithmV2.m b/MasterPassword/ObjC/MPAlgorithmV2.m index b5db03a2..a68d24bf 100644 --- a/MasterPassword/ObjC/MPAlgorithmV2.m +++ b/MasterPassword/ObjC/MPAlgorithmV2.m @@ -9,7 +9,7 @@ */ // -// MPAlgorithmV1 +// MPAlgorithmV2 // // Created by Maarten Billemont on 17/07/12. // Copyright 2012 lhunath (Maarten Billemont). All rights reserved. @@ -21,9 +21,9 @@ @implementation MPAlgorithmV2 -- (NSUInteger)version { +- (MPAlgorithmVersion)version { - return 2; + return MPAlgorithmVersion2; } - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit { @@ -46,52 +46,4 @@ return YES; } -- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter - variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key { - - // Determine the seed whose bytes will be used for calculating a password - NSData *nameBytes = [name dataUsingEncoding:NSUTF8StringEncoding]; - NSData *contextBytes = [context dataUsingEncoding:NSUTF8StringEncoding]; - uint32_t ncounter = htonl( counter ), nnameLength = htonl( nameBytes.length ), ncontextLength = htonl( contextBytes.length ); - NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; - NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; - NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )]; - NSString *scope = [self scopeForVariant:variant]; - NSData *scopeBytes = [scope dataUsingEncoding:NSUTF8StringEncoding]; - trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)", - [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); - NSData *seed = [[NSData dataByConcatenatingDatas: - scopeBytes, - nameLengthBytes, - nameBytes, - counterBytes, - context? contextLengthBytes: nil, - contextBytes, - nil] - hmacWith:PearlHashSHA256 key:key.keyData]; - trc( @"seed is: %@", [seed encodeHex] ); - const unsigned char *seedBytes = seed.bytes; - - // Determine the cipher from the first seed byte. - NSAssert( [seed length], @"Missing seed." ); - NSArray *typeCiphers = [self ciphersForType:type]; - NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]]; - trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher ); - - // Encode the content, character by character, using subsequent seed bytes and the cipher. - NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." ); - NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; - for (NSUInteger c = 0; c < [cipher length]; ++c) { - uint16_t keyByte = seedBytes[c + 1]; - NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )]; - NSString *cipherClassCharacters = [self charactersForCipherClass:cipherClass]; - NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )]; - - trc( @"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character ); - [content appendString:character]; - } - - return content; -} - @end diff --git a/MasterPassword/ObjC/MPAlgorithmV3.h b/MasterPassword/ObjC/MPAlgorithmV3.h new file mode 100644 index 00000000..a7e0bbeb --- /dev/null +++ b/MasterPassword/ObjC/MPAlgorithmV3.h @@ -0,0 +1,21 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPAlgorithmV3 +// +// Created by Maarten Billemont on 13/01/15. +// Copyright 2015 lhunath (Maarten Billemont). All rights reserved. +// + +#import "MPAlgorithmV2.h" + +@interface MPAlgorithmV3 : MPAlgorithmV2 +@end diff --git a/MasterPassword/ObjC/MPAlgorithmV3.m b/MasterPassword/ObjC/MPAlgorithmV3.m new file mode 100644 index 00000000..0be028ee --- /dev/null +++ b/MasterPassword/ObjC/MPAlgorithmV3.m @@ -0,0 +1,48 @@ +/** +* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) +* +* See the enclosed file LICENSE for license information (LGPLv3). If you did +* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt +* +* @author Maarten Billemont +* @license http://www.gnu.org/licenses/lgpl-3.0.txt +*/ + +// +// MPAlgorithmV3 +// +// Created by Maarten Billemont on 13/01/15. +// Copyright 2015 lhunath (Maarten Billemont). All rights reserved. +// + +#import "MPAlgorithmV3.h" +#import "MPEntities.h" + +@implementation MPAlgorithmV3 + +- (MPAlgorithmVersion)version { + + return MPAlgorithmVersion3; +} + +- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit { + + if (site.version != [self version] - 1) + // Only migrate from previous version. + return NO; + + if (!explicit) { + if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) { + // This migration requires explicit permission for types of the generated class. + site.requiresExplicitMigration = YES; + return NO; + } + } + + // Apply migration. + site.requiresExplicitMigration = NO; + site.version = [self version]; + return YES; +} + +@end diff --git a/MasterPassword/ObjC/MPTypes.h b/MasterPassword/ObjC/MPTypes.h index f5d4f763..4d86cce7 100644 --- a/MasterPassword/ObjC/MPTypes.h +++ b/MasterPassword/ObjC/MPTypes.h @@ -6,45 +6,6 @@ // Copyright (c) 2012 Lyndir. All rights reserved. // -#import "MPKey.h" - -typedef NS_ENUM( NSUInteger, MPSiteTypeClass ) { - /** Generate the password. */ - MPSiteTypeClassGenerated = 1 << 4, - /** Store the password. */ - MPSiteTypeClassStored = 1 << 5, -}; - -typedef NS_ENUM( NSUInteger, MPSiteVariant ) { - /** Generate the password. */ - MPSiteVariantPassword, - /** Generate the login name. */ - MPSiteVariantLogin, - /** Generate a security answer. */ - MPSiteVariantAnswer, -}; - -typedef NS_ENUM( NSUInteger, MPSiteFeature ) { - /** Export the key-protected content data. */ - MPSiteFeatureExportContent = 1 << 10, - /** Never export content. */ - MPSiteFeatureDevicePrivate = 1 << 11, -}; - -typedef NS_ENUM(NSUInteger, MPSiteType) { - MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0, - MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0, - - MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent, - MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate, -}; - #define MPErrorDomain @"MPErrorDomain" #define MPSignedInNotification @"MPSignedInNotification" diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.m b/MasterPassword/ObjC/iOS/MPPasswordCell.m index 369ffffc..5fe6ec36 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordCell.m @@ -284,7 +284,7 @@ initSheet:^(UIActionSheet *sheet) { MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) { - MPSiteType type = [typeNumber unsignedIntegerValue]; + MPSiteType type = (MPSiteType)[typeNumber unsignedIntegerValue]; NSString *typeName = [MPAlgorithmDefault nameOfType:type]; if (type == mainSite.type) [sheet addButtonWithTitle:strf( @"● %@", typeName )]; @@ -295,7 +295,7 @@ if (buttonIndex == [sheet cancelButtonIndex]) return; - MPSiteType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong; + MPSiteType type = (MPSiteType)[[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong; [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { MPSiteEntity *site = [self siteInContext:context]; diff --git a/MasterPassword/ObjC/iOS/MPPreferencesViewController.m b/MasterPassword/ObjC/iOS/MPPreferencesViewController.m index f813958d..c4e09e19 100644 --- a/MasterPassword/ObjC/iOS/MPPreferencesViewController.m +++ b/MasterPassword/ObjC/iOS/MPPreferencesViewController.m @@ -182,7 +182,7 @@ return nil; } -- (enum MPSiteType)typeForSelectedSegment { +- (MPSiteType)typeForSelectedSegment { NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex; NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex; diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index d06f6604..81dc709c 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -8,7 +8,9 @@ /* Begin PBXBuildFile section */ 93D390228D0B8ED51C0AFDB4 /* bashlib in Resources */ = {isa = PBXBuildFile; fileRef = 93D39E71D6BAECEC4CD886F4 /* bashlib */; }; + 93D3906F9ED6493C81A59FC5 /* mpw-algorithm_v2.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */; }; 93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C426E03358384018E85 /* MPAnswersViewController.m */; }; + 93D390CE9E30180167317730 /* mpw-algorithm_v1.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396F918E6470DB846C17F /* mpw-algorithm_v1.c */; }; 93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */; }; 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; }; 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; }; @@ -20,12 +22,14 @@ 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; }; 93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3957D76F71A652716EECC /* MPStoreViewController.m */; }; 93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; }; + 93D393E4A18BF21ABC229C1E /* mpw-algorithm_v3.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */; }; 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; }; 93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; }; 93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */; }; 93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; }; 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; }; 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; }; + 93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */; }; 93D3958C13B557F60F63C72B /* distribute in Resources */ = {isa = PBXBuildFile; fileRef = 93D3979016BF0C5B29D1340D /* distribute */; }; 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 */; }; @@ -52,6 +56,7 @@ 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; }; 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; + 93D39C7B89BEA08A829C66F4 /* mpw-algorithm_v0.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */; }; 93D39CEA71BD460ED4876920 /* build in Resources */ = {isa = PBXBuildFile; fileRef = 93D39B573E4DE98BAE518215 /* build */; }; 93D39D38356F59DBEF934D70 /* MPAppDelegate_InApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */; }; 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; }; @@ -216,6 +221,7 @@ DAA1765219D8B82B0044227B /* copy_pw.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763C19D8B82B0044227B /* copy_pw.png */; }; DAA1765319D8B82B0044227B /* choose_type@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763D19D8B82B0044227B /* choose_type@2x.png */; }; DAA1765419D8B82B0044227B /* choose_type.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763E19D8B82B0044227B /* choose_type.png */; }; + DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */; }; DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; }; DABD39371711E29700CF925C /* avatar-0.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366C1711E29400CF925C /* avatar-0.png */; }; DABD39381711E29700CF925C /* avatar-0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366D1711E29400CF925C /* avatar-0@2x.png */; }; @@ -462,6 +468,8 @@ 93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = ""; }; 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = ""; }; + 93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = ""; }; + 93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v0.c"; sourceTree = ""; }; 93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = ""; }; 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = ""; }; 93D39149A5F1F9B174D6D061 /* MPStoreViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoreViewController.h; sourceTree = ""; }; @@ -484,12 +492,14 @@ 93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = ""; }; 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = ""; }; 93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_InApp.m; sourceTree = ""; }; + 93D394D73F5BC92297CE8D7B /* MPAlgorithmV3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV3.h; sourceTree = ""; }; 93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = ""; }; 93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = ""; }; 93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = ""; }; 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm.c"; sourceTree = ""; }; 93D396C311C3725870343EE0 /* mpw-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-util.c"; sourceTree = ""; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; }; + 93D396F918E6470DB846C17F /* mpw-algorithm_v1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v1.c"; sourceTree = ""; }; 93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = ""; }; 93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = ""; }; 93D397288D0655822A73A539 /* mpw-tests-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-tests-util.c"; sourceTree = ""; }; @@ -538,6 +548,7 @@ 93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = ""; }; 93D39CF7DB942C69D1C5D6BE /* mpw-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-util.h"; sourceTree = ""; }; 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = ""; }; + 93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v3.c"; sourceTree = ""; }; 93D39D6604447D7708039155 /* MPAnswersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAnswersViewController.h; sourceTree = ""; }; 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = ""; }; 93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = ""; }; @@ -548,6 +559,7 @@ 93D39F556F2F142740A65E59 /* MPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPWebViewController.h; sourceTree = ""; }; 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = ""; }; 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = ""; }; + 93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV3.m; sourceTree = ""; }; DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; DA071BF1190187FE00179766 /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "empty@2x.png"; sourceTree = ""; }; DA071BF2190187FE00179766 /* empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = empty.png; sourceTree = ""; }; @@ -1622,6 +1634,10 @@ 93D396C311C3725870343EE0 /* mpw-util.c */, 93D39CF7DB942C69D1C5D6BE /* mpw-util.h */, 93D39245A478883C672818F3 /* mpw.bashrc */, + 93D396F918E6470DB846C17F /* mpw-algorithm_v1.c */, + 93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */, + 93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */, + 93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */, ); name = C; path = ../../C; @@ -2581,6 +2597,8 @@ 93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */, 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */, 93D39A97A7D48CB3B784194D /* MPAlgorithmV2.h */, + 93D394D73F5BC92297CE8D7B /* MPAlgorithmV3.h */, + 93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */, ); name = ObjC; path = ..; @@ -3622,6 +3640,7 @@ 93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */, 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */, 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */, + DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */, 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */, DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */, 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */, @@ -3636,6 +3655,11 @@ 93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */, 93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */, 93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */, + 93D390CE9E30180167317730 /* mpw-algorithm_v1.c in Sources */, + 93D39C7B89BEA08A829C66F4 /* mpw-algorithm_v0.c in Sources */, + 93D3906F9ED6493C81A59FC5 /* mpw-algorithm_v2.c in Sources */, + 93D393E4A18BF21ABC229C1E /* mpw-algorithm_v3.c in Sources */, + 93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };