diff --git a/core/c/mpw-algorithm.c b/core/c/mpw-algorithm.c index d7fd4c22..f09b1f55 100644 --- a/core/c/mpw-algorithm.c +++ b/core/c/mpw-algorithm.c @@ -27,7 +27,7 @@ #define MP_p 2 #define MP_hash PearlHashSHA256 -const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) { +MPMasterKey mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) { if (!fullName || !masterPassword) return NULL; @@ -47,7 +47,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(MPMasterKey masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion) { if (!masterKey || !siteName) diff --git a/core/c/mpw-algorithm.h b/core/c/mpw-algorithm.h index 9cc8da24..13d08986 100644 --- a/core/c/mpw-algorithm.h +++ b/core/c/mpw-algorithm.h @@ -19,7 +19,10 @@ // NOTE: mpw is currently NOT thread-safe. #include "mpw-types.h" -typedef enum(unsigned int, MPAlgorithmVersion) { +#ifndef _MPW_ALGORITHM_H +#define _MPW_ALGORITHM_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. */ @@ -28,16 +31,20 @@ typedef enum(unsigned int, MPAlgorithmVersion) { MPAlgorithmVersion2, /** V3 is the current version. */ MPAlgorithmVersion3, + + MPAlgorithmVersionCurrent = MPAlgorithmVersion3, + MPAlgorithmVersionLatest = 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. */ -const uint8_t *mpw_masterKeyForUser( +MPMasterKey mpw_masterKeyForUser( 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. */ const char *mpw_passwordForSite( - const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, + MPMasterKey masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion); + +#endif // _MPW_ALGORITHM_H diff --git a/core/c/mpw-algorithm_v0.c b/core/c/mpw-algorithm_v0.c index 7f9a63e3..7ae392e0 100644 --- a/core/c/mpw-algorithm_v0.c +++ b/core/c/mpw-algorithm_v0.c @@ -43,7 +43,7 @@ static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedBy return classCharacters[seedByte % strlen( classCharacters )]; } -static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) { +static MPMasterKey mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) { const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); trc( "algorithm: v%d\n", 0 ); @@ -66,18 +66,18 @@ static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char * // 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 ); + const uint8_t *masterKey = mpw_scrypt( MPMasterKeySize, 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_id_buf( masterKey, MP_dkLen ) ); + trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); return masterKey; } -static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, +static const char *mpw_passwordForSite_v0(MPMasterKey masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPSiteVariant siteVariant, const char *siteContext) { const char *siteScope = mpw_scopeForVariant( siteVariant ); @@ -110,7 +110,7 @@ static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char * } trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) ); - const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MPMasterKeySize, sitePasswordInfo, sitePasswordInfoSize ); mpw_free( sitePasswordInfo, sitePasswordInfoSize ); if (!sitePasswordSeed) { ftl( "Could not allocate site seed: %d\n", errno ); diff --git a/core/c/mpw-algorithm_v1.c b/core/c/mpw-algorithm_v1.c index bc84ed43..19fa8d88 100644 --- a/core/c/mpw-algorithm_v1.c +++ b/core/c/mpw-algorithm_v1.c @@ -28,7 +28,7 @@ #define MP_p 2 #define MP_hash PearlHashSHA256 -static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) { +static MPMasterKey mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) { const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); trc( "algorithm: v%d\n", 1 ); @@ -51,18 +51,18 @@ static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char * // 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 ); + MPMasterKey masterKey = mpw_scrypt( MPMasterKeySize, 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_id_buf( masterKey, MP_dkLen ) ); + trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); return masterKey; } -static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, +static const char *mpw_passwordForSite_v1(MPMasterKey masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPSiteVariant siteVariant, const char *siteContext) { const char *siteScope = mpw_scopeForVariant( siteVariant ); @@ -95,7 +95,7 @@ static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char * } trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) ); - const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MPMasterKeySize, sitePasswordInfo, sitePasswordInfoSize ); mpw_free( sitePasswordInfo, sitePasswordInfoSize ); if (!sitePasswordSeed) { ftl( "Could not allocate site seed: %d\n", errno ); diff --git a/core/c/mpw-algorithm_v2.c b/core/c/mpw-algorithm_v2.c index b35a2f30..16354530 100644 --- a/core/c/mpw-algorithm_v2.c +++ b/core/c/mpw-algorithm_v2.c @@ -28,7 +28,7 @@ #define MP_p 2 #define MP_hash PearlHashSHA256 -static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) { +static MPMasterKey mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) { const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); trc( "algorithm: v%d\n", 2 ); @@ -51,18 +51,18 @@ static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char * // 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 ); + const uint8_t *masterKey = mpw_scrypt( MPMasterKeySize, 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_id_buf( masterKey, MP_dkLen ) ); + trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); return masterKey; } -static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, +static const char *mpw_passwordForSite_v2(MPMasterKey masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPSiteVariant siteVariant, const char *siteContext) { const char *siteScope = mpw_scopeForVariant( siteVariant ); @@ -95,7 +95,7 @@ static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char * } trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) ); - const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MPMasterKeySize, sitePasswordInfo, sitePasswordInfoSize ); mpw_free( sitePasswordInfo, sitePasswordInfoSize ); if (!sitePasswordSeed) { ftl( "Could not allocate site seed: %d\n", errno ); diff --git a/core/c/mpw-algorithm_v3.c b/core/c/mpw-algorithm_v3.c index 06694f8e..96f48eba 100644 --- a/core/c/mpw-algorithm_v3.c +++ b/core/c/mpw-algorithm_v3.c @@ -28,7 +28,7 @@ #define MP_p 2 #define MP_hash PearlHashSHA256 -static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) { +static MPMasterKey mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) { const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); trc( "algorithm: v%d\n", 3 ); @@ -51,18 +51,18 @@ static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char * // 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 ); + const uint8_t *masterKey = mpw_scrypt( MPMasterKeySize, 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_id_buf( masterKey, MP_dkLen ) ); + trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); return masterKey; } -static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, +static const char *mpw_passwordForSite_v3(MPMasterKey masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPSiteVariant siteVariant, const char *siteContext) { const char *siteScope = mpw_scopeForVariant( siteVariant ); @@ -95,7 +95,7 @@ static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char * } trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) ); - const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize ); + const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MPMasterKeySize, sitePasswordInfo, sitePasswordInfoSize ); mpw_free( sitePasswordInfo, sitePasswordInfoSize ); if (!sitePasswordSeed) { ftl( "Could not allocate site seed: %d\n", errno ); diff --git a/core/c/mpw-marshall.c b/core/c/mpw-marshall.c new file mode 100644 index 00000000..4d5cf629 --- /dev/null +++ b/core/c/mpw-marshall.c @@ -0,0 +1,522 @@ +//============================================================================== +// This file is part of Master Password. +// Copyright (c) 2011-2017, Maarten Billemont. +// +// Master Password is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Master Password is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You can find a copy of the GNU General Public License in the +// LICENSE file. Alternatively, see . +//============================================================================== + + +#include +#include +#include +#include "mpw-marshall.h" +#include "mpw-util.h" + +MPMarshalledUser mpw_marshall_user( + const char *fullName, MPMasterKey masterKey, const MPAlgorithmVersion algorithmVersion) { + + return (MPMarshalledUser){ + .name = fullName, + .key = masterKey, + .version = algorithmVersion, + + .avatar = 0, + .defaultType = MPSiteTypeGeneratedLong, + .lastUsed = 0, + + .sites_count = 0, + .sites = NULL, + }; +}; + +MPMarshalledSite mpw_marshall_site( + MPMarshalledUser *marshalledUser, + const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion) { + + marshalledUser->sites = realloc( marshalledUser->sites, marshalledUser->sites_count + 1 ); + return marshalledUser->sites[marshalledUser->sites_count++] = (MPMarshalledSite){ + .name = siteName, + .type = siteType, + .counter = siteCounter, + .version = algorithmVersion, + + .loginName = NULL, + .loginGenerated = 0, + + .url = NULL, + .uses = 0, + .lastUsed = 0, + + .questions_count = 0, + .questions = NULL, + }; +}; + +MPMarshalledQuestion mpw_marshal_question( + MPMarshalledSite *marshalledSite, const char *keyword) { + + marshalledSite->questions = realloc( marshalledSite->questions, marshalledSite->questions_count + 1 ); + return marshalledSite->questions[marshalledSite->questions_count++] = (MPMarshalledQuestion){ + .keyword = keyword, + }; +} + +#define try_asprintf(...) ({ if (asprintf( __VA_ARGS__ ) < 0) return false; }) + +bool mpw_marshall_write_flat( + char **out, bool redacted, const MPMarshalledUser *marshalledUser) { + + try_asprintf( out, "# Master Password site export\n" ); + if (redacted) + try_asprintf( out, "# Export of site names and passwords in clear-text.\n" ); + else + try_asprintf( out, "# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" ); + try_asprintf( out, "# \n" ); + try_asprintf( out, "##\n" ); + try_asprintf( out, "# Format: %d\n", 1 ); + + size_t dateSize = 21; + char dateString[dateSize]; + time_t now = time( NULL ); + if (strftime( dateString, dateSize, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", gmtime( &now ) )) + try_asprintf( out, "# Date: %s\n", dateString ); + try_asprintf( out, "# User Name: %s\n", marshalledUser->name ); + try_asprintf( out, "# Full Name: %s\n", marshalledUser->name ); + try_asprintf( out, "# Avatar: %u\n", marshalledUser->avatar ); + try_asprintf( out, "# Key ID: %s\n", mpw_id_buf( marshalledUser->key, MPMasterKeySize ) ); + try_asprintf( out, "# Algorithm: %d\n", marshalledUser->version ); + try_asprintf( out, "# Default Type: %d\n", marshalledUser->defaultType ); + try_asprintf( out, "# Passwords: %s\n", redacted? "PROTECTED": "VISIBLE" ); + try_asprintf( out, "##\n" ); + try_asprintf( out, "#\n" ); + try_asprintf( out, "# Last Times Password Login\t Site\tSite\n" ); + try_asprintf( out, "# used used type name\t name\tpassword\n" ); + + // Sites. + for (int s = 0; s < marshalledUser->sites_count; ++s) { + MPMarshalledSite site = marshalledUser->sites[s]; + + const char *content = NULL; + if (!redacted && site.type & MPSiteTypeClassGenerated) + content = mpw_passwordForSite( marshalledUser->key, site.name, site.type, site.counter, + MPSiteVariantPassword, NULL, site.version ); + // TODO: Personal Passwords + + if (strftime( dateString, dateSize, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", gmtime( &site.lastUsed ) )) + try_asprintf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n", + dateString, (long)site.uses, (long)site.type, (long)site.version, (long)site.counter, + site.loginName?: "", site.name, content?: "" ); + } + return true; +} + +bool mpw_marshall_write_json( + char **out, bool redacted, const MPMarshalledUser *marshalledUser) { + + json_object *json_out = json_object_new_object(); + + // Section: "export" + json_object *json_export = json_object_new_object(); + json_object_object_add( json_out, "export", json_export ); + json_object_object_add( json_export, "format", json_object_new_int( 1 ) ); + json_object_object_add( json_export, "redacted", json_object_new_boolean( redacted ) ); + + size_t dateSize = 21; + char dateString[dateSize]; + time_t now = time( NULL ); + if (strftime( dateString, dateSize, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", gmtime( &now ) )) + json_object_object_add( json_export, "date", json_object_new_string( dateString ) ); + json_object_put( json_export ); + + // Section: "user" + json_object *json_user = json_object_new_object(); + json_object_object_add( json_out, "user", json_user ); + json_object_object_add( json_user, "avatar", json_object_new_int( marshalledUser->avatar ) ); + json_object_object_add( json_user, "full_name", json_object_new_string( marshalledUser->name ) ); + + if (strftime( dateString, dateSize, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", gmtime( &marshalledUser->lastUsed ) )) + json_object_object_add( json_user, "last_used", json_object_new_string( dateString ) ); + json_object_object_add( json_user, "key_id", json_object_new_string( mpw_id_buf( marshalledUser->key, MPMasterKeySize ) ) ); + + json_object_object_add( json_user, "algorithm", json_object_new_int( marshalledUser->version ) ); + json_object_object_add( json_user, "default_type", json_object_new_int( marshalledUser->defaultType ) ); + json_object_put( json_user ); + + // Section "sites" + json_object *json_sites = json_object_new_object(); + json_object_object_add( json_out, "sites", json_sites ); + for (int s = 0; s < marshalledUser->sites_count; ++s) { + MPMarshalledSite site = marshalledUser->sites[s]; + + const char *content = site.content; + if (!redacted && site.type & MPSiteTypeClassGenerated) + content = mpw_passwordForSite( marshalledUser->key, site.name, site.type, site.counter, + MPSiteVariantPassword, NULL, site.version ); + // TODO: Personal Passwords + //else if (redacted && content) + // content = aes128_cbc( marshalledUser->key, content ); + + json_object *json_site = json_object_new_object(); + json_object_object_add( json_sites, site.name, json_site ); + json_object_object_add( json_site, "type", json_object_new_int( site.type ) ); + json_object_object_add( json_site, "counter", json_object_new_int( site.counter ) ); + json_object_object_add( json_site, "algorithm", json_object_new_int( site.version ) ); + if (content) + json_object_object_add( json_site, "password", json_object_new_string( content ) ); + + json_object_object_add( json_site, "login_name", json_object_new_string( site.loginName?: "" ) ); + json_object_object_add( json_site, "login_generated", json_object_new_boolean( site.loginGenerated ) ); + + json_object_object_add( json_site, "uses", json_object_new_int( site.uses ) ); + if (strftime( dateString, dateSize, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", gmtime( &site.lastUsed ) )) + json_object_object_add( json_site, "last_used", json_object_new_string( dateString ) ); + + json_object *json_site_questions = json_object_new_object(); + json_object_object_add( json_site, "questions", json_site_questions ); + for (int q = 0; q < site.questions_count; ++q) { + MPMarshalledQuestion question = site.questions[q]; + + json_object *json_site_question = json_object_new_object(); + json_object_object_add( json_site_questions, question.keyword, json_site_question ); + + if (!redacted) + json_object_object_add( json_site_question, "answer", json_object_new_string( + mpw_passwordForSite( marshalledUser->key, site.name, MPSiteTypeGeneratedPhrase, 1, + MPSiteVariantAnswer, question.keyword, site.version ) ) ); + json_object_put( json_site_question ); + } + json_object_put( json_site_questions ); + + json_object *json_site_mpw = json_object_new_object(); + json_object_object_add( json_site, "_ext_mpw", json_site_mpw ); + json_object_object_add( json_site_mpw, "url", json_object_new_string( site.url ) ); + json_object_put( json_site_mpw ); + json_object_put( json_site ); + } + json_object_put( json_sites ); + + try_asprintf( out, "%s\n", json_object_to_json_string_ext( json_out, JSON_C_TO_STRING_PRETTY ) ); + json_object_put( json_out ); + + return true; +} + +bool mpw_marshall_write( + char **out, const MPMarshallFormat outFormat, bool redacted, + const MPMarshalledUser *marshalledUser) { + + switch (outFormat) { + case MPMarshallFormatFlat: + return mpw_marshall_write_flat( out, redacted, marshalledUser ); + case MPMarshallFormatJSON: + return mpw_marshall_write_json( out, redacted, marshalledUser ); + } + + return false; +} + +MPMarshalledUser mpw_marshall_read_flat( + char *in) { + +// // Compile patterns. +// static NSRegularExpression *headerPattern; +// static NSArray *sitePatterns; +// NSError *error = NULL; +// if (!headerPattern) { +// headerPattern = [[NSRegularExpression alloc] +// initWithPattern:"^#[[:space:]]*([^:]+): (.*)" +// options:(NSRegularExpressionOptions)0 error:&error]; +// if (error) { +// MPError( error, "Error loading the header pattern." ); +// return MPImportResultInternalError; +// } +// } +// if (!sitePatterns) { +// sitePatterns = @[ +// [[NSRegularExpression alloc] // Format 0 +// initWithPattern:"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)" +// options:(NSRegularExpressionOptions)0 error:&error], +// [[NSRegularExpression alloc] // Format 1 +// initWithPattern:"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)" +// options:(NSRegularExpressionOptions)0 error:&error] +// ]; +// if (error) { +// MPError( error, "Error loading the site patterns." ); +// return MPImportResultInternalError; +// } +// } +// + // Parse import data. + int importFormat = 0; + MPMarshalledUser user; + int importAvatar = -1; + int importKeyID; + char *importUserName = NULL; + MPAlgorithmVersion importAlgorithm = MPAlgorithmVersionCurrent; + MPSiteType importDefaultType = (MPSiteType)0; + bool headerStarted = false, headerEnded = false, clearText = false; +// NSMutableSet *sitesToDelete = [NSMutableSet set]; +// NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]]; +// NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; +// for (NSString *importedSiteLine in importedSiteLines) { + +// if ([importedSiteLine hasPrefix:"#"]) { +// // Comment or header +// if (!headerStarted) { +// if ([importedSiteLine isEqualToString:"##"]) +// headerStarted = YES; +// continue; +// } +// if (headerEnded) +// continue; +// if ([importedSiteLine isEqualToString:"##"]) { +// headerEnded = YES; +// continue; +// } +// +// // Header +// if ([headerPattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0 +// range:NSMakeRange( 0, [importedSiteLine length] )] != 1) { +// err( "Invalid header format in line: %", importedSiteLine ); +// return MPImportResultMalformedInput; +// } +// NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0 +// range:NSMakeRange( 0, [importedSiteLine length] )] lastObject]; +// NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]]; +// NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]]; +// +// if ([headerName isEqualToString:"Format"]) { +// importFormat = (NSUInteger)[headerValue integerValue]; +// if (importFormat >= [sitePatterns count]) { +// err( "Unsupported import format: %lu", (unsigned long)importFormat ); +// return MPImportResultInternalError; +// } +// } +// if (([headerName isEqualToString:"User Name"] || [headerName isEqualToString:"Full Name"]) && !importUserName) { +// importUserName = headerValue; +// +// NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; +// userFetchRequest.predicate = [NSPredicate predicateWithFormat:"name == %", importUserName]; +// NSArray *users = [context executeFetchRequest:userFetchRequest error:&error]; +// if (!users) { +// MPError( error, "While looking for user: %@.", importUserName ); +// return MPImportResultInternalError; +// } +// if ([users count] > 1) { +// err( "While looking for user: %@, found more than one: %lu", importUserName, (unsigned long)[users count] ); +// return MPImportResultInternalError; +// } +// +// user = [users lastObject]; +// dbg( "Existing user? %", [user debugDescription] ); +// } +// if ([headerName isEqualToString:"Avatar"]) +// importAvatar = (NSUInteger)[headerValue integerValue]; +// if ([headerName isEqualToString:"Key ID"]) +// importKeyID = [headerValue decodeHex]; +// if ([headerName isEqualToString:"Version"]) { +// importBundleVersion = headerValue; +// importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion ); +// } +// if ([headerName isEqualToString:"Algorithm"]) +// importAlgorithm = MPAlgorithmForVersion( (MPAlgorithmVersion)[headerValue integerValue] ); +// if ([headerName isEqualToString:"Default Type"]) +// importDefaultType = (MPSiteType)[headerValue integerValue]; +// if ([headerName isEqualToString:"Passwords"]) { +// if ([headerValue isEqualToString:"VISIBLE"]) +// clearText = YES; +// } +// +// continue; +// } +// if (!headerEnded) +// continue; +// if (![importUserName length]) +// return MPImportResultMalformedInput; +// if (![importedSiteLine length]) +// continue; +// +// // Site +// NSRegularExpression *sitePattern = sitePatterns[importFormat]; +// if ([sitePattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0 +// range:NSMakeRange( 0, [importedSiteLine length] )] != 1) { +// err( "Invalid site format in line: %", importedSiteLine ); +// return MPImportResultMalformedInput; +// } +// NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0 +// range:NSMakeRange( 0, [importedSiteLine length] )] lastObject]; +// NSString *lastUsed, *uses, *type, *version, *counter, *siteName, *loginName, *exportContent; +// switch (importFormat) { +// case 0: +// lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]]; +// uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]]; +// type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]]; +// version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]]; +// if ([version length]) +// version = [version substringFromIndex:1]; // Strip the leading colon. +// counter = ""; +// loginName = ""; +// siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]]; +// exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]]; +// break; +// case 1: +// lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]]; +// uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]]; +// type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]]; +// version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]]; +// if ([version length]) +// version = [version substringFromIndex:1]; // Strip the leading colon. +// counter = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]]; +// if ([counter length]) +// counter = [counter substringFromIndex:1]; // Strip the leading colon. +// loginName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]]; +// siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:7]]; +// exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:8]]; +// break; +// default: +// err( "Unexpected import format: %lu", (unsigned long)importFormat ); +// return MPImportResultInternalError; +// } +// +// // Find existing site. +// if (user) { +// siteFetchRequest.predicate = [NSPredicate predicateWithFormat:"name == %@ AND user == %", siteName, user]; +// NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error]; +// if (!existingSites) { +// MPError( error, "Lookup of existing sites failed for site: %@, user: %@.", siteName, user.userID ); +// return MPImportResultInternalError; +// } +// if ([existingSites count]) { +// dbg( "Existing sites: %", existingSites ); +// [sitesToDelete addObjectsFromArray:existingSites]; +// } +// } +// [importedSiteSites addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]]; +// dbg( "Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%", +// lastUsed, uses, type, version, counter, loginName, siteName, exportContent ); +// } +// +// // Ask for confirmation to import these sites and the master password of the user. +// inf( "Importing %lu sites, deleting %lu sites, for user: %", (unsigned long)[importedSiteSites count], +// (unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] ); +// NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count], +// [sitesToDelete count] ); +// if (!userMasterPassword) { +// inf( "Import cancelled." ); +// return MPImportResultCancelled; +// } +// MPKey *userKey = [[MPKey alloc] initForFullName:user? user.name: importUserName withMasterPassword:userMasterPassword]; +// if (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID]) +// return MPImportResultInvalidPassword; +// __block MPKey *importKey = userKey; +// if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID]) +// importKey = [[MPKey alloc] initForFullName:importUserName withMasterPassword:askImportPassword( importUserName )]; +// if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID]) +// return MPImportResultInvalidPassword; +// +// // Delete existing sites. +// if (sitesToDelete.count) +// [sitesToDelete enumerateObjectsUsingBlock:^(id obj, bool *stop) { +// inf( "Deleting site: %@, it will be replaced by an imported site.", [obj name] ); +// [context deleteObject:obj]; +// }]; +// +// // Make sure there is a user. +// if (user) { +// if (importAvatar != NSNotFound) +// user.avatar = importAvatar; +// if (importDefaultType) +// user.defaultType = importDefaultType; +// dbg( "Updating User: %", [user debugDescription] ); +// } +// else { +// user = [MPUserEntity insertNewObjectInContext:context]; +// user.name = importUserName; +// user.algorithm = MPAlgorithmDefault; +// user.keyID = [userKey keyIDForAlgorithm:user.algorithm]; +// user.defaultType = importDefaultType?: user.algorithm.defaultType; +// if (importAvatar != NSNotFound) +// user.avatar = importAvatar; +// dbg( "Created User: %", [user debugDescription] ); +// } +// +// // Import new sites. +// for (NSArray *siteElements in importedSiteSites) { +// NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]]; +// NSUInteger uses = (unsigned)[siteElements[1] integerValue]; +// MPSiteType type = (MPSiteType)[siteElements[2] integerValue]; +// MPAlgorithmVersion version = (MPAlgorithmVersion)[siteElements[3] integerValue]; +// NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound; +// NSString *loginName = [siteElements[5] length]? siteElements[5]: NULL; +// NSString *siteName = siteElements[6]; +// NSString *exportContent = siteElements[7]; +// +// // Create new site. +// id algorithm = MPAlgorithmForVersion( version ); +// Class entityType = [algorithm classOfType:type]; +// if (!entityType) { +// err( "Invalid site type in import file: %@ has type %lu", siteName, (long)type ); +// return MPImportResultInternalError; +// } +// MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context]; +// site.name = siteName; +// site.loginName = loginName; +// site.user = user; +// site.type = type; +// site.uses = uses; +// site.lastUsed = lastUsed; +// site.algorithm = algorithm; +// if ([exportContent length]) { +// if (clearText) +// [site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey]; +// else +// [site.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoSite:site usingKey:userKey]; +// } +// if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound) +// ((MPGeneratedSiteEntity *)site).counter = counter; +// +// dbg( "Created Site: %", [site debugDescription] ); +// } +// +// if (![context saveToStore]) +// return MPImportResultInternalError; +// +// inf( "Import completed successfully." ); +// +// [[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:NULL userInfo:@{ +// MPSitesImportedNotificationUserKey: user +// }]; +// +// return MPImportResultSuccess; + return (MPMarshalledUser){}; +} + +MPMarshalledUser mpw_marshall_read_json( + char *in) { + + return (MPMarshalledUser){}; +} + +MPMarshalledUser mpw_marshall_read( + char *in, const MPMarshallFormat outFormat) { + + switch (outFormat) { + case MPMarshallFormatFlat: + return mpw_marshall_read_flat( in ); + case MPMarshallFormatJSON: + return mpw_marshall_read_json( in ); + } + + return (MPMarshalledUser){}; +} diff --git a/core/c/mpw-marshall.h b/core/c/mpw-marshall.h new file mode 100644 index 00000000..b32fb4bc --- /dev/null +++ b/core/c/mpw-marshall.h @@ -0,0 +1,94 @@ +//============================================================================== +// This file is part of Master Password. +// Copyright (c) 2011-2017, Maarten Billemont. +// +// Master Password is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Master Password is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You can find a copy of the GNU General Public License in the +// LICENSE file. Alternatively, see . +//============================================================================== + +#ifndef _MPW_MARSHALL_H +#define _MPW_MARSHALL_H +#include "mpw-algorithm.h" + +#ifdef NS_ENUM +#define enum(_type, _name) NS_ENUM(_type, _name) +#else +#define enum(_type, _name) _type _name; enum +#endif + +//// Types. + +typedef enum( unsigned int, MPMarshallFormat ) { + /** Generate a key for authentication. */ + MPMarshallFormatFlat, + /** Generate a name for identification. */ + MPMarshallFormatJSON, +}; + +typedef struct MPMarshalledQuestion { + const char *keyword; +} MPMarshalledQuestion; + +typedef struct MPMarshalledSite { + const char *name; + const char *content; + MPSiteType type; + uint32_t counter; + MPAlgorithmVersion version; + + const char *loginName; + bool loginGenerated; + + const char *url; + unsigned int uses; + time_t lastUsed; + + size_t questions_count; + MPMarshalledQuestion *questions; +} MPMarshalledSite; + +typedef struct MPMarshalledUser { + const char *name; + MPMasterKey key; + MPAlgorithmVersion version; + + unsigned int avatar; + MPSiteType defaultType; + time_t lastUsed; + + size_t sites_count; + MPMarshalledSite *sites; +} MPMarshalledUser; + +//// Marshalling. + +bool mpw_marshall_write( + char **out, const MPMarshallFormat outFormat, bool redacted, + const MPMarshalledUser *marshalledUser); + +//// Unmarshalling. + +MPMarshalledUser mpw_marshall_read( + char *in, const MPMarshallFormat outFormat); + +//// Utilities. + +MPMarshalledUser mpw_marshall_user( + const char *fullName, MPMasterKey masterKey, const MPAlgorithmVersion algorithmVersion); +MPMarshalledSite mpw_marshall_site( + MPMarshalledUser *marshalledUser, + const char *siteName, const MPSiteType siteType, const uint32_t siteCounter, const MPAlgorithmVersion algorithmVersion); +MPMarshalledQuestion mpw_marshal_question( + MPMarshalledSite *marshalledSite, const char *keyword); + +#endif // _MPW_MARSHALL_H diff --git a/core/c/mpw-types.c b/core/c/mpw-types.c index 26c36d0c..4915c719 100644 --- a/core/c/mpw-types.c +++ b/core/c/mpw-types.c @@ -143,8 +143,7 @@ const MPSiteVariant mpw_variantWithName(const char *variantName) { if (0 == strcmp( stdVariantName, "a" ) || 0 == strcmp( stdVariantName, "answer" )) return MPSiteVariantAnswer; - fprintf( stderr, "Not a variant name: %s", stdVariantName ); - abort(); + ftl( "Not a variant name: %s", stdVariantName ); } const char *mpw_scopeForVariant(MPSiteVariant variant) { @@ -160,8 +159,7 @@ const char *mpw_scopeForVariant(MPSiteVariant variant) { return "com.lyndir.masterpassword.answer"; } default: { - fprintf( stderr, "Unknown variant: %d", variant ); - abort(); + ftl( "Unknown variant: %d", variant ); } } } @@ -190,8 +188,7 @@ const char *mpw_charactersInClass(char characterClass) { case ' ': return " "; default: { - fprintf( stderr, "Unknown character class: %c", characterClass ); - abort(); + ftl( "Unknown character class: %c", characterClass ); } } } diff --git a/core/c/mpw-types.h b/core/c/mpw-types.h index e3162c48..8cd04a94 100644 --- a/core/c/mpw-types.h +++ b/core/c/mpw-types.h @@ -20,6 +20,7 @@ #define _MPW_TYPES_H #include #include +#include #ifdef NS_ENUM #define enum(_type, _name) NS_ENUM(_type, _name) @@ -27,10 +28,11 @@ #define enum(_type, _name) _type _name; enum #endif -#define MP_dkLen 64 - //// Types. +#define MPMasterKeySize 64 +typedef const uint8_t *MPMasterKey; + typedef enum( unsigned int, MPSiteVariant ) { /** Generate a key for authentication. */ MPSiteVariantPassword, @@ -54,7 +56,7 @@ typedef enum( unsigned int, MPSiteFeature ) { MPSiteFeatureDevicePrivate = 1 << 11, }; -typedef enum( unsigned int, MPSiteType) { +typedef enum( unsigned int, MPSiteType ) { MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0, MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0, MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0, diff --git a/core/c/mpw-util.c b/core/c/mpw-util.c index 52dc059d..fd67d490 100644 --- a/core/c/mpw-util.c +++ b/core/c/mpw-util.c @@ -20,7 +20,7 @@ #include #include -#if COLOR +#if MPW_COLOR #include #include #include @@ -33,17 +33,14 @@ #include "sodium.h" #endif -#ifndef trc -int mpw_verbosity; -#endif - #include "mpw-util.h" +int mpw_verbosity = inf_level; -void mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) { +bool mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) { if (*bufferSize == (size_t)-1) // The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content. - return; + return false; *bufferSize += pushSize; uint8_t *resizedBuffer = realloc( *buffer, *bufferSize ); @@ -52,35 +49,38 @@ void mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void * mpw_free( *buffer, *bufferSize - pushSize ); *bufferSize = (size_t)-1; *buffer = NULL; - return; + return false; } *buffer = resizedBuffer; uint8_t *pushDst = *buffer + *bufferSize - pushSize; memcpy( pushDst, pushBuffer, pushSize ); + return true; } -void mpw_push_string(uint8_t **buffer, size_t *const bufferSize, const char *pushString) { +bool mpw_push_string(uint8_t **buffer, size_t *const bufferSize, const char *pushString) { - mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) ); + return mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) ); } -void mpw_push_int(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) { +bool mpw_push_int(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) { - mpw_push_buf( buffer, bufferSize, &pushInt, sizeof( pushInt ) ); + return mpw_push_buf( buffer, bufferSize, &pushInt, sizeof( pushInt ) ); } -void mpw_free(const void *buffer, const size_t bufferSize) { +bool mpw_free(const void *buffer, const size_t bufferSize) { - if (buffer) { - memset( (void *)buffer, 0, bufferSize ); - free( (void *)buffer ); - } + if (!buffer) + return false; + + memset( (void *)buffer, 0, bufferSize ); + free( (void *)buffer ); + return true; } -void mpw_free_string(const char *string) { +bool mpw_free_string(const char *string) { - mpw_free( string, strlen( string ) ); + return mpw_free( string, strlen( string ) ); } uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize, diff --git a/core/c/mpw-util.h b/core/c/mpw-util.h index 260ed736..74b38d5f 100644 --- a/core/c/mpw-util.h +++ b/core/c/mpw-util.h @@ -17,7 +17,10 @@ //============================================================================== #include -#include +#include "mpw-types.h" + +#ifndef _MPW_UTIL_H +#define _MPW_UTIL_H //// Logging. @@ -25,41 +28,39 @@ extern int mpw_verbosity; #define trc_level 3 #define trc(...) \ - if (mpw_verbosity >= 3) \ - fprintf( stderr, __VA_ARGS__ ) + ({ if (mpw_verbosity >= 3) \ + fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef dbg #define dbg_level 2 #define dbg(...) \ - if (mpw_verbosity >= 2) \ - fprintf( stderr, __VA_ARGS__ ) + ({ if (mpw_verbosity >= 2) \ + fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef inf #define inf_level 1 #define inf(...) \ - if (mpw_verbosity >= 1) \ - fprintf( stderr, __VA_ARGS__ ) + ({ if (mpw_verbosity >= 1) \ + fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef wrn #define wrn_level 0 #define wrn(...) \ - if (mpw_verbosity >= 0) \ - fprintf( stderr, __VA_ARGS__ ) + ({ if (mpw_verbosity >= 0) \ + fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef err #define err_level -1 #define err(...) \ - if (mpw_verbosity >= -1) \ - fprintf( stderr, __VA_ARGS__ ) + ({ if (mpw_verbosity >= -1) \ + fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef ftl #define ftl_level -2 #define ftl(...) \ - do { \ - if (mpw_verbosity >= -2) \ - fprintf( stderr, __VA_ARGS__ ); \ - exit( 2 ); \ - } while (0) + ({ if (mpw_verbosity >= -2) \ + fprintf( stderr, __VA_ARGS__ ); \ + exit( 2 ); }) #endif //// Buffers and memory. @@ -73,19 +74,19 @@ extern int mpw_verbosity; }) /** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */ -void mpw_push_buf( +bool mpw_push_buf( 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_push_string( +bool mpw_push_string( 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_push_int( +bool mpw_push_int( uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt); /** Free a buffer after zero'ing its contents. */ -void mpw_free( +bool mpw_free( const void *buffer, const size_t bufferSize); /** Free a string after zero'ing its contents. */ -void mpw_free_string( +bool mpw_free_string( const char *string); //// Cryptographic functions. @@ -117,3 +118,5 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword); /** @return The amount of display characters in the given UTF-8 string. */ const size_t mpw_utf8_strlen(const char *utf8String); + +#endif // _MPW_UTIL_H diff --git a/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj b/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj index ed0a8821..7f8bb3ff 100644 --- a/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -248,6 +248,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 */; }; + DAA449D21EEC4B5800E7BDD5 /* mpw-marshall.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */; }; 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 */; }; @@ -855,6 +856,8 @@ DAA1763C19D8B82B0044227B /* copy_pw.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = copy_pw.png; sourceTree = ""; }; DAA1763D19D8B82B0044227B /* choose_type@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "choose_type@2x.png"; sourceTree = ""; }; DAA1763E19D8B82B0044227B /* choose_type.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = choose_type.png; sourceTree = ""; }; + DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshall.c"; sourceTree = ""; }; + DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshall.h"; sourceTree = ""; }; DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; DABB981515100B4000B05417 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; DABD360F1711E29400CF925C /* ui_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ui_background.png; sourceTree = ""; }; @@ -1722,6 +1725,8 @@ 93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */, 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */, 93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */, + DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */, + DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */, 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */, 93D39AFD17CBE324D745DAB0 /* mpw-types.h */, 93D396C311C3725870343EE0 /* mpw-util.c */, @@ -3862,6 +3867,7 @@ DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */, DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */, DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */, + DAA449D21EEC4B5800E7BDD5 /* mpw-marshall.c in Sources */, DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */, DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */, DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */, diff --git a/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj b/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj index 9217ceda..94d3a2f6 100644 --- a/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj +++ b/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj @@ -23,8 +23,6 @@ 93D39F833DEC1C89B2F795AC /* MPSitesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPSitesWindowController.m */; }; DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; }; DA0933D01747B91B00DE1CEF /* appstore.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CF1747B91B00DE1CEF /* appstore.png */; }; - DA09745A1E99582900F0BFE8 /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974561E99582200F0BFE8 /* mpw-tests-util.c */; }; - DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; }; DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; }; DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; }; DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */; }; @@ -34,6 +32,22 @@ DA16B342170661E0000A0EAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.framework */; }; DA16B344170661EE000A0EAB /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; }; DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; }; + DA1C7AAA1F1A8F24009A3551 /* mpw-marshall.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D31EEC4B6B00E7BDD5 /* mpw-marshall.c */; }; + DA1C7AAB1F1A8F24009A3551 /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; }; + DA1C7AAC1F1A8F24009A3551 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; }; + DA1C7AAD1F1A8F24009A3551 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773BB1A4746AF004F356A /* mpw-algorithm.c */; }; + DA1C7AAF1F1A8F24009A3551 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; }; + DA1C7AB01F1A8F24009A3551 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; }; + DA1C7AC31F1A8FBA009A3551 /* mpw-cli.c in Sources */ = {isa = PBXBuildFile; fileRef = DA1C7AB91F1A8F6E009A3551 /* mpw-cli.c */; }; + DA1C7AC81F1A8FD8009A3551 /* mpw-marshall.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D31EEC4B6B00E7BDD5 /* mpw-marshall.c */; }; + DA1C7ACA1F1A8FD8009A3551 /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; }; + DA1C7ACB1F1A8FD8009A3551 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; }; + DA1C7ACD1F1A8FD8009A3551 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773BB1A4746AF004F356A /* mpw-algorithm.c */; }; + DA1C7ACF1F1A8FD8009A3551 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; }; + DA1C7AD01F1A8FD8009A3551 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; }; + DA1C7AD71F1A8FE6009A3551 /* mpw-bench.c in Sources */ = {isa = PBXBuildFile; fileRef = DA1C7AB81F1A8F6E009A3551 /* mpw-bench.c */; }; + DA1C7AD81F1A8FF4009A3551 /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA1C7ABA1F1A8F6E009A3551 /* mpw-tests-util.c */; }; + DA1C7AD91F1A8FF4009A3551 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA1C7ABC1F1A8F6E009A3551 /* mpw-tests.c */; }; DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; }; DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DA26861D1EBFD7A40001E37E /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA26860A1EBFD7A40001E37E /* MPGeneratedSiteEntity+CoreDataClass.m */; }; @@ -70,6 +84,7 @@ DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE931A7D8117003E5423 /* MPTypes.m */; }; DA5180CA19FF2F9200A587E9 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5180C719FF2F9200A587E9 /* MPAlgorithmV2.m */; }; DA5180CE19FF307E00A587E9 /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5180CD19FF307E00A587E9 /* MPAppDelegate_Store.m */; }; + DA531CC31EFF3BF4008C72CB /* mpw-marshall.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D31EEC4B6B00E7BDD5 /* mpw-marshall.c */; }; DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5C981724A667003798D8 /* MPAlgorithm.m */; }; DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5C9A1724A667003798D8 /* MPAlgorithmV0.m */; }; DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5C9C1724A667003798D8 /* MPAlgorithmV1.m */; }; @@ -103,6 +118,7 @@ DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261501BE1A86700369DE5 /* Fabric.framework */; }; DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; }; DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.tbd */; }; + DAA449D51EEC4B6B00E7BDD5 /* mpw-marshall.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D31EEC4B6B00E7BDD5 /* mpw-marshall.c */; }; DAAA81B0195A8D1300FA30D9 /* gradient.png in Resources */ = {isa = PBXBuildFile; fileRef = DAAA81AF195A8D1300FA30D9 /* gradient.png */; }; DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */; }; DAADCC4819FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */; }; @@ -234,6 +250,24 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + DA1C7AB11F1A8F24009A3551 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + DA1C7AD11F1A8FD8009A3551 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; DA6774391A474A03004F356A /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -291,9 +325,6 @@ DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = ""; }; DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = ""; }; DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = ""; }; - DA0974561E99582200F0BFE8 /* mpw-tests-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mpw-tests-util.c"; path = "../../platform-independent/cli-c/cli/mpw-tests-util.c"; sourceTree = ""; }; - DA0974571E99582200F0BFE8 /* mpw-tests.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mpw-tests.c"; path = "../../platform-independent/cli-c/cli/mpw-tests.c"; sourceTree = ""; }; - DA09745C1E99583B00F0BFE8 /* mpw-tests-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mpw-tests-util.h"; path = "../../platform-independent/cli-c/cli/mpw-tests-util.h"; sourceTree = ""; }; DA09745D1E99586600F0BFE8 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; }; DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = mpw_tests.xml; path = ../../core/java/tests/src/main/resources/mpw_tests.xml; sourceTree = ""; }; DA09791B1E9A824700F0BFE8 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; @@ -367,6 +398,13 @@ DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 9.xcdatamodel"; sourceTree = ""; }; DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + DA1C7AB61F1A8F24009A3551 /* mpw-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "mpw-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; + DA1C7AB81F1A8F6E009A3551 /* mpw-bench.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-bench.c"; sourceTree = ""; }; + DA1C7AB91F1A8F6E009A3551 /* mpw-cli.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli.c"; sourceTree = ""; }; + DA1C7ABA1F1A8F6E009A3551 /* mpw-tests-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-tests-util.c"; sourceTree = ""; }; + DA1C7ABB1F1A8F6E009A3551 /* mpw-tests-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-tests-util.h"; sourceTree = ""; }; + DA1C7ABC1F1A8F6E009A3551 /* mpw-tests.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-tests.c"; sourceTree = ""; }; + DA1C7AD61F1A8FD8009A3551 /* mpw-bench */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "mpw-bench"; sourceTree = BUILT_PRODUCTS_DIR; }; DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = ""; }; DA2508F919513C1400AC23F1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; DA2508FA19513C1400AC23F1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; @@ -874,6 +912,8 @@ DA9261501BE1A86700369DE5 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = ""; }; DA9261531BE1A88900369DE5 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; DA9261551BE1A89600369DE5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + DAA449D31EEC4B6B00E7BDD5 /* mpw-marshall.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshall.c"; sourceTree = ""; }; + DAA449D41EEC4B6B00E7BDD5 /* mpw-marshall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshall.h"; sourceTree = ""; }; DAAA81AF195A8D1300FA30D9 /* gradient.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = gradient.png; sourceTree = ""; }; DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = ""; }; DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = ""; }; @@ -999,6 +1039,24 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + DA1C7AAE1F1A8F24009A3551 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DA1C7AAF1F1A8F24009A3551 /* libsodium.a in Frameworks */, + DA1C7AB01F1A8F24009A3551 /* libxml2.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DA1C7ACE1F1A8FD8009A3551 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DA1C7ACF1F1A8FD8009A3551 /* libsodium.a in Frameworks */, + DA1C7AD01F1A8FD8009A3551 /* libxml2.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DA5BFA41147E415C00F98B1E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1156,6 +1214,19 @@ path = Fabric; sourceTree = ""; }; + DA1C7AB71F1A8F6E009A3551 /* cli */ = { + isa = PBXGroup; + children = ( + DA1C7AB81F1A8F6E009A3551 /* mpw-bench.c */, + DA1C7AB91F1A8F6E009A3551 /* mpw-cli.c */, + DA1C7ABA1F1A8F6E009A3551 /* mpw-tests-util.c */, + DA1C7ABB1F1A8F6E009A3551 /* mpw-tests-util.h */, + DA1C7ABC1F1A8F6E009A3551 /* mpw-tests.c */, + ); + name = cli; + path = "../../platform-independent/cli-c/cli"; + sourceTree = ""; + }; DA2508F819513C1400AC23F1 /* Other Frameworks */ = { isa = PBXGroup; children = ( @@ -1196,6 +1267,8 @@ DAC6326C148680650075AEA5 /* libjrswizzle.a */, DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */, DA67743B1A474A03004F356A /* mpw-test */, + DA1C7AB61F1A8F24009A3551 /* mpw-cli */, + DA1C7AD61F1A8FD8009A3551 /* mpw-bench */, ); name = Products; sourceTree = ""; @@ -1734,15 +1807,15 @@ DA6773291A4746AF004F356A /* C */ = { isa = PBXGroup; children = ( + DA1C7AB71F1A8F6E009A3551 /* cli */, DA831A271A6E1146000AC234 /* mpw-algorithm_v0.c */, DA831A281A6E1146000AC234 /* mpw-algorithm_v1.c */, DA831A291A6E1146000AC234 /* mpw-algorithm_v2.c */, DA831A2A1A6E1146000AC234 /* mpw-algorithm_v3.c */, DA6773BB1A4746AF004F356A /* mpw-algorithm.c */, DA6773BC1A4746AF004F356A /* mpw-algorithm.h */, - DA0974561E99582200F0BFE8 /* mpw-tests-util.c */, - DA09745C1E99583B00F0BFE8 /* mpw-tests-util.h */, - DA0974571E99582200F0BFE8 /* mpw-tests.c */, + DAA449D31EEC4B6B00E7BDD5 /* mpw-marshall.c */, + DAA449D41EEC4B6B00E7BDD5 /* mpw-marshall.h */, DA6773C21A4746AF004F356A /* mpw-types.c */, DA6773C31A4746AF004F356A /* mpw-types.h */, DA6773C51A4746AF004F356A /* mpw-util.c */, @@ -2101,6 +2174,40 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + DA1C7AA61F1A8F24009A3551 /* mpw-cli */ = { + isa = PBXNativeTarget; + buildConfigurationList = DA1C7AB21F1A8F24009A3551 /* Build configuration list for PBXNativeTarget "mpw-cli" */; + buildPhases = ( + DA1C7AA71F1A8F24009A3551 /* Sources */, + DA1C7AAE1F1A8F24009A3551 /* Frameworks */, + DA1C7AB11F1A8F24009A3551 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "mpw-cli"; + productName = "mpw-test"; + productReference = DA1C7AB61F1A8F24009A3551 /* mpw-cli */; + productType = "com.apple.product-type.tool"; + }; + DA1C7AC61F1A8FD8009A3551 /* mpw-bench */ = { + isa = PBXNativeTarget; + buildConfigurationList = DA1C7AD21F1A8FD8009A3551 /* Build configuration list for PBXNativeTarget "mpw-bench" */; + buildPhases = ( + DA1C7AC71F1A8FD8009A3551 /* Sources */, + DA1C7ACE1F1A8FD8009A3551 /* Frameworks */, + DA1C7AD11F1A8FD8009A3551 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "mpw-bench"; + productName = "mpw-test"; + productReference = DA1C7AD61F1A8FD8009A3551 /* mpw-bench */; + productType = "com.apple.product-type.tool"; + }; DA5BFA43147E415C00F98B1E /* MasterPassword */ = { isa = PBXNativeTarget; buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */; @@ -2205,6 +2312,12 @@ LastUpgradeCheck = 0830; ORGANIZATIONNAME = Lyndir; TargetAttributes = { + DA1C7AA61F1A8F24009A3551 = { + DevelopmentTeam = HL3Q45LX9N; + }; + DA1C7AC61F1A8FD8009A3551 = { + DevelopmentTeam = HL3Q45LX9N; + }; DA5BFA43147E415C00F98B1E = { DevelopmentTeam = HL3Q45LX9N; ProvisioningStyle = Automatic; @@ -2254,6 +2367,8 @@ DAC6326B148680650075AEA5 /* jrswizzle */, DAADCC4F19FB006500987B1D /* KCOrderedAccessorFix */, DA67743A1A474A03004F356A /* mpw-test */, + DA1C7AC61F1A8FD8009A3551 /* mpw-bench */, + DA1C7AA61F1A8F24009A3551 /* mpw-cli */, ); }; /* End PBXProject section */ @@ -2403,6 +2518,30 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + DA1C7AA71F1A8F24009A3551 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DA1C7AAA1F1A8F24009A3551 /* mpw-marshall.c in Sources */, + DA1C7AAB1F1A8F24009A3551 /* mpw-types.c in Sources */, + DA1C7AAC1F1A8F24009A3551 /* mpw-util.c in Sources */, + DA1C7AC31F1A8FBA009A3551 /* mpw-cli.c in Sources */, + DA1C7AAD1F1A8F24009A3551 /* mpw-algorithm.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DA1C7AC71F1A8FD8009A3551 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DA1C7AC81F1A8FD8009A3551 /* mpw-marshall.c in Sources */, + DA1C7ACA1F1A8FD8009A3551 /* mpw-types.c in Sources */, + DA1C7ACB1F1A8FD8009A3551 /* mpw-util.c in Sources */, + DA1C7AD71F1A8FE6009A3551 /* mpw-bench.c in Sources */, + DA1C7ACD1F1A8FD8009A3551 /* mpw-algorithm.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DA5BFA40147E415C00F98B1E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2411,6 +2550,7 @@ DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */, DA2686201EBFD7A40001E37E /* MPSiteEntity+CoreDataProperties.m in Sources */, DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */, + DAA449D51EEC4B6B00E7BDD5 /* mpw-marshall.c in Sources */, DA26861E1EBFD7A40001E37E /* MPGeneratedSiteEntity+CoreDataProperties.m in Sources */, DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */, DA2686231EBFD7A40001E37E /* MPStoredSiteEntity+CoreDataClass.m in Sources */, @@ -2453,10 +2593,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */, - DA09745A1E99582900F0BFE8 /* mpw-tests-util.c in Sources */, + DA531CC31EFF3BF4008C72CB /* mpw-marshall.c in Sources */, + DA1C7AD81F1A8FF4009A3551 /* mpw-tests-util.c in Sources */, DA6774451A474A3B004F356A /* mpw-types.c in Sources */, DA6774461A474A3B004F356A /* mpw-util.c in Sources */, + DA1C7AD91F1A8FF4009A3551 /* mpw-tests.c in Sources */, DA6774431A474A3B004F356A /* mpw-algorithm.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2724,12 +2865,8 @@ ); LIBRARY_SEARCH_PATHS = ( "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, "$(inherited)", - "$(PROJECT_DIR)/External/Pearl/Pearl-Crypto/lib", - /usr/local/Cellar/libsodium/1.0.12/lib, - /usr/local/Cellar/libscrypt/1.21/lib, - "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", - "$(PROJECT_DIR)/External/libsodium/libsodium-osx/lib", ); OTHER_CFLAGS = ( "-DHAS_CPERCIVA=0", @@ -2738,6 +2875,138 @@ }; name = Test; }; + DA1C7AB31F1A8F24009A3551 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/libxml2, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, + "$(inherited)", + ); + OTHER_CFLAGS = ( + "-DHAS_CPERCIVA=0", + "-DHAS_SODIUM=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + DA1C7AB41F1A8F24009A3551 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/libxml2, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, + "$(inherited)", + ); + OTHER_CFLAGS = ( + "-DHAS_CPERCIVA=0", + "-DHAS_SODIUM=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + DA1C7AB51F1A8F24009A3551 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/libxml2, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, + "$(inherited)", + ); + OTHER_CFLAGS = ( + "-DHAS_CPERCIVA=0", + "-DHAS_SODIUM=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Test; + }; + DA1C7AD31F1A8FD8009A3551 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/libxml2, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, + "$(inherited)", + ); + OTHER_CFLAGS = ( + "-DHAS_CPERCIVA=0", + "-DHAS_SODIUM=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + DA1C7AD41F1A8FD8009A3551 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/libxml2, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, + "$(inherited)", + ); + OTHER_CFLAGS = ( + "-DHAS_CPERCIVA=0", + "-DHAS_SODIUM=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + DA1C7AD51F1A8FD8009A3551 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /usr/include/libxml2, + /usr/local/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, + "$(inherited)", + ); + OTHER_CFLAGS = ( + "-DHAS_CPERCIVA=0", + "-DHAS_SODIUM=1", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Test; + }; DA5BFA6B147E415C00F98B1E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2987,12 +3256,8 @@ ); LIBRARY_SEARCH_PATHS = ( "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, "$(inherited)", - "$(PROJECT_DIR)/External/Pearl/Pearl-Crypto/lib", - /usr/local/Cellar/libsodium/1.0.12/lib, - /usr/local/Cellar/libscrypt/1.21/lib, - "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", - "$(PROJECT_DIR)/External/libsodium/libsodium-osx/lib", ); OTHER_CFLAGS = ( "-DHAS_CPERCIVA=0", @@ -3012,12 +3277,8 @@ ); LIBRARY_SEARCH_PATHS = ( "$(SRCROOT)/External/Pearl/Pearl-Crypto/lib", + /usr/local/lib, "$(inherited)", - "$(PROJECT_DIR)/External/Pearl/Pearl-Crypto/lib", - /usr/local/Cellar/libsodium/1.0.12/lib, - /usr/local/Cellar/libscrypt/1.21/lib, - "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", - "$(PROJECT_DIR)/External/libsodium/libsodium-osx/lib", ); OTHER_CFLAGS = ( "-DHAS_CPERCIVA=0", @@ -3097,6 +3358,26 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + DA1C7AB21F1A8F24009A3551 /* Build configuration list for PBXNativeTarget "mpw-cli" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DA1C7AB31F1A8F24009A3551 /* Debug */, + DA1C7AB41F1A8F24009A3551 /* Release */, + DA1C7AB51F1A8F24009A3551 /* Test */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Test; + }; + DA1C7AD21F1A8FD8009A3551 /* Build configuration list for PBXNativeTarget "mpw-bench" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DA1C7AD31F1A8FD8009A3551 /* Debug */, + DA1C7AD41F1A8FD8009A3551 /* Release */, + DA1C7AD51F1A8FD8009A3551 /* Test */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Test; + }; DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/platform-darwin/Source/MPAlgorithm.h b/platform-darwin/Source/MPAlgorithm.h index 1b69c039..cfb1e258 100644 --- a/platform-darwin/Source/MPAlgorithm.h +++ b/platform-darwin/Source/MPAlgorithm.h @@ -49,7 +49,7 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack); - (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc; - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit; -- (NSData *)keyIDForKeyData:(NSData *)keyData; +- (NSData *)keyIDForKey:(MPMasterKey)masterKey; - (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword; - (NSString *)nameOfType:(MPSiteType)type; diff --git a/platform-darwin/Source/MPAlgorithmV0.m b/platform-darwin/Source/MPAlgorithmV0.m index cd281ed5..03c23e1e 100644 --- a/platform-darwin/Source/MPAlgorithmV0.m +++ b/platform-darwin/Source/MPAlgorithmV0.m @@ -128,21 +128,21 @@ static NSOperationQueue *_mpwQueue = nil; __block NSData *keyData; [self mpw_perform:^{ NSDate *start = [NSDate date]; - uint8_t const *masterKeyBytes = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] ); - if (masterKeyBytes) { - keyData = [NSData dataWithBytes:masterKeyBytes length:MP_dkLen]; + MPMasterKey masterKey = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] ); + if (masterKey) { + keyData = [NSData dataWithBytes:masterKey length:MPMasterKeySize]; trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", // - fullName, masterPassword, [self keyIDForKeyData:keyData], -[start timeIntervalSinceNow] ); - mpw_free( masterKeyBytes, MP_dkLen ); + fullName, masterPassword, [self keyIDForKey:masterKey], -[start timeIntervalSinceNow] ); + mpw_free( masterKey, MPMasterKeySize ); } }]; return keyData; } -- (NSData *)keyIDForKeyData:(NSData *)keyData { +- (NSData *)keyIDForKey:(MPMasterKey)masterKey { - return [keyData hashWith:PearlHashSHA256]; + return [[NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] hashWith:PearlHashSHA256]; } - (NSString *)nameOfType:(MPSiteType)type { @@ -350,9 +350,9 @@ static NSOperationQueue *_mpwQueue = nil; - (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key { - __block NSString *content; + __block NSString *content = nil; [self mpw_perform:^{ - char const *contentBytes = mpw_passwordForSite( [key keyDataForAlgorithm:self].bytes, + char const *contentBytes = mpw_passwordForSite( [key keyForAlgorithm:self], name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] ); if (contentBytes) { content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding]; @@ -396,7 +396,7 @@ static NSOperationQueue *_mpwQueue = nil; return NO; } - NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize]; + NSData *encryptionKey = [siteKey keyForAlgorithm:self trimmedLength:PearlCryptKeySize]; NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding] encryptWithSymmetricKey:encryptionKey padding:YES]; if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent]) @@ -412,7 +412,7 @@ static NSOperationQueue *_mpwQueue = nil; return NO; } - NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize]; + NSData *encryptionKey = [siteKey keyForAlgorithm:self trimmedLength:PearlCryptKeySize]; NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding] encryptWithSymmetricKey:encryptionKey padding:YES]; NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name]; @@ -713,7 +713,7 @@ static NSOperationQueue *_mpwQueue = nil; return nil; NSData *decryptedContent = nil; if ([encryptedContent length]) { - NSData *encryptionKey = [key keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize]; + NSData *encryptionKey = [key keyForAlgorithm:self trimmedLength:PearlCryptKeySize]; decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES]; } if (!decryptedContent) diff --git a/platform-darwin/Source/MPAppDelegate_Key.m b/platform-darwin/Source/MPAppDelegate_Key.m index 7183b658..78c3ce42 100644 --- a/platform-darwin/Source/MPAppDelegate_Key.m +++ b/platform-darwin/Source/MPAppDelegate_Key.m @@ -95,13 +95,14 @@ - (void)storeSavedKeyFor:(MPUserEntity *)user { if (user.saveKey) { - NSData *keyData = [self.key keyDataForAlgorithm:user.algorithm]; - if (keyData) { + MPMasterKey masterKey = [self.key keyForAlgorithm:user.algorithm]; + if (masterKey) { [self forgetSavedKeyFor:user]; inf( @"Saving key in keychain for user: %@", user.userID ); - [PearlKeyChain addOrUpdateItemForQuery:[self createKeyQueryforUser:user origin:nil] - withAttributes:@{ (__bridge id)kSecValueData: keyData }]; + [PearlKeyChain addOrUpdateItemForQuery:[self createKeyQueryforUser:user origin:nil] withAttributes:@{ + (__bridge id)kSecValueData: [NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] + }]; } } } diff --git a/platform-darwin/Source/MPAppDelegate_Store.m b/platform-darwin/Source/MPAppDelegate_Store.m index 42e3948a..58d5bd88 100644 --- a/platform-darwin/Source/MPAppDelegate_Store.m +++ b/platform-darwin/Source/MPAppDelegate_Store.m @@ -17,6 +17,7 @@ //============================================================================== #import "MPAppDelegate_Store.h" +#import "mpw-marshall.h" #if TARGET_OS_IPHONE #define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete, @@ -840,68 +841,26 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted ); MPUserEntity *activeUser = [self activeUserForMainThread]; inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID ); - // Header. - NSMutableString *export = [NSMutableString new]; - [export appendFormat:@"# Master Password site export\n"]; - if (revealPasswords) - [export appendFormat:@"# Export of site names and passwords in clear-text.\n"]; - else - [export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"]; - [export appendFormat:@"# \n"]; - [export appendFormat:@"##\n"]; - [export appendFormat:@"# Format: 1\n"]; - [export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]]; - [export appendFormat:@"# User Name: %@\n", activeUser.name]; - [export appendFormat:@"# Full Name: %@\n", activeUser.name]; - [export appendFormat:@"# Avatar: %lu\n", (unsigned long)activeUser.avatar]; - [export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]]; - [export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion]; - [export appendFormat:@"# Algorithm: %d\n", activeUser.algorithm.version]; - [export appendFormat:@"# Default Type: %d\n", activeUser.defaultType]; - [export appendFormat:@"# Passwords: %@\n", revealPasswords? @"VISIBLE": @"PROTECTED"]; - [export appendFormat:@"##\n"]; - [export appendFormat:@"#\n"]; - [export appendFormat:@"# Last Times Password Login\t Site\tSite\n"]; - [export appendFormat:@"# used used type name\t name\tpassword\n"]; + MPMarshalledUser exportUser = mpw_marshall_user( activeUser.name.UTF8String, + [self.key keyForAlgorithm:activeUser.algorithm], activeUser.algorithm.version ); + exportUser.avatar = activeUser.avatar; + exportUser.defaultType = activeUser.defaultType; + exportUser.lastUsed = (time_t)activeUser.lastUsed.timeIntervalSince1970; + - // Sites. for (MPSiteEntity *site in activeUser.sites) { - NSDate *lastUsed = site.lastUsed; - NSUInteger uses = site.uses; - MPSiteType type = site.type; - id algorithm = site.algorithm; - NSUInteger counter = 0; - NSString *loginName = site.loginName; - NSString *siteName = site.name; - NSString *content = nil; + MPMarshalledSite exportSite = mpw_marshall_site( &exportUser, + site.name.UTF8String, site.type, site.counter, site.algorithm.version ); + exportSite.loginName = site.loginName.UTF8String; + exportSite.url = site.url.UTF8String; + exportSite.uses = site.uses; + exportSite.lastUsed = (time_t)site.lastUsed.timeIntervalSince1970; - // Generated-specific - if ([site isKindOfClass:[MPGeneratedSiteEntity class]]) - counter = ((MPGeneratedSiteEntity *)site).counter; - - - // Determine the content to export. - if (!(type & MPSiteFeatureDevicePrivate)) { - if (revealPasswords) - content = [site.algorithm resolvePasswordForSite:site usingKey:self.key]; - else if (type & MPSiteFeatureExportContent) - content = [site.algorithm exportPasswordForSite:site usingKey:self.key]; - } - - NSString *lastUsedExport = [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed]; - long usesExport = (long)uses; - NSString *typeExport = strf( @"%lu:%lu:%lu", (long)type, (long)[algorithm version], (long)counter ); - NSString *loginNameExport = loginName?: @""; - NSString *contentExport = content?: @""; - [export appendFormat:@"%@ %8ld %8S %25S\t%25S\t%@\n", - lastUsedExport, usesExport, - (const unsigned short *)[typeExport cStringUsingEncoding:NSUTF16StringEncoding], - (const unsigned short *)[loginNameExport cStringUsingEncoding:NSUTF16StringEncoding], - (const unsigned short *)[siteName cStringUsingEncoding:NSUTF16StringEncoding], - contentExport]; + for (MPSiteQuestionEntity *siteQuestion in site.questions) + mpw_marshal_question( &exportSite, siteQuestion.keyword.UTF8String ); } - return export; + mpw_marshall_write( &export, MPMarshallFormatFlat, exportUser ); } @end diff --git a/platform-darwin/Source/MPKey.h b/platform-darwin/Source/MPKey.h index c1eeb24c..15d26617 100644 --- a/platform-darwin/Source/MPKey.h +++ b/platform-darwin/Source/MPKey.h @@ -18,6 +18,7 @@ #import #import "MPAlgorithm.h" +#import "mpw-types.h" @protocol MPAlgorithm; @@ -37,8 +38,7 @@ typedef NS_ENUM( NSUInteger, MPKeyOrigin ) { keyOrigin:(MPKeyOrigin)origin; - (NSData *)keyIDForAlgorithm:(id)algorithm; -- (NSData *)keyDataForAlgorithm:(id)algorithm; -- (NSData *)keyDataForAlgorithm:(id)algorithm trimmedLength:(NSUInteger)subKeyLength; +- (MPMasterKey)keyForAlgorithm:(id)algorithm; - (BOOL)isEqualToKey:(MPKey *)key; diff --git a/platform-darwin/Source/MPKey.m b/platform-darwin/Source/MPKey.m index 9d5819cc..af9b0753 100644 --- a/platform-darwin/Source/MPKey.m +++ b/platform-darwin/Source/MPKey.m @@ -53,30 +53,23 @@ - (NSData *)keyIDForAlgorithm:(id)algorithm { - return [algorithm keyIDForKeyData:[self keyDataForAlgorithm:algorithm]]; + return [algorithm keyIDForKey:[self keyForAlgorithm:algorithm]]; } -- (NSData *)keyDataForAlgorithm:(id)algorithm { +- (MPMasterKey)keyForAlgorithm:(id)algorithm { @synchronized (self) { NSData *keyData = [self.keyCache objectForKey:algorithm]; - if (keyData) - return keyData; + if (!keyData) { + keyData = self.keyResolver( algorithm ); + if (keyData) + [self.keyCache setObject:keyData forKey:algorithm]; + } - keyData = self.keyResolver( algorithm ); - if (keyData) - [self.keyCache setObject:keyData forKey:algorithm]; - - return keyData; + return keyData.length == MPMasterKeySize? keyData.bytes: NULL; } } -- (NSData *)keyDataForAlgorithm:(id)algorithm trimmedLength:(NSUInteger)subKeyLength { - - NSData *keyData = [self keyDataForAlgorithm:algorithm]; - return [keyData subdataWithRange:NSMakeRange( 0, MIN( subKeyLength, keyData.length ) )]; -} - - (BOOL)isEqualToKey:(MPKey *)key { return [[self keyIDForAlgorithm:MPAlgorithmDefault] isEqualToData:[key keyIDForAlgorithm:MPAlgorithmDefault]]; @@ -84,10 +77,7 @@ - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[MPKey class]]) - return NO; - - return [self isEqualToKey:object]; + return [object isKindOfClass:[MPKey class]] && [self isEqualToKey:object]; } @end diff --git a/platform-darwin/Source/iOS/Storyboard.storyboard b/platform-darwin/Source/iOS/Storyboard.storyboard index fd94ee1e..c653d9cd 100644 --- a/platform-darwin/Source/iOS/Storyboard.storyboard +++ b/platform-darwin/Source/iOS/Storyboard.storyboard @@ -1,10 +1,10 @@ - + - + @@ -103,7 +103,7 @@ - +