Make Java and C debug output comparable.
This commit is contained in:
parent
a82ce7310d
commit
8006b7096f
@ -1,6 +1,7 @@
|
|||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -171,24 +172,20 @@ int main(int argc, char *const argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trc("siteName: %s\n", siteName);
|
|
||||||
if (siteCounterString)
|
if (siteCounterString)
|
||||||
siteCounter = atoi( siteCounterString );
|
siteCounter = atoi( siteCounterString );
|
||||||
if (siteCounter < 1) {
|
if (siteCounter < 1) {
|
||||||
fprintf(stderr, "Invalid site counter: %d\n", siteCounter);
|
fprintf(stderr, "Invalid site counter: %d\n", siteCounter);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
trc("siteCounter: %d\n", siteCounter);
|
|
||||||
if (siteVariantString)
|
if (siteVariantString)
|
||||||
siteVariant = VariantWithName( siteVariantString );
|
siteVariant = VariantWithName( siteVariantString );
|
||||||
trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString);
|
|
||||||
if (siteVariant == MPElementVariantLogin)
|
if (siteVariant == MPElementVariantLogin)
|
||||||
siteType = MPElementTypeGeneratedName;
|
siteType = MPElementTypeGeneratedName;
|
||||||
if (siteVariant == MPElementVariantAnswer)
|
if (siteVariant == MPElementVariantAnswer)
|
||||||
siteType = MPElementTypeGeneratedPhrase;
|
siteType = MPElementTypeGeneratedPhrase;
|
||||||
if (siteTypeString)
|
if (siteTypeString)
|
||||||
siteType = TypeWithName( siteTypeString );
|
siteType = TypeWithName( siteTypeString );
|
||||||
trc("siteType: %d (%s)\n", siteType, siteTypeString);
|
|
||||||
|
|
||||||
// Read the master password.
|
// Read the master password.
|
||||||
char *mpwConfigPath = homedir(".mpw");
|
char *mpwConfigPath = homedir(".mpw");
|
||||||
@ -218,6 +215,11 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Summarize operation.
|
// Summarize operation.
|
||||||
fprintf(stderr, "%s's password for %s:\n[ %s ]: ", userName, siteName, Identicon( userName, masterPassword ));
|
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.
|
// Calculate the master key salt.
|
||||||
const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword);
|
const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword);
|
||||||
@ -250,17 +252,27 @@ int main(int argc, char *const argv[]) {
|
|||||||
}
|
}
|
||||||
memset(masterKeySalt, 0, masterKeySaltLength);
|
memset(masterKeySalt, 0, masterKeySaltLength);
|
||||||
free(masterKeySalt);
|
free(masterKeySalt);
|
||||||
trc("masterPassword Hex: %s\n", Hex(masterPassword, strlen(masterPassword)));
|
struct timeval endTime;
|
||||||
trc("masterPassword ID: %s\n", IDForBuf(masterPassword, strlen(masterPassword)));
|
if (gettimeofday(&endTime, NULL) != 0) {
|
||||||
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
|
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.
|
// Calculate the site seed.
|
||||||
const char *mpSiteScope = ScopeForVariant(siteVariant);
|
trc("siteName: %s\n", siteName);
|
||||||
trc("site scope: %s, context: %s\n", mpSiteScope, siteContextString == NULL? "<empty>": siteContextString);
|
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? "<empty>": siteContextString);
|
||||||
const uint32_t n_siteNameLength = htonl(strlen(siteName));
|
const uint32_t n_siteNameLength = htonl(strlen(siteName));
|
||||||
const uint32_t n_siteCounter = htonl(siteCounter);
|
const uint32_t n_siteCounter = htonl(siteCounter);
|
||||||
const uint32_t n_siteContextLength = siteContextString == NULL? 0: htonl(strlen(siteContextString));
|
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)
|
if (siteContextString)
|
||||||
sitePasswordInfoLength += sizeof(n_siteContextLength) + strlen(siteContextString);
|
sitePasswordInfoLength += sizeof(n_siteContextLength) + strlen(siteContextString);
|
||||||
char *sitePasswordInfo = (char *)malloc( sitePasswordInfoLength );
|
char *sitePasswordInfo = (char *)malloc( sitePasswordInfoLength );
|
||||||
@ -270,7 +282,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *sPI = sitePasswordInfo;
|
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, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
|
||||||
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
||||||
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
|
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)
|
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
|
||||||
abort();
|
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));
|
trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength));
|
||||||
|
|
||||||
uint8_t sitePasswordSeed[32];
|
uint8_t sitePasswordSeed[32];
|
||||||
@ -291,17 +303,17 @@ int main(int argc, char *const argv[]) {
|
|||||||
free(sitePasswordInfo);
|
free(sitePasswordInfo);
|
||||||
trc("sitePasswordSeed ID: %s\n", IDForBuf(sitePasswordSeed, 32));
|
trc("sitePasswordSeed ID: %s\n", IDForBuf(sitePasswordSeed, 32));
|
||||||
|
|
||||||
// Determine the cipher.
|
// Determine the template.
|
||||||
const char *cipher = CipherForType(siteType, sitePasswordSeed[0]);
|
const char *template = TemplateForType(siteType, sitePasswordSeed[0]);
|
||||||
trc("type %s, cipher: %s\n", siteTypeString, cipher);
|
trc("type %s, template: %s\n", siteTypeString, template);
|
||||||
if (strlen(cipher) > 32)
|
if (strlen(template) > 32)
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
// Encode the password from the seed using the cipher.
|
// Encode the password from the seed using the template.
|
||||||
char *sitePassword = (char *)calloc(strlen(cipher) + 1, sizeof(char));
|
char *sitePassword = (char *)calloc(strlen(template) + 1, sizeof(char));
|
||||||
for (int c = 0; c < strlen(cipher); ++c) {
|
for (int c = 0; c < strlen(template); ++c) {
|
||||||
sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]);
|
sitePassword[c] = CharacterFromClass(template[c], sitePasswordSeed[c + 1]);
|
||||||
trc("class %c, character: %c\n", cipher[c], sitePassword[c]);
|
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));
|
memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed));
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ const MPElementType TypeWithName(const char *typeName) {
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *CipherForType(MPElementType type, uint8_t seedByte) {
|
const char *TemplateForType(MPElementType type, uint8_t seedByte) {
|
||||||
if (!(type & MPElementTypeClassGenerated)) {
|
if (!(type & MPElementTypeClassGenerated)) {
|
||||||
fprintf(stderr, "Not a generated type: %d", type);
|
fprintf(stderr, "Not a generated type: %d", type);
|
||||||
abort();
|
abort();
|
||||||
@ -57,20 +57,20 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeGeneratedMaximum: {
|
case MPElementTypeGeneratedMaximum: {
|
||||||
const char *ciphers[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
const char *templates[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
||||||
return ciphers[seedByte % 2];
|
return templates[seedByte % 2];
|
||||||
}
|
}
|
||||||
case MPElementTypeGeneratedLong: {
|
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" };
|
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 ciphers[seedByte % 21];
|
return templates[seedByte % 21];
|
||||||
}
|
}
|
||||||
case MPElementTypeGeneratedMedium: {
|
case MPElementTypeGeneratedMedium: {
|
||||||
const char *ciphers[] = { "CvcnoCvc", "CvcCvcno" };
|
const char *templates[] = { "CvcnoCvc", "CvcCvcno" };
|
||||||
return ciphers[seedByte % 2];
|
return templates[seedByte % 2];
|
||||||
}
|
}
|
||||||
case MPElementTypeGeneratedBasic: {
|
case MPElementTypeGeneratedBasic: {
|
||||||
const char *ciphers[] = { "aaanaaan", "aannaaan", "aaannaaa" };
|
const char *templates[] = { "aaanaaan", "aannaaan", "aaannaaa" };
|
||||||
return ciphers[seedByte % 3];
|
return templates[seedByte % 3];
|
||||||
}
|
}
|
||||||
case MPElementTypeGeneratedShort: {
|
case MPElementTypeGeneratedShort: {
|
||||||
return "Cvcn";
|
return "Cvcn";
|
||||||
@ -82,8 +82,8 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) {
|
|||||||
return "cvccvcvcv";
|
return "cvccvcvcv";
|
||||||
}
|
}
|
||||||
case MPElementTypeGeneratedPhrase: {
|
case MPElementTypeGeneratedPhrase: {
|
||||||
const char *ciphers[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
const char *templates[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
||||||
return ciphers[seedByte % 3];
|
return templates[seedByte % 3];
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "Unknown generated type: %d", type);
|
fprintf(stderr, "Unknown generated type: %d", type);
|
||||||
|
@ -52,7 +52,7 @@ typedef enum {
|
|||||||
const MPElementVariant VariantWithName(const char *variantName);
|
const MPElementVariant VariantWithName(const char *variantName);
|
||||||
const char *ScopeForVariant(MPElementVariant variant);
|
const char *ScopeForVariant(MPElementVariant variant);
|
||||||
const MPElementType TypeWithName(const char *typeName);
|
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 CharacterFromClass(char characterClass, uint8_t seedByte);
|
||||||
const char *IDForBuf(const void *buf, size_t length);
|
const char *IDForBuf(const void *buf, size_t length);
|
||||||
const char *Hex(const void *buf, size_t length);
|
const char *Hex(const void *buf, size_t length);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.lyndir.masterpassword;
|
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.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
||||||
@ -14,15 +16,21 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class MPTemplate extends MetaObject {
|
public class MPTemplate extends MetaObject {
|
||||||
|
|
||||||
|
private final String templateString;
|
||||||
private final List<MPTemplateCharacterClass> template;
|
private final List<MPTemplateCharacterClass> template;
|
||||||
|
|
||||||
MPTemplate(final String template) {
|
MPTemplate(final String templateString) {
|
||||||
|
|
||||||
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
|
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
|
||||||
for (int i = 0; i < template.length(); ++i)
|
for (int i = 0; i < templateString.length(); ++i)
|
||||||
builder.add( MPTemplateCharacterClass.forIdentifier( template.charAt( 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) {
|
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
|
||||||
@ -34,4 +42,9 @@ public class MPTemplate extends MetaObject {
|
|||||||
|
|
||||||
return template.size();
|
return template.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return strf( "{MPTemplate: %s}", templateString );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import java.nio.ByteOrder;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,29 +33,30 @@ public class MasterKey {
|
|||||||
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
||||||
|
|
||||||
private final String userName;
|
private final String userName;
|
||||||
private final byte[] key;
|
private final byte[] masterKey;
|
||||||
|
|
||||||
private boolean valid;
|
private boolean valid;
|
||||||
|
|
||||||
public MasterKey(final String userName, final String masterPassword) {
|
public MasterKey(final String userName, final String masterPassword) {
|
||||||
|
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
|
logger.trc( "userName: %s", userName );
|
||||||
|
logger.trc( "masterPassword: %s", masterPassword );
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
byte[] userNameBytes = userName.getBytes( MP_charset );
|
byte[] userNameBytes = userName.getBytes( MP_charset );
|
||||||
byte[] userNameLengthBytes = ByteBuffer.allocate( MP_intLen / Byte.SIZE )
|
byte[] userNameLengthBytes = bytesForInt( userNameBytes.length );
|
||||||
.order( MP_byteOrder )
|
|
||||||
.putInt( userNameBytes.length )
|
String mpKeyScope = MPElementVariant.Password.getScope();
|
||||||
.array();
|
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes );
|
||||||
byte[] salt = Bytes.concat( MPElementVariant.Password.getScope().getBytes( MP_charset ), //
|
logger.trc( "key scope: %s", mpKeyScope );
|
||||||
userNameLengthBytes, userNameBytes );
|
logger.trc( "masterKeySalt ID: %s", idForBytes( masterKeySalt ) );
|
||||||
|
|
||||||
try {
|
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;
|
valid = true;
|
||||||
|
|
||||||
logger.trc( "User: %s, master password derives to key ID: %s (took %.2fs)", //
|
logger.trc( "masterKey ID: %s (derived in %.2fs)", idForBytes( masterKey ), (System.currentTimeMillis() - start) / 1000D );
|
||||||
userName, getKeyID(), (double) (System.currentTimeMillis() - start) / 1000 );
|
|
||||||
}
|
}
|
||||||
catch (GeneralSecurityException e) {
|
catch (GeneralSecurityException e) {
|
||||||
throw logger.bug( e );
|
throw logger.bug( e );
|
||||||
@ -69,50 +71,61 @@ public class MasterKey {
|
|||||||
public String getKeyID() {
|
public String getKeyID() {
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
Preconditions.checkState( valid );
|
||||||
return CodeUtils.encodeHex( MP_hash.of( key ) );
|
return idForBytes( masterKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getSubkey(final int subkeyLength) {
|
private byte[] getSubkey(final int subkeyLength) {
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
Preconditions.checkState( valid );
|
||||||
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
|
byte[] subkey = new byte[Math.min( subkeyLength, masterKey.length )];
|
||||||
System.arraycopy( key, 0, subkey, 0, subkey.length );
|
System.arraycopy( masterKey, 0, subkey, 0, subkey.length );
|
||||||
|
|
||||||
return subkey;
|
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.checkState( valid );
|
||||||
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
|
Preconditions.checkArgument( siteType.getTypeClass() == MPElementTypeClass.Generated );
|
||||||
Preconditions.checkArgument( !name.isEmpty() );
|
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||||
|
|
||||||
if (counter == 0)
|
logger.trc( "siteName: %s", siteName );
|
||||||
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
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 );
|
if (siteCounter == 0)
|
||||||
byte[] nameLengthBytes = ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( nameBytes.length ).array();
|
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
||||||
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 ) );
|
|
||||||
|
|
||||||
Preconditions.checkState( seed.length > 0 );
|
String siteScope = siteVariant.getScope();
|
||||||
int templateIndex = seed[0] & 0xFF; // Mask the integer's sign.
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
MPTemplate template = type.getTemplateAtRollingIndex( templateIndex );
|
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
|
||||||
logger.trc( "type: %s, template: %s", type, template );
|
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? "<empty>": 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() );
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
for (int i = 0; i < template.length(); ++i) {
|
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 );
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
logger.trc( "class: %s, index: %d, byte: 0x%02X, chosen password character: %s", characterClass, characterIndex, seed[i + 1],
|
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||||
passwordCharacter );
|
sitePasswordSeed[i + 1], passwordCharacter );
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
password.append( passwordCharacter );
|
||||||
}
|
}
|
||||||
@ -123,6 +136,14 @@ public class MasterKey {
|
|||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
|
|
||||||
valid = false;
|
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 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public class MasterKeyTest {
|
|||||||
"Jejr5[RepuSosp" );
|
"Jejr5[RepuSosp" );
|
||||||
|
|
||||||
assertEquals( masterKey.encode( "\u26C4", MPElementType.GeneratedMaximum, 1, MPElementVariant.Password, null ), //
|
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 ), //
|
assertEquals( masterKey.encode( "\u26C4", MPElementType.GeneratedLong, 1, MPElementVariant.Password, null ), //
|
||||||
"LiheCuwhSerz6)" );
|
"LiheCuwhSerz6)" );
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.lhunath</groupId>
|
<groupId>com.lyndir.lhunath</groupId>
|
||||||
<artifactId>lyndir</artifactId>
|
<artifactId>lyndir</artifactId>
|
||||||
<version>1.20</version>
|
<version>1.18</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password</name>
|
<name>Master Password</name>
|
||||||
|
Loading…
Reference in New Issue
Block a user