diff --git a/MasterPassword/C/build b/MasterPassword/C/build index 632dc318..fe914e5d 100755 --- a/MasterPassword/C/build +++ b/MasterPassword/C/build @@ -13,10 +13,15 @@ cd "${BASH_SOURCE%/*}" set -e +# optional features. options=( - # optional features. -DDEBUG # Turn on debugging verbosity. ) +# available targets. +targets=( + mpw # C CLI version of Master Password. + mpw-bench # C CLI Master Password benchmark utility. +) ### DEPENDENCIES @@ -67,8 +72,67 @@ if ! [[ -e lib/scrypt/scrypt-scryptenc.o ]]; then popd fi -echo -echo "Building mpw..." + +### MPW +mpw() { + CFLAGS=( + # include paths + -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" + ) + LDFLAGS=( + # library paths + -L"." -L"lib/scrypt" + # link libraries + -l"crypto" + # scrypt + "lib/scrypt/scrypt-crypto_aesctr.o" + "lib/scrypt/scrypt-sha256.o" + "lib/scrypt/scrypt-crypto_scrypt-nosse.o" + "lib/scrypt/scrypt-memlimit.o" + "lib/scrypt/scrypt-scryptenc_cpuperf.o" + "lib/scrypt/scrypt-scryptenc.o" + ) + + cc "${CFLAGS[@]}" "${options[@]}" -c types.c -o types.o "$@" + cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "${options[@]}" "types.o" mpw.c -o mpw "$@" + echo "done! Now run ./install or use ./mpw" +} + + +### MPW-BENCH +mpw-bench() { + CFLAGS=( + # include paths + -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" + -I"lib/bcrypt" + ) + LDFLAGS=( + # library paths + -L"." -L"lib/scrypt" + -L"lib/bcrypt" + # libraries + -l"crypto" + # scrypt + "lib/scrypt/scrypt-crypto_aesctr.o" + "lib/scrypt/scrypt-sha256.o" + "lib/scrypt/scrypt-crypto_scrypt-nosse.o" + "lib/scrypt/scrypt-memlimit.o" + "lib/scrypt/scrypt-scryptenc_cpuperf.o" + "lib/scrypt/scrypt-scryptenc.o" + # bcrypt + "lib/bcrypt/crypt_blowfish.o" + "lib/bcrypt/crypt_gensalt.o" + "lib/bcrypt/wrapper.o" + "lib/bcrypt/x86.o" + ) + + cc "${CFLAGS[@]}" "${options[@]}" -c types.c -o types.o "$@" + cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "${options[@]}" "types.o" mpw-bench.c -o mpw-bench "$@" + echo "done! Now use ./mpw-bench" +} + + +### TARGETS cc() { if hash llvm-gcc 2>/dev/null; then @@ -78,26 +142,8 @@ cc() { fi } - -### MPW - -CFLAGS=( - # include paths. - -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" -) -LDFLAGS=( - # library paths. - -L"." -L"lib/scrypt" - # link libraries. - "lib/scrypt/scrypt-crypto_aesctr.o" - "lib/scrypt/scrypt-sha256.o" - "lib/scrypt/scrypt-crypto_scrypt-nosse.o" - "lib/scrypt/scrypt-memlimit.o" - "lib/scrypt/scrypt-scryptenc_cpuperf.o" - "lib/scrypt/scrypt-scryptenc.o" - -l"crypto" -) - -cc "${CFLAGS[@]}" "${options[@]}" -c types.c -o types.o "$@" -cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "${options[@]}" "types.o" mpw.c -o mpw "$@" -echo "done! Now run ./install or use ./mpw" +for target in "${targets[@]}"; do + echo + echo "Building target: $target..." + "$target" "$@" +done diff --git a/MasterPassword/C/mpw-bench.c b/MasterPassword/C/mpw-bench.c new file mode 100644 index 00000000..112f3b7f --- /dev/null +++ b/MasterPassword/C/mpw-bench.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "types.h" + +#define MP_N 32768 +#define MP_r 8 +#define MP_p 2 +#define MP_dkLen 64 +#define MP_hash PearlHashSHA256 + + +int main(int argc, char *const argv[]) { + + char *userName = "Robert Lee Mitchel"; + char *masterPassword = "banana colored duckling"; + char *siteName = "masterpasswordapp.com"; + uint32_t siteCounter = 1; + MPElementType siteType = MPElementTypeGeneratedLong; + + // Start MP + struct timeval startTime; + if (gettimeofday(&startTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } + + int iterations = 100; + for (int i = 0; i < iterations; ++i) { + // Calculate the master key salt. + char *mpNameSpace = "com.lyndir.masterpassword"; + const uint32_t n_userNameLength = htonl(strlen(userName)); + const size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_userNameLength) + strlen(userName); + char *masterKeySalt = malloc( masterKeySaltLength ); + if (!masterKeySalt) { + fprintf(stderr, "Could not allocate master key salt: %d\n", errno); + return 1; + } + + char *mKS = masterKeySalt; + memcpy(mKS, mpNameSpace, strlen(mpNameSpace)); mKS += strlen(mpNameSpace); + memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength); + memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName); + if (mKS - masterKeySalt != masterKeySaltLength) + abort(); + trc("masterKeySalt ID: %s\n", IDForBuf(masterKeySalt, masterKeySaltLength)); + + // Calculate the master key. + uint8_t *masterKey = malloc( MP_dkLen ); + if (!masterKey) { + fprintf(stderr, "Could not allocate master key: %d\n", errno); + return 1; + } + if (crypto_scrypt( (const uint8_t *)masterPassword, strlen(masterPassword), (const uint8_t *)masterKeySalt, masterKeySaltLength, MP_N, MP_r, MP_p, masterKey, MP_dkLen ) < 0) { + fprintf(stderr, "Could not generate master key: %d\n", errno); + return 1; + } + memset(masterKeySalt, 0, masterKeySaltLength); + free(masterKeySalt); + + // Calculate the site seed. + const uint32_t n_siteNameLength = htonl(strlen(siteName)); + const uint32_t n_siteCounter = htonl(siteCounter); + const size_t sitePasswordInfoLength = strlen(mpNameSpace) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter); + char *sitePasswordInfo = malloc( sitePasswordInfoLength ); + if (!sitePasswordInfo) { + fprintf(stderr, "Could not allocate site seed: %d\n", errno); + return 1; + } + + char *sPI = sitePasswordInfo; + memcpy(sPI, mpNameSpace, strlen(mpNameSpace)); sPI += strlen(mpNameSpace); + memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength); + memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName); + memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter); + if (sPI - sitePasswordInfo != sitePasswordInfoLength) + abort(); + + uint8_t sitePasswordSeed[32]; + HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed); + memset(masterKey, 0, MP_dkLen); + memset(sitePasswordInfo, 0, sitePasswordInfoLength); + free(masterKey); + free(sitePasswordInfo); + + // Determine the cipher. + const char *cipher = CipherForType(siteType, sitePasswordSeed[0]); + trc("type %d, cipher: %s\n", siteType, cipher); + if (strlen(cipher) > 32) + abort(); + + // Encode the password from the seed using the cipher. + char *sitePassword = calloc(strlen(cipher) + 1, sizeof(char)); + for (int c = 0; c < strlen(cipher); ++c) { + sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]); + trc("class %c, character: %c\n", cipher[c], sitePassword[c]); + } + memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed)); + + fprintf( stderr, "\riteration %d / %d..", i, iterations ); + } + + // Output timing results. + struct timeval endTime; + if (gettimeofday(&endTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } + long long secs = (endTime.tv_sec - startTime.tv_sec); + long long usecs = (endTime.tv_usec - startTime.tv_usec); + double elapsed = secs + usecs / 1000000.0; + fprintf( stdout, "Master Password: %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, iterations / elapsed ); + + // Start SHA-256 + if (gettimeofday(&startTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } + + iterations = 50000000; + uint8_t hash[32]; + for (int i = 0; i < iterations; ++i) + SHA256_Buf(masterPassword, strlen(masterPassword), hash); + + // Output timing results. + if (gettimeofday(&endTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } + secs = (endTime.tv_sec - startTime.tv_sec); + usecs = (endTime.tv_usec - startTime.tv_usec); + elapsed = secs + usecs / 1000000.0; + fprintf( stdout, "SHA-256: %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, iterations / elapsed ); + + // Start BCrypt + if (gettimeofday(&startTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } + + int bcrypt_cost = 9; + iterations = 600; + for (int i = 0; i < iterations; ++i) + crypt(masterPassword, crypt_gensalt("$2b$", bcrypt_cost, userName, strlen(userName))); + + // Output timing results. + if (gettimeofday(&endTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } + secs = (endTime.tv_sec - startTime.tv_sec); + usecs = (endTime.tv_usec - startTime.tv_usec); + elapsed = secs + usecs / 1000000.0; + fprintf( stdout, "BCrypt (cost %d): %d iterations in %llds %lldµs -> %.2f/s\n", bcrypt_cost, iterations, secs, usecs, iterations / elapsed ); + + return 0; +}