From 322e05666138282f5c98c62885753e8ff70de78f Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Sat, 5 Aug 2017 17:33:45 -0400 Subject: [PATCH] Implement algorithm support for hybrid personal passwords. --- core/c/base64.c | 171 ++++++++++++++++++ core/c/base64.h | 64 +++++++ core/c/mpw-algorithm_v0.c | 32 +++- core/c/mpw-algorithm_v1.c | 4 +- core/c/mpw-algorithm_v2.c | 4 +- core/c/mpw-algorithm_v3.c | 4 +- core/c/mpw-marshall-util.c | 2 +- core/c/mpw-marshall.c | 27 ++- core/c/mpw-util.c | 85 +++++++-- core/c/mpw-util.h | 18 +- .../project.pbxproj | 6 + .../project.pbxproj | 12 ++ platform-independent/cli-c/build | 9 +- 13 files changed, 400 insertions(+), 38 deletions(-) create mode 100644 core/c/base64.c create mode 100644 core/c/base64.h diff --git a/core/c/base64.c b/core/c/base64.c new file mode 100644 index 00000000..2f7b4f5e --- /dev/null +++ b/core/c/base64.c @@ -0,0 +1,171 @@ +/* ==================================================================== +* Copyright (c) 1995-1999 The Apache Group. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* 3. All advertising materials mentioning features or use of this +* software must display the following acknowledgment: +* "This product includes software developed by the Apache Group +* for use in the Apache HTTP server project (http://www.apache.org/)." +* +* 4. The names "Apache Server" and "Apache Group" must not be used to +* endorse or promote products derived from this software without +* prior written permission. For written permission, please contact +* apache@apache.org. +* +* 5. Products derived from this software may not be called "Apache" +* nor may "Apache" appear in their names without prior written +* permission of the Apache Group. +* +* 6. Redistributions of any form whatsoever must retain the following +* acknowledgment: +* "This product includes software developed by the Apache Group +* for use in the Apache HTTP server project (http://www.apache.org/)." +* +* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* ==================================================================== +* +* This software consists of voluntary contributions made by many +* individuals on behalf of the Apache Group and was originally based +* on public domain software written at the National Center for +* Supercomputing Applications, University of Illinois, Urbana-Champaign. +* For more information on the Apache Group and the Apache HTTP server +* project, please see . +*/ + +#include "base64.h" + +/* aaaack but it's fast and const should make it shared text page. */ +static const unsigned char pr2six[256] = + { + /* ASCII table */ + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + +size_t Base64decode_len(const char *bufcoded) { + + size_t nbytesdecoded; + register const unsigned char *bufin; + register int nprbytes; + + bufin = (const unsigned char *)bufcoded; + while (pr2six[*(bufin++)] <= 63); + + nprbytes = (int)(bufin - (const unsigned char *)bufcoded) - 1; + nbytesdecoded = (size_t)(((nprbytes + 3) / 4) * 3); + + return nbytesdecoded; +} + +int Base64decode(uint8_t *bufplain, const char *bufcoded) { + + int nbytesdecoded; + register const unsigned char *bufin; + register unsigned char *bufout; + register int nprbytes; + + bufin = (const unsigned char *)bufcoded; + while (pr2six[*(bufin++)] <= 63); + nprbytes = (int)(bufin - (const unsigned char *)bufcoded) - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + + bufout = bufplain; + bufin = (const unsigned char *)bufcoded; + + while (nprbytes > 4) { + *(bufout++) = (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + *(bufout++) = (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + *(bufout++) = (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + bufin += 4; + nprbytes -= 4; + } + + /* Note: (nprbytes == 1) would be an error, so just ingore that case */ + if (nprbytes > 1) { + *(bufout++) = (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + } + if (nprbytes > 2) { + *(bufout++) = (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + } + if (nprbytes > 3) { + *(bufout++) = (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + } + + nbytesdecoded -= (4 - nprbytes) & 3; + return nbytesdecoded; +} + +static const char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +size_t Base64encode_len(size_t len) { + + return ((len + 2) / 3 * 4); +} + +int Base64encode(char *encoded, const uint8_t *string, size_t len) { + + int i; + char *p; + + p = encoded; + for (i = 0; i < len - 2; i += 3) { + *p++ = basis_64[(string[i] >> 2) & 0x3F]; + *p++ = basis_64[((string[i] & 0x3) << 4) | + ((string[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((string[i + 1] & 0xF) << 2) | + ((string[i + 2] & 0xC0) >> 6)]; + *p++ = basis_64[string[i + 2] & 0x3F]; + } + if (i < len) { + *p++ = basis_64[(string[i] >> 2) & 0x3F]; + if (i == (len - 1)) { + *p++ = basis_64[((string[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = basis_64[((string[i] & 0x3) << 4) | + ((string[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return (int)(p - encoded); +} diff --git a/core/c/base64.h b/core/c/base64.h new file mode 100644 index 00000000..d696b270 --- /dev/null +++ b/core/c/base64.h @@ -0,0 +1,64 @@ +/* ==================================================================== +* Copyright (c) 1995-1999 The Apache Group. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* 3. All advertising materials mentioning features or use of this +* software must display the following acknowledgment: +* "This product includes software developed by the Apache Group +* for use in the Apache HTTP server project (http://www.apache.org/)." +* +* 4. The names "Apache Server" and "Apache Group" must not be used to +* endorse or promote products derived from this software without +* prior written permission. For written permission, please contact +* apache@apache.org. +* +* 5. Products derived from this software may not be called "Apache" +* nor may "Apache" appear in their names without prior written +* permission of the Apache Group. +* +* 6. Redistributions of any form whatsoever must retain the following +* acknowledgment: +* "This product includes software developed by the Apache Group +* for use in the Apache HTTP server project (http://www.apache.org/)." +* +* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* ==================================================================== +* +* This software consists of voluntary contributions made by many +* individuals on behalf of the Apache Group and was originally based +* on public domain software written at the National Center for +* Supercomputing Applications, University of Illinois, Urbana-Champaign. +* For more information on the Apache Group and the Apache HTTP server +* project, please see . +*/ + +#include +#include + +size_t Base64decode_len(const char *bufcoded); +int Base64decode(uint8_t *bufplain, const char *bufcoded); + +size_t Base64encode_len(size_t len); +int Base64encode(char *encoded, const uint8_t *string, size_t len); diff --git a/core/c/mpw-algorithm_v0.c b/core/c/mpw-algorithm_v0.c index 5e755769..5a819d2e 100644 --- a/core/c/mpw-algorithm_v0.c +++ b/core/c/mpw-algorithm_v0.c @@ -22,6 +22,7 @@ #include "mpw-types.h" #include "mpw-util.h" +#include "base64.h" #define MP_N 32768 #define MP_r 8 @@ -155,11 +156,38 @@ static const char *mpw_sitePassword_v0( const char *mpw_encrypt_v0( MPMasterKey masterKey, const char *plainText) { - return NULL; // TODO: aes128_cbc + // Encrypt + dbg( "-- encrypting plainText: %s\n", plainText ); + size_t bufSize = strlen( plainText ); + const uint8_t *cipherBuf = mpw_aes_encrypt( masterKey, MPMasterKeySize, (const uint8_t *)plainText, bufSize ); + dbg( "-- cipherBuf: %lu bytes = ", bufSize ); + printb( cipherBuf, bufSize ); + + // Base64-encode + char *cipherText = calloc( 1, Base64encode_len( bufSize ) + 1 ); + Base64encode( cipherText, cipherBuf, bufSize ); + dbg( "-- b64 encoded -> cipherText: %s\n", cipherText ); + mpw_free( cipherBuf, bufSize ); + + return cipherText; } const char *mpw_decrypt_v0( MPMasterKey masterKey, const char *cipherText) { - return NULL; // TODO: aes128_cbc + // Base64-decode + dbg( "-- decrypting cipherText: %s\n", cipherText ); + size_t bufSize = Base64decode_len( cipherText ) + 1; + uint8_t *cipherBuf = calloc( 1, bufSize ); + Base64decode( cipherBuf, cipherText ); + dbg( "-- b64 decoded: %lu bytes = ", bufSize ); + printb( cipherBuf, bufSize ); + + + // Decrypt + const char *plainText = (const char *)mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, bufSize ); + dbg( "-- decrypted -> plainText: %s\n", plainText ); + mpw_free( cipherBuf, bufSize ); + + return plainText; } diff --git a/core/c/mpw-algorithm_v1.c b/core/c/mpw-algorithm_v1.c index 6fc1fbb9..5bbb36e3 100644 --- a/core/c/mpw-algorithm_v1.c +++ b/core/c/mpw-algorithm_v1.c @@ -136,11 +136,11 @@ static const char *mpw_sitePassword_v1( const char *mpw_encrypt_v1( MPMasterKey masterKey, const char *plainText) { - return NULL; // TODO: aes128_cbc + return mpw_encrypt_v0( masterKey, plainText ); } const char *mpw_decrypt_v1( MPMasterKey masterKey, const char *cipherText) { - return NULL; // TODO: aes128_cbc + return mpw_decrypt_v0( masterKey, cipherText ); } diff --git a/core/c/mpw-algorithm_v2.c b/core/c/mpw-algorithm_v2.c index 49766e17..c0cd6a73 100644 --- a/core/c/mpw-algorithm_v2.c +++ b/core/c/mpw-algorithm_v2.c @@ -136,11 +136,11 @@ static const char *mpw_sitePassword_v2( const char *mpw_encrypt_v2( MPMasterKey masterKey, const char *plainText) { - return NULL; // TODO: aes128_cbc + return mpw_encrypt_v1( masterKey, plainText ); } const char *mpw_decrypt_v2( MPMasterKey masterKey, const char *cipherText) { - return NULL; // TODO: aes128_cbc + return mpw_decrypt_v1( masterKey, cipherText ); } diff --git a/core/c/mpw-algorithm_v3.c b/core/c/mpw-algorithm_v3.c index d2043111..54e77668 100644 --- a/core/c/mpw-algorithm_v3.c +++ b/core/c/mpw-algorithm_v3.c @@ -136,11 +136,11 @@ static const char *mpw_sitePassword_v3( const char *mpw_encrypt_v3( MPMasterKey masterKey, const char *plainText) { - return NULL; // TODO: aes128_cbc + return mpw_encrypt_v2( masterKey, plainText ); } const char *mpw_decrypt_v3( MPMasterKey masterKey, const char *cipherText) { - return NULL; // TODO: aes128_cbc + return mpw_decrypt_v2( masterKey, cipherText ); } diff --git a/core/c/mpw-marshall-util.c b/core/c/mpw-marshall-util.c index 438ed048..5ba22765 100644 --- a/core/c/mpw-marshall-util.c +++ b/core/c/mpw-marshall-util.c @@ -57,7 +57,7 @@ json_object *mpw_get_json_section( char *sectionTokenizer = strdup( section ), *sectionToken = sectionTokenizer; for (sectionToken = strtok( sectionToken, "." ); sectionToken; sectionToken = strtok( NULL, "." )) if (!json_object_object_get_ex( json_value, sectionToken, &json_value ) || !json_value) { - dbg( "While resolving: %s: Missing value for: %s\n", section, sectionToken ); + trc( "While resolving: %s: Missing value for: %s\n", section, sectionToken ); json_value = NULL; break; } diff --git a/core/c/mpw-marshall.c b/core/c/mpw-marshall.c index ab7eb2e2..b470d924 100644 --- a/core/c/mpw-marshall.c +++ b/core/c/mpw-marshall.c @@ -55,7 +55,8 @@ MPMarshalledSite *mpw_marshall_site( realloc( marshalledUser->sites, sizeof( MPMarshalledSite ) * (++marshalledUser->sites_count) ))) return NULL; - marshalledUser->sites[marshalledUser->sites_count - 1] = (MPMarshalledSite){ + MPMarshalledSite *site = &marshalledUser->sites[marshalledUser->sites_count - 1]; + *site = (MPMarshalledSite){ .name = strdup( siteName ), .content = NULL, .type = siteType, @@ -72,7 +73,7 @@ MPMarshalledSite *mpw_marshall_site( .questions_count = 0, .questions = NULL, }; - return marshalledUser->sites + sizeof( MPMarshalledSite ) * (marshalledUser->sites_count - 1); + return site; }; MPMarshalledQuestion *mpw_marshal_question( @@ -82,10 +83,11 @@ MPMarshalledQuestion *mpw_marshal_question( realloc( marshalledSite->questions, sizeof( MPMarshalledQuestion ) * (++marshalledSite->questions_count) ))) return NULL; - marshalledSite->questions[marshalledSite->questions_count - 1] = (MPMarshalledQuestion){ + MPMarshalledQuestion *question = &marshalledSite->questions[marshalledSite->questions_count - 1]; + *question = (MPMarshalledQuestion){ .keyword = strdup( keyword ), }; - return marshalledSite->questions + sizeof( MPMarshalledSite ) * (marshalledSite->questions_count - 1); + return question; } bool mpw_marshal_free( @@ -130,9 +132,9 @@ static bool mpw_marshall_write_flat( mpw_string_pushf( out, "# Master Password site export\n" ); if (user->redacted) - mpw_string_pushf( out, "# Export of site names and passwords in clear-text.\n" ); - else mpw_string_pushf( out, "# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" ); + else + 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, "# Format: %d\n", 1 ); @@ -161,6 +163,7 @@ static bool mpw_marshall_write_flat( const char *content = NULL; if (!user->redacted) { + // Clear Text if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site.algorithm, user->name, user->masterPassword )) { *error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; return false; @@ -174,6 +177,7 @@ static bool mpw_marshall_write_flat( else if (site.type & MPSiteFeatureExportContent && site.content && strlen( site.content )) content = mpw_decrypt( masterKey, site.content, site.algorithm ); } else if (site.type & MPSiteFeatureExportContent && site.content && strlen( site.content )) + // Redacted content = strdup( site.content ); if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site.lastUsed ) )) @@ -242,6 +246,7 @@ static bool mpw_marshall_write_json( const char *content = NULL; if (!user->redacted) { + // Clear Text if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site.algorithm, user->name, user->masterPassword )) { *error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; return false; @@ -256,6 +261,7 @@ static bool mpw_marshall_write_json( content = mpw_decrypt( masterKey, site.content, site.algorithm ); } else if (site.type & MPSiteFeatureExportContent && site.content && strlen( site.content )) + // Redacted content = strdup( site.content ); json_object *json_site = json_object_new_object(); @@ -284,6 +290,7 @@ static bool mpw_marshall_write_json( json_object_object_add( json_site_questions, question.keyword, json_site_question ); if (!user->redacted) { + // Clear Text MPSiteKey siteKey = mpw_siteKey( masterKey, site.name, 1, MPKeyPurposeRecovery, question.keyword, site.algorithm ); const char *answer = mpw_sitePassword( siteKey, MPPasswordTypeGeneratedPhrase, site.algorithm ); mpw_free( siteKey, MPSiteKeySize ); @@ -500,7 +507,8 @@ static MPMarshalledUser *mpw_marshall_read_flat( site->uses = (unsigned int)atoi( str_uses ); site->lastUsed = siteLastUsed; if (siteContent && strlen( siteContent )) { - if (user->redacted) { + if (!user->redacted) { + // Clear Text if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) { *error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; return NULL; @@ -509,6 +517,7 @@ static MPMarshalledUser *mpw_marshall_read_flat( site->content = mpw_encrypt( masterKey, siteContent, site->algorithm ); } else + // Redacted site->content = strdup( siteContent ); } } @@ -653,7 +662,8 @@ static MPMarshalledUser *mpw_marshall_read_json( site->uses = siteUses; site->lastUsed = siteLastUsed; if (siteContent && strlen( siteContent )) { - if (user->redacted) { + if (!user->redacted) { + // Clear Text if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) { *error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; return NULL; @@ -662,6 +672,7 @@ static MPMarshalledUser *mpw_marshall_read_json( site->content = mpw_encrypt( masterKey, siteContent, site->algorithm ); } else + // Redacted site->content = strdup( siteContent ); } diff --git a/core/c/mpw-util.c b/core/c/mpw-util.c index f9d4729e..6e784f70 100644 --- a/core/c/mpw-util.c +++ b/core/c/mpw-util.c @@ -30,6 +30,9 @@ #include #elif HAS_SODIUM #include "sodium.h" +#ifdef SODIUM_LIBRARY_MINIMAL +#include "crypto_stream_aes128ctr.h" +#endif #endif #include "mpw-util.h" @@ -67,6 +70,9 @@ bool mpw_push_string(uint8_t **const buffer, size_t *const bufferSize, const cha bool mpw_string_push(char **const string, const char *pushString) { + if (!*string) + *string = calloc( 1, sizeof( char ) ); + size_t stringLength = strlen( *string ); return pushString && mpw_push_buf( (uint8_t **const)string, &stringLength, pushString, strlen( pushString ) + 1 ); } @@ -123,37 +129,79 @@ uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_ mpw_free( key, keySize ); return NULL; } +#else +#error No crypto support for mpw_scrypt. #endif return key; } -uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) { +uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *message, const size_t messageSize) { -#if HAS_CPERCIVA - uint8_t *const buffer = malloc( 32 ); - if (!buffer) + if (!key || !keySize || !message || !messageSize) return NULL; - HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer ); - return buffer; +#if HAS_CPERCIVA + uint8_t *const mac = malloc( 32 ); + if (!mac) + return NULL; + + HMAC_SHA256_Buf( key, keySize, message, messageSize, mac ); #elif HAS_SODIUM - uint8_t *const buffer = malloc( crypto_auth_hmacsha256_BYTES ); - if (!buffer) + uint8_t *const mac = malloc( crypto_auth_hmacsha256_BYTES ); + if (!mac) return NULL; crypto_auth_hmacsha256_state state; if (crypto_auth_hmacsha256_init( &state, key, keySize ) != 0 || - crypto_auth_hmacsha256_update( &state, salt, saltSize ) != 0 || - crypto_auth_hmacsha256_final( &state, buffer ) != 0) { - mpw_free( buffer, crypto_auth_hmacsha256_BYTES ); + crypto_auth_hmacsha256_update( &state, message, messageSize ) != 0 || + crypto_auth_hmacsha256_final( &state, mac ) != 0) { + mpw_free( mac, crypto_auth_hmacsha256_BYTES ); return NULL; } - - return buffer; +#else +#error No crypto support for mpw_hmac_sha256. #endif - return NULL; + return mac; +} + +static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, const size_t bufSize) { + +#if HAS_SODIUM + if (!key || keySize < crypto_stream_KEYBYTES) + return NULL; + + uint8_t cipherKey[crypto_stream_KEYBYTES]; + memcpy( cipherKey, key, sizeof( cipherKey ) ); + + uint8_t nonce[crypto_stream_NONCEBYTES]; + bzero( (void *)nonce, sizeof( nonce ) ); + + if (encrypt) { + uint8_t *const cipherBuf = malloc( bufSize ); + crypto_stream_aes128ctr_xor( cipherBuf, buf, bufSize, nonce, cipherKey ); + return cipherBuf; + } else { + uint8_t *const plainBuf = malloc( bufSize ); + crypto_stream_aes128ctr( plainBuf, bufSize, nonce, cipherKey ); + for (size_t c = 0; c < bufSize; ++c) + plainBuf[c] = buf[c] ^ plainBuf[c]; + return plainBuf; + } +#else +#error No crypto support for mpw_aes. +#endif +} + +uint8_t const *mpw_aes_encrypt(const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, const size_t bufSize) { + + return mpw_aes( true, key, keySize, plainBuf, bufSize ); +} + +uint8_t const *mpw_aes_decrypt(const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, const size_t bufSize) { + + return mpw_aes( false, key, keySize, cipherBuf, bufSize ); } MPKeyID mpw_id_buf(const void *buf, size_t length) { @@ -167,6 +215,8 @@ MPKeyID mpw_id_buf(const void *buf, size_t length) { #elif HAS_SODIUM uint8_t hash[crypto_hash_sha256_BYTES]; crypto_hash_sha256( hash, buf, length ); +#else +#error No crypto support for mpw_id_buf. #endif return mpw_hex( hash, sizeof( hash ) / sizeof( uint8_t ) ); @@ -325,3 +375,10 @@ const size_t mpw_utf8_strlen(const char *utf8String) { return charlen; } + +void printb(const void *p, size_t size) { + + for (int i = 0; i < size; ++i) + dbg( "%02hhX ", ((const uint8_t *)p)[i] ); + dbg( "\n" ); +} diff --git a/core/c/mpw-util.h b/core/c/mpw-util.h index 3f68d42b..5769bcb1 100644 --- a/core/c/mpw-util.h +++ b/core/c/mpw-util.h @@ -121,15 +121,23 @@ bool mpw_free_string( //// Cryptographic functions. -/** Perform a scrypt-based key derivation on the given key using the given salt and scrypt parameters. - * @return A new keySize-size allocated buffer. */ +/** Derive a key from the given secret and salt using the scrypt KDF. + * @return A new keySize allocated buffer containing the key. */ uint8_t const *mpw_scrypt( const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize, uint64_t N, uint32_t r, uint32_t p); -/** Calculate a SHA256-based HMAC by encrypting the given salt with the given key. - * @return A new 32-byte allocated buffer. */ +/** Calculate the MAC for the given message with the given key using SHA256-HMAC. + * @return A new 32-byte allocated buffer containing the MAC. */ uint8_t const *mpw_hmac_sha256( const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize); +/** Encrypt a plainBuf with the given key using AES-128-CBC. + * @return A new bufSize allocated buffer containing the cipherBuf. */ +uint8_t const *mpw_aes_encrypt( + const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, const size_t bufSize); +/** Decrypt a cipherBuf with the given key using AES-128-CBC. + * @return A new bufSize allocated buffer containing the plainBuf. */ +uint8_t const *mpw_aes_decrypt( + const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, const size_t bufSize); //// Visualizers. @@ -155,4 +163,6 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword); /** @return The amount of display characters in the given UTF-8 string. */ const size_t mpw_utf8_strlen(const char *utf8String); +void printb(const void *p, size_t size); + #endif // _MPW_UTIL_H diff --git a/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj b/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj index 7f8bb3ff..ee5eb5d9 100644 --- a/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -182,6 +182,7 @@ DA5A09E0171A70E4005284AB /* play@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DE171A70E4005284AB /* play@2x.png */; }; DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09E8171BB0F7005284AB /* unlocked.png */; }; DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09E9171BB0F7005284AB /* unlocked@2x.png */; }; + DA5B0B401F36469400B663F0 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = DA5B0B3E1F36469400B663F0 /* base64.c */; }; DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; }; DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; }; @@ -785,6 +786,8 @@ DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = ""; }; DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = ""; }; DA5A09E9171BB0F7005284AB /* unlocked@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "unlocked@2x.png"; sourceTree = ""; }; + DA5B0B3E1F36469400B663F0 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; + DA5B0B3F1F36469400B663F0 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; }; DA5BFA48147E415C00F98B1E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -1719,6 +1722,8 @@ 93D39A2239FFFE6BEC83E191 /* C */ = { isa = PBXGroup; children = ( + DA5B0B3E1F36469400B663F0 /* base64.c */, + DA5B0B3F1F36469400B663F0 /* base64.h */, 93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */, 93D396F918E6470DB846C17F /* mpw-algorithm_v1.c */, 93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */, @@ -3905,6 +3910,7 @@ 93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */, 93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */, 93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */, + DA5B0B401F36469400B663F0 /* base64.c in Sources */, 93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */, 93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */, DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */, diff --git a/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj b/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj index f4781501..00d32fed 100644 --- a/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj +++ b/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj @@ -83,6 +83,10 @@ DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE931A7D8117003E5423 /* MPTypes.m */; }; DA5180CA19FF2F9200A587E9 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5180C719FF2F9200A587E9 /* MPAlgorithmV2.m */; }; DA5180CE19FF307E00A587E9 /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5180CD19FF307E00A587E9 /* MPAppDelegate_Store.m */; }; + DA5B0B3A1F36467300B663F0 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = DA5B0B381F36467300B663F0 /* base64.c */; }; + DA5B0B3B1F36467800B663F0 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = DA5B0B381F36467300B663F0 /* base64.c */; }; + DA5B0B3C1F36467900B663F0 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = DA5B0B381F36467300B663F0 /* base64.c */; }; + DA5B0B3D1F36467900B663F0 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = DA5B0B381F36467300B663F0 /* base64.c */; }; DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5C981724A667003798D8 /* MPAlgorithm.m */; }; DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5C9A1724A667003798D8 /* MPAlgorithmV0.m */; }; DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5C9C1724A667003798D8 /* MPAlgorithmV1.m */; }; @@ -456,6 +460,8 @@ DA5180C719FF2F9200A587E9 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = ""; }; DA5180CC19FF307E00A587E9 /* MPAppDelegate_Store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_Store.h; sourceTree = ""; }; DA5180CD19FF307E00A587E9 /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = ""; }; + DA5B0B381F36467300B663F0 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; + DA5B0B391F36467300B663F0 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; DA5BFA44147E415C00F98B1E /* Master Password.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Master Password.app"; sourceTree = BUILT_PRODUCTS_DIR; }; DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -1809,6 +1815,8 @@ DA6773291A4746AF004F356A /* C */ = { isa = PBXGroup; children = ( + DA5B0B381F36467300B663F0 /* base64.c */, + DA5B0B391F36467300B663F0 /* base64.h */, DA1C7AB71F1A8F6E009A3551 /* cli */, DA831A271A6E1146000AC234 /* mpw-algorithm_v0.c */, DA831A281A6E1146000AC234 /* mpw-algorithm_v1.c */, @@ -2528,6 +2536,7 @@ files = ( DA1C7AAA1F1A8F24009A3551 /* mpw-marshall.c in Sources */, DA1C7AAB1F1A8F24009A3551 /* mpw-types.c in Sources */, + DA5B0B3D1F36467900B663F0 /* base64.c in Sources */, DA1C7AAC1F1A8F24009A3551 /* mpw-util.c in Sources */, DA1C7AC31F1A8FBA009A3551 /* mpw-cli.c in Sources */, DA7471A31F2B71AE005F3468 /* mpw-marshall-util.c in Sources */, @@ -2539,6 +2548,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DA5B0B3C1F36467900B663F0 /* base64.c in Sources */, DA1C7ACA1F1A8FD8009A3551 /* mpw-types.c in Sources */, DA1C7ACB1F1A8FD8009A3551 /* mpw-util.c in Sources */, DA1C7AD71F1A8FE6009A3551 /* mpw-bench.c in Sources */, @@ -2566,6 +2576,7 @@ DA5180CE19FF307E00A587E9 /* MPAppDelegate_Store.m in Sources */, DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */, DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */, + DA5B0B3A1F36467300B663F0 /* base64.c in Sources */, DA26861F1EBFD7A40001E37E /* MPSiteEntity+CoreDataClass.m in Sources */, DA3B8456190FC89700246EEA /* MPFixable.m in Sources */, DA5E5D001724A667003798D8 /* MPEntities.m in Sources */, @@ -2602,6 +2613,7 @@ DA6774451A474A3B004F356A /* mpw-types.c in Sources */, DA6774461A474A3B004F356A /* mpw-util.c in Sources */, DA1C7AD91F1A8FF4009A3551 /* mpw-tests.c in Sources */, + DA5B0B3B1F36467800B663F0 /* base64.c in Sources */, DA6774431A474A3B004F356A /* mpw-algorithm.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/platform-independent/cli-c/build b/platform-independent/cli-c/build index b5b9d7dd..1b426e0d 100755 --- a/platform-independent/cli-c/build +++ b/platform-independent/cli-c/build @@ -259,12 +259,13 @@ mpw() { (( mpw_color )) && CFLAGS+=( -DMPW_COLOR ) LDFLAGS+=( -l"curses" ) (( mpw_json )) && CFLAGS+=( -DMPW_JSON ) LDFLAGS+=( -l"json-c" ) + cc "${CFLAGS[@]}" "$@" -c core/base64.c -o core/base64.o cc "${CFLAGS[@]}" "$@" -c core/mpw-algorithm.c -o core/mpw-algorithm.o cc "${CFLAGS[@]}" "$@" -c core/mpw-types.c -o core/mpw-types.o cc "${CFLAGS[@]}" "$@" -c core/mpw-util.c -o core/mpw-util.o cc "${CFLAGS[@]}" "$@" -c core/mpw-marshall-util.c -o core/mpw-marshall-util.o cc "${CFLAGS[@]}" "$@" -c core/mpw-marshall.c -o core/mpw-marshall.o - cc "${CFLAGS[@]}" "$@" "core/mpw-algorithm.o" "core/mpw-types.o" "core/mpw-util.o" "core/mpw-marshall-util.o" "core/mpw-marshall.o" \ + cc "${CFLAGS[@]}" "$@" "core/base64.o" "core/mpw-algorithm.o" "core/mpw-types.o" "core/mpw-util.o" "core/mpw-marshall-util.o" "core/mpw-marshall.o" \ "${LDFLAGS[@]}" "cli/mpw-cli.c" -o "mpw" echo "done! Now run ./install or use ./mpw" } @@ -299,10 +300,11 @@ mpw-bench() { -l"crypto" ) + cc "${CFLAGS[@]}" "$@" -c core/base64.c -o core/base64.o cc "${CFLAGS[@]}" "$@" -c core/mpw-algorithm.c -o core/mpw-algorithm.o cc "${CFLAGS[@]}" "$@" -c core/mpw-types.c -o core/mpw-types.o cc "${CFLAGS[@]}" "$@" -c core/mpw-util.c -o core/mpw-util.o - cc "${CFLAGS[@]}" "$@" "core/mpw-algorithm.o" "core/mpw-types.o" "core/mpw-util.o" \ + cc "${CFLAGS[@]}" "$@" "core/base64.o" "core/mpw-algorithm.o" "core/mpw-types.o" "core/mpw-util.o" \ "${LDFLAGS[@]}" "cli/mpw-bench.c" -o "mpw-bench" echo "done! Now use ./mpw-bench" } @@ -331,11 +333,12 @@ mpw-tests() { -l"crypto" -l"xml2" ) + cc "${CFLAGS[@]}" "$@" -c core/base64.c -o core/base64.o cc "${CFLAGS[@]}" "$@" -c core/mpw-algorithm.c -o core/mpw-algorithm.o cc "${CFLAGS[@]}" "$@" -c core/mpw-types.c -o core/mpw-types.o cc "${CFLAGS[@]}" "$@" -c core/mpw-util.c -o core/mpw-util.o cc "${CFLAGS[@]}" "$@" -c cli/mpw-tests-util.c -o cli/mpw-tests-util.o - cc "${CFLAGS[@]}" "$@" "core/mpw-algorithm.o" "core/mpw-types.o" "core/mpw-util.o" \ + cc "${CFLAGS[@]}" "$@" "core/base64.o" "core/mpw-algorithm.o" "core/mpw-types.o" "core/mpw-util.o" \ "${LDFLAGS[@]}" "cli/mpw-tests-util.o" "cli/mpw-tests.c" -o "mpw-tests" echo "done! Now use ./mpw-tests" }