2
0

Standardize UTF8 mbyte length, identicon encoding, be explicit about storage duration in C API.

This commit is contained in:
Maarten Billemont 2020-01-23 15:57:07 -05:00
parent ad4081be61
commit ccd9763649
13 changed files with 235 additions and 170 deletions

View File

@ -336,7 +336,7 @@ static char *mpw_tputs(const char *str, int affcnt) {
#endif
const char *mpw_identicon_str(MPIdenticon identicon) {
const char *mpw_identicon_render(MPIdenticon identicon) {
char *colorString, *resetString;
#ifdef MPW_COLOR

View File

@ -66,4 +66,4 @@ const char *mpw_read_file(FILE *file);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string or NULL if the identicon couldn't be allocated. */
const char *mpw_identicon_str(MPIdenticon identicon);
const char *mpw_identicon_render(MPIdenticon identicon);

View File

@ -459,8 +459,8 @@ void cli_user(Arguments *args, Operation *operation) {
// Find mpsites file from parameters.
FILE *sitesFile = NULL;
const char **extensions = NULL;
int count = mpw_marshal_format_extensions( operation->sitesFormat, &extensions );
size_t count = 0;
const char **extensions = mpw_marshal_format_extensions( operation->sitesFormat, &count );
for (int e = 0; !sitesFile && e < count; ++e) {
mpw_free_string( &operation->sitesPath );
operation->sitesPath = mpw_path( operation->fullName, extensions[e] );
@ -471,7 +471,8 @@ void cli_user(Arguments *args, Operation *operation) {
if (!sitesFile && !operation->sitesFormatFixed)
for (MPMarshalFormat format = MPMarshalFormatFirst; !sitesFile && format <= MPMarshalFormatLast; ++format) {
count = mpw_marshal_format_extensions( operation->sitesFormat, &extensions );
mpw_free( &extensions, count * sizeof( *extensions ) );
extensions = mpw_marshal_format_extensions( operation->sitesFormat, &count );
for (int e = 0; !sitesFile && e < count; ++e) {
mpw_free_string( &operation->sitesPath );
@ -481,7 +482,7 @@ void cli_user(Arguments *args, Operation *operation) {
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
}
}
free( extensions );
mpw_free( &extensions, count * sizeof( *extensions ) );
// Load the user object from mpsites.
if (!sitesFile)
@ -586,7 +587,7 @@ void cli_question(Arguments *args, Operation *operation) {
void cli_operation(Arguments *args, Operation *operation) {
mpw_free_string( &operation->identicon );
operation->identicon = mpw_identicon_str( mpw_identicon( operation->user->fullName, operation->masterPassword ) );
operation->identicon = mpw_identicon_render( mpw_identicon( operation->user->fullName, operation->masterPassword ) );
if (!operation->site)
abort();
@ -788,14 +789,15 @@ void cli_save(Arguments *args, Operation *operation) {
if (!operation->sitesFormatFixed)
operation->sitesFormat = MPMarshalFormatDefault;
const char **extensions = NULL;
if (!mpw_marshal_format_extensions( operation->sitesFormat, &extensions ))
size_t count = 0;
const char **extensions = mpw_marshal_format_extensions( operation->sitesFormat, &count );
if (!extensions || !count)
return;
mpw_free_string( &operation->sitesPath );
operation->sitesPath = mpw_path( operation->user->fullName, extensions[0] );
dbg( "Updating: %s (%s)", operation->sitesPath, mpw_format_name( operation->sitesFormat ) );
free( extensions );
mpw_free( &extensions, count * sizeof( *extensions ) );
FILE *sitesFile = NULL;
if (!operation->sitesPath || !mpw_mkdirs( operation->sitesPath ) || !(sitesFile = fopen( operation->sitesPath, "w" ))) {

View File

@ -214,39 +214,98 @@ const char *mpw_site_state(
}
}
static const char *mpw_identicon_leftArms[] = { "", "", "", "" };
static const char *mpw_identicon_bodies[] = { "", "", "", "", "", "" };
static const char *mpw_identicon_rightArms[] = { "", "", "", "" };
static const char *mpw_identicon_accessories[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
const uint8_t *identiconSeed = NULL;
const uint8_t *seed = NULL;
if (fullName && strlen( fullName ) && masterPassword && strlen( masterPassword ))
identiconSeed = mpw_hash_hmac_sha256(
seed = mpw_hash_hmac_sha256(
(const uint8_t *)masterPassword, strlen( masterPassword ),
(const uint8_t *)fullName, strlen( fullName ) );
if (!identiconSeed)
return (MPIdenticon){
.leftArm = "",
.body = "",
.rightArm = "",
.accessory = "",
.color = MPIdenticonColorBlack,
};
if (!seed)
return MPIdenticonUnset;
MPIdenticon identicon = {
.leftArm = leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( *leftArm ))],
.body = body[identiconSeed[1] % (sizeof( body ) / sizeof( *body ))],
.rightArm = rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( *rightArm ))],
.accessory = accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( *accessory ))],
.color = (uint8_t)(identiconSeed[4] % (MPIdenticonColorLast - MPIdenticonColorFirst + 1) + MPIdenticonColorFirst),
.leftArm = mpw_identicon_leftArms[seed[0] % (sizeof( mpw_identicon_leftArms ) / sizeof( *mpw_identicon_leftArms ))],
.body = mpw_identicon_bodies[seed[1] % (sizeof( mpw_identicon_bodies ) / sizeof( *mpw_identicon_bodies ))],
.rightArm = mpw_identicon_rightArms[seed[2] % (sizeof( mpw_identicon_rightArms ) / sizeof( *mpw_identicon_rightArms ))],
.accessory = mpw_identicon_accessories[seed[3] % (sizeof( mpw_identicon_accessories ) / sizeof( *mpw_identicon_accessories ))],
.color = (uint8_t)(seed[4] % (MPIdenticonColorLast - MPIdenticonColorFirst + 1) + MPIdenticonColorFirst),
};
mpw_free( &identiconSeed, 32 );
mpw_free( &seed, 32 );
return identicon;
}
const char *mpw_identicon_encode(
MPIdenticon identicon) {
return mpw_str( "%hhu:%s%s%s%s",
identicon.color, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory );
}
MPIdenticon mpw_identicon_encoded(
const char *encoding) {
MPIdenticon identicon = MPIdenticonUnset;
char *string = calloc( strlen( encoding ), sizeof( *string ) ), *parser = string;
const char *leftArm = NULL, *body = NULL, *rightArm = NULL, *accessory = NULL;
unsigned int color;
if (encoding && string && sscanf( encoding, "%u:%s", &color, string ) == 2) {
if (*parser && color)
for (int s = 0; s < sizeof( mpw_identicon_leftArms ) / sizeof( *mpw_identicon_leftArms ); ++s) {
const char *limb = mpw_identicon_leftArms[s];
if (strncmp( parser, limb, strlen( limb ) ) == 0) {
leftArm = limb;
parser += strlen( limb );
break;
}
}
if (*parser && leftArm)
for (int s = 0; s < sizeof( mpw_identicon_bodies ) / sizeof( *mpw_identicon_bodies ); ++s) {
const char *limb = mpw_identicon_bodies[s];
if (strncmp( parser, limb, strlen( limb ) ) == 0) {
body = limb;
parser += strlen( limb );
break;
}
}
if (*parser && body)
for (int s = 0; s < sizeof( mpw_identicon_rightArms ) / sizeof( *mpw_identicon_rightArms ); ++s) {
const char *limb = mpw_identicon_rightArms[s];
if (strncmp( parser, limb, strlen( limb ) ) == 0) {
rightArm = limb;
parser += strlen( limb );
break;
}
}
if (*parser && rightArm)
for (int s = 0; s < sizeof( mpw_identicon_accessories ) / sizeof( *mpw_identicon_accessories ); ++s) {
const char *limb = mpw_identicon_accessories[s];
if (strncmp( parser, limb, strlen( limb ) ) == 0) {
accessory = limb;
break;
}
}
if (leftArm && body && rightArm && color >= MPIdenticonColorFirst && color <= MPIdenticonColorLast)
identicon = (MPIdenticon){
.leftArm = leftArm,
.body = body,
.rightArm = rightArm,
.accessory = accessory,
.color = (MPIdenticonColor)color,
};
}
mpw_free_string( &string );
return identicon;
}

View File

@ -38,19 +38,19 @@ typedef mpw_enum( unsigned int, MPAlgorithmVersion ) {
};
/** Derive the master key for a user based on their name and master password.
* @return A new MPMasterKeySize-byte allocated buffer or NULL if the fullName or masterPassword is missing, the algorithm is unknown, or an algorithm error occurred. */
* @return A buffer (allocated, MPMasterKeySize) or NULL if the fullName or masterPassword is missing, the algorithm is unknown, or an algorithm error occurred. */
MPMasterKey mpw_master_key(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
/** Derive the site key for a user's site from the given master key and site parameters.
* @return A new MPSiteKeySize-byte allocated buffer or NULL if the masterKey or siteName is missing, the algorithm is unknown, or an algorithm error occurred. */
* @return A buffer (allocated, MPSiteKeySize) or NULL if the masterKey or siteName is missing, the algorithm is unknown, or an algorithm error occurred. */
MPSiteKey mpw_site_key(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion);
/** Generate a site result token from the given parameters.
* @param resultParam A parameter for the resultType. For stateful result types, the output of mpw_site_state.
* @return A newly allocated string or NULL if the masterKey or siteName is missing, the algorithm is unknown, or an algorithm error occurred. */
* @return A string (allocated) or NULL if the masterKey or siteName is missing, the algorithm is unknown, or an algorithm error occurred. */
const char *mpw_site_result(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext,
@ -59,14 +59,21 @@ const char *mpw_site_result(
/** Encrypt a stateful site token for persistence.
* @param resultParam A parameter for the resultType. For stateful result types, the desired mpw_site_result.
* @return A newly allocated string or NULL if the masterKey, siteName or resultParam is missing, the algorithm is unknown, or an algorithm error occurred. */
* @return A string (allocated) or NULL if the masterKey, siteName or resultParam is missing, the algorithm is unknown, or an algorithm error occurred. */
const char *mpw_site_state(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion);
/** @return A fingerprint for a user. */
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword);
/** @return An identicon (static) that represents the user's identity. */
MPIdenticon mpw_identicon(
const char *fullName, const char *masterPassword);
/** @return An encoded representation (shared) of the given identicon. */
const char *mpw_identicon_encode(
MPIdenticon identicon);
/** @return An identicon (static) decoded from the given encoded identicon representation or an identicon with empty fields if the identicon could not be parsed. */
MPIdenticon mpw_identicon_encoded(
const char *encoding);
#endif // _MPW_ALGORITHM_H

View File

@ -56,11 +56,11 @@ static MPMasterKey mpw_master_key_v0(
// Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( fullName ) ), fullName );
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strchars( fullName ) ), fullName );
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)mpw_utf8_strlen( fullName ) );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)mpw_utf8_strchars( fullName ) );
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
err( "Could not allocate master key salt: %s", strerror( errno ) );
@ -95,16 +95,16 @@ static MPSiteKey mpw_site_key_v0(
// Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
keyContext? mpw_hex_l( (uint32_t)mpw_utf8_strlen( keyContext ) ): NULL, keyContext );
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strchars( siteName ) ), siteName, mpw_hex_l( siteCounter ),
keyContext? mpw_hex_l( (uint32_t)mpw_utf8_strchars( keyContext ) ): NULL, keyContext );
size_t siteSaltSize = 0;
uint8_t *siteSalt = NULL;
mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( siteName ) );
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strchars( siteName ) );
mpw_push_string( &siteSalt, &siteSaltSize, siteName );
mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
if (keyContext) {
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( keyContext ) );
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strchars( keyContext ) );
mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
}
if (!siteSalt) {

View File

@ -29,8 +29,8 @@
/// Type parsing.
/** Get a token from a string by searching until the first character in delim, no farther than eol.
* 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. */
* The input string reference is advanced beyond the token delimitor if one is found.
* @return A string (allocated) containing the token or NULL if the delim wasn't found before eol. */
char *mpw_get_token(
const char **in, const char *eol, char *delim);
/** Convert an RFC 3339 time string into epoch time.
@ -42,23 +42,23 @@ time_t mpw_timegm(
#if MPW_JSON
/** Search for a JSON child object in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A new JSON object or NULL if one of the section's object keys was not found in the source object's tree. */
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A JSON object (shared) or NULL if one of the section's object keys was not found in the source object's tree. */
json_object *mpw_get_json_section(
json_object *obj, const char *section);
/** Search for a string in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A new string or defaultValue if one of the section's object keys was not found in the source object's tree. */
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A string (shared) or defaultValue if one of the section's object keys was not found in the source object's tree. */
const char *mpw_get_json_string(
json_object *obj, const char *section, const char *defaultValue);
/** Search for an integer in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The integer value or defaultValue if one of the section's object keys was not found in the source object's tree. */
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The integer value or defaultValue if one of the section's object keys was not found in the source object's tree. */
int64_t mpw_get_json_int(
json_object *obj, const char *section, int64_t defaultValue);
/** Search for a boolean in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The boolean value or defaultValue if one of the section's object keys was not found in the source object's tree. */
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The boolean value or defaultValue if one of the section's object keys was not found in the source object's tree. */
bool mpw_get_json_boolean(
json_object *obj, const char *section, bool defaultValue);
#endif
@ -66,7 +66,8 @@ bool mpw_get_json_boolean(
/// mpw.
/** Calculate a master key if the target master key algorithm is different from the given master key algorithm.
* @return false if an error occurred during the derivation of the master key. */
* @param masterKey A buffer (allocated, MPMasterKeySize).
* @return false if an error occurred during the derivation of the master key. */
bool mpw_update_master_key(
MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
const char *fullName, const char *masterPassword);

View File

@ -837,15 +837,17 @@ MPMarshalInfo *mpw_marshal_read_info(
const char *in) {
MPMarshalInfo *info = malloc( sizeof( MPMarshalInfo ) );
*info = (MPMarshalInfo){ .format = MPMarshalFormatNone };
if (!info)
return NULL;
*info = (MPMarshalInfo){ .format = MPMarshalFormatNone, .identicon = MPIdenticonUnset };
if (in && strlen( in )) {
if (in[0] == '#') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatFlat };
info->format = MPMarshalFormatFlat;
mpw_marshal_read_flat_info( in, info );
}
else if (in[0] == '{') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatJSON };
info->format = MPMarshalFormatJSON;
#if MPW_JSON
mpw_marshal_read_json_info( in, info );
#endif
@ -925,37 +927,22 @@ const char *mpw_marshal_format_extension(
}
}
int mpw_marshal_format_extensions(
const MPMarshalFormat format, const char ***extensions) {
const char **mpw_marshal_format_extensions(
const MPMarshalFormat format, size_t *count) {
int count = 0;
*count = 0;
switch (format) {
case MPMarshalFormatNone: {
break;
}
case MPMarshalFormatFlat: {
*extensions = realloc( *extensions, (count = 3) * sizeof( const char * ) );
memcpy( *extensions, (const char *[]){
mpw_marshal_format_extension( format ),
"mpsites.txt",
"txt",
}, count * sizeof( const char * ) );
break;
}
case MPMarshalFormatJSON: {
*extensions = realloc( *extensions, (count = 3) * sizeof( const char * ) );
memcpy( *extensions, (const char *[]){
mpw_marshal_format_extension( format ),
"mpsites.json",
"json",
}, count * sizeof( const char * ) );
break;
}
case MPMarshalFormatNone:
return NULL;
case MPMarshalFormatFlat:
return mpw_strings( count,
mpw_marshal_format_extension( format ), "mpsites.txt", "txt" );
case MPMarshalFormatJSON:
return mpw_strings( count,
mpw_marshal_format_extension( format ), "mpsites.json", "json" );
default: {
dbg( "Unknown format: %d", format );
break;
return NULL;
}
}
return count;
}

View File

@ -113,6 +113,7 @@ typedef struct MPMarshalInfo {
MPAlgorithmVersion algorithm;
unsigned int avatar;
const char *fullName;
MPIdenticon identicon;
const char *keyID;
time_t lastUsed;
} MPMarshalInfo;
@ -122,29 +123,31 @@ typedef struct MPMarshalInfo {
/** Write the user and all associated data out to the given output buffer using the given marshalling format. */
bool mpw_marshal_write(
char **out, 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. */
MPMarshalInfo *mpw_marshal_read_info(
const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format.
* @return 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 an error occurred. */
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error);
//// Utilities.
/** Create a new user object ready for marshalling.
* @return NULL if the fullName or masterKeyProvider is missing, or the marshalled user couldn't be allocated. */
* @return A user object (allocated), or NULL if the fullName or masterKeyProvider is missing, or the marshalled user couldn't be allocated. */
MPMarshalledUser *mpw_marshal_user(
const char *fullName, MPMasterKeyProvider masterKeyProvider, const MPAlgorithmVersion algorithmVersion);
/** Create a new site attached to the given user object, ready for marshalling.
* @return NULL if the siteName is missing, or the marshalled site couldn't be allocated. */
* @return A site object (allocated), or NULL if the siteName is missing, or the marshalled site couldn't be allocated. */
MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user,
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.
* @return NULL if the marshalled question couldn't be allocated. */
* @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */
MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword);
/** Free the given user object and all associated data. */
bool mpw_marshal_info_free(
MPMarshalInfo **info);
@ -159,21 +162,21 @@ bool mpw_marshal_free(
const MPMarshalFormat mpw_format_named(
const char *formatName);
/**
* @return The standard name for the given purpose or NULL if the format was not recognized.
* @return The standard name (static) for the given purpose or NULL if the format was not recognized.
*/
const char *mpw_format_name(
const MPMarshalFormat format);
/**
* @return The file extension that's recommended for files that use the given marshalling format or NULL if the format was not recognized.
* @return The file extension (static) that's recommended for files that use the given marshalling format or NULL if the format was not recognized.
*/
const char *mpw_marshal_format_extension(
const MPMarshalFormat format);
/**
* @param extensions An array of filename extensions that are used for files of this format,
* the first being the currently preferred/output extension. Free after use.
* @return The amount of filename extensions returned in the array or 0 if the format does not support marshalling or was not recognized.
* @return An array (allocated, count) of filename extensions (static) that are used for files of this format,
* the first being the currently preferred/output extension.
* NULL if the format is unrecognized or does not support marshalling.
*/
int mpw_marshal_format_extensions(
const MPMarshalFormat format, const char ***extensions);
const char **mpw_marshal_format_extensions(
const MPMarshalFormat format, size_t *count);
#endif // _MPW_MARSHAL_H

View File

@ -24,6 +24,13 @@
const size_t MPMasterKeySize = 64;
const size_t MPSiteKeySize = 256 / 8; // Size of HMAC-SHA-256
const MPIdenticon MPIdenticonUnset = (MPIdenticon){
.leftArm = "",
.body = "",
.rightArm = "",
.accessory = "",
.color = MPIdenticonColorUnset,
};
const MPResultType mpw_type_named(const char *typeName) {

View File

@ -116,6 +116,7 @@ typedef mpw_enum( uint8_t, MPIdenticonColor ) {
MPIdenticonColorCyan,
MPIdenticonColorWhite,
MPIdenticonColorUnset = MPIdenticonColorBlack,
MPIdenticonColorFirst = MPIdenticonColorRed,
MPIdenticonColorLast = MPIdenticonColorWhite,
};
@ -127,6 +128,7 @@ typedef struct {
const char *accessory;
MPIdenticonColor color;
} MPIdenticon;
extern const MPIdenticon MPIdenticonUnset;
//// Type utilities.
@ -135,11 +137,11 @@ typedef struct {
*/
const MPKeyPurpose mpw_purpose_named(const char *purposeName);
/**
* @return The standard name for the given purpose or NULL if the purpose is not known.
* @return The standard name (static) for the given purpose or NULL if the purpose is not known.
*/
const char *mpw_purpose_name(MPKeyPurpose purpose);
/**
* @return An internal string containing the scope identifier to apply when encoding for the given purpose or NULL if the purpose is not known.
* @return The scope identifier (static) to apply when encoding for the given purpose or NULL if the purpose is not known.
*/
const char *mpw_purpose_scope(MPKeyPurpose purpose);
@ -148,35 +150,31 @@ const char *mpw_purpose_scope(MPKeyPurpose purpose);
*/
const MPResultType mpw_type_named(const char *typeName);
/**
* @return The standard identifying name for the given password type or NULL if the type is not known.
* @return The standard identifying name (static) for the given password type or NULL if the type is not known.
*/
const char *mpw_type_abbreviation(MPResultType resultType);
/**
* @return The standard identifying name for the given password type or NULL if the type is not known.
* @return The standard identifying name (static) for the given password type or NULL if the type is not known.
*/
const char *mpw_type_short_name(MPResultType resultType);
/**
* @return The descriptive name for the given password type or NULL if the type is not known.
* @return The descriptive name (static) for the given password type or NULL if the type is not known.
*/
const char *mpw_type_long_name(MPResultType resultType);
/**
* @return A newly allocated array of internal strings that express the templates to use for the given type.
* The amount of elements in the array is stored in count.
* If an unsupported type is given, count will be 0 and will return NULL.
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
* @return An array (allocated, count) of strings (static) that express the templates to use for the given type.
* NULL if the type is not known or is not a MPResultTypeClassTemplate.
*/
const char **mpw_type_templates(MPResultType type, size_t *count);
/**
* @return An internal string that contains the password encoding template of the given type
* for a seed that starts with the given byte.
* @return A string (static) that contains the password encoding template of the given type for a seed that starts with the given byte.
* NULL if the type is not known or is not a MPResultTypeClassTemplate.
*/
const char *mpw_type_template(MPResultType type, uint8_t templateIndex);
/**
* @return An string literal with all the characters in the given character class or NULL if the character class is not known.
* @return An string (static) with all the characters in the given character class or NULL if the character class is not known.
*/
const char *mpw_class_characters(char characterClass);
/**

View File

@ -471,37 +471,29 @@ const char *mpw_hex_l(uint32_t number) {
return mpw_hex( &buf, sizeof( buf ) );
}
/**
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
*/
static int mpw_utf8_sizeof(unsigned char utf8Byte) {
size_t mpw_utf8_charlen(const char *utf8String) {
if (!utf8Byte)
return 0;
if ((utf8Byte & 0x80) == 0)
return 1;
if ((utf8Byte & 0xC0) != 0xC0)
return 0;
if ((utf8Byte & 0xE0) == 0xC0)
return 2;
if ((utf8Byte & 0xF0) == 0xE0)
return 3;
if ((utf8Byte & 0xF8) == 0xF0)
return 4;
// Legal UTF-8 byte sequences: <http://www.unicode.org/unicode/uni2errata/UTF-8_Corrigendum.html>
unsigned char utf8Char = (unsigned char)*utf8String;
if (utf8Char >= 0x00 && utf8Char <= 0x7F)
return min( 1, strlen( utf8String ) );
if (utf8Char >= 0xC2 && utf8Char <= 0xDF)
return min( 2, strlen( utf8String ) );
if (utf8Char >= 0xE0 && utf8Char <= 0xEF)
return min( 3, strlen( utf8String ) );
if (utf8Char >= 0xF0 && utf8Char <= 0xF4)
return min( 4, strlen( utf8String ) );
return 0;
}
const size_t mpw_utf8_strlen(const char *utf8String) {
size_t mpw_utf8_strchars(const char *utf8String) {
size_t charlen = 0;
char *remainingString = (char *)utf8String;
for (int charByteSize;
remainingString && (charByteSize = mpw_utf8_sizeof( (unsigned char)*remainingString ));
remainingString += charByteSize)
++charlen;
size_t strchars = 0, charlen;
for (char *remaining = (char *)utf8String; (charlen = mpw_utf8_charlen( remaining )); remaining += charlen)
++strchars;
return charlen;
return strchars;
}
void *mpw_memdup(const void *src, size_t len) {

View File

@ -97,40 +97,44 @@ void mpw_uint16(const uint16_t number, uint8_t buf[2]);
void mpw_uint32(const uint32_t number, uint8_t buf[4]);
void mpw_uint64(const uint64_t number, uint8_t buf[8]);
/** Allocate a new array of _type, assign its element count to _count if not NULL and populate it with the varargs.
* @return NULL if no strings were given or we could not allocate space for the new array. */
/** @return An array of strings (allocated, count) or NULL if no strings were given or we could not allocate space for the new array. */
const char **mpw_strings(
size_t *count, const char *strings, ...);
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer.
* @param buffer A pointer to the buffer (allocated, bufferSize) to append to, may be NULL. */
bool mpw_push_buf(
uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize);
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer.
* @param buffer A pointer to the buffer (allocated, bufferSize) to append to, may be NULL. */
bool mpw_push_int(
uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt);
/** Push a string onto a buffer. reallocs the given buffer and appends the given string.
* @param buffer A pointer to the buffer (allocated, bufferSize) to append to, may be NULL. */
bool mpw_push_string(
uint8_t **buffer, size_t *bufferSize, const char *pushString);
/** Push a string onto another string. reallocs the target string and appends the source string. */
/** Push a string onto another string. reallocs the target string and appends the source string.
* @param string A pointer to the string (allocated) to append to, may be NULL. */
bool mpw_string_push(
char **string, const char *pushString);
bool mpw_string_pushf(
char **string, const char *pushFormat, ...);
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
bool mpw_push_int(
uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt);
// These defines merely exist to force the void** cast (& do type-checking), since void** casts are not automatic.
/** Reallocate the given buffer from the given size by adding the delta size.
* On success, the buffer size pointer will be updated to the buffer's new size
* and the buffer pointer may be updated to a new memory address.
* On failure, the pointers will remain unaffected.
* @param buffer A pointer to the buffer to reallocate.
* @param bufferSize A pointer to the buffer's current size.
* @param deltaSize The amount to increase the buffer's size by.
* @return true if successful, false if reallocation failed.
*/
* On success, the buffer size pointer will be updated to the buffer's new size
* and the buffer pointer may be updated to a new memory address.
* On failure, the pointers will remain unaffected.
* @param buffer A pointer to the buffer (allocated, bufferSize) to reallocate.
* @param bufferSize A pointer to the buffer's current size.
* @param deltaSize The amount to increase the buffer's size by.
* @return true if successful, false if reallocation failed.
*/
#define mpw_realloc(\
/* const void** */buffer, /* size_t* */bufferSize, /* const size_t */deltaSize) \
({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_realloc( (const void **)_b, bufferSize, deltaSize ); })
/** Free a buffer after zero'ing its contents, then set the reference to NULL. */
/** Free a buffer after zero'ing its contents, then set the reference to NULL.
* @param bufferSize The byte-size of the buffer, these bytes will be zeroed prior to deallocation. */
#define mpw_free(\
/* void** */buffer, /* size_t */ bufferSize) \
({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_free( (void **)_b, bufferSize ); })
@ -143,7 +147,7 @@ bool mpw_push_int(
/* char** */strings, ...) \
({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
/** Free a string after zero'ing its contents, then set the reference to the replacement string.
* The replacement string is generated before the original is freed; so it can be a derivative of the original. */
* The replacement string is generated before the original is freed; so it can be a derivative of the original. */
#define mpw_replace_string(\
/* char* */string, /* char* */replacement) \
do { const char *replacement_ = replacement; mpw_free_string( &string ); string = replacement_; } while (0)
@ -175,30 +179,30 @@ void mpw_zero(
//// Cryptographic functions.
/** Derive a key from the given secret and salt using the scrypt KDF.
* @return A new keySize allocated buffer containing the key or NULL if secret or salt is missing, key could not be allocated or the KDF failed. */
* @return A buffer (allocated, keySize) containing the key or NULL if secret or salt is missing, key could not be allocated or the KDF failed. */
uint8_t const *mpw_kdf_scrypt(
const size_t keySize, const uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p);
/** Derive a subkey from the given key using the blake2b KDF.
* @return A new keySize allocated buffer containing the key or NULL if the key or subkeySize is missing, the key sizes are out of bounds, the subkey could not be allocated or derived. */
* @return A buffer (allocated, keySize) containing the key or NULL if the key or subkeySize is missing, the key sizes are out of bounds, the subkey could not be allocated or derived. */
uint8_t const *mpw_kdf_blake2b(
const size_t subkeySize, const uint8_t *key, const size_t keySize,
const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal);
/** Calculate the MAC for the given message with the given key using SHA256-HMAC.
* @return A new 32-byte allocated buffer containing the MAC or NULL if the key or message is missing, the MAC could not be allocated or generated. */
* @return A buffer (allocated, 32-byte) containing the MAC or NULL if the key or message is missing, the MAC could not be allocated or generated. */
uint8_t const *mpw_hash_hmac_sha256(
const uint8_t *key, const size_t keySize, const uint8_t *message, const size_t messageSize);
/** Encrypt a plainBuf with the given key using AES-128-CBC.
* @return A new bufSize allocated buffer containing the cipherBuf or NULL if the key or buffer is missing, the key size is out of bounds or the result could not be allocated. */
* @return A buffer (allocated, bufSize) containing the cipherBuf or NULL if the key or buffer is missing, the key size is out of bounds or the result could not be allocated. */
uint8_t const *mpw_aes_encrypt(
const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, size_t *bufSize);
/** Decrypt a cipherBuf with the given key using AES-128-CBC.
* @return A new bufSize allocated buffer containing the plainBuf or NULL if the key or buffer is missing, the key size is out of bounds or the result could not be allocated. */
* @return A buffer (allocated, bufSize) containing the plainBuf or NULL if the key or buffer is missing, the key size is out of bounds or the result could not be allocated. */
uint8_t const *mpw_aes_decrypt(
const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, size_t *bufSize);
/** Calculate an OTP using RFC-4226.
* @return A newly allocated string containing exactly `digits` decimal OTP digits. */
#if UNUSED
/** Calculate an OTP using RFC-4226.
* @return A string (allocated) containing exactly `digits` decimal OTP digits. */
const char *mpw_hotp(
const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset);
#endif
@ -206,29 +210,34 @@ const char *mpw_hotp(
//// Visualizers.
/** Compose a formatted string.
* @return A C-string in a reused buffer, do not free or store it; or NULL if the format is missing or the result could not be allocated or formatted. */
* @return A string (shared); or NULL if the format is missing or the result could not be allocated or formatted. */
const char *mpw_str(const char *format, ...);
const char *mpw_vstr(const char *format, va_list args);
/** Encode a buffer as a string of hexadecimal characters.
* @return A C-string in a reused buffer, do not free or store it; or NULL if the buffer is missing or the result could not be allocated. */
* @return A string (shared); or NULL if the buffer is missing or the result could not be allocated. */
const char *mpw_hex(const void *buf, size_t length);
const char *mpw_hex_l(uint32_t number);
/** Encode a fingerprint for a buffer.
* @return A C-string in a reused buffer, do not free or store it; or NULL if the buffer is missing or the result could not be allocated. */
* @return A string (shared); or NULL if the buffer is missing or the result could not be allocated. */
MPKeyID mpw_id_buf(const void *buf, size_t length);
/** Compare two fingerprints for equality.
* @return true if the buffers represent identical fingerprints or are both NULL. */
* @return true if the buffers represent identical fingerprints or are both NULL. */
bool mpw_id_buf_equals(const char *id1, const char *id2);
//// String utilities.
/** @return The amount of display characters in the given UTF-8 string. */
const size_t mpw_utf8_strlen(const char *utf8String);
/** Drop-in for memdup(3). */
/** @return The byte length of the UTF-8 character at the start of the given string. */
size_t mpw_utf8_charlen(const char *utf8String);
/** @return The amount of UTF-8 characters in the given string. */
size_t mpw_utf8_strchars(const char *utf8String);
/** Drop-in for memdup(3).
* @return A buffer (allocated, len) with len bytes copied from src or NULL if src is missing or the buffer could not be allocated. */
void *mpw_memdup(const void *src, size_t len);
/** Drop-in for POSIX strdup(3). */
/** Drop-in for POSIX strdup(3).
* @return A string (allocated) copied from src or NULL if src is missing or the buffer could not be allocated. */
char *mpw_strdup(const char *src);
/** Drop-in for POSIX strndup(3). */
/** Drop-in for POSIX strndup(3).
* @return A string (allocated) with no more than max bytes copied from src or NULL if src is missing or the buffer could not be allocated. */
char *mpw_strndup(const char *src, size_t max);
/** Drop-in for POSIX strncasecmp(3). */
int mpw_strncasecmp(const char *s1, const char *s2, size_t max);