2
0

Change marshal API to return output directly.

Avoids an ambiguity between return type and out value (eg. true but
NULL), and improves Swift API access.
This commit is contained in:
Maarten Billemont 2020-01-23 15:59:21 -05:00
parent f0acd1fed1
commit 6d36f17e57
4 changed files with 64 additions and 56 deletions

View File

@ -806,9 +806,9 @@ void cli_save(Arguments *args, Operation *operation) {
return; return;
} }
char *buf = NULL;
MPMarshalError marshalError = { .type = MPMarshalSuccess }; MPMarshalError marshalError = { .type = MPMarshalSuccess };
if (!mpw_marshal_write( &buf, operation->sitesFormat, operation->user, &marshalError ) || marshalError.type != MPMarshalSuccess || !buf) const char *buf = mpw_marshal_write( operation->sitesFormat, operation->user, &marshalError );
if (!buf || marshalError.type != MPMarshalSuccess)
wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.description ); wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.description );
else if (fwrite( buf, sizeof( char ), strlen( buf ), sitesFile ) != strlen( buf )) else if (fwrite( buf, sizeof( char ), strlen( buf ), sitesFile ) != strlen( buf ))

View File

@ -134,45 +134,47 @@ bool mpw_marshal_free(
return success; return success;
} }
static bool mpw_marshal_write_flat( static const char *mpw_marshal_write_flat(
char **out, const MPMarshalledUser *user, MPMarshalError *error) { const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false; return NULL;
} }
MPMasterKey masterKey = user->masterKeyProvider( user->algorithm, user->fullName ); MPMasterKey masterKey = user->masterKeyProvider( user->algorithm, user->fullName );
if (!masterKey) { if (!masterKey) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; return NULL;
} }
mpw_string_pushf( out, "# Master Password site export\n" ); char *out = NULL;
mpw_string_pushf( &out, "# Master Password site export\n" );
if (user->redacted) if (user->redacted)
mpw_string_pushf( out, "# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" ); mpw_string_pushf( &out,
"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" );
else else
mpw_string_pushf( out, "# Export of site names and passwords in clear-text.\n" ); mpw_string_pushf( &out, "# Export of site names and passwords in clear-text.\n" );
mpw_string_pushf( out, "# \n" ); mpw_string_pushf( &out, "# \n" );
mpw_string_pushf( out, "##\n" ); mpw_string_pushf( &out, "##\n" );
mpw_string_pushf( out, "# Format: %d\n", 1 ); mpw_string_pushf( &out, "# Format: %d\n", 1 );
char dateString[21]; char dateString[21];
time_t now = time( NULL ); time_t now = time( NULL );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) )) if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
mpw_string_pushf( out, "# Date: %s\n", dateString ); mpw_string_pushf( &out, "# Date: %s\n", dateString );
mpw_string_pushf( out, "# User Name: %s\n", user->fullName ); mpw_string_pushf( &out, "# User Name: %s\n", user->fullName );
mpw_string_pushf( out, "# Full Name: %s\n", user->fullName ); mpw_string_pushf( &out, "# Full Name: %s\n", user->fullName );
mpw_string_pushf( out, "# Avatar: %u\n", user->avatar ); mpw_string_pushf( &out, "# Avatar: %u\n", user->avatar );
mpw_string_pushf( out, "# Identicon: %s\n", mpw_identicon_encode( user->identicon ) ); mpw_string_pushf( &out, "# Identicon: %s\n", mpw_identicon_encode( user->identicon ) );
mpw_string_pushf( out, "# Key ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); mpw_string_pushf( &out, "# Key ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
mpw_string_pushf( out, "# Algorithm: %d\n", user->algorithm ); mpw_string_pushf( &out, "# Algorithm: %d\n", user->algorithm );
mpw_string_pushf( out, "# Default Type: %d\n", user->defaultType ); mpw_string_pushf( &out, "# Default Type: %d\n", user->defaultType );
mpw_string_pushf( out, "# Passwords: %s\n", user->redacted? "PROTECTED": "VISIBLE" ); mpw_string_pushf( &out, "# Passwords: %s\n", user->redacted? "PROTECTED": "VISIBLE" );
mpw_string_pushf( out, "##\n" ); mpw_string_pushf( &out, "##\n" );
mpw_string_pushf( out, "#\n" ); mpw_string_pushf( &out, "#\n" );
mpw_string_pushf( out, "# Last Times Password Login\t Site\tSite\n" ); mpw_string_pushf( &out, "# Last Times Password Login\t Site\tSite\n" );
mpw_string_pushf( out, "# used used type name\t name\tpassword\n" ); mpw_string_pushf( &out, "# used used type name\t name\tpassword\n" );
// Sites. // Sites.
for (size_t s = 0; s < user->sites_count; ++s) { for (size_t s = 0; s < user->sites_count; ++s) {
@ -186,7 +188,8 @@ static bool mpw_marshal_write_flat(
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) { if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; mpw_free_string( &out );
return NULL;
} }
resultState = mpw_site_result( masterKey, site->siteName, site->counter, resultState = mpw_site_result( masterKey, site->siteName, site->counter,
@ -203,7 +206,7 @@ static bool mpw_marshal_write_flat(
} }
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) )) if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n", mpw_string_pushf( &out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
dateString, (long)site->uses, (long)site->resultType, (long)site->algorithm, (long)site->counter, dateString, (long)site->uses, (long)site->resultType, (long)site->algorithm, (long)site->counter,
loginState? loginState: "", site->siteName, resultState? resultState: "" ); loginState? loginState: "", site->siteName, resultState? resultState: "" );
mpw_free_strings( &resultState, &loginState, NULL ); mpw_free_strings( &resultState, &loginState, NULL );
@ -211,23 +214,23 @@ static bool mpw_marshal_write_flat(
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return true; return out;
} }
#if MPW_JSON #if MPW_JSON
static bool mpw_marshal_write_json( static const char *mpw_marshal_write_json(
char **out, const MPMarshalledUser *user, MPMarshalError *error) { const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false; return NULL;
} }
MPMasterKey masterKey = user->masterKeyProvider( user->algorithm, user->fullName ); MPMasterKey masterKey = user->masterKeyProvider( user->algorithm, user->fullName );
if (!masterKey) { if (!masterKey) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; return NULL;
} }
// Section: "export" // Section: "export"
@ -261,7 +264,7 @@ static bool mpw_marshal_write_json(
json_object_object_add( json_file, "sites", json_sites ); json_object_object_add( json_file, "sites", json_sites );
for (size_t s = 0; s < user->sites_count; ++s) { for (size_t s = 0; s < user->sites_count; ++s) {
MPMarshalledSite *site = &user->sites[s]; MPMarshalledSite *site = &user->sites[s];
if (!site->name || !strlen( site->name )) if (!site->siteName || !strlen( site->siteName ))
continue; continue;
const char *resultState = NULL, *loginState = NULL; const char *resultState = NULL, *loginState = NULL;
@ -271,12 +274,12 @@ static bool mpw_marshal_write_json(
if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) { if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
json_object_put( json_file ); json_object_put( json_file );
return false; return NULL;
} }
resultState = mpw_site_result( masterKey, site->name, site->counter, resultState = mpw_site_result( masterKey, site->siteName, site->counter,
MPKeyPurposeAuthentication, NULL, site->resultType, site->resultState, site->algorithm ); MPKeyPurposeAuthentication, NULL, site->resultType, site->resultState, site->algorithm );
loginState = mpw_site_result( masterKey, site->name, MPCounterValueInitial, loginState = mpw_site_result( masterKey, site->siteName, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, site->loginState, site->algorithm ); MPKeyPurposeIdentification, NULL, site->loginType, site->loginState, site->algorithm );
} }
else { else {
@ -288,7 +291,7 @@ static bool mpw_marshal_write_json(
} }
json_object *json_site = json_object_new_object(); json_object *json_site = json_object_new_object();
json_object_object_add( json_sites, site->name, json_site ); json_object_object_add( json_sites, site->siteName, json_site );
json_object_object_add( json_site, "type", json_object_new_int( (int32_t)site->resultType ) ); json_object_object_add( json_site, "type", json_object_new_int( (int32_t)site->resultType ) );
json_object_object_add( json_site, "counter", json_object_new_int( (int32_t)site->counter ) ); json_object_object_add( json_site, "counter", json_object_new_int( (int32_t)site->counter ) );
json_object_object_add( json_site, "algorithm", json_object_new_int( (int32_t)site->algorithm ) ); json_object_object_add( json_site, "algorithm", json_object_new_int( (int32_t)site->algorithm ) );
@ -316,7 +319,7 @@ static bool mpw_marshal_write_json(
if (!user->redacted) { if (!user->redacted) {
// Clear Text // Clear Text
const char *answerState = mpw_site_result( masterKey, site->name, MPCounterValueInitial, const char *answerState = mpw_site_result( masterKey, site->siteName, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, question->state, site->algorithm ); MPKeyPurposeRecovery, question->keyword, question->type, question->state, site->algorithm );
json_object_object_add( json_site_question, "answer", json_object_new_string( answerState ) ); json_object_object_add( json_site_question, "answer", json_object_new_string( answerState ) );
} }
@ -337,33 +340,37 @@ static bool mpw_marshal_write_json(
mpw_free_strings( &resultState, &loginState, NULL ); mpw_free_strings( &resultState, &loginState, NULL );
} }
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file, const char *out = mpw_strdup( json_object_to_json_string_ext( json_file,
JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) ); JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) );
json_object_put( json_file ); json_object_put( json_file );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
if (out)
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return true; else
*error = (MPMarshalError){ .type = MPMarshalErrorFormat, .description = "Couldn't encode JSON." };
return out;
} }
#endif #endif
bool mpw_marshal_write( const char *mpw_marshal_write(
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) { const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) {
switch (outFormat) { switch (outFormat) {
case MPMarshalFormatNone: case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return false; return NULL;
case MPMarshalFormatFlat: case MPMarshalFormatFlat:
return mpw_marshal_write_flat( out, user, error ); return mpw_marshal_write_flat( user, error );
#if MPW_JSON #if MPW_JSON
case MPMarshalFormatJSON: case MPMarshalFormatJSON:
return mpw_marshal_write_json( out, user, error ); return mpw_marshal_write_json( user, error );
#endif #endif
default: default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
return false; return NULL;
} }
} }
@ -913,10 +920,10 @@ static MPMarshalledUser *mpw_marshal_read_json(
} }
if (siteResultState && strlen( siteResultState )) if (siteResultState && strlen( siteResultState ))
site->resultState = mpw_site_state( masterKey, site->name, site->counter, site->resultState = mpw_site_state( masterKey, site->siteName, site->counter,
MPKeyPurposeAuthentication, NULL, site->resultType, siteResultState, site->algorithm ); MPKeyPurposeAuthentication, NULL, site->resultType, siteResultState, site->algorithm );
if (siteLoginState && strlen( siteLoginState )) if (siteLoginState && strlen( siteLoginState ))
site->loginState = mpw_site_state( masterKey, site->name, MPCounterValueInitial, site->loginState = mpw_site_state( masterKey, site->siteName, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, siteLoginState, site->algorithm ); MPKeyPurposeIdentification, NULL, site->loginType, siteLoginState, site->algorithm );
} }
else { else {
@ -938,7 +945,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!user->redacted) { if (!user->redacted) {
// Clear Text // Clear Text
if (answerState && strlen( answerState )) if (answerState && strlen( answerState ))
question->state = mpw_site_state( masterKey, site->name, MPCounterValueInitial, question->state = mpw_site_state( masterKey, site->siteName, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, answerState, site->algorithm ); MPKeyPurposeRecovery, question->keyword, question->type, answerState, site->algorithm );
} }
else { else {

View File

@ -121,15 +121,16 @@ typedef struct MPMarshalInfo {
//// Marshalling. //// Marshalling.
/** Write the user and all associated data out to the given output buffer using the given marshalling format. */ /** Write the user and all associated data out using the given marshalling format.
bool mpw_marshal_write( * @return A string (allocated), or NULL if the format is unrecognized, does not support marshalling or a format error occurred. */
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error); const char *mpw_marshal_write(
const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error);
/** Try to read metadata on the sites in the input buffer. /** Try to read metadata on the sites in the input buffer.
* @return A metadata object (allocated), or NULL if the object could not be allocated. */ * @return A metadata object (allocated), or NULL if the object could not be allocated or a format error occurred. */
MPMarshalInfo *mpw_marshal_read_info( MPMarshalInfo *mpw_marshal_read_info(
const char *in); const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. /** Unmarshall sites in the given input buffer by parsing it using the given marshalling format.
* @return A user object (allocated), or NULL if the format provides no marshalling or an error occurred. */ * @return A user object (allocated), or NULL if the format provides no marshalling or a format error occurred. */
MPMarshalledUser *mpw_marshal_read( MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error); const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error);

View File

@ -24,7 +24,7 @@
const size_t MPMasterKeySize = 64; const size_t MPMasterKeySize = 64;
const size_t MPSiteKeySize = 256 / 8; // Size of HMAC-SHA-256 const size_t MPSiteKeySize = 256 / 8; // Size of HMAC-SHA-256
const MPIdenticon MPIdenticonUnset = (MPIdenticon){ const MPIdenticon MPIdenticonUnset = {
.leftArm = "", .leftArm = "",
.body = "", .body = "",
.rightArm = "", .rightArm = "",