From 8006b7096fde6f626db5d659d22c2edb5015582a Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Wed, 3 Dec 2014 23:36:18 -0500 Subject: [PATCH] Make Java and C debug output comparable. --- MasterPassword/C/mpw.c | 54 +++++++---- MasterPassword/C/types.c | 22 ++--- MasterPassword/C/types.h | 2 +- .../com/lyndir/masterpassword/MPTemplate.java | 21 +++- .../com/lyndir/masterpassword/MasterKey.java | 95 +++++++++++-------- .../lyndir/masterpassword/MasterKeyTest.java | 2 +- MasterPassword/Java/pom.xml | 2 +- 7 files changed, 122 insertions(+), 76 deletions(-) diff --git a/MasterPassword/C/mpw.c b/MasterPassword/C/mpw.c index a4a59734..3eff6f1e 100644 --- a/MasterPassword/C/mpw.c +++ b/MasterPassword/C/mpw.c @@ -1,6 +1,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -171,24 +172,20 @@ int main(int argc, char *const argv[]) { return 1; } } - trc("siteName: %s\n", siteName); if (siteCounterString) siteCounter = atoi( siteCounterString ); if (siteCounter < 1) { fprintf(stderr, "Invalid site counter: %d\n", siteCounter); return 1; } - trc("siteCounter: %d\n", siteCounter); if (siteVariantString) siteVariant = VariantWithName( siteVariantString ); - trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString); if (siteVariant == MPElementVariantLogin) siteType = MPElementTypeGeneratedName; if (siteVariant == MPElementVariantAnswer) siteType = MPElementTypeGeneratedPhrase; if (siteTypeString) siteType = TypeWithName( siteTypeString ); - trc("siteType: %d (%s)\n", siteType, siteTypeString); // Read the master password. char *mpwConfigPath = homedir(".mpw"); @@ -218,6 +215,11 @@ int main(int argc, char *const argv[]) { // Summarize operation. fprintf(stderr, "%s's password for %s:\n[ %s ]: ", userName, siteName, Identicon( userName, masterPassword )); + struct timeval startTime; + if (gettimeofday(&startTime, NULL) != 0) { + fprintf(stderr, "Could not get time: %d\n", errno); + return 1; + } // Calculate the master key salt. const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword); @@ -250,17 +252,27 @@ int main(int argc, char *const argv[]) { } memset(masterKeySalt, 0, masterKeySaltLength); free(masterKeySalt); - trc("masterPassword Hex: %s\n", Hex(masterPassword, strlen(masterPassword))); - trc("masterPassword ID: %s\n", IDForBuf(masterPassword, strlen(masterPassword))); - trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen)); + 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; + trc("masterKey ID: %s (derived in %.2fs)\n", IDForBuf(masterKey, MP_dkLen), elapsed); // Calculate the site seed. - const char *mpSiteScope = ScopeForVariant(siteVariant); - trc("site scope: %s, context: %s\n", mpSiteScope, siteContextString == NULL? "": siteContextString); + trc("siteName: %s\n", siteName); + trc("siteCounter: %d\n", siteCounter); + trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString); + trc("siteType: %d (%s)\n", siteType, siteTypeString); + const char *siteScope = ScopeForVariant(siteVariant); + trc("site scope: %s, context: %s\n", siteScope, siteContextString == NULL? "": siteContextString); const uint32_t n_siteNameLength = htonl(strlen(siteName)); const uint32_t n_siteCounter = htonl(siteCounter); const uint32_t n_siteContextLength = siteContextString == NULL? 0: htonl(strlen(siteContextString)); - size_t sitePasswordInfoLength = strlen(mpSiteScope) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter); + size_t sitePasswordInfoLength = strlen(siteScope) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter); if (siteContextString) sitePasswordInfoLength += sizeof(n_siteContextLength) + strlen(siteContextString); char *sitePasswordInfo = (char *)malloc( sitePasswordInfoLength ); @@ -270,7 +282,7 @@ int main(int argc, char *const argv[]) { } char *sPI = sitePasswordInfo; - memcpy(sPI, mpSiteScope, strlen(mpSiteScope)); sPI += strlen(mpSiteScope); + memcpy(sPI, siteScope, strlen(siteScope)); sPI += strlen(siteScope); 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); @@ -280,7 +292,7 @@ int main(int argc, char *const argv[]) { } if (sPI - sitePasswordInfo != sitePasswordInfoLength) abort(); - trc("seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n", mpSiteScope, Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)), Hex(&n_siteContextLength, sizeof(n_siteContextLength)), siteContextString); + trc("seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n", siteScope, Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)), Hex(&n_siteContextLength, sizeof(n_siteContextLength)), siteContextString); trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength)); uint8_t sitePasswordSeed[32]; @@ -291,17 +303,17 @@ int main(int argc, char *const argv[]) { free(sitePasswordInfo); trc("sitePasswordSeed ID: %s\n", IDForBuf(sitePasswordSeed, 32)); - // Determine the cipher. - const char *cipher = CipherForType(siteType, sitePasswordSeed[0]); - trc("type %s, cipher: %s\n", siteTypeString, cipher); - if (strlen(cipher) > 32) + // Determine the template. + const char *template = TemplateForType(siteType, sitePasswordSeed[0]); + trc("type %s, template: %s\n", siteTypeString, template); + if (strlen(template) > 32) abort(); - // Encode the password from the seed using the cipher. - char *sitePassword = (char *)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]); + // Encode the password from the seed using the template. + char *sitePassword = (char *)calloc(strlen(template) + 1, sizeof(char)); + for (int c = 0; c < strlen(template); ++c) { + sitePassword[c] = CharacterFromClass(template[c], sitePasswordSeed[c + 1]); + trc("class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1], sitePassword[c]); } memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed)); diff --git a/MasterPassword/C/types.c b/MasterPassword/C/types.c index 0dccb204..7e33914c 100644 --- a/MasterPassword/C/types.c +++ b/MasterPassword/C/types.c @@ -49,7 +49,7 @@ const MPElementType TypeWithName(const char *typeName) { abort(); } -const char *CipherForType(MPElementType type, uint8_t seedByte) { +const char *TemplateForType(MPElementType type, uint8_t seedByte) { if (!(type & MPElementTypeClassGenerated)) { fprintf(stderr, "Not a generated type: %d", type); abort(); @@ -57,20 +57,20 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) { switch (type) { case MPElementTypeGeneratedMaximum: { - const char *ciphers[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" }; - return ciphers[seedByte % 2]; + const char *templates[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" }; + return templates[seedByte % 2]; } case MPElementTypeGeneratedLong: { - const char *ciphers[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" }; - return ciphers[seedByte % 21]; + const char *templates[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" }; + return templates[seedByte % 21]; } case MPElementTypeGeneratedMedium: { - const char *ciphers[] = { "CvcnoCvc", "CvcCvcno" }; - return ciphers[seedByte % 2]; + const char *templates[] = { "CvcnoCvc", "CvcCvcno" }; + return templates[seedByte % 2]; } case MPElementTypeGeneratedBasic: { - const char *ciphers[] = { "aaanaaan", "aannaaan", "aaannaaa" }; - return ciphers[seedByte % 3]; + const char *templates[] = { "aaanaaan", "aannaaan", "aaannaaa" }; + return templates[seedByte % 3]; } case MPElementTypeGeneratedShort: { return "Cvcn"; @@ -82,8 +82,8 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) { return "cvccvcvcv"; } case MPElementTypeGeneratedPhrase: { - const char *ciphers[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" }; - return ciphers[seedByte % 3]; + const char *templates[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" }; + return templates[seedByte % 3]; } default: { fprintf(stderr, "Unknown generated type: %d", type); diff --git a/MasterPassword/C/types.h b/MasterPassword/C/types.h index c3ef0b61..5870f753 100644 --- a/MasterPassword/C/types.h +++ b/MasterPassword/C/types.h @@ -52,7 +52,7 @@ typedef enum { const MPElementVariant VariantWithName(const char *variantName); const char *ScopeForVariant(MPElementVariant variant); const MPElementType TypeWithName(const char *typeName); -const char *CipherForType(MPElementType type, uint8_t seedByte); +const char *TemplateForType(MPElementType type, uint8_t seedByte); const char CharacterFromClass(char characterClass, uint8_t seedByte); const char *IDForBuf(const void *buf, size_t length); const char *Hex(const void *buf, size_t length); diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java index 2ada4543..6a7c3d8d 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java @@ -1,5 +1,7 @@ package com.lyndir.masterpassword; +import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.lyndir.lhunath.opal.system.util.MetaObject; @@ -14,15 +16,21 @@ import java.util.Map; */ public class MPTemplate extends MetaObject { + private final String templateString; private final List template; - MPTemplate(final String template) { + MPTemplate(final String templateString) { ImmutableList.Builder builder = ImmutableList.builder(); - for (int i = 0; i < template.length(); ++i) - builder.add( MPTemplateCharacterClass.forIdentifier( template.charAt( i ) ) ); + for (int i = 0; i < templateString.length(); ++i) + builder.add( MPTemplateCharacterClass.forIdentifier( templateString.charAt( i ) ) ); - this.template = builder.build(); + this.templateString = templateString; + template = builder.build(); + } + + public String getTemplateString() { + return templateString; } public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) { @@ -34,4 +42,9 @@ public class MPTemplate extends MetaObject { return template.size(); } + + @Override + public String toString() { + return strf( "{MPTemplate: %s}", templateString ); + } } diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java index 2dc259f2..55fc522e 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java @@ -12,6 +12,7 @@ import java.nio.ByteOrder; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.util.Arrays; +import javax.annotation.Nullable; /** @@ -32,29 +33,30 @@ public class MasterKey { private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256; private final String userName; - private final byte[] key; + private final byte[] masterKey; private boolean valid; public MasterKey(final String userName, final String masterPassword) { this.userName = userName; + logger.trc( "userName: %s", userName ); + logger.trc( "masterPassword: %s", masterPassword ); long start = System.currentTimeMillis(); byte[] userNameBytes = userName.getBytes( MP_charset ); - byte[] userNameLengthBytes = ByteBuffer.allocate( MP_intLen / Byte.SIZE ) - .order( MP_byteOrder ) - .putInt( userNameBytes.length ) - .array(); - byte[] salt = Bytes.concat( MPElementVariant.Password.getScope().getBytes( MP_charset ), // - userNameLengthBytes, userNameBytes ); + byte[] userNameLengthBytes = bytesForInt( userNameBytes.length ); + + String mpKeyScope = MPElementVariant.Password.getScope(); + byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes ); + logger.trc( "key scope: %s", mpKeyScope ); + logger.trc( "masterKeySalt ID: %s", idForBytes( masterKeySalt ) ); try { - key = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen ); + masterKey = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen ); valid = true; - logger.trc( "User: %s, master password derives to key ID: %s (took %.2fs)", // - userName, getKeyID(), (double) (System.currentTimeMillis() - start) / 1000 ); + logger.trc( "masterKey ID: %s (derived in %.2fs)", idForBytes( masterKey ), (System.currentTimeMillis() - start) / 1000D ); } catch (GeneralSecurityException e) { throw logger.bug( e ); @@ -69,50 +71,61 @@ public class MasterKey { public String getKeyID() { Preconditions.checkState( valid ); - return CodeUtils.encodeHex( MP_hash.of( key ) ); + return idForBytes( masterKey ); } private byte[] getSubkey(final int subkeyLength) { Preconditions.checkState( valid ); - byte[] subkey = new byte[Math.min( subkeyLength, key.length )]; - System.arraycopy( key, 0, subkey, 0, subkey.length ); + byte[] subkey = new byte[Math.min( subkeyLength, masterKey.length )]; + System.arraycopy( masterKey, 0, subkey, 0, subkey.length ); return subkey; } - public String encode(final String name, final MPElementType type, int counter, final MPElementVariant variant, final String context) { - + public String encode(final String siteName, final MPElementType siteType, int siteCounter, final MPElementVariant siteVariant, + @Nullable final String siteContext) { Preconditions.checkState( valid ); - Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated ); - Preconditions.checkArgument( !name.isEmpty() ); + Preconditions.checkArgument( siteType.getTypeClass() == MPElementTypeClass.Generated ); + Preconditions.checkArgument( !siteName.isEmpty() ); - if (counter == 0) - counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300; + logger.trc( "siteName: %s", siteName ); + logger.trc( "siteCounter: %d", siteCounter ); + logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant ); + logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType ); - byte[] nameBytes = name.getBytes( MP_charset ); - byte[] nameLengthBytes = ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( nameBytes.length ).array(); - byte[] counterBytes = ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( counter ).array(); - logger.trc( "seed from: hmac-sha256(%s, %s | %s | %s | %s)", variant.getScope(), CryptUtils.encodeBase64( key ), - CodeUtils.encodeHex( nameLengthBytes ), name, CodeUtils.encodeHex( counterBytes ) ); - byte[] seed = MP_mac.of( key, Bytes.concat( variant.getScope().getBytes( MP_charset ), // - nameLengthBytes, // - nameBytes, // - counterBytes ) ); - logger.trc( "seed is: %s", CryptUtils.encodeBase64( seed ) ); + if (siteCounter == 0) + siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300; - Preconditions.checkState( seed.length > 0 ); - int templateIndex = seed[0] & 0xFF; // Mask the integer's sign. - MPTemplate template = type.getTemplateAtRollingIndex( templateIndex ); - logger.trc( "type: %s, template: %s", type, template ); + String siteScope = siteVariant.getScope(); + byte[] siteNameBytes = siteName.getBytes( MP_charset ); + byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length ); + byte[] siteCounterBytes = bytesForInt( siteCounter ); + byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset ); + byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length ); + logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext ); + logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, + CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), + CodeUtils.encodeHex( siteContextLengthBytes ), siteContext == null? "(null)": siteContext ); + + byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); + logger.trc( "sitePasswordInfo ID: %s", idForBytes( sitePasswordInfo ) ); + + byte[] sitePasswordSeed = MP_mac.of( masterKey, sitePasswordInfo ); + logger.trc( "sitePasswordSeed ID: %s", idForBytes( sitePasswordSeed ) ); + + Preconditions.checkState( sitePasswordSeed.length > 0 ); + int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign. + MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); + logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); StringBuilder password = new StringBuilder( template.length() ); for (int i = 0; i < template.length(); ++i) { - int characterIndex = seed[i + 1] & 0xFF; // Mask the integer's sign. + int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign. MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); - logger.trc( "class: %s, index: %d, byte: 0x%02X, chosen password character: %s", characterClass, characterIndex, seed[i + 1], - passwordCharacter ); + logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, + sitePasswordSeed[i + 1], passwordCharacter ); password.append( passwordCharacter ); } @@ -123,6 +136,14 @@ public class MasterKey { public void invalidate() { valid = false; - Arrays.fill( key, (byte) 0 ); + Arrays.fill( masterKey, (byte) 0 ); + } + + private static byte[] bytesForInt(final int integer) { + return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array(); + } + + private static String idForBytes(final byte[] bytes) { + return CodeUtils.encodeHex( MP_hash.of( bytes ) ); } } diff --git a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java index 5039e2ac..7358e34b 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java @@ -21,7 +21,7 @@ public class MasterKeyTest { "Jejr5[RepuSosp" ); assertEquals( masterKey.encode( "\u26C4", MPElementType.GeneratedMaximum, 1, MPElementVariant.Password, null ), // - "b9]1#2g*suJ^E@OJXZTQ" ); + "bp7rJKc7kaXc4sxOwG0*" ); assertEquals( masterKey.encode( "\u26C4", MPElementType.GeneratedLong, 1, MPElementVariant.Password, null ), // "LiheCuwhSerz6)" ); diff --git a/MasterPassword/Java/pom.xml b/MasterPassword/Java/pom.xml index 5ce57342..e249ba6a 100644 --- a/MasterPassword/Java/pom.xml +++ b/MasterPassword/Java/pom.xml @@ -7,7 +7,7 @@ com.lyndir.lhunath lyndir - 1.20 + 1.18 Master Password