Add marshalling metadata lookup & adapt iOS for new APIs.
This commit is contained in:
parent
c0ba96daa2
commit
f5c7d11f0e
@ -102,7 +102,7 @@ const char *mpw_siteResult(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (resultType & MPResultTypeClassState) {
|
else if (resultType & MPResultTypeClassStateful) {
|
||||||
switch (algorithmVersion) {
|
switch (algorithmVersion) {
|
||||||
case MPAlgorithmVersion0:
|
case MPAlgorithmVersion0:
|
||||||
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, resultParam );
|
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, resultParam );
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "mpw-marshall-util.h"
|
#include "mpw-marshall-util.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
char *mpw_get_token(char **in, char *eol, char *delim) {
|
char *mpw_get_token(const char **in, const char *eol, char *delim) {
|
||||||
|
|
||||||
// Skip leading spaces.
|
// Skip leading spaces.
|
||||||
for (; **in == ' '; ++*in);
|
for (; **in == ' '; ++*in);
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
* The input string reference is advanced beyond the token delimitor if one is found.
|
* The input string reference is advanced beyond the token delimitor if one is found.
|
||||||
* @return A new string containing the token or NULL if the delim wasn't found before eol. */
|
* @return A new string containing the token or NULL if the delim wasn't found before eol. */
|
||||||
char *mpw_get_token(
|
char *mpw_get_token(
|
||||||
char **in, char *eol, char *delim);
|
const char **in, const char *eol, char *delim);
|
||||||
/** Convert an RFC 3339 time string into epoch time. */
|
/** Convert an RFC 3339 time string into epoch time. */
|
||||||
time_t mpw_mktime(
|
time_t mpw_mktime(
|
||||||
const char *time);
|
const char *time);
|
||||||
|
@ -89,6 +89,20 @@ MPMarshalledQuestion *mpw_marshal_question(
|
|||||||
return question;
|
return question;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mpw_marshal_info_free(
|
||||||
|
MPMarshallInfo *info) {
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
success &= mpw_free_string( info->fullName );
|
||||||
|
success &= mpw_free_string( info->keyID );
|
||||||
|
success &= mpw_free( info, sizeof( MPMarshallInfo ) );
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
bool mpw_marshal_free(
|
bool mpw_marshal_free(
|
||||||
MPMarshalledUser *user) {
|
MPMarshalledUser *user) {
|
||||||
|
|
||||||
@ -313,6 +327,9 @@ bool mpw_marshall_write(
|
|||||||
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error) {
|
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error) {
|
||||||
|
|
||||||
switch (outFormat) {
|
switch (outFormat) {
|
||||||
|
case MPMarshallFormatNone:
|
||||||
|
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||||
|
return false;
|
||||||
case MPMarshallFormatFlat:
|
case MPMarshallFormatFlat:
|
||||||
return mpw_marshall_write_flat( out, user, error );
|
return mpw_marshall_write_flat( out, user, error );
|
||||||
case MPMarshallFormatJSON:
|
case MPMarshallFormatJSON:
|
||||||
@ -323,10 +340,63 @@ bool mpw_marshall_write(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mpw_marshall_read_flat_info(
|
||||||
|
const char *in, MPMarshallInfo *info) {
|
||||||
|
|
||||||
|
info->algorithm = MPAlgorithmVersionCurrent;
|
||||||
|
|
||||||
|
// Parse import data.
|
||||||
|
bool headerStarted = false;
|
||||||
|
for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
|
||||||
|
|
||||||
|
// Comment or header
|
||||||
|
if (*positionInLine == '#') {
|
||||||
|
++positionInLine;
|
||||||
|
|
||||||
|
if (!headerStarted) {
|
||||||
|
if (*positionInLine == '#')
|
||||||
|
// ## starts header
|
||||||
|
headerStarted = true;
|
||||||
|
// Comment before header
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*positionInLine == '#')
|
||||||
|
// ## ends header
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Header
|
||||||
|
char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
|
||||||
|
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
||||||
|
if (!headerName || !headerValue)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp( headerName, "Algorithm" ) == 0)
|
||||||
|
info->algorithm = (MPAlgorithmVersion)atoi( headerValue );
|
||||||
|
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
|
||||||
|
info->fullName = strdup( headerValue );
|
||||||
|
if (strcmp( headerName, "Key ID" ) == 0)
|
||||||
|
info->keyID = strdup( headerValue );
|
||||||
|
if (strcmp( headerName, "Passwords" ) == 0)
|
||||||
|
info->redacted = strcmp( headerValue, "VISIBLE" ) != 0;
|
||||||
|
if (strcmp( headerName, "Date" ) == 0)
|
||||||
|
info->date = mpw_mktime( headerValue );
|
||||||
|
|
||||||
|
mpw_free_string( headerName );
|
||||||
|
mpw_free_string( headerValue );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static MPMarshalledUser *mpw_marshall_read_flat(
|
static MPMarshalledUser *mpw_marshall_read_flat(
|
||||||
char *in, const char *masterPassword, MPMarshallError *error) {
|
const char *in, const char *masterPassword, MPMarshallError *error) {
|
||||||
|
|
||||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
||||||
|
if (!in || !strlen( in )) {
|
||||||
|
error->type = MPMarshallErrorStructure;
|
||||||
|
error->description = mpw_str( "No input data." );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse import data.
|
// Parse import data.
|
||||||
MPMasterKey masterKey = NULL;
|
MPMasterKey masterKey = NULL;
|
||||||
@ -336,7 +406,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
|
|||||||
MPAlgorithmVersion algorithm = MPAlgorithmVersionCurrent, masterKeyAlgorithm = (MPAlgorithmVersion)-1;
|
MPAlgorithmVersion algorithm = MPAlgorithmVersionCurrent, masterKeyAlgorithm = (MPAlgorithmVersion)-1;
|
||||||
MPResultType defaultType = MPResultTypeDefault;
|
MPResultType defaultType = MPResultTypeDefault;
|
||||||
bool headerStarted = false, headerEnded = false, importRedacted = false;
|
bool headerStarted = false, headerEnded = false, importRedacted = false;
|
||||||
for (char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
|
for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
|
||||||
|
|
||||||
// Comment or header
|
// Comment or header
|
||||||
if (*positionInLine == '#') {
|
if (*positionInLine == '#') {
|
||||||
@ -541,10 +611,39 @@ static MPMarshalledUser *mpw_marshall_read_flat(
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mpw_marshall_read_json_info(
|
||||||
|
const char *in, MPMarshallInfo *info) {
|
||||||
|
|
||||||
|
// Parse JSON.
|
||||||
|
enum json_tokener_error json_error = json_tokener_success;
|
||||||
|
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
|
||||||
|
if (!json_file || json_error != json_tokener_success)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Section: "export"
|
||||||
|
int64_t fileFormat = mpw_get_json_int( json_file, "export.format", 0 );
|
||||||
|
if (fileFormat < 1)
|
||||||
|
return;
|
||||||
|
info->redacted = mpw_get_json_boolean( json_file, "export.redacted", true );
|
||||||
|
info->date = mpw_mktime( mpw_get_json_string( json_file, "export.date", NULL ) );
|
||||||
|
|
||||||
|
// Section: "user"
|
||||||
|
info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
|
||||||
|
info->fullName = strdup( mpw_get_json_string( json_file, "user.full_name", NULL ) );
|
||||||
|
info->keyID = strdup( mpw_get_json_string( json_file, "user.key_id", NULL ) );
|
||||||
|
|
||||||
|
json_object_put( json_file );
|
||||||
|
}
|
||||||
|
|
||||||
static MPMarshalledUser *mpw_marshall_read_json(
|
static MPMarshalledUser *mpw_marshall_read_json(
|
||||||
char *in, const char *masterPassword, MPMarshallError *error) {
|
const char *in, const char *masterPassword, MPMarshallError *error) {
|
||||||
|
|
||||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
||||||
|
if (!in || !strlen( in )) {
|
||||||
|
error->type = MPMarshallErrorStructure;
|
||||||
|
error->description = mpw_str( "No input data." );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse JSON.
|
// Parse JSON.
|
||||||
enum json_tokener_error json_error = json_tokener_success;
|
enum json_tokener_error json_error = json_tokener_success;
|
||||||
@ -683,10 +782,33 @@ static MPMarshalledUser *mpw_marshall_read_json(
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MPMarshallInfo *mpw_marshall_read_info(
|
||||||
|
const char *in) {
|
||||||
|
|
||||||
|
MPMarshallInfo *info = malloc( sizeof( MPMarshallInfo ) );
|
||||||
|
*info = (MPMarshallInfo){ .format = MPMarshallFormatNone };
|
||||||
|
|
||||||
|
if (in && strlen( in )) {
|
||||||
|
if (in[0] == '#') {
|
||||||
|
*info = (MPMarshallInfo){ .format = MPMarshallFormatFlat };
|
||||||
|
mpw_marshall_read_flat_info( in, info );
|
||||||
|
}
|
||||||
|
else if (in[0] == '{') {
|
||||||
|
*info = (MPMarshallInfo){ .format = MPMarshallFormatJSON };
|
||||||
|
mpw_marshall_read_json_info( in, info );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
MPMarshalledUser *mpw_marshall_read(
|
MPMarshalledUser *mpw_marshall_read(
|
||||||
char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error) {
|
const char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error) {
|
||||||
|
|
||||||
switch (inFormat) {
|
switch (inFormat) {
|
||||||
|
case MPMarshallFormatNone:
|
||||||
|
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||||
|
return false;
|
||||||
case MPMarshallFormatFlat:
|
case MPMarshallFormatFlat:
|
||||||
return mpw_marshall_read_flat( in, masterPassword, error );
|
return mpw_marshall_read_flat( in, masterPassword, error );
|
||||||
case MPMarshallFormatJSON:
|
case MPMarshallFormatJSON:
|
||||||
@ -700,6 +822,9 @@ MPMarshalledUser *mpw_marshall_read(
|
|||||||
const MPMarshallFormat mpw_formatWithName(
|
const MPMarshallFormat mpw_formatWithName(
|
||||||
const char *formatName) {
|
const char *formatName) {
|
||||||
|
|
||||||
|
if (!formatName || !strlen( formatName ))
|
||||||
|
return MPMarshallFormatNone;
|
||||||
|
|
||||||
// Lower-case to standardize it.
|
// Lower-case to standardize it.
|
||||||
size_t stdFormatNameSize = strlen( formatName );
|
size_t stdFormatNameSize = strlen( formatName );
|
||||||
char stdFormatName[stdFormatNameSize + 1];
|
char stdFormatName[stdFormatNameSize + 1];
|
||||||
@ -707,6 +832,8 @@ const MPMarshallFormat mpw_formatWithName(
|
|||||||
stdFormatName[c] = (char)tolower( formatName[c] );
|
stdFormatName[c] = (char)tolower( formatName[c] );
|
||||||
stdFormatName[stdFormatNameSize] = '\0';
|
stdFormatName[stdFormatNameSize] = '\0';
|
||||||
|
|
||||||
|
if (strncmp( mpw_nameForFormat( MPMarshallFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||||
|
return MPMarshallFormatNone;
|
||||||
if (strncmp( mpw_nameForFormat( MPMarshallFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
if (strncmp( mpw_nameForFormat( MPMarshallFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||||
return MPMarshallFormatFlat;
|
return MPMarshallFormatFlat;
|
||||||
if (strncmp( mpw_nameForFormat( MPMarshallFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
if (strncmp( mpw_nameForFormat( MPMarshallFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||||
@ -720,6 +847,8 @@ const char *mpw_nameForFormat(
|
|||||||
const MPMarshallFormat format) {
|
const MPMarshallFormat format) {
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
case MPMarshallFormatNone:
|
||||||
|
return "none";
|
||||||
case MPMarshallFormatFlat:
|
case MPMarshallFormatFlat:
|
||||||
return "flat";
|
return "flat";
|
||||||
case MPMarshallFormatJSON:
|
case MPMarshallFormatJSON:
|
||||||
@ -735,6 +864,8 @@ const char *mpw_marshall_format_extension(
|
|||||||
const MPMarshallFormat format) {
|
const MPMarshallFormat format) {
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
case MPMarshallFormatNone:
|
||||||
|
return NULL;
|
||||||
case MPMarshallFormatFlat:
|
case MPMarshallFormatFlat:
|
||||||
return "mpsites";
|
return "mpsites";
|
||||||
case MPMarshallFormatJSON:
|
case MPMarshallFormatJSON:
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
//// Types.
|
//// Types.
|
||||||
|
|
||||||
typedef enum( unsigned int, MPMarshallFormat ) {
|
typedef enum( unsigned int, MPMarshallFormat ) {
|
||||||
|
/** Generate a key for authentication. */
|
||||||
|
MPMarshallFormatNone,
|
||||||
/** Generate a key for authentication. */
|
/** Generate a key for authentication. */
|
||||||
MPMarshallFormatFlat,
|
MPMarshallFormatFlat,
|
||||||
/** Generate a name for identification. */
|
/** Generate a name for identification. */
|
||||||
@ -91,22 +93,42 @@ typedef struct MPMarshalledUser {
|
|||||||
MPMarshalledSite *sites;
|
MPMarshalledSite *sites;
|
||||||
} MPMarshalledUser;
|
} MPMarshalledUser;
|
||||||
|
|
||||||
|
typedef struct MPMarshallInfo {
|
||||||
|
MPMarshallFormat format;
|
||||||
|
MPAlgorithmVersion algorithm;
|
||||||
|
const char *fullName;
|
||||||
|
const char *keyID;
|
||||||
|
bool redacted;
|
||||||
|
time_t date;
|
||||||
|
} MPMarshallInfo;
|
||||||
|
|
||||||
//// Marshalling.
|
//// Marshalling.
|
||||||
|
|
||||||
|
/** Write the user and all associated data out to the given output buffer using the given marshalling format. */
|
||||||
bool mpw_marshall_write(
|
bool mpw_marshall_write(
|
||||||
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error);
|
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error);
|
||||||
|
/** Try to read metadata on the sites in the input buffer. */
|
||||||
|
MPMarshallInfo *mpw_marshall_read_info(
|
||||||
|
const char *in);
|
||||||
|
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. */
|
||||||
MPMarshalledUser *mpw_marshall_read(
|
MPMarshalledUser *mpw_marshall_read(
|
||||||
char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error);
|
const char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error);
|
||||||
|
|
||||||
//// Utilities.
|
//// Utilities.
|
||||||
|
|
||||||
|
/** Create a new user object ready for marshalling. */
|
||||||
MPMarshalledUser *mpw_marshall_user(
|
MPMarshalledUser *mpw_marshall_user(
|
||||||
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
||||||
|
/** Create a new site attached to the given user object, ready for marshalling. */
|
||||||
MPMarshalledSite *mpw_marshall_site(
|
MPMarshalledSite *mpw_marshall_site(
|
||||||
MPMarshalledUser *user,
|
MPMarshalledUser *user,
|
||||||
const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion);
|
const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion);
|
||||||
|
/** Create a new question attached to the given site object, ready for marshalling. */
|
||||||
MPMarshalledQuestion *mpw_marshal_question(
|
MPMarshalledQuestion *mpw_marshal_question(
|
||||||
MPMarshalledSite *site, const char *keyword);
|
MPMarshalledSite *site, const char *keyword);
|
||||||
|
/** Free the given user object and all associated data. */
|
||||||
|
bool mpw_marshal_info_free(
|
||||||
|
MPMarshallInfo *info);
|
||||||
bool mpw_marshal_free(
|
bool mpw_marshal_free(
|
||||||
MPMarshalledUser *user);
|
MPMarshalledUser *user);
|
||||||
|
|
||||||
@ -122,6 +144,9 @@ const MPMarshallFormat mpw_formatWithName(
|
|||||||
*/
|
*/
|
||||||
const char *mpw_nameForFormat(
|
const char *mpw_nameForFormat(
|
||||||
const MPMarshallFormat format);
|
const MPMarshallFormat format);
|
||||||
|
/**
|
||||||
|
* @return The file extension that's recommended for files that use the given marshalling format.
|
||||||
|
*/
|
||||||
const char *mpw_marshall_format_extension(
|
const char *mpw_marshall_format_extension(
|
||||||
const MPMarshallFormat format);
|
const MPMarshallFormat format);
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ const MPResultType mpw_typeWithName(const char *typeName) {
|
|||||||
if ('n' == typeName[0])
|
if ('n' == typeName[0])
|
||||||
return MPResultTypeTemplateName;
|
return MPResultTypeTemplateName;
|
||||||
if ('P' == typeName[0])
|
if ('P' == typeName[0])
|
||||||
return MPResultTypeStatePersonal;
|
return MPResultTypeStatefulPersonal;
|
||||||
if ('D' == typeName[0])
|
if ('D' == typeName[0])
|
||||||
return MPResultTypeStateDevice;
|
return MPResultTypeStatefulDevice;
|
||||||
if ('k' == typeName[0])
|
if ('k' == typeName[0])
|
||||||
return MPResultTypeDeriveKey;
|
return MPResultTypeDeriveKey;
|
||||||
}
|
}
|
||||||
@ -82,10 +82,10 @@ const MPResultType mpw_typeWithName(const char *typeName) {
|
|||||||
return MPResultTypeTemplateName;
|
return MPResultTypeTemplateName;
|
||||||
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||||
return MPResultTypeTemplatePhrase;
|
return MPResultTypeTemplatePhrase;
|
||||||
if (strncmp( mpw_nameForType( MPResultTypeStatePersonal ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
if (strncmp( mpw_nameForType( MPResultTypeStatefulPersonal ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||||
return MPResultTypeStatePersonal;
|
return MPResultTypeStatefulPersonal;
|
||||||
if (strncmp( mpw_nameForType( MPResultTypeStateDevice ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
if (strncmp( mpw_nameForType( MPResultTypeStatefulDevice ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||||
return MPResultTypeStateDevice;
|
return MPResultTypeStatefulDevice;
|
||||||
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||||
return MPResultTypeDeriveKey;
|
return MPResultTypeDeriveKey;
|
||||||
|
|
||||||
@ -112,9 +112,9 @@ const char *mpw_nameForType(MPResultType resultType) {
|
|||||||
return "name";
|
return "name";
|
||||||
case MPResultTypeTemplatePhrase:
|
case MPResultTypeTemplatePhrase:
|
||||||
return "phrase";
|
return "phrase";
|
||||||
case MPResultTypeStatePersonal:
|
case MPResultTypeStatefulPersonal:
|
||||||
return "personal";
|
return "personal";
|
||||||
case MPResultTypeStateDevice:
|
case MPResultTypeStatefulDevice:
|
||||||
return "device";
|
return "device";
|
||||||
case MPResultTypeDeriveKey:
|
case MPResultTypeDeriveKey:
|
||||||
return "key";
|
return "key";
|
||||||
|
@ -51,7 +51,7 @@ typedef enum( uint16_t, MPResultTypeClass ) {
|
|||||||
/** Use the site key to generate a password from a template. */
|
/** Use the site key to generate a password from a template. */
|
||||||
MPResultTypeClassTemplate = 1 << 4,
|
MPResultTypeClassTemplate = 1 << 4,
|
||||||
/** Use the site key to encrypt and decrypt a stateful entity. */
|
/** Use the site key to encrypt and decrypt a stateful entity. */
|
||||||
MPResultTypeClassState = 1 << 5,
|
MPResultTypeClassStateful = 1 << 5,
|
||||||
/** Use the site key to derive a site-specific object. */
|
/** Use the site key to derive a site-specific object. */
|
||||||
MPResultTypeClassDerive = 1 << 6,
|
MPResultTypeClassDerive = 1 << 6,
|
||||||
};
|
};
|
||||||
@ -86,9 +86,9 @@ typedef enum( uint32_t, MPResultType ) {
|
|||||||
MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0,
|
MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0,
|
||||||
|
|
||||||
/** Custom saved password. */
|
/** Custom saved password. */
|
||||||
MPResultTypeStatePersonal = 0x0 | MPResultTypeClassState | MPSiteFeatureExportContent,
|
MPResultTypeStatefulPersonal = 0x0 | MPResultTypeClassStateful | MPSiteFeatureExportContent,
|
||||||
/** Custom saved password that should not be exported from the device. */
|
/** Custom saved password that should not be exported from the device. */
|
||||||
MPResultTypeStateDevice = 0x1 | MPResultTypeClassState | MPSiteFeatureDevicePrivate,
|
MPResultTypeStatefulDevice = 0x1 | MPResultTypeClassStateful | MPSiteFeatureDevicePrivate,
|
||||||
|
|
||||||
/** Derive a unique binary key. */
|
/** Derive a unique binary key. */
|
||||||
MPResultTypeDeriveKey = 0x0 | MPResultTypeClassDerive | MPSiteFeatureAlternative,
|
MPResultTypeDeriveKey = 0x0 | MPResultTypeClassDerive | MPSiteFeatureAlternative,
|
||||||
|
@ -35,7 +35,9 @@
|
|||||||
|
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#ifdef inf_level
|
||||||
int mpw_verbosity = inf_level;
|
int mpw_verbosity = inf_level;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool 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) {
|
||||||
|
|
||||||
|
@ -253,6 +253,7 @@
|
|||||||
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */; };
|
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */; };
|
||||||
DAB7AE5D1F3D752900C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE5C1F3D752900C856B1 /* libjson-c.a */; };
|
DAB7AE5D1F3D752900C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE5C1F3D752900C856B1 /* libjson-c.a */; };
|
||||||
DAB7AE771F3D755B00C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE761F3D755B00C856B1 /* libjson-c.a */; };
|
DAB7AE771F3D755B00C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE761F3D755B00C856B1 /* libjson-c.a */; };
|
||||||
|
DAB7AE991F3DDEE000C856B1 /* mpw-marshall-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB7AE981F3DDEE000C856B1 /* mpw-marshall-util.c */; };
|
||||||
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; };
|
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; };
|
||||||
DABD39371711E29700CF925C /* avatar-0.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366C1711E29400CF925C /* avatar-0.png */; };
|
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 */; };
|
DABD39381711E29700CF925C /* avatar-0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366D1711E29400CF925C /* avatar-0@2x.png */; };
|
||||||
@ -903,6 +904,8 @@
|
|||||||
DAB7AE731F3D755B00C856B1 /* strdup_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strdup_compat.h; sourceTree = "<group>"; };
|
DAB7AE731F3D755B00C856B1 /* strdup_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strdup_compat.h; sourceTree = "<group>"; };
|
||||||
DAB7AE741F3D755B00C856B1 /* vasprintf_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vasprintf_compat.h; sourceTree = "<group>"; };
|
DAB7AE741F3D755B00C856B1 /* vasprintf_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vasprintf_compat.h; sourceTree = "<group>"; };
|
||||||
DAB7AE761F3D755B00C856B1 /* libjson-c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libjson-c.a"; sourceTree = "<group>"; };
|
DAB7AE761F3D755B00C856B1 /* libjson-c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libjson-c.a"; sourceTree = "<group>"; };
|
||||||
|
DAB7AE971F3DDEE000C856B1 /* mpw-marshall-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshall-util.h"; sourceTree = "<group>"; };
|
||||||
|
DAB7AE981F3DDEE000C856B1 /* mpw-marshall-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshall-util.c"; sourceTree = "<group>"; };
|
||||||
DABB981515100B4000B05417 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.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 = "<group>"; };
|
DABD360F1711E29400CF925C /* ui_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ui_background.png; sourceTree = "<group>"; };
|
||||||
DABD36101711E29400CF925C /* ui_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ui_background@2x.png"; sourceTree = "<group>"; };
|
DABD36101711E29400CF925C /* ui_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ui_background@2x.png"; sourceTree = "<group>"; };
|
||||||
@ -1773,6 +1776,8 @@
|
|||||||
93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */,
|
93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */,
|
||||||
93D3969393A3A46BD27D7078 /* mpw-algorithm.c */,
|
93D3969393A3A46BD27D7078 /* mpw-algorithm.c */,
|
||||||
93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */,
|
93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */,
|
||||||
|
DAB7AE981F3DDEE000C856B1 /* mpw-marshall-util.c */,
|
||||||
|
DAB7AE971F3DDEE000C856B1 /* mpw-marshall-util.h */,
|
||||||
DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */,
|
DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */,
|
||||||
DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */,
|
DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */,
|
||||||
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */,
|
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */,
|
||||||
@ -4030,6 +4035,7 @@
|
|||||||
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
|
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
|
||||||
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */,
|
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */,
|
||||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
|
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
|
||||||
|
DAB7AE991F3DDEE000C856B1 /* mpw-marshall-util.c in Sources */,
|
||||||
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
|
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
|
||||||
93D394B5036C882B33C71872 /* MPSitesSegue.m in Sources */,
|
93D394B5036C882B33C71872 /* MPSitesSegue.m in Sources */,
|
||||||
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
|
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
|
||||||
@ -4327,6 +4333,8 @@
|
|||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
@ -4510,6 +4518,8 @@
|
|||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
@ -4598,6 +4608,8 @@
|
|||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
@ -2979,8 +2979,9 @@
|
|||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include",
|
|
||||||
);
|
);
|
||||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
||||||
@ -3315,8 +3316,9 @@
|
|||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include",
|
|
||||||
);
|
);
|
||||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
||||||
@ -3407,8 +3409,9 @@
|
|||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||||
|
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include",
|
|
||||||
);
|
);
|
||||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
echo "ARGS: $*"
|
|
||||||
|
|
||||||
cd "${BASH_SOURCE%/*}/../External/libjson-c"
|
cd "${BASH_SOURCE%/*}/../External/libjson-c"
|
||||||
[[ -d libjson-c-ios ]] && exit
|
[[ -d libjson-c-ios ]] && exit
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
echo "ARGS: $*"
|
|
||||||
|
|
||||||
cd "${BASH_SOURCE%/*}/../External/libjson-c"
|
cd "${BASH_SOURCE%/*}/../External/libjson-c"
|
||||||
[[ -d libjson-c-osx ]] && exit
|
[[ -d libjson-c-osx ]] && exit
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
echo "ARGS: $*"
|
|
||||||
|
|
||||||
cd "${BASH_SOURCE%/*}/../External/libsodium"
|
cd "${BASH_SOURCE%/*}/../External/libsodium"
|
||||||
[[ -d libsodium-ios ]] && exit
|
[[ -d libsodium-ios ]] && exit
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
echo "ARGS: $*"
|
|
||||||
|
|
||||||
cd "${BASH_SOURCE%/*}/../External/libsodium"
|
cd "${BASH_SOURCE%/*}/../External/libsodium"
|
||||||
[[ -d libsodium-osx ]] && exit
|
[[ -d libsodium-osx ]] && exit
|
||||||
|
@ -52,49 +52,44 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
|||||||
- (NSData *)keyIDForKey:(MPMasterKey)masterKey;
|
- (NSData *)keyIDForKey:(MPMasterKey)masterKey;
|
||||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||||
|
|
||||||
- (NSString *)nameOfType:(MPSiteType)type;
|
- (NSString *)nameOfType:(MPResultType)type;
|
||||||
- (NSString *)shortNameOfType:(MPSiteType)type;
|
- (NSString *)shortNameOfType:(MPResultType)type;
|
||||||
- (NSString *)classNameOfType:(MPSiteType)type;
|
- (NSString *)classNameOfType:(MPResultType)type;
|
||||||
- (Class)classOfType:(MPSiteType)type;
|
- (Class)classOfType:(MPResultType)type;
|
||||||
- (NSArray *)allTypes;
|
- (NSArray *)allTypes;
|
||||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
- (NSArray *)allTypesStartingWith:(MPResultType)startingType;
|
||||||
- (MPSiteType)defaultType;
|
- (MPResultType)defaultType;
|
||||||
- (MPSiteType)nextType:(MPSiteType)type;
|
- (MPResultType)nextType:(MPResultType)type;
|
||||||
- (MPSiteType)previousType:(MPSiteType)type;
|
- (MPResultType)previousType:(MPResultType)type;
|
||||||
|
|
||||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
- (NSString *)mpwLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
||||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
- (NSString *)mpwTemplateForSiteNamed:(NSString *)name ofType:(MPResultType)type
|
||||||
usingKey:(MPKey *)key;
|
withCounter:(NSUInteger)counter usingKey:(MPKey *)key;
|
||||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
|
- (NSString *)mpwAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
|
||||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
- (NSString *)mpwResultForSiteNamed:(NSString *)name ofType:(MPResultType)type parameter:(NSString *)parameter
|
||||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key;
|
withCounter:(NSUInteger)counter variant:(MPKeyPurpose)purpose context:(NSString *)context usingKey:(MPKey *)key;
|
||||||
|
|
||||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
|
||||||
|
|
||||||
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
|
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
|
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
|
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key;
|
||||||
|
|
||||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
|
||||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey;
|
|
||||||
|
|
||||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
result:(void ( ^ )(NSString *result))resultBlock;
|
||||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
result:(void ( ^ )(NSString *result))resultBlock;
|
||||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
result:(void ( ^ )(NSString *result))resultBlock;
|
||||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
result:(void ( ^ )(NSString *result))resultBlock;
|
||||||
|
|
||||||
- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
- (void)importPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
||||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
- (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site
|
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker;
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker;
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -145,125 +145,125 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
return [[NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] hashWith:PearlHashSHA256];
|
return [[NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] hashWith:PearlHashSHA256];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)nameOfType:(MPSiteType)type {
|
- (NSString *)nameOfType:(MPResultType)type {
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPResultTypeTemplateMaximum:
|
||||||
return @"Maximum Security Password";
|
return @"Maximum Security Password";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPResultTypeTemplateLong:
|
||||||
return @"Long Password";
|
return @"Long Password";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
case MPResultTypeTemplateMedium:
|
||||||
return @"Medium Password";
|
return @"Medium Password";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
case MPResultTypeTemplateBasic:
|
||||||
return @"Basic Password";
|
return @"Basic Password";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedShort:
|
case MPResultTypeTemplateShort:
|
||||||
return @"Short Password";
|
return @"Short Password";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
case MPResultTypeTemplatePIN:
|
||||||
return @"PIN";
|
return @"PIN";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedName:
|
case MPResultTypeTemplateName:
|
||||||
return @"Name";
|
return @"Name";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
case MPResultTypeTemplatePhrase:
|
||||||
return @"Phrase";
|
return @"Phrase";
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal:
|
case MPResultTypeStatefulPersonal:
|
||||||
return @"Personal Password";
|
return @"Personal Password";
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
case MPResultTypeStatefulDevice:
|
||||||
return @"Device Private Password";
|
return @"Device Private Password";
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
Throw( @"Type not supported: %lu", (long)type );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)shortNameOfType:(MPSiteType)type {
|
- (NSString *)shortNameOfType:(MPResultType)type {
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPResultTypeTemplateMaximum:
|
||||||
return @"Maximum";
|
return @"Maximum";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPResultTypeTemplateLong:
|
||||||
return @"Long";
|
return @"Long";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
case MPResultTypeTemplateMedium:
|
||||||
return @"Medium";
|
return @"Medium";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
case MPResultTypeTemplateBasic:
|
||||||
return @"Basic";
|
return @"Basic";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedShort:
|
case MPResultTypeTemplateShort:
|
||||||
return @"Short";
|
return @"Short";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
case MPResultTypeTemplatePIN:
|
||||||
return @"PIN";
|
return @"PIN";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedName:
|
case MPResultTypeTemplateName:
|
||||||
return @"Name";
|
return @"Name";
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
case MPResultTypeTemplatePhrase:
|
||||||
return @"Phrase";
|
return @"Phrase";
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal:
|
case MPResultTypeStatefulPersonal:
|
||||||
return @"Personal";
|
return @"Personal";
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
case MPResultTypeStatefulDevice:
|
||||||
return @"Device";
|
return @"Device";
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
Throw( @"Type not supported: %lu", (long)type );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)classNameOfType:(MPSiteType)type {
|
- (NSString *)classNameOfType:(MPResultType)type {
|
||||||
|
|
||||||
return NSStringFromClass( [self classOfType:type] );
|
return NSStringFromClass( [self classOfType:type] );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (Class)classOfType:(MPSiteType)type {
|
- (Class)classOfType:(MPResultType)type {
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
Throw( @"No type given." );
|
Throw( @"No type given." );
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPResultTypeTemplateMaximum:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPResultTypeTemplateLong:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
case MPResultTypeTemplateMedium:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
case MPResultTypeTemplateBasic:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedShort:
|
case MPResultTypeTemplateShort:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
case MPResultTypeTemplatePIN:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedName:
|
case MPResultTypeTemplateName:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
case MPResultTypeTemplatePhrase:
|
||||||
return [MPGeneratedSiteEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal:
|
case MPResultTypeStatefulPersonal:
|
||||||
return [MPStoredSiteEntity class];
|
return [MPStoredSiteEntity class];
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
case MPResultTypeStatefulDevice:
|
||||||
return [MPStoredSiteEntity class];
|
return [MPStoredSiteEntity class];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,13 +272,13 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
|
|
||||||
- (NSArray *)allTypes {
|
- (NSArray *)allTypes {
|
||||||
|
|
||||||
return [self allTypesStartingWith:MPSiteTypeGeneratedPhrase];
|
return [self allTypesStartingWith:MPResultTypeTemplatePhrase];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
|
- (NSArray *)allTypesStartingWith:(MPResultType)startingType {
|
||||||
|
|
||||||
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
||||||
MPSiteType currentType = startingType;
|
MPResultType currentType = startingType;
|
||||||
do {
|
do {
|
||||||
[allTypes addObject:@(currentType)];
|
[allTypes addObject:@(currentType)];
|
||||||
} while ((currentType = [self nextType:currentType]) != startingType);
|
} while ((currentType = [self nextType:currentType]) != startingType);
|
||||||
@ -286,199 +286,170 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
return allTypes;
|
return allTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)defaultType {
|
- (MPResultType)defaultType {
|
||||||
|
|
||||||
return MPSiteTypeGeneratedLong;
|
return MPResultTypeTemplateLong;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)nextType:(MPSiteType)type {
|
- (MPResultType)nextType:(MPResultType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedPhrase:
|
case MPResultTypeTemplatePhrase:
|
||||||
return MPSiteTypeGeneratedName;
|
return MPResultTypeTemplateName;
|
||||||
case MPSiteTypeGeneratedName:
|
case MPResultTypeTemplateName:
|
||||||
return MPSiteTypeGeneratedMaximum;
|
return MPResultTypeTemplateMaximum;
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPResultTypeTemplateMaximum:
|
||||||
return MPSiteTypeGeneratedLong;
|
return MPResultTypeTemplateLong;
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPResultTypeTemplateLong:
|
||||||
return MPSiteTypeGeneratedMedium;
|
return MPResultTypeTemplateMedium;
|
||||||
case MPSiteTypeGeneratedMedium:
|
case MPResultTypeTemplateMedium:
|
||||||
return MPSiteTypeGeneratedBasic;
|
return MPResultTypeTemplateBasic;
|
||||||
case MPSiteTypeGeneratedBasic:
|
case MPResultTypeTemplateBasic:
|
||||||
return MPSiteTypeGeneratedShort;
|
return MPResultTypeTemplateShort;
|
||||||
case MPSiteTypeGeneratedShort:
|
case MPResultTypeTemplateShort:
|
||||||
return MPSiteTypeGeneratedPIN;
|
return MPResultTypeTemplatePIN;
|
||||||
case MPSiteTypeGeneratedPIN:
|
case MPResultTypeTemplatePIN:
|
||||||
return MPSiteTypeStoredPersonal;
|
return MPResultTypeStatefulPersonal;
|
||||||
case MPSiteTypeStoredPersonal:
|
case MPResultTypeStatefulPersonal:
|
||||||
return MPSiteTypeStoredDevicePrivate;
|
return MPResultTypeStatefulDevice;
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
case MPResultTypeStatefulDevice:
|
||||||
return MPSiteTypeGeneratedPhrase;
|
return MPResultTypeTemplatePhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [self defaultType];
|
return [self defaultType];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)previousType:(MPSiteType)type {
|
- (MPResultType)previousType:(MPResultType)type {
|
||||||
|
|
||||||
MPSiteType previousType = type, nextType = type;
|
MPResultType previousType = type, nextType = type;
|
||||||
while ((nextType = [self nextType:nextType]) != type)
|
while ((nextType = [self nextType:nextType]) != type)
|
||||||
previousType = nextType;
|
previousType = nextType;
|
||||||
|
|
||||||
return previousType;
|
return previousType;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
|
- (NSString *)mpwLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1
|
return [self mpwResultForSiteNamed:name ofType:MPResultTypeTemplateName parameter:nil withCounter:1
|
||||||
variant:MPKeyPurposeIdentification context:nil usingKey:key];
|
variant:MPKeyPurposeIdentification context:nil usingKey:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
- (NSString *)mpwTemplateForSiteNamed:(NSString *)name ofType:(MPResultType)type
|
||||||
usingKey:(MPKey *)key {
|
withCounter:(NSUInteger)counter usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return [self generateContentForSiteNamed:name ofType:type withCounter:counter
|
return [self mpwResultForSiteNamed:name ofType:type parameter:nil withCounter:counter
|
||||||
variant:MPKeyPurposeAuthentication context:nil usingKey:key];
|
variant:MPKeyPurposeAuthentication context:nil usingKey:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key {
|
- (NSString *)mpwAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedPhrase withCounter:1
|
return [self mpwResultForSiteNamed:name ofType:MPResultTypeTemplatePhrase parameter:nil withCounter:1
|
||||||
variant:MPKeyPurposeRecovery context:question usingKey:key];
|
variant:MPKeyPurposeRecovery context:question usingKey:key];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
- (NSString *)mpwResultForSiteNamed:(NSString *)name ofType:(MPResultType)type parameter:(NSString *)parameter
|
||||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
withCounter:(NSUInteger)counter variant:(MPKeyPurpose)purpose context:(NSString *)context usingKey:(MPKey *)key {
|
||||||
|
|
||||||
__block NSString *content = nil;
|
__block NSString *result = nil;
|
||||||
[self mpw_perform:^{
|
[self mpw_perform:^{
|
||||||
char const *contentBytes = mpw_passwordForSite( [key keyForAlgorithm:self],
|
char const *resultBytes = mpw_siteResult( [key keyForAlgorithm:self],
|
||||||
name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] );
|
name.UTF8String, (uint32_t)counter, purpose, context.UTF8String, type, parameter.UTF8String, [self version] );
|
||||||
if (contentBytes) {
|
if (resultBytes) {
|
||||||
content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
|
result = [NSString stringWithCString:resultBytes encoding:NSUTF8StringEncoding];
|
||||||
mpw_free_string( contentBytes );
|
mpw_free_string( resultBytes );
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
return content;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
- (BOOL)savePassword:(NSString *)plainText toSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return nil;
|
if (!(site.type & MPResultTypeClassStateful)) {
|
||||||
}
|
wrn( @"Can only save content to site with a stateful type: %lu.", (long)site.type );
|
||||||
|
return NO;
|
||||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return [self decryptContent:site.contentObject usingKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
switch (site.type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *encryptionKey = [siteKey keyForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
|
||||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
|
||||||
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
((MPStoredSiteEntity *)site).contentObject = encryptedContent;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
case MPSiteTypeStoredDevicePrivate: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *encryptionKey = [siteKey keyForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
|
||||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
|
||||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
|
||||||
if (!encryptedContent)
|
|
||||||
[PearlKeyChain deleteItemForQuery:siteQuery];
|
|
||||||
else
|
|
||||||
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
|
||||||
(__bridge id)kSecValueData : encryptedContent,
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
||||||
#endif
|
|
||||||
}];
|
|
||||||
((MPStoredSiteEntity *)site).contentObject = nil;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw( @"Unsupported type: %ld", (long)site.type );
|
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
|
(long)site.type, [site class] );
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
__block NSData *state = nil;
|
||||||
|
if (plainText)
|
||||||
|
[self mpw_perform:^{
|
||||||
|
char const *stateBytes = mpw_siteState( [key keyForAlgorithm:self], site.name.UTF8String,
|
||||||
|
MPCounterValueInitial, MPKeyPurposeAuthentication, NULL, site.type, plainText.UTF8String, [self version] );
|
||||||
|
if (stateBytes) {
|
||||||
|
state = [[NSString stringWithCString:stateBytes encoding:NSUTF8StringEncoding] decodeBase64];
|
||||||
|
mpw_free_string( stateBytes );
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
NSDictionary *siteQuery = [self queryForSite:site];
|
||||||
|
if (!state)
|
||||||
|
[PearlKeyChain deleteItemForQuery:siteQuery];
|
||||||
|
else
|
||||||
|
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
||||||
|
(__bridge id)kSecValueData : state,
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
(__bridge id)kSecAttrAccessible:
|
||||||
|
site.type & MPSiteFeatureDevicePrivate? (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly
|
||||||
|
: (__bridge id)kSecAttrAccessibleWhenUnlocked,
|
||||||
|
#endif
|
||||||
|
}];
|
||||||
|
((MPStoredSiteEntity *)site).contentObject = nil;
|
||||||
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return PearlAwait( ^(void (^setResult)(id)) {
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
[self resolveLoginForSite:site usingKey:key result:^(NSString *result_) {
|
||||||
setResult( result_ );
|
setResult( result_ );
|
||||||
}];
|
}];
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return PearlAwait( ^(void (^setResult)(id)) {
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
[self resolvePasswordForSite:site usingKey:key result:^(NSString *result_) {
|
||||||
setResult( result_ );
|
setResult( result_ );
|
||||||
}];
|
}];
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return PearlAwait( ^(void (^setResult)(id)) {
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
[self resolveAnswerForSite:site usingKey:key result:^(NSString *result_) {
|
||||||
setResult( result_ );
|
setResult( result_ );
|
||||||
}];
|
}];
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
|
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key {
|
||||||
|
|
||||||
return PearlAwait( ^(void (^setResult)(id)) {
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
[self resolveAnswerForQuestion:question usingKey:key result:^(NSString *result_) {
|
||||||
setResult( result_ );
|
setResult( result_ );
|
||||||
}];
|
}];
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
NSString *name = site.name;
|
NSString *name = site.name;
|
||||||
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
||||||
NSString *loginName = site.loginName;
|
NSString *loginName = site.loginName;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!name.length)
|
if (!name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey)
|
else if (!key)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = site.algorithm;
|
algorithm = site.algorithm;
|
||||||
@ -487,244 +458,139 @@ static NSOperationQueue *_mpwQueue = nil;
|
|||||||
resultBlock( loginName );
|
resultBlock( loginName );
|
||||||
else
|
else
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
resultBlock( [algorithm mpwLoginForSiteNamed:name usingKey:key] );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
|
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
|
NSString *name = site.name;
|
||||||
|
MPResultType type = site.type;
|
||||||
|
id<MPAlgorithm> algorithm = nil;
|
||||||
|
if (!site.name.length)
|
||||||
|
err( @"Missing name." );
|
||||||
|
else if (!key)
|
||||||
|
err( @"Missing key." );
|
||||||
|
else
|
||||||
|
algorithm = site.algorithm;
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
switch (site.type) {
|
switch (site.type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPResultTypeTemplateMaximum:
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPResultTypeTemplateLong:
|
||||||
case MPSiteTypeGeneratedMedium:
|
case MPResultTypeTemplateMedium:
|
||||||
case MPSiteTypeGeneratedBasic:
|
case MPResultTypeTemplateBasic:
|
||||||
case MPSiteTypeGeneratedShort:
|
case MPResultTypeTemplateShort:
|
||||||
case MPSiteTypeGeneratedPIN:
|
case MPResultTypeTemplatePIN:
|
||||||
case MPSiteTypeGeneratedName:
|
case MPResultTypeTemplateName:
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
case MPResultTypeTemplatePhrase: {
|
||||||
if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||||
wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
|
wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
|
||||||
(long)site.type, [site class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *name = site.name;
|
|
||||||
MPSiteType type = site.type;
|
|
||||||
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
|
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
|
||||||
if (!site.name.length)
|
|
||||||
err( @"Missing name." );
|
|
||||||
else if (!siteKey)
|
|
||||||
err( @"Missing key." );
|
|
||||||
else
|
|
||||||
algorithm = site.algorithm;
|
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
resultBlock( [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey] );
|
resultBlock( [algorithm mpwTemplateForSiteNamed:name ofType:type withCounter:counter usingKey:key] );
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
case MPResultTypeStatefulPersonal:
|
||||||
|
case MPResultTypeStatefulDevice: {
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
(long)site.type, [site class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
NSDictionary *siteQuery = [self queryForSite:site];
|
||||||
|
NSData *state = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||||
|
state = state?: ((MPStoredSiteEntity *)site).contentObject;
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
resultBlock( [algorithm mpwResultForSiteNamed:name ofType:type parameter:[state encodeBase64]
|
||||||
} );
|
withCounter:MPCounterValueInitial variant:MPKeyPurposeAuthentication context:nil
|
||||||
break;
|
usingKey:key] );
|
||||||
}
|
|
||||||
case MPSiteTypeStoredDevicePrivate: {
|
|
||||||
NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
|
|
||||||
@"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
|
|
||||||
[site class] );
|
|
||||||
|
|
||||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
|
||||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
|
||||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
NSString *name = site.name;
|
NSString *name = site.name;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!site.name.length)
|
if (!site.name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey)
|
else if (!key)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = site.algorithm;
|
algorithm = site.algorithm;
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey] );
|
resultBlock( [algorithm mpwAnswerForSiteNamed:name onQuestion:nil usingKey:key] );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key
|
||||||
result:(void ( ^ )(NSString *result))resultBlock {
|
result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID],
|
NSAssert( [[key keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID],
|
||||||
@"Site does not belong to current user." );
|
@"Site does not belong to current user." );
|
||||||
NSString *name = question.site.name;
|
NSString *name = question.site.name;
|
||||||
NSString *keyword = question.keyword;
|
NSString *keyword = question.keyword;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!name.length)
|
if (!name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey)
|
else if (!key)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
||||||
algorithm = question.site.algorithm;
|
algorithm = question.site.algorithm;
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey] );
|
resultBlock( [algorithm mpwAnswerForSiteNamed:name onQuestion:keyword usingKey:key] );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
- (void)importPassword:(NSString *)cipherText protectedByKey:(MPKey *)importKey
|
||||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (site.type) {
|
if (cipherText && cipherText.length && site.type & MPResultTypeClassStateful) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
NSString *plainText = [self mpwResultForSiteNamed:site.name ofType:site.type parameter:cipherText
|
||||||
case MPSiteTypeGeneratedLong:
|
withCounter:MPCounterValueInitial variant:MPKeyPurposeAuthentication context:nil
|
||||||
case MPSiteTypeGeneratedMedium:
|
usingKey:importKey];
|
||||||
case MPSiteTypeGeneratedBasic:
|
if (plainText)
|
||||||
case MPSiteTypeGeneratedShort:
|
[self savePassword:plainText toSite:site usingKey:key];
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
|
|
||||||
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
|
||||||
|
|
||||||
else {
|
|
||||||
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
|
|
||||||
[self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (NSDictionary *)queryForSite:(MPSiteEntity *)site {
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:@{
|
||||||
switch (site.type) {
|
(__bridge id)kSecAttrService: site.type & MPSiteFeatureDevicePrivate? @"DevicePrivate": @"Private",
|
||||||
case MPSiteTypeGeneratedMaximum:
|
(__bridge id)kSecAttrAccount: site.name
|
||||||
case MPSiteTypeGeneratedLong:
|
} matches:nil];
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
[self savePassword:clearContent toSite:site usingKey:siteKey];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
if (!(site.type & MPSiteFeatureExportContent))
|
if (!(site.type & MPSiteFeatureExportContent))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSString *result = nil;
|
NSDictionary *siteQuery = [self queryForSite:site];
|
||||||
switch (site.type) {
|
NSData *state = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||||
case MPSiteTypeGeneratedMaximum:
|
return [state?: ((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
result = nil;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate: {
|
|
||||||
result = nil;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateExplicitly:(BOOL)explicit {
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker {
|
||||||
|
|
||||||
return NO;
|
if (!(type & MPResultTypeClassTemplate))
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
|
|
||||||
|
|
||||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
|
||||||
attributes:@{
|
|
||||||
(__bridge id)kSecAttrService: @"DevicePrivate",
|
|
||||||
(__bridge id)kSecAttrAccount: name
|
|
||||||
}
|
|
||||||
matches:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)decryptContent:(NSData *)encryptedContent usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
if (!key)
|
|
||||||
return nil;
|
|
||||||
NSData *decryptedContent = nil;
|
|
||||||
if ([encryptedContent length]) {
|
|
||||||
NSData *encryptionKey = [key keyForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
|
||||||
decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
|
|
||||||
}
|
|
||||||
if (!decryptedContent)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
|
||||||
|
|
||||||
if (!(type & MPSiteTypeClassGenerated))
|
|
||||||
return NO;
|
return NO;
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
const char **templates = mpw_templatesForType( type, &count );
|
const char **templates = mpw_templatesForType( type, &count );
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!explicit) {
|
if (!explicit) {
|
||||||
if (site.type & MPSiteTypeClassGenerated) {
|
if (site.type & MPResultTypeClassTemplate) {
|
||||||
// This migration requires explicit permission for types of the generated class.
|
// This migration requires explicit permission for types of the generated class.
|
||||||
site.requiresExplicitMigration = YES;
|
site.requiresExplicitMigration = YES;
|
||||||
return NO;
|
return NO;
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!explicit) {
|
if (!explicit) {
|
||||||
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
if (site.type & MPResultTypeClassTemplate && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||||
// This migration requires explicit permission for types of the generated class.
|
// This migration requires explicit permission for types of the generated class.
|
||||||
site.requiresExplicitMigration = YES;
|
site.requiresExplicitMigration = YES;
|
||||||
return NO;
|
return NO;
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!explicit) {
|
if (!explicit) {
|
||||||
if (site.type & MPSiteTypeClassGenerated &&
|
if (site.type & MPResultTypeClassTemplate &&
|
||||||
site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||||
// This migration requires explicit permission for types of the generated class.
|
// This migration requires explicit permission for types of the generated class.
|
||||||
site.requiresExplicitMigration = YES;
|
site.requiresExplicitMigration = YES;
|
||||||
|
@ -248,9 +248,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (MPSiteEntity *site in user.sites) {
|
for (MPSiteEntity *site in user.sites) {
|
||||||
if (site.type & MPSiteTypeClassStored) {
|
if (site.type & MPResultTypeClassStateful) {
|
||||||
NSString *content;
|
NSString *content;
|
||||||
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
while (!(content = [site.algorithm resolvePasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||||
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
||||||
NSString *masterPassword = nil;
|
NSString *masterPassword = nil;
|
||||||
|
|
||||||
|
@ -20,14 +20,6 @@
|
|||||||
|
|
||||||
#import "MPFixable.h"
|
#import "MPFixable.h"
|
||||||
|
|
||||||
typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
|
||||||
MPImportResultSuccess,
|
|
||||||
MPImportResultCancelled,
|
|
||||||
MPImportResultInvalidPassword,
|
|
||||||
MPImportResultMalformedInput,
|
|
||||||
MPImportResultInternalError,
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared(Store)
|
@interface MPAppDelegate_Shared(Store)
|
||||||
|
|
||||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
||||||
@ -42,10 +34,13 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
|||||||
|
|
||||||
/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
|
/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
|
||||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
|
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
|
||||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
|
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPResultType)type;
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
- (void)importSites:(NSString *)importData
|
||||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
|
||||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords;
|
result:(void ( ^ )(NSError *error))resultBlock;
|
||||||
|
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||||
|
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||||
|
result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import "mpw-marshall.h"
|
#import "mpw-marshall.h"
|
||||||
|
#import "mpw-util.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
||||||
@ -489,7 +490,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPSiteType type = activeUser.defaultType;
|
MPResultType type = activeUser.defaultType;
|
||||||
id<MPAlgorithm> algorithm = MPAlgorithmDefault;
|
id<MPAlgorithm> algorithm = MPAlgorithmDefault;
|
||||||
Class entityType = [algorithm classOfType:type];
|
Class entityType = [algorithm classOfType:type];
|
||||||
|
|
||||||
@ -506,7 +507,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type {
|
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPResultType)type {
|
||||||
|
|
||||||
if (site.type == type)
|
if (site.type == type)
|
||||||
return site;
|
return site;
|
||||||
@ -539,328 +540,214 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
return site;
|
return site;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
- (void)importSites:(NSString *)importData
|
||||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
|
||||||
|
result:(void ( ^ )(NSError *error))resultBlock {
|
||||||
|
|
||||||
NSAssert( ![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread." );
|
NSAssert( ![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread." );
|
||||||
|
|
||||||
__block MPImportResult result = MPImportResultCancelled;
|
|
||||||
do {
|
do {
|
||||||
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||||
result = [self importSites:importedSitesString askImportPassword:importPassword askUserPassword:userPassword
|
NSError *error = [self importSites:importData askImportPassword:importPassword askUserPassword:userPassword
|
||||||
saveInContext:context];
|
saveInContext:context];
|
||||||
|
PearlMainQueue( ^{
|
||||||
|
resultBlock( error );
|
||||||
|
} );
|
||||||
}])
|
}])
|
||||||
break;
|
break;
|
||||||
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
||||||
} while (YES);
|
} while (YES);
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
- (NSError *)importSites:(NSString *)importData
|
||||||
askImportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
askImportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))askUserPassword
|
askUserPassword:(NSString *( ^ )(NSString *userName))askUserPassword
|
||||||
saveInContext:(NSManagedObjectContext *)context {
|
saveInContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
// Compile patterns.
|
// Read metadata for the import file.
|
||||||
static NSRegularExpression *headerPattern;
|
MPMarshallInfo *info = mpw_marshall_read_info( importData.UTF8String );
|
||||||
static NSArray *sitePatterns;
|
if (info->format == MPMarshallFormatNone)
|
||||||
NSError *error = nil;
|
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshallCode userInfo:@{
|
||||||
if (!headerPattern) {
|
@"type" : @(MPMarshallErrorFormat),
|
||||||
headerPattern = [[NSRegularExpression alloc]
|
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
}]), @"While importing sites." );
|
||||||
options:(NSRegularExpressionOptions)0 error:&error];
|
|
||||||
if (error) {
|
// Get master password for import file.
|
||||||
MPError( error, @"Error loading the header pattern." );
|
MPKey *importKey;
|
||||||
return MPImportResultInternalError;
|
NSString *importMasterPassword;
|
||||||
|
do {
|
||||||
|
importMasterPassword = askImportPassword( @(info->fullName) );
|
||||||
|
if (!importMasterPassword) {
|
||||||
|
inf( @"Import cancelled." );
|
||||||
|
mpw_marshal_info_free( info );
|
||||||
|
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!sitePatterns) {
|
importKey = [[MPKey alloc] initForFullName:@(info->fullName) withMasterPassword:importMasterPassword];
|
||||||
sitePatterns = @[
|
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( info->algorithm )] encodeHex]
|
||||||
[[NSRegularExpression alloc] // Format 0
|
caseInsensitiveCompare:@(info->keyID)] != NSOrderedSame);
|
||||||
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.
|
// Parse import data.
|
||||||
inf( @"Importing sites." );
|
MPMarshallError importError = { .type = MPMarshallSuccess };
|
||||||
NSUInteger importFormat = 0;
|
MPMarshalledUser *importUser = mpw_marshall_read( importData.UTF8String, info->format, importMasterPassword.UTF8String, &importError );
|
||||||
__block MPUserEntity *user = nil;
|
mpw_marshal_info_free( info );
|
||||||
NSUInteger importAvatar = NSNotFound;
|
|
||||||
NSData *importKeyID = nil;
|
@try {
|
||||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
if (!importUser || importError.type != MPMarshallSuccess)
|
||||||
id<MPAlgorithm> importAlgorithm = nil;
|
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshallCode userInfo:@{
|
||||||
MPSiteType importDefaultType = (MPSiteType)0;
|
@"type" : @(importError.type),
|
||||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
NSLocalizedDescriptionKey: @(importError.description),
|
||||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
}]), @"While importing sites." );
|
||||||
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
|
||||||
NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
// Find an existing user to update.
|
||||||
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
NSError *error = nil;
|
||||||
for (NSString *importedSiteLine in importedSiteLines) {
|
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||||
if ([importedSiteLine hasPrefix:@"#"]) {
|
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", @(importUser->fullName)];
|
||||||
// Comment or header
|
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||||
if (!headerStarted) {
|
if (!users)
|
||||||
if ([importedSiteLine isEqualToString:@"##"])
|
return MPError( error, @"While looking for user: %@.", @(importUser->fullName) );
|
||||||
headerStarted = YES;
|
if ([users count] > 1)
|
||||||
continue;
|
return MPMakeError( @"While looking for user: %@, found more than one: %zu",
|
||||||
}
|
@(importUser->fullName), (size_t)[users count] );
|
||||||
if (headerEnded)
|
|
||||||
continue;
|
// Get master key for user.
|
||||||
if ([importedSiteLine isEqualToString:@"##"]) {
|
MPUserEntity *user = [users lastObject];
|
||||||
headerEnded = YES;
|
MPKey *userKey = importKey;
|
||||||
continue;
|
while (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID]) {
|
||||||
|
NSString *userMasterPassword = askUserPassword( user.name );
|
||||||
|
if (!userMasterPassword) {
|
||||||
|
inf( @"Import cancelled." );
|
||||||
|
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header
|
userKey = [[MPKey alloc] initForFullName:@(importUser->fullName) withMasterPassword:userMasterPassword];
|
||||||
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.
|
// Update or create user.
|
||||||
if (user) {
|
if (!user) {
|
||||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
user = [MPUserEntity insertNewObjectInContext:context];
|
||||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
user.name = @(importUser->fullName);
|
||||||
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 ]];
|
user.algorithm = MPAlgorithmForVersion( importUser->algorithm );
|
||||||
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.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
||||||
user.defaultType = importDefaultType?: user.algorithm.defaultType;
|
user.avatar = importUser->avatar;
|
||||||
if (importAvatar != NSNotFound)
|
user.defaultType = importUser->defaultType;
|
||||||
user.avatar = importAvatar;
|
user.lastUsed = [NSDate dateWithTimeIntervalSince1970:MAX( user.lastUsed.timeIntervalSince1970, importUser->lastUsed )];
|
||||||
dbg( @"Created User: %@", [user debugDescription] );
|
dbg( @"Importing user: %@", [user debugDescription] );
|
||||||
}
|
|
||||||
|
|
||||||
// Import new sites.
|
// Update or create sites.
|
||||||
for (NSArray *siteElements in importedSiteSites) {
|
for (size_t s = 0; s < importUser->sites_count; ++s) {
|
||||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
|
MPMarshalledSite *importSite = &importUser->sites[s];
|
||||||
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]: nil;
|
|
||||||
NSString *siteName = siteElements[6];
|
|
||||||
NSString *exportContent = siteElements[7];
|
|
||||||
|
|
||||||
// Create new site.
|
// Find an existing site to update.
|
||||||
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( version );
|
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
Class entityType = [algorithm classOfType:type];
|
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", @(importSite->name), user];
|
||||||
if (!entityType) {
|
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||||
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
|
if (!existingSites)
|
||||||
return MPImportResultInternalError;
|
return MPError( error, @"Lookup of existing sites failed for site: %@, user: %@", @(importSite->name), user.userID );
|
||||||
|
if ([existingSites count])
|
||||||
|
// Update existing site.
|
||||||
|
for (MPSiteEntity *site in existingSites) {
|
||||||
|
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||||
|
dbg( @"Updated site: %@", [site debugDescription] );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Create new site.
|
||||||
|
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
||||||
|
Class entityType = [algorithm classOfType:importSite->type];
|
||||||
|
if (!entityType)
|
||||||
|
return MPMakeError( @"Invalid site type in import file: %@ has type %lu", @(importSite->name), (long)importSite->type );
|
||||||
|
|
||||||
|
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||||
|
site.user = user;
|
||||||
|
|
||||||
|
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||||
|
dbg( @"Created site: %@", [site debugDescription] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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 MPMakeError( @"Failed saving imported changes." );
|
||||||
|
|
||||||
|
inf( @"Import completed successfully." );
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:nil userInfo:@{
|
||||||
|
MPSitesImportedNotificationUserKey: user
|
||||||
|
}];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
@finally {
|
||||||
|
mpw_marshal_free( importUser );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![context saveToStore])
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
|
|
||||||
inf( @"Import completed successfully." );
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:nil userInfo:@{
|
|
||||||
MPSitesImportedNotificationUserKey: user
|
|
||||||
}];
|
|
||||||
|
|
||||||
return MPImportResultSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
|
- (void)importSite:(const MPMarshalledSite *)importSite protectedByKey:(MPKey *)importKey intoSite:(MPSiteEntity *)site
|
||||||
|
usingKey:(MPKey *)userKey {
|
||||||
|
|
||||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
site.name = @(importSite->name);
|
||||||
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
|
if (importSite->content)
|
||||||
|
[site.algorithm importPassword:@(importSite->content) protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||||
|
site.type = importSite->type;
|
||||||
|
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
|
((MPGeneratedSiteEntity *)site).counter = importSite->counter;
|
||||||
|
site.algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
||||||
|
site.loginName = importSite->loginName? @(importSite->loginName): nil;
|
||||||
|
site.loginGenerated = importSite->loginGenerated;
|
||||||
|
site.url = importSite->url? @(importSite->url): nil;
|
||||||
|
site.uses = importSite->uses;
|
||||||
|
site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
|
||||||
|
}
|
||||||
|
|
||||||
MPMarshalledUser exportUser = mpw_marshall_user( activeUser.name.UTF8String,
|
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||||
[self.key keyForAlgorithm:activeUser.algorithm], activeUser.algorithm.version );
|
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||||
exportUser.avatar = activeUser.avatar;
|
result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock {
|
||||||
exportUser.defaultType = activeUser.defaultType;
|
|
||||||
exportUser.lastUsed = (time_t)activeUser.lastUsed.timeIntervalSince1970;
|
|
||||||
|
|
||||||
|
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
|
MPUserEntity *user = [self activeUserInContext:context];
|
||||||
|
NSString *masterPassword = askImportPassword( user.name );
|
||||||
|
|
||||||
for (MPSiteEntity *site in activeUser.sites) {
|
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
|
||||||
MPMarshalledSite exportSite = mpw_marshall_site( &exportUser,
|
MPMarshalledUser *exportUser = mpw_marshall_user( user.name.UTF8String, masterPassword.UTF8String, user.algorithm.version );
|
||||||
site.name.UTF8String, site.type, site.counter, site.algorithm.version );
|
exportUser->redacted = !revealPasswords;
|
||||||
exportSite.loginName = site.loginName.UTF8String;
|
exportUser->avatar = (unsigned int)user.avatar;
|
||||||
exportSite.url = site.url.UTF8String;
|
exportUser->defaultType = user.defaultType;
|
||||||
exportSite.uses = site.uses;
|
exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
|
||||||
exportSite.lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
|
|
||||||
|
|
||||||
for (MPSiteQuestionEntity *siteQuestion in site.questions)
|
for (MPSiteEntity *site in user.sites) {
|
||||||
mpw_marshal_question( &exportSite, siteQuestion.keyword.UTF8String );
|
MPCounterValue counter = MPCounterValueInitial;
|
||||||
}
|
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
|
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||||
|
NSString *content = revealPasswords
|
||||||
|
? [site.algorithm exportPasswordForSite:site usingKey:self.key]
|
||||||
|
: [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
||||||
|
|
||||||
mpw_marshall_write( &export, MPMarshallFormatFlat, exportUser );
|
MPMarshalledSite *exportSite = mpw_marshall_site( exportUser,
|
||||||
|
site.name.UTF8String, site.type, counter, site.algorithm.version );
|
||||||
|
exportSite->content = content.UTF8String;
|
||||||
|
exportSite->loginName = site.loginName.UTF8String;
|
||||||
|
exportSite->loginGenerated = site.loginGenerated;
|
||||||
|
exportSite->url = site.url.UTF8String;
|
||||||
|
exportSite->uses = (unsigned int)site.uses;
|
||||||
|
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
|
||||||
|
|
||||||
|
for (MPSiteQuestionEntity *siteQuestion in site.questions)
|
||||||
|
mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *export = NULL;
|
||||||
|
MPMarshallError exportError = (MPMarshallError){ .type= MPMarshallSuccess };
|
||||||
|
mpw_marshall_write( &export, MPMarshallFormatFlat, exportUser, &exportError );
|
||||||
|
NSString *mpsites = nil;
|
||||||
|
if (export && exportError.type == MPMarshallSuccess)
|
||||||
|
mpsites = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
|
||||||
|
mpw_free_string( export );
|
||||||
|
|
||||||
|
resultBlock( mpsites, exportError.type == MPMarshallSuccess? nil:
|
||||||
|
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshallCode userInfo:@{
|
||||||
|
@"type" : @(exportError.type),
|
||||||
|
NSLocalizedDescriptionKey: @(exportError.description),
|
||||||
|
}] );
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
@interface MPSiteEntity(MP)<MPFixable>
|
@interface MPSiteEntity(MP)<MPFixable>
|
||||||
|
|
||||||
@property(assign) BOOL loginGenerated;
|
@property(assign) BOOL loginGenerated;
|
||||||
@property(assign) MPSiteType type;
|
@property(assign) MPResultType type;
|
||||||
@property(readonly) NSString *typeName;
|
@property(readonly) NSString *typeName;
|
||||||
@property(readonly) NSString *typeShortName;
|
@property(readonly) NSString *typeShortName;
|
||||||
@property(readonly) NSString *typeClassName;
|
@property(readonly) NSString *typeClassName;
|
||||||
@ -71,7 +71,7 @@
|
|||||||
|
|
||||||
@interface MPGeneratedSiteEntity(MP)
|
@interface MPGeneratedSiteEntity(MP)
|
||||||
|
|
||||||
@property(assign) NSUInteger counter;
|
@property(assign) MPCounterValue counter;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -80,7 +80,7 @@
|
|||||||
@property(assign) NSUInteger avatar;
|
@property(assign) NSUInteger avatar;
|
||||||
@property(assign) BOOL saveKey;
|
@property(assign) BOOL saveKey;
|
||||||
@property(assign) BOOL touchID;
|
@property(assign) BOOL touchID;
|
||||||
@property(assign) MPSiteType defaultType;
|
@property(assign) MPResultType defaultType;
|
||||||
@property(readonly) NSString *userID;
|
@property(readonly) NSString *userID;
|
||||||
@property(strong) id<MPAlgorithm> algorithm;
|
@property(strong) id<MPAlgorithm> algorithm;
|
||||||
|
|
||||||
|
@ -82,9 +82,9 @@
|
|||||||
return MPFixableResultNoProblems;
|
return MPFixableResultNoProblems;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)type {
|
- (MPResultType)type {
|
||||||
|
|
||||||
return (MPSiteType)[self.type_ unsignedIntegerValue];
|
return (MPResultType)[self.type_ unsignedIntegerValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setLoginGenerated:(BOOL)aLoginGenerated {
|
- (void)setLoginGenerated:(BOOL)aLoginGenerated {
|
||||||
@ -97,7 +97,7 @@
|
|||||||
return [self.loginGenerated_ boolValue];
|
return [self.loginGenerated_ boolValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setType:(MPSiteType)aType {
|
- (void)setType:(MPResultType)aType {
|
||||||
|
|
||||||
self.type_ = @(aType);
|
self.type_ = @(aType);
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@
|
|||||||
|
|
||||||
MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
|
MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
|
||||||
|
|
||||||
if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
if (!self.type || self.type == (MPResultType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||||
// Invalid self.type
|
// Invalid self.type
|
||||||
result = MPApplyFix( result, ^MPFixableResult {
|
result = MPApplyFix( result, ^MPFixableResult {
|
||||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||||
@ -259,7 +259,7 @@
|
|||||||
self.type = self.user.defaultType;
|
self.type = self.user.defaultType;
|
||||||
return MPFixableResultProblemsFixed;
|
return MPFixableResultProblemsFixed;
|
||||||
} );
|
} );
|
||||||
if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
if (!self.type || self.type == (MPResultType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||||
// Invalid self.user.defaultType
|
// Invalid self.user.defaultType
|
||||||
result = MPApplyFix( result, ^MPFixableResult {
|
result = MPApplyFix( result, ^MPFixableResult {
|
||||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||||
@ -270,7 +270,7 @@
|
|||||||
if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
|
if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
|
||||||
// Mismatch between self.type and self.class
|
// Mismatch between self.type and self.class
|
||||||
result = MPApplyFix( result, ^MPFixableResult {
|
result = MPApplyFix( result, ^MPFixableResult {
|
||||||
for (MPSiteType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
|
for (MPResultType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
|
||||||
if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
|
if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
|
||||||
wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.",
|
wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.",
|
||||||
self.name, self.user.name, (long)self.type, self.class, (long)newType );
|
self.name, self.user.name, (long)self.type, self.class, (long)newType );
|
||||||
@ -286,12 +286,12 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)counter {
|
- (MPCounterValue)counter {
|
||||||
|
|
||||||
return [self.counter_ unsignedIntegerValue];
|
return (MPCounterValue)[self.counter_ unsignedIntegerValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCounter:(NSUInteger)aCounter {
|
- (void)setCounter:(MPCounterValue)aCounter {
|
||||||
|
|
||||||
self.counter_ = @(aCounter);
|
self.counter_ = @(aCounter);
|
||||||
}
|
}
|
||||||
@ -354,12 +354,12 @@
|
|||||||
self.touchID_ = @(aTouchID);
|
self.touchID_ = @(aTouchID);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)defaultType {
|
- (MPResultType)defaultType {
|
||||||
|
|
||||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: self.algorithm.defaultType;
|
return (MPResultType)[self.defaultType_ unsignedIntegerValue]?: self.algorithm.defaultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDefaultType:(MPSiteType)aDefaultType {
|
- (void)setDefaultType:(MPResultType)aDefaultType {
|
||||||
|
|
||||||
self.defaultType_ = @(aDefaultType);
|
self.defaultType_ = @(aDefaultType);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
extern NSString *const MPErrorDomain;
|
extern NSString *const MPErrorDomain;
|
||||||
extern NSInteger const MPErrorHangCode;
|
extern NSInteger const MPErrorHangCode;
|
||||||
|
extern NSInteger const MPErrorMarshallCode;
|
||||||
|
|
||||||
extern NSString *const MPSignedInNotification;
|
extern NSString *const MPSignedInNotification;
|
||||||
extern NSString *const MPSignedOutNotification;
|
extern NSString *const MPSignedOutNotification;
|
||||||
@ -38,13 +39,20 @@ __END_DECLS
|
|||||||
|
|
||||||
#ifdef CRASHLYTICS
|
#ifdef CRASHLYTICS
|
||||||
#define MPError(error_, message, ...) ({ \
|
#define MPError(error_, message, ...) ({ \
|
||||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
NSError *error = error_; \
|
||||||
|
err( message @"%@%@", ##__VA_ARGS__, error && [message length]? @"\n": @"", [error fullDescription]?: @"" ); \
|
||||||
\
|
\
|
||||||
if ([[MPConfig get].sendInfo boolValue]) { \
|
if ([[MPConfig get].sendInfo boolValue]) { \
|
||||||
[[Crashlytics sharedInstance] recordError:error_ withAdditionalUserInfo:@{ \
|
[[Crashlytics sharedInstance] recordError:error withAdditionalUserInfo:@{ \
|
||||||
@"location": strf( @"%@:%d %@", @(basename((char *)__FILE__)), __LINE__, NSStringFromSelector(_cmd) ), \
|
@"location": strf( @"%@:%d %@", @(basename((char *)__FILE__)), __LINE__, NSStringFromSelector(_cmd) ), \
|
||||||
}]; \
|
}]; \
|
||||||
} \
|
} \
|
||||||
|
error; \
|
||||||
|
})
|
||||||
|
#define MPMakeError(message, ...) ({ \
|
||||||
|
MPError( [NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{ \
|
||||||
|
NSLocalizedDescriptionKey: strf( message, ##__VA_ARGS__ ) \
|
||||||
|
}], @"" ); \
|
||||||
})
|
})
|
||||||
#else
|
#else
|
||||||
#define MPError(error_, message, ...) ({ \
|
#define MPError(error_, message, ...) ({ \
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
NSString *const MPErrorDomain = @"MPErrorDomain";
|
NSString *const MPErrorDomain = @"MPErrorDomain";
|
||||||
NSInteger const MPErrorHangCode = 1;
|
NSInteger const MPErrorHangCode = 1;
|
||||||
|
NSInteger const MPErrorMarshallCode = 1;
|
||||||
|
|
||||||
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
||||||
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
||||||
|
@ -233,14 +233,14 @@
|
|||||||
else
|
else
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
[self updatePasswordWithResult:
|
[self updatePasswordWithResult:
|
||||||
[self.algorithm generatePasswordForSiteNamed:self.name ofType:self.type withCounter:self.counter
|
[self.algorithm mpwTemplateForSiteNamed:self.name ofType:self.type withCounter:self.counter
|
||||||
usingKey:[MPAppDelegate_Shared get].key]];
|
usingKey:[MPAppDelegate_Shared get].key]];
|
||||||
[self updateLoginNameWithResult:
|
[self updateLoginNameWithResult:
|
||||||
[self.algorithm generateLoginForSiteNamed:self.name
|
[self.algorithm mpwLoginForSiteNamed:self.name
|
||||||
usingKey:[MPAppDelegate_Shared get].key]];
|
usingKey:[MPAppDelegate_Shared get].key]];
|
||||||
[self updateAnswerWithResult:
|
[self updateAnswerWithResult:
|
||||||
[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
|
[self.algorithm mpwAnswerForSiteNamed:self.name onQuestion:self.question
|
||||||
usingKey:[MPAppDelegate_Shared get].key]];
|
usingKey:[MPAppDelegate_Shared get].key]];
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,8 +252,8 @@
|
|||||||
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
||||||
[self updateLoginNameWithResult:result];
|
[self updateLoginNameWithResult:result];
|
||||||
}];
|
}];
|
||||||
[self updateAnswerWithResult:[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
|
[self updateAnswerWithResult:[self.algorithm mpwAnswerForSiteNamed:self.name onQuestion:self.question
|
||||||
usingKey:[MPAppDelegate_Shared get].key]];
|
usingKey:[MPAppDelegate_Shared get].key]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updatePasswordWithResult:(NSString *)result {
|
- (void)updatePasswordWithResult:(NSString *)result {
|
||||||
|
@ -376,8 +376,8 @@
|
|||||||
MPSiteType type = (MPSiteType)[types[t] unsignedIntegerValue];
|
MPSiteType type = (MPSiteType)[types[t] unsignedIntegerValue];
|
||||||
NSString *title = [site.algorithm nameOfType:type];
|
NSString *title = [site.algorithm nameOfType:type];
|
||||||
if (type & MPSiteTypeClassGenerated)
|
if (type & MPSiteTypeClassGenerated)
|
||||||
title = strf( @"%@ – %@", [site.algorithm generatePasswordForSiteNamed:site.name ofType:type withCounter:site.counter
|
title = strf( @"%@ – %@", [site.algorithm mpwTemplateForSiteNamed:site.name ofType:type withCounter:site.counter
|
||||||
usingKey:[MPMacAppDelegate get].key], title );
|
usingKey:[MPMacAppDelegate get].key], title );
|
||||||
|
|
||||||
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
||||||
cell.tag = type;
|
cell.tag = type;
|
||||||
|
@ -137,7 +137,7 @@
|
|||||||
- (void)updatePassword {
|
- (void)updatePassword {
|
||||||
|
|
||||||
NSString *siteName = self.siteField.text;
|
NSString *siteName = self.siteField.text;
|
||||||
MPSiteType siteType = [self siteType];
|
MPResultType siteType = [self siteType];
|
||||||
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
||||||
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
||||||
|
|
||||||
@ -147,8 +147,8 @@
|
|||||||
[self.emergencyPasswordQueue addOperationWithBlock:^{
|
[self.emergencyPasswordQueue addOperationWithBlock:^{
|
||||||
NSString *sitePassword = nil;
|
NSString *sitePassword = nil;
|
||||||
if (self.key && [siteName length])
|
if (self.key && [siteName length])
|
||||||
sitePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:siteName ofType:siteType withCounter:siteCounter
|
sitePassword = [MPAlgorithmDefault mpwTemplateForSiteNamed:siteName ofType:siteType withCounter:siteCounter
|
||||||
usingKey:self.key];
|
usingKey:self.key];
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
[self.activity stopAnimating];
|
[self.activity stopAnimating];
|
||||||
@ -157,21 +157,21 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (enum MPSiteType)siteType {
|
- (MPResultType)siteType {
|
||||||
|
|
||||||
switch (self.typeControl.selectedSegmentIndex) {
|
switch (self.typeControl.selectedSegmentIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPSiteTypeGeneratedMaximum;
|
return MPResultTypeTemplateMaximum;
|
||||||
case 1:
|
case 1:
|
||||||
return MPSiteTypeGeneratedLong;
|
return MPResultTypeTemplateLong;
|
||||||
case 2:
|
case 2:
|
||||||
return MPSiteTypeGeneratedMedium;
|
return MPResultTypeTemplateMedium;
|
||||||
case 3:
|
case 3:
|
||||||
return MPSiteTypeGeneratedBasic;
|
return MPResultTypeTemplateBasic;
|
||||||
case 4:
|
case 4:
|
||||||
return MPSiteTypeGeneratedShort;
|
return MPResultTypeTemplateShort;
|
||||||
case 5:
|
case 5:
|
||||||
return MPSiteTypeGeneratedPIN;
|
return MPResultTypeTemplatePIN;
|
||||||
default:
|
default:
|
||||||
Throw( @"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex );
|
Throw( @"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex );
|
||||||
}
|
}
|
||||||
|
@ -60,15 +60,15 @@
|
|||||||
self.touchIDSwitch.on = activeUser.touchID;
|
self.touchIDSwitch.on = activeUser.touchID;
|
||||||
self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID];
|
self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID];
|
||||||
|
|
||||||
MPSiteType defaultType = activeUser.defaultType;
|
MPResultType defaultType = activeUser.defaultType;
|
||||||
self.generated1TypeControl.selectedSegmentIndex = [self generated1SegmentIndexForType:defaultType];
|
self.generated1TypeControl.selectedSegmentIndex = [self generated1SegmentIndexForType:defaultType];
|
||||||
self.generated2TypeControl.selectedSegmentIndex = [self generated2SegmentIndexForType:defaultType];
|
self.generated2TypeControl.selectedSegmentIndex = [self generated2SegmentIndexForType:defaultType];
|
||||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
|
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
NSString *examplePassword = nil;
|
NSString *examplePassword = nil;
|
||||||
if (defaultType & MPSiteTypeClassGenerated)
|
if (defaultType & MPResultTypeClassTemplate)
|
||||||
examplePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:@"test" ofType:defaultType
|
examplePassword = [MPAlgorithmDefault mpwTemplateForSiteNamed:@"test" ofType:defaultType
|
||||||
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.typeSamplePassword.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
|
self.typeSamplePassword.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
|
||||||
} );
|
} );
|
||||||
@ -164,7 +164,7 @@
|
|||||||
if (sender != self.storedTypeControl)
|
if (sender != self.storedTypeControl)
|
||||||
self.storedTypeControl.selectedSegmentIndex = -1;
|
self.storedTypeControl.selectedSegmentIndex = -1;
|
||||||
|
|
||||||
MPSiteType defaultType = [self typeForSelectedSegment];
|
MPResultType defaultType = [self typeForSelectedSegment];
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType = defaultType;
|
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType = defaultType;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
@ -242,7 +242,7 @@
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)typeForSelectedSegment {
|
- (MPResultType)typeForSelectedSegment {
|
||||||
|
|
||||||
NSInteger selectedGenerated1Index = self.generated1TypeControl.selectedSegmentIndex;
|
NSInteger selectedGenerated1Index = self.generated1TypeControl.selectedSegmentIndex;
|
||||||
NSInteger selectedGenerated2Index = self.generated2TypeControl.selectedSegmentIndex;
|
NSInteger selectedGenerated2Index = self.generated2TypeControl.selectedSegmentIndex;
|
||||||
@ -250,30 +250,30 @@
|
|||||||
|
|
||||||
switch (selectedGenerated1Index) {
|
switch (selectedGenerated1Index) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPSiteTypeGeneratedPhrase;
|
return MPResultTypeTemplatePhrase;
|
||||||
case 1:
|
case 1:
|
||||||
return MPSiteTypeGeneratedName;
|
return MPResultTypeTemplateName;
|
||||||
default:
|
default:
|
||||||
switch (selectedGenerated2Index) {
|
switch (selectedGenerated2Index) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPSiteTypeGeneratedMaximum;
|
return MPResultTypeTemplateMaximum;
|
||||||
case 1:
|
case 1:
|
||||||
return MPSiteTypeGeneratedLong;
|
return MPResultTypeTemplateLong;
|
||||||
case 2:
|
case 2:
|
||||||
return MPSiteTypeGeneratedMedium;
|
return MPResultTypeTemplateMedium;
|
||||||
case 3:
|
case 3:
|
||||||
return MPSiteTypeGeneratedBasic;
|
return MPResultTypeTemplateBasic;
|
||||||
case 4:
|
case 4:
|
||||||
return MPSiteTypeGeneratedShort;
|
return MPResultTypeTemplateShort;
|
||||||
case 5:
|
case 5:
|
||||||
return MPSiteTypeGeneratedPIN;
|
return MPResultTypeTemplatePIN;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
switch (selectedStoredIndex) {
|
switch (selectedStoredIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPSiteTypeStoredPersonal;
|
return MPResultTypeStatefulPersonal;
|
||||||
case 1:
|
case 1:
|
||||||
return MPSiteTypeStoredDevicePrivate;
|
return MPResultTypeStatefulDevice;
|
||||||
default:
|
default:
|
||||||
Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
|
Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
|
||||||
(long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
|
(long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
|
||||||
@ -282,44 +282,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)generated1SegmentIndexForType:(MPSiteType)type {
|
- (NSInteger)generated1SegmentIndexForType:(MPResultType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedPhrase:
|
case MPResultTypeTemplatePhrase:
|
||||||
return 0;
|
return 0;
|
||||||
case MPSiteTypeGeneratedName:
|
case MPResultTypeTemplateName:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)generated2SegmentIndexForType:(MPSiteType)type {
|
- (NSInteger)generated2SegmentIndexForType:(MPResultType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPResultTypeTemplateMaximum:
|
||||||
return 0;
|
return 0;
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPResultTypeTemplateLong:
|
||||||
return 1;
|
return 1;
|
||||||
case MPSiteTypeGeneratedMedium:
|
case MPResultTypeTemplateMedium:
|
||||||
return 2;
|
return 2;
|
||||||
case MPSiteTypeGeneratedBasic:
|
case MPResultTypeTemplateBasic:
|
||||||
return 3;
|
return 3;
|
||||||
case MPSiteTypeGeneratedShort:
|
case MPResultTypeTemplateShort:
|
||||||
return 4;
|
return 4;
|
||||||
case MPSiteTypeGeneratedPIN:
|
case MPResultTypeTemplatePIN:
|
||||||
return 5;
|
return 5;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)storedSegmentIndexForType:(MPSiteType)type {
|
- (NSInteger)storedSegmentIndexForType:(MPResultType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeStoredPersonal:
|
case MPResultTypeStatefulPersonal:
|
||||||
return 0;
|
return 0;
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
case MPResultTypeStatefulDevice:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -284,7 +284,7 @@
|
|||||||
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
||||||
initSheet:^(UIActionSheet *sheet) {
|
initSheet:^(UIActionSheet *sheet) {
|
||||||
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
|
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
|
||||||
MPSiteType type = (MPSiteType)[typeNumber unsignedIntegerValue];
|
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
|
||||||
NSString *typeName = [mainSite.algorithm nameOfType:type];
|
NSString *typeName = [mainSite.algorithm nameOfType:type];
|
||||||
if (type == mainSite.type)
|
if (type == mainSite.type)
|
||||||
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
||||||
@ -295,7 +295,7 @@
|
|||||||
if (buttonIndex == [sheet cancelButtonIndex])
|
if (buttonIndex == [sheet cancelButtonIndex])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MPSiteType type = (MPSiteType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
|
MPResultType type = (MPResultType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
|
||||||
mainSite.user.defaultType?: mainSite.algorithm.defaultType;
|
mainSite.user.defaultType?: mainSite.algorithm.defaultType;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
@ -311,7 +311,7 @@
|
|||||||
self.loginNameField.enabled = YES;
|
self.loginNameField.enabled = YES;
|
||||||
self.passwordField.enabled = YES;
|
self.passwordField.enabled = YES;
|
||||||
|
|
||||||
if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPSiteTypeClassStored)
|
if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPResultTypeClassStateful)
|
||||||
[self.passwordField becomeFirstResponder];
|
[self.passwordField becomeFirstResponder];
|
||||||
else
|
else
|
||||||
[self.loginNameField becomeFirstResponder];
|
[self.loginNameField becomeFirstResponder];
|
||||||
@ -537,7 +537,7 @@
|
|||||||
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
|
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
|
||||||
self.modeButton.visible = !self.transientSite;
|
self.modeButton.visible = !self.transientSite;
|
||||||
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
|
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
|
||||||
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPSiteTypeClassGenerated;
|
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPResultTypeClassTemplate;
|
||||||
self.modeButton.selected = settingsMode;
|
self.modeButton.selected = settingsMode;
|
||||||
self.strengthLabel.gone = !settingsMode;
|
self.strengthLabel.gone = !settingsMode;
|
||||||
self.modeScrollView.scrollEnabled = !self.transientSite;
|
self.modeScrollView.scrollEnabled = !self.transientSite;
|
||||||
@ -565,8 +565,8 @@
|
|||||||
// Site Password
|
// Site Password
|
||||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||||
self.passwordField.attributedPlaceholder = stra(
|
self.passwordField.attributedPlaceholder = stra(
|
||||||
mainSite.type & MPSiteTypeClassStored? strl( @"No password" ):
|
mainSite.type & MPResultTypeClassStateful? strl( @"No password" ):
|
||||||
mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
|
mainSite.type & MPResultTypeClassTemplate? strl( @"..." ): @"", @{
|
||||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@ -585,10 +585,10 @@
|
|||||||
|
|
||||||
BOOL loginGenerated = site.loginGenerated;
|
BOOL loginGenerated = site.loginGenerated;
|
||||||
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
|
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
|
||||||
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
|
MPResultType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
|
||||||
if (self.transientSite && transientType & MPSiteTypeClassGenerated)
|
if (self.transientSite && transientType & MPResultTypeClassTemplate)
|
||||||
password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:transientType
|
password = [MPAlgorithmDefault mpwTemplateForSiteNamed:self.transientSite ofType:transientType
|
||||||
withCounter:1 usingKey:key];
|
withCounter:1 usingKey:key];
|
||||||
else if (site)
|
else if (site)
|
||||||
password = [site resolvePasswordUsingKey:key];
|
password = [site resolvePasswordUsingKey:key];
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
@protocol MPTypeDelegate<NSObject>
|
@protocol MPTypeDelegate<NSObject>
|
||||||
|
|
||||||
@required
|
@required
|
||||||
- (void)didSelectType:(MPSiteType)type;
|
- (void)didSelectType:(MPResultType)type;
|
||||||
- (MPSiteType)selectedType;
|
- (MPResultType)selectedType;
|
||||||
|
|
||||||
@optional
|
@optional
|
||||||
- (MPSiteEntity *)selectedSite;
|
- (MPSiteEntity *)selectedSite;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
@interface MPTypeViewController()
|
@interface MPTypeViewController()
|
||||||
|
|
||||||
- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath;
|
- (MPResultType)typeAtIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -75,11 +75,11 @@
|
|||||||
if ([self.delegate respondsToSelector:@selector( selectedSite )])
|
if ([self.delegate respondsToSelector:@selector( selectedSite )])
|
||||||
selectedSite = [self.delegate selectedSite];
|
selectedSite = [self.delegate selectedSite];
|
||||||
|
|
||||||
MPSiteType cellType = [self typeAtIndexPath:indexPath];
|
MPResultType cellType = [self typeAtIndexPath:indexPath];
|
||||||
MPSiteType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
|
MPResultType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
|
||||||
cell.selected = (selectedType == cellType);
|
cell.selected = (selectedType == cellType);
|
||||||
|
|
||||||
if (cellType != (MPSiteType)NSNotFound && cellType & MPSiteTypeClassGenerated) {
|
if (cellType != (MPResultType)NSNotFound && cellType & MPResultTypeClassTemplate) {
|
||||||
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
|
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
|
||||||
|
|
||||||
NSString *name = selectedSite.name;
|
NSString *name = selectedSite.name;
|
||||||
@ -88,8 +88,8 @@
|
|||||||
counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
|
counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
|
NSString *typeContent = [MPAlgorithmDefault mpwTemplateForSiteNamed:name ofType:cellType
|
||||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
||||||
@ -104,8 +104,8 @@
|
|||||||
|
|
||||||
NSAssert( self.navigationController.topViewController == self, @"Not the currently active navigation item." );
|
NSAssert( self.navigationController.topViewController == self, @"Not the currently active navigation item." );
|
||||||
|
|
||||||
MPSiteType type = [self typeAtIndexPath:indexPath];
|
MPResultType type = [self typeAtIndexPath:indexPath];
|
||||||
if (type == (MPSiteType)NSNotFound)
|
if (type == (MPResultType)NSNotFound)
|
||||||
// Selected a non-type row.
|
// Selected a non-type row.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -113,28 +113,28 @@
|
|||||||
[self.navigationController popViewControllerAnimated:YES];
|
[self.navigationController popViewControllerAnimated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath {
|
- (MPResultType)typeAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
switch (indexPath.section) {
|
switch (indexPath.section) {
|
||||||
case 0: {
|
case 0: {
|
||||||
// Generated
|
// Generated
|
||||||
switch (indexPath.row) {
|
switch (indexPath.row) {
|
||||||
case 0:
|
case 0:
|
||||||
return (MPSiteType)NSNotFound;
|
return (MPResultType)NSNotFound;
|
||||||
case 1:
|
case 1:
|
||||||
return MPSiteTypeGeneratedMaximum;
|
return MPResultTypeTemplateMaximum;
|
||||||
case 2:
|
case 2:
|
||||||
return MPSiteTypeGeneratedLong;
|
return MPResultTypeTemplateLong;
|
||||||
case 3:
|
case 3:
|
||||||
return MPSiteTypeGeneratedMedium;
|
return MPResultTypeTemplateMedium;
|
||||||
case 4:
|
case 4:
|
||||||
return MPSiteTypeGeneratedBasic;
|
return MPResultTypeTemplateBasic;
|
||||||
case 5:
|
case 5:
|
||||||
return MPSiteTypeGeneratedShort;
|
return MPResultTypeTemplateShort;
|
||||||
case 6:
|
case 6:
|
||||||
return MPSiteTypeGeneratedPIN;
|
return MPResultTypeTemplatePIN;
|
||||||
case 7:
|
case 7:
|
||||||
return (MPSiteType)NSNotFound;
|
return (MPResultType)NSNotFound;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Throw( @"Unsupported row: %ld, when selecting generated site type.", (long)indexPath.row );
|
Throw( @"Unsupported row: %ld, when selecting generated site type.", (long)indexPath.row );
|
||||||
@ -146,13 +146,13 @@
|
|||||||
// Stored
|
// Stored
|
||||||
switch (indexPath.row) {
|
switch (indexPath.row) {
|
||||||
case 0:
|
case 0:
|
||||||
return (MPSiteType)NSNotFound;
|
return (MPResultType)NSNotFound;
|
||||||
case 1:
|
case 1:
|
||||||
return MPSiteTypeStoredPersonal;
|
return MPResultTypeStatefulPersonal;
|
||||||
case 2:
|
case 2:
|
||||||
return MPSiteTypeStoredDevicePrivate;
|
return MPResultTypeStatefulDevice;
|
||||||
case 3:
|
case 3:
|
||||||
return (MPSiteType)NSNotFound;
|
return (MPResultType)NSNotFound;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Throw( @"Unsupported row: %ld, when selecting stored site type.", (long)indexPath.row );
|
Throw( @"Unsupported row: %ld, when selecting stored site type.", (long)indexPath.row );
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import "MPStoreViewController.h"
|
#import "MPStoreViewController.h"
|
||||||
|
#import "mpw-marshall.h"
|
||||||
|
|
||||||
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
|
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
|
||||||
|
|
||||||
@ -177,62 +178,46 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)importSites:(NSString *)importedSitesString {
|
- (void)importSites:(NSString *)importData {
|
||||||
|
|
||||||
if ([NSThread isMainThread]) {
|
if ([NSThread isMainThread]) {
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
[self importSites:importedSitesString];
|
[self importSites:importData];
|
||||||
} );
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
|
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
|
||||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
[self importSites:importData askImportPassword:^NSString *(NSString *userName) {
|
||||||
return PearlAwait( ^(void (^setResult)(id)) {
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
|
[PearlAlert showAlertWithTitle:strf( @"Importing Sites For\n%@", userName )
|
||||||
message:strf( @"%@'s export was done using a different master password.\n"
|
message:@"Enter the master password used to create this export file."
|
||||||
@"Enter that master password to unlock the exported data.", userName )
|
|
||||||
viewStyle:UIAlertViewStyleSecureTextInput
|
viewStyle:UIAlertViewStyleSecureTextInput
|
||||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||||
setResult( nil );
|
setResult( nil );
|
||||||
else
|
else
|
||||||
setResult( [alert_ textFieldAtIndex:0].text );
|
setResult( [alert_ textFieldAtIndex:0].text );
|
||||||
}
|
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
|
||||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
|
|
||||||
} );
|
} );
|
||||||
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
|
} askUserPassword:^NSString *(NSString *userName) {
|
||||||
return PearlAwait( (id)^(void (^setResult)(id)) {
|
return PearlAwait( (id)^(void (^setResult)(id)) {
|
||||||
[PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
|
[PearlAlert showAlertWithTitle:strf( @"Master Password For\n%@", userName )
|
||||||
message:strf( @"Imports %lu sites, overwriting %lu.",
|
message:@"Enter the current master password for this user."
|
||||||
(unsigned long)importCount, (unsigned long)deleteCount )
|
|
||||||
viewStyle:UIAlertViewStyleSecureTextInput
|
viewStyle:UIAlertViewStyleSecureTextInput
|
||||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||||
setResult( nil );
|
setResult( nil );
|
||||||
else
|
else
|
||||||
setResult( [alert_ textFieldAtIndex:0].text );
|
setResult( [alert_ textFieldAtIndex:0].text );
|
||||||
}
|
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
|
||||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
|
|
||||||
} );
|
} );
|
||||||
|
} result:^(NSError *error) {
|
||||||
|
[activityOverlay cancelOverlayAnimated:YES];
|
||||||
|
|
||||||
|
if (error && !(error.domain == NSCocoaErrorDomain && error.code == NSUserCancelledError))
|
||||||
|
[PearlAlert showError:error.localizedDescription];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
switch (result) {
|
|
||||||
case MPImportResultSuccess:
|
|
||||||
case MPImportResultCancelled:
|
|
||||||
break;
|
|
||||||
case MPImportResultInternalError:
|
|
||||||
[PearlAlert showError:@"Import failed because of an internal error."];
|
|
||||||
break;
|
|
||||||
case MPImportResultMalformedInput:
|
|
||||||
[PearlAlert showError:@"The import doesn't look like a Master Password export."];
|
|
||||||
break;
|
|
||||||
case MPImportResultInvalidPassword:
|
|
||||||
[PearlAlert showError:@"Incorrect master password for the import sites."];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[activityOverlay cancelOverlayAnimated:YES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||||
@ -250,10 +235,9 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||||
|
|
||||||
PearlNotMainQueue( ^{
|
PearlNotMainQueue( ^{
|
||||||
NSString *importHeader = @"# Master Password site export";
|
NSString *importData = [UIPasteboard generalPasteboard].string;
|
||||||
NSString *importedSitesString = [UIPasteboard generalPasteboard].string;
|
MPMarshallInfo *importInfo = mpw_marshall_read_info( importData.UTF8String );
|
||||||
if ([importedSitesString length] > [importHeader length] &&
|
if (importInfo->format != MPMarshallFormatNone)
|
||||||
[[importedSitesString substringToIndex:[importHeader length]] isEqualToString:importHeader])
|
|
||||||
[PearlAlert showAlertWithTitle:@"Import Sites?" message:
|
[PearlAlert showAlertWithTitle:@"Import Sites?" message:
|
||||||
@"We've detected Master Password import sites on your pasteboard, would you like to import them?"
|
@"We've detected Master Password import sites on your pasteboard, would you like to import them?"
|
||||||
viewStyle:UIAlertViewStyleDefault initAlert:nil
|
viewStyle:UIAlertViewStyleDefault initAlert:nil
|
||||||
@ -261,9 +245,10 @@
|
|||||||
if (buttonIndex == [alert cancelButtonIndex])
|
if (buttonIndex == [alert cancelButtonIndex])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[self importSites:importedSitesString];
|
[self importSites:importData];
|
||||||
[UIPasteboard generalPasteboard].string = @"";
|
[UIPasteboard generalPasteboard].string = @"";
|
||||||
} cancelTitle:@"No" otherTitles:@"Import Sites", nil];
|
} cancelTitle:@"No" otherTitles:@"Import Sites", nil];
|
||||||
|
mpw_marshal_info_free( importInfo );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
[super applicationDidBecomeActive:application];
|
[super applicationDidBecomeActive:application];
|
||||||
@ -449,62 +434,86 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
|
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) {
|
||||||
NSString *message;
|
return PearlAwait( ^(void (^setResult)(id)) {
|
||||||
|
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
|
||||||
|
message:strf( @"%@'s export was done using a different master password.\n"
|
||||||
|
@"Enter that master password to unlock the exported data.", userName )
|
||||||
|
viewStyle:UIAlertViewStyleSecureTextInput
|
||||||
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||||
|
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||||
|
setResult( nil );
|
||||||
|
else
|
||||||
|
setResult( [alert_ textFieldAtIndex:0].text );
|
||||||
|
}
|
||||||
|
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
|
||||||
|
} );
|
||||||
|
} result:^(NSString *mpsites, NSError *error) {
|
||||||
|
if (!mpsites || error) {
|
||||||
|
MPError( error, @"Failed to export mpsites." );
|
||||||
|
[PearlAlert showAlertWithTitle:@"Export Error"
|
||||||
|
message:error.localizedDescription
|
||||||
|
viewStyle:UIAlertViewStyleDefault
|
||||||
|
initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay
|
||||||
|
otherTitles:nil];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (revealPasswords)
|
[PearlSheet showSheetWithTitle:@"Export Destination" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil
|
||||||
message = strf( @"Export of Master Password sites with passwords included.\n\n"
|
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||||
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n"
|
if (buttonIndex == [sheet cancelButtonIndex])
|
||||||
@"--\n"
|
return;
|
||||||
@"%@\n"
|
|
||||||
@"Master Password %@, build %@",
|
|
||||||
[self activeUserForMainThread].name,
|
|
||||||
[PearlInfoPlist get].CFBundleShortVersionString,
|
|
||||||
[PearlInfoPlist get].CFBundleVersion );
|
|
||||||
else
|
|
||||||
message = strf( @"Backup of Master Password sites.\n\n\n"
|
|
||||||
@"--\n"
|
|
||||||
@"%@\n"
|
|
||||||
@"Master Password %@, build %@",
|
|
||||||
[self activeUserForMainThread].name,
|
|
||||||
[PearlInfoPlist get].CFBundleShortVersionString,
|
|
||||||
[PearlInfoPlist get].CFBundleVersion );
|
|
||||||
|
|
||||||
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
|
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
|
||||||
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
||||||
|
NSString *exportFileName = strf( @"%@ (%@).mpsites",
|
||||||
|
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
|
||||||
|
|
||||||
NSString *exportFileName = strf( @"%@ (%@).mpsites",
|
if (buttonIndex == [sheet firstOtherButtonIndex]) {
|
||||||
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
|
NSString *message;
|
||||||
[PearlSheet showSheetWithTitle:@"Export Destination" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil
|
if (revealPasswords)
|
||||||
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
message = strf( @"Export of Master Password sites with passwords included.\n\n"
|
||||||
if (buttonIndex == [sheet cancelButtonIndex])
|
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n"
|
||||||
return;
|
@"--\n"
|
||||||
|
@"%@\n"
|
||||||
|
@"Master Password %@, build %@",
|
||||||
|
[self activeUserForMainThread].name,
|
||||||
|
[PearlInfoPlist get].CFBundleShortVersionString,
|
||||||
|
[PearlInfoPlist get].CFBundleVersion );
|
||||||
|
else
|
||||||
|
message = strf( @"Backup of Master Password sites.\n\n\n"
|
||||||
|
@"--\n"
|
||||||
|
@"%@\n"
|
||||||
|
@"Master Password %@, build %@",
|
||||||
|
[self activeUserForMainThread].name,
|
||||||
|
[PearlInfoPlist get].CFBundleShortVersionString,
|
||||||
|
[PearlInfoPlist get].CFBundleVersion );
|
||||||
|
|
||||||
if (buttonIndex == [sheet firstOtherButtonIndex]) {
|
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
|
||||||
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
|
attachments:[[PearlEMailAttachment alloc]
|
||||||
attachments:[[PearlEMailAttachment alloc]
|
initWithContent:[mpsites dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
mimeType:@"text/plain" fileName:exportFileName],
|
||||||
mimeType:@"text/plain" fileName:exportFileName],
|
nil];
|
||||||
nil];
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
||||||
inDomains:NSUserDomainMask] lastObject];
|
inDomains:NSUserDomainMask] lastObject];
|
||||||
NSURL *exportURL = [[applicationSupportURL
|
NSURL *exportURL = [[applicationSupportURL
|
||||||
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
||||||
URLByAppendingPathComponent:exportFileName isDirectory:NO];
|
URLByAppendingPathComponent:exportFileName isDirectory:NO];
|
||||||
NSError *error = nil;
|
NSError *writeError = nil;
|
||||||
if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
if (![[mpsites dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error])
|
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
|
||||||
MPError( error, @"Failed to write export data to URL %@.", exportURL );
|
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
|
||||||
else {
|
else {
|
||||||
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
|
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
|
||||||
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
|
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
|
||||||
self.interactionController.delegate = self;
|
self.interactionController.delegate = self;
|
||||||
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
|
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
|
||||||
}
|
}
|
||||||
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:@"Send As E-Mail", @"Share / Airdrop", nil];
|
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:@"Send As E-Mail", @"Share / Airdrop", nil];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
|
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#define MP_ENV_fullName "MP_FULLNAME"
|
#define MP_ENV_fullName "MP_FULLNAME"
|
||||||
#define MP_ENV_algorithm "MP_ALGORITHM"
|
#define MP_ENV_algorithm "MP_ALGORITHM"
|
||||||
|
#define MP_ENV_format "MP_FORMAT"
|
||||||
|
|
||||||
static void usage() {
|
static void usage() {
|
||||||
|
|
||||||
@ -75,10 +76,11 @@ static void usage() {
|
|||||||
" -f|F format The mpsites format to use for reading/writing site parameters.\n"
|
" -f|F format The mpsites format to use for reading/writing site parameters.\n"
|
||||||
" -F forces the use of the given format,\n"
|
" -F forces the use of the given format,\n"
|
||||||
" -f allows fallback/migration.\n"
|
" -f allows fallback/migration.\n"
|
||||||
" Defaults to json, falls back to plain.\n"
|
" Defaults to %s in env or json, falls back to plain.\n"
|
||||||
|
" n, none | No file\n"
|
||||||
" f, flat | ~/.mpw.d/Full Name.%s\n"
|
" f, flat | ~/.mpw.d/Full Name.%s\n"
|
||||||
" j, json | ~/.mpw.d/Full Name.%s\n\n",
|
" j, json | ~/.mpw.d/Full Name.%s\n\n",
|
||||||
mpw_marshall_format_extension( MPMarshallFormatFlat ), mpw_marshall_format_extension( MPMarshallFormatJSON ) );
|
MP_ENV_format, mpw_marshall_format_extension( MPMarshallFormatFlat ), mpw_marshall_format_extension( MPMarshallFormatJSON ) );
|
||||||
inf( ""
|
inf( ""
|
||||||
" -R redacted Whether to save the mpsites in redacted format or not.\n"
|
" -R redacted Whether to save the mpsites in redacted format or not.\n"
|
||||||
" Defaults to 1, redacted.\n\n" );
|
" Defaults to 1, redacted.\n\n" );
|
||||||
@ -166,6 +168,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
const char *keyPurposeArg = NULL, *keyContextArg = NULL, *sitesFormatArg = NULL, *sitesRedactedArg = NULL;
|
const char *keyPurposeArg = NULL, *keyContextArg = NULL, *sitesFormatArg = NULL, *sitesRedactedArg = NULL;
|
||||||
fullNameArg = mpw_getenv( MP_ENV_fullName );
|
fullNameArg = mpw_getenv( MP_ENV_fullName );
|
||||||
algorithmVersionArg = mpw_getenv( MP_ENV_algorithm );
|
algorithmVersionArg = mpw_getenv( MP_ENV_algorithm );
|
||||||
|
sitesFormatArg = mpw_getenv( MP_ENV_format );
|
||||||
|
|
||||||
// Read the command-line options.
|
// Read the command-line options.
|
||||||
for (int opt; (opt = getopt( argc, argv, "u:U:M:t:P:c:a:s:p:C:f:F:R:vqh" )) != EOF;)
|
for (int opt; (opt = getopt( argc, argv, "u:U:M:t:P:c:a:s:p:C:f:F:R:vqh" )) != EOF;)
|
||||||
@ -269,13 +272,14 @@ int main(int argc, char *const argv[]) {
|
|||||||
char *sitesPath = mpw_path( fullName, mpw_marshall_format_extension( sitesFormat ) );
|
char *sitesPath = mpw_path( fullName, mpw_marshall_format_extension( sitesFormat ) );
|
||||||
if (!sitesPath || !(sitesFile = fopen( sitesPath, "r" ))) {
|
if (!sitesPath || !(sitesFile = fopen( sitesPath, "r" ))) {
|
||||||
dbg( "Couldn't open configuration file:\n %s: %s\n", sitesPath, strerror( errno ) );
|
dbg( "Couldn't open configuration file:\n %s: %s\n", sitesPath, strerror( errno ) );
|
||||||
free( sitesPath );
|
|
||||||
|
|
||||||
// Try to fall back to the flat format.
|
// Try to fall back to the flat format.
|
||||||
if (!sitesFormatFixed) {
|
if (!sitesFormatFixed) {
|
||||||
sitesFormat = MPMarshallFormatFlat;
|
free( sitesPath );
|
||||||
sitesPath = mpw_path( fullName, mpw_marshall_format_extension( sitesFormat ) );
|
sitesPath = mpw_path( fullName, mpw_marshall_format_extension( MPMarshallFormatFlat ) );
|
||||||
if (!sitesPath || !(sitesFile = fopen( sitesPath, "r" )))
|
if (sitesPath && (sitesFile = fopen( sitesPath, "r" )))
|
||||||
|
sitesFormat = MPMarshallFormatFlat;
|
||||||
|
else
|
||||||
dbg( "Couldn't open configuration file:\n %s: %s\n", sitesPath, strerror( errno ) );
|
dbg( "Couldn't open configuration file:\n %s: %s\n", sitesPath, strerror( errno ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,9 +294,9 @@ int main(int argc, char *const argv[]) {
|
|||||||
else {
|
else {
|
||||||
// Read file.
|
// Read file.
|
||||||
size_t readAmount = 4096, bufSize = 0, bufOffset = 0, readSize = 0;
|
size_t readAmount = 4096, bufSize = 0, bufOffset = 0, readSize = 0;
|
||||||
char *buf = NULL;
|
char *sitesInputData = NULL;
|
||||||
while ((mpw_realloc( &buf, &bufSize, readAmount )) &&
|
while ((mpw_realloc( &sitesInputData, &bufSize, readAmount )) &&
|
||||||
(bufOffset += (readSize = fread( buf + bufOffset, 1, readAmount, sitesFile ))) &&
|
(bufOffset += (readSize = fread( sitesInputData + bufOffset, 1, readAmount, sitesFile ))) &&
|
||||||
(readSize == readAmount));
|
(readSize == readAmount));
|
||||||
if (ferror( sitesFile ))
|
if (ferror( sitesFile ))
|
||||||
wrn( "Error while reading configuration file:\n %s: %d\n", sitesPath, ferror( sitesFile ) );
|
wrn( "Error while reading configuration file:\n %s: %d\n", sitesPath, ferror( sitesFile ) );
|
||||||
@ -300,13 +304,14 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Parse file.
|
// Parse file.
|
||||||
MPMarshallError marshallError = { .type = MPMarshallSuccess };
|
MPMarshallError marshallError = { .type = MPMarshallSuccess };
|
||||||
user = mpw_marshall_read( buf, sitesFormat, masterPassword, &marshallError );
|
MPMarshallFormat sitesInputFormat = sitesFormatArg? sitesFormat: mpw_marshall_format_guess( sitesInputData );
|
||||||
|
user = mpw_marshall_read( sitesInputData, sitesInputFormat, masterPassword, &marshallError );
|
||||||
if (marshallError.type == MPMarshallErrorMasterPassword) {
|
if (marshallError.type == MPMarshallErrorMasterPassword) {
|
||||||
// Incorrect master password.
|
// Incorrect master password.
|
||||||
if (!allowPasswordUpdate) {
|
if (!allowPasswordUpdate) {
|
||||||
ftl( "Incorrect master password according to configuration:\n %s: %s\n", sitesPath, marshallError.description );
|
ftl( "Incorrect master password according to configuration:\n %s: %s\n", sitesPath, marshallError.description );
|
||||||
mpw_marshal_free( user );
|
mpw_marshal_free( user );
|
||||||
mpw_free( buf, bufSize );
|
mpw_free( sitesInputData, bufSize );
|
||||||
free( sitesPath );
|
free( sitesPath );
|
||||||
return EX_DATAERR;
|
return EX_DATAERR;
|
||||||
}
|
}
|
||||||
@ -321,14 +326,14 @@ int main(int argc, char *const argv[]) {
|
|||||||
importMasterPassword = mpw_getpass( "Old master password: " );
|
importMasterPassword = mpw_getpass( "Old master password: " );
|
||||||
|
|
||||||
mpw_marshal_free( user );
|
mpw_marshal_free( user );
|
||||||
user = mpw_marshall_read( buf, sitesFormat, importMasterPassword, &marshallError );
|
user = mpw_marshall_read( sitesInputData, sitesInputFormat, importMasterPassword, &marshallError );
|
||||||
}
|
}
|
||||||
if (user) {
|
if (user) {
|
||||||
mpw_free_string( user->masterPassword );
|
mpw_free_string( user->masterPassword );
|
||||||
user->masterPassword = strdup( masterPassword );
|
user->masterPassword = strdup( masterPassword );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mpw_free( buf, bufSize );
|
mpw_free( sitesInputData, bufSize );
|
||||||
if (!user || marshallError.type != MPMarshallSuccess) {
|
if (!user || marshallError.type != MPMarshallSuccess) {
|
||||||
err( "Couldn't parse configuration file:\n %s: %s\n", sitesPath, marshallError.description );
|
err( "Couldn't parse configuration file:\n %s: %s\n", sitesPath, marshallError.description );
|
||||||
mpw_marshal_free( user );
|
mpw_marshal_free( user );
|
||||||
@ -471,7 +476,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
if (keyPurpose == MPKeyPurposeIdentification && site && !site->loginGenerated && site->loginName)
|
if (keyPurpose == MPKeyPurposeIdentification && site && !site->loginGenerated && site->loginName)
|
||||||
fprintf( stdout, "%s\n", site->loginName );
|
fprintf( stdout, "%s\n", site->loginName );
|
||||||
|
|
||||||
else if (resultParam && site && resultType & MPResultTypeClassState) {
|
else if (resultParam && site && resultType & MPResultTypeClassStateful) {
|
||||||
mpw_free_string( site->content );
|
mpw_free_string( site->content );
|
||||||
if (!(site->content = mpw_siteState( masterKey, siteName, siteCounter,
|
if (!(site->content = mpw_siteState( masterKey, siteName, siteCounter,
|
||||||
keyPurpose, keyContext, resultType, resultParam, algorithmVersion ))) {
|
keyPurpose, keyContext, resultType, resultParam, algorithmVersion ))) {
|
||||||
@ -483,7 +488,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
inf( "saved.\n" );
|
inf( "saved.\n" );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!resultParam && site && site->content && resultType & MPResultTypeClassState)
|
if (!resultParam && site && site->content && resultType & MPResultTypeClassStateful)
|
||||||
resultParam = strdup( site->content );
|
resultParam = strdup( site->content );
|
||||||
const char *siteResult = mpw_siteResult( masterKey, siteName, siteCounter,
|
const char *siteResult = mpw_siteResult( masterKey, siteName, siteCounter,
|
||||||
keyPurpose, keyContext, resultType, resultParam, algorithmVersion );
|
keyPurpose, keyContext, resultType, resultParam, algorithmVersion );
|
||||||
|
Loading…
Reference in New Issue
Block a user