2
0

Compare commits

..

10 Commits
1.2.1 ... 1.2

Author SHA1 Message Date
Maarten Billemont
8b997528c9 Improved logging of password generation.
[IMPROVED]  Trace-level logging during password generation.
2012-07-05 13:47:50 +02:00
Maarten Billemont
04a6c8e68d A Java proof-of-concept CLI for Master Password. 2012-07-05 13:42:32 +02:00
Maarten Billemont
0e8e4dc06d Add site tip: Promo code for feedback. 2012-07-05 13:40:31 +02:00
Maarten Billemont
8b91c2a0b8 Press data and site experimentation with nivo-slider.
[ADDED]     Press resources: Releases, media, etc.
2012-07-05 00:02:45 +02:00
Maarten Billemont
e967affddb Added unit tests for playing around with the logic. 2012-07-04 23:57:29 +02:00
Maarten Billemont
dea7434bd4 prMac Press Release
[ADDED]     prMac press release for 1.2.1, REV1b
2012-07-03 11:02:58 +02:00
Maarten Billemont
1da63e450d High-resolution iTunesArtwork. 2012-07-03 11:00:25 +02:00
Maarten Billemont
029d240999 Fixed a crash when tapping a non-type row.
[FIXED]     MP-23 Crash when tapping non-type row
2012-07-03 10:04:39 +02:00
Maarten Billemont
110f7069e1 Lock when backgrounded.
[FIXED]     When backgrounding the app and re-showing it, don't reveal
            the UI when logout is enabled.
2012-06-28 00:02:00 +02:00
Maarten Billemont
d77cde1929 Moved MPSearchDelegate to code.
[FIXED]     Another potential crash because of an over-released
            top-level object in storyboard.
2012-06-27 23:44:37 +02:00
101 changed files with 2410 additions and 106 deletions

5
.gitignore vendored
View File

@@ -20,6 +20,11 @@
!/*.xcodeproj/project.xcworkspace/*
/*.xcodeproj/project.xcworkspace/xcuserdata
# Media
Press/Background.png
Press/Front-Page.png
Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
# IPA
/sendipa/*
!/sendipa/sendipa.conf

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.1.2</string>
<string>1.1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleSupportedPlatforms</key>
@@ -25,7 +25,7 @@
<string>iPhoneOS</string>
</array>
<key>CFBundleVersion</key>
<string>0101.02.00</string>
<string>0101.03.00</string>
<key>CrashlyticsAPIKey</key>
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
<key>DTCompiler</key>

2
External/Pearl vendored

View File

@@ -20,6 +20,11 @@
DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; };
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
DA3EF17B15A47744003ABF4E /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */; };
DA3EF17C15A47744003ABF4E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; };
DA3EF17D15A47744003ABF4E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA3EF18315A47744003ABF4E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA3EF18115A47744003ABF4E /* InfoPlist.strings */; };
DA3EF18615A47744003ABF4E /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3EF18515A47744003ABF4E /* Tests.m */; };
DA40C2611586099D0079CE6E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA40C2601586099D0079CE6E /* MPUserEntity.m */; };
DA40C2641586099E0079CE6E /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA40C2631586099E0079CE6E /* MPElementEntity.m */; };
DA40C2671586099E0079CE6E /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA40C2661586099E0079CE6E /* MPElementGeneratedEntity.m */; };
@@ -828,6 +833,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DA3EF19D15A47AEB003ABF4E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
proxyType = 1;
remoteGlobalIDString = DA5BFA43147E415C00F98B1E;
remoteInfo = MasterPassword;
};
DA4DA1D71564470200F6F596 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA79A9BD1557DDC700BAA07A /* scrypt.xcodeproj */;
@@ -893,6 +905,13 @@
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; };
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
DA3EF17915A47744003ABF4E /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
DA3EF18015A47744003ABF4E /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; };
DA3EF18215A47744003ABF4E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
DA3EF18415A47744003ABF4E /* Tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tests.h; sourceTree = "<group>"; };
DA3EF18515A47744003ABF4E /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; };
DA3EF18715A47744003ABF4E /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = "<group>"; };
DA40C25F1586099D0079CE6E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
DA40C2601586099D0079CE6E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
DA40C2621586099E0079CE6E /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
@@ -1787,6 +1806,16 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DA3EF17515A47744003ABF4E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DA3EF17B15A47744003ABF4E /* SenTestingKit.framework in Frameworks */,
DA3EF17C15A47744003ABF4E /* UIKit.framework in Frameworks */,
DA3EF17D15A47744003ABF4E /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DA4425C81557BED40052177D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -1874,6 +1903,26 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DA3EF17E15A47744003ABF4E /* Tests */ = {
isa = PBXGroup;
children = (
DA3EF18415A47744003ABF4E /* Tests.h */,
DA3EF18515A47744003ABF4E /* Tests.m */,
DA3EF17F15A47744003ABF4E /* Supporting Files */,
);
path = Tests;
sourceTree = "<group>";
};
DA3EF17F15A47744003ABF4E /* Supporting Files */ = {
isa = PBXGroup;
children = (
DA3EF18015A47744003ABF4E /* Tests-Info.plist */,
DA3EF18115A47744003ABF4E /* InfoPlist.strings */,
DA3EF18715A47744003ABF4E /* Tests-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
DA4425D71557BF260052177D /* iCloudStoreManager */ = {
isa = PBXGroup;
children = (
@@ -1900,6 +1949,7 @@
DAD3127315528CD200A3F9ED /* Localytics */,
DA4425D71557BF260052177D /* iCloudStoreManager */,
DA829E5D15984812002417D3 /* FontReplacer */,
DA3EF17E15A47744003ABF4E /* Tests */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
);
@@ -1916,6 +1966,7 @@
DAD3127115528CD200A3F9ED /* libLocalytics.a */,
DA4425CB1557BED40052177D /* libiCloudStoreManager.a */,
DA829E51159847E0002417D3 /* libFontReplacer.a */,
DA3EF17915A47744003ABF4E /* Tests.octest */,
);
name = Products;
sourceTree = "<group>";
@@ -1935,6 +1986,7 @@
DA5BFA4A147E415C00F98B1E /* Foundation.framework */,
DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */,
DA5BFA4E147E415C00F98B1E /* CoreData.framework */,
DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -3222,6 +3274,25 @@
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
DA3EF17815A47744003ABF4E /* Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA3EF18815A47744003ABF4E /* Build configuration list for PBXNativeTarget "Tests" */;
buildPhases = (
DA3EF17415A47744003ABF4E /* Sources */,
DA3EF17515A47744003ABF4E /* Frameworks */,
DA3EF17615A47744003ABF4E /* Resources */,
DA3EF17715A47744003ABF4E /* ShellScript */,
);
buildRules = (
);
dependencies = (
DA3EF19E15A47AEB003ABF4E /* PBXTargetDependency */,
);
name = Tests;
productName = Tests;
productReference = DA3EF17915A47744003ABF4E /* Tests.octest */;
productType = "com.apple.product-type.bundle";
};
DA4425CA1557BED40052177D /* iCloudStoreManager */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "iCloudStoreManager" */;
@@ -3400,6 +3471,7 @@
DAD3127015528CD200A3F9ED /* Localytics */,
DA4425CA1557BED40052177D /* iCloudStoreManager */,
DA829E50159847E0002417D3 /* FontReplacer */,
DA3EF17815A47744003ABF4E /* Tests */,
);
};
/* End PBXProject section */
@@ -3429,6 +3501,14 @@
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
DA3EF17615A47744003ABF4E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DA3EF18315A47744003ABF4E /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DA5BFA42147E415C00F98B1E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -4074,6 +4154,19 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
DA3EF17715A47744003ABF4E /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
};
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -4107,6 +4200,14 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DA3EF17415A47744003ABF4E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DA3EF18615A47744003ABF4E /* Tests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DA4425C71557BED40052177D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -4252,6 +4353,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DA3EF19E15A47AEB003ABF4E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA5BFA43147E415C00F98B1E /* MasterPassword */;
targetProxy = DA3EF19D15A47AEB003ABF4E /* PBXContainerItemProxy */;
};
DA4DA1D81564470200F6F596 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "Makefile-scrypt";
@@ -4270,6 +4376,14 @@
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
DA3EF18115A47744003ABF4E /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
DA3EF18215A47744003ABF4E /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
DAB8D43F15036BCF00CED3BC /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
@@ -4289,6 +4403,88 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
DA3EF18915A47744003ABF4E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MasterPassword.app/MasterPassword";
CLANG_ENABLE_OBJC_ARC = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(DEVELOPER_LIBRARY_DIR)/Frameworks",
"\"$(SRCROOT)/Crashlytics\"",
);
GCC_DYNAMIC_NO_PIC = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
GCC_WARN_UNINITIALIZED_AUTOS = YES;
INFOPLIST_FILE = "Tests/Tests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = octest;
};
name = Debug;
};
DA3EF18A15A47744003ABF4E /* AdHoc */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MasterPassword.app/MasterPassword";
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(DEVELOPER_LIBRARY_DIR)/Frameworks",
"\"$(SRCROOT)/Crashlytics\"",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Tests/Tests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TEST_HOST = "$(BUNDLE_LOADER)";
VALIDATE_PRODUCT = YES;
WRAPPER_EXTENSION = octest;
};
name = AdHoc;
};
DA3EF18B15A47744003ABF4E /* AppStore */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MasterPassword.app/MasterPassword";
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(DEVELOPER_LIBRARY_DIR)/Frameworks",
"\"$(SRCROOT)/Crashlytics\"",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "Tests/Tests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TEST_HOST = "$(BUNDLE_LOADER)";
VALIDATE_PRODUCT = YES;
WRAPPER_EXTENSION = octest;
};
name = AppStore;
};
DA4425D41557BED40052177D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -4757,6 +4953,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DA3EF18815A47744003ABF4E /* Build configuration list for PBXNativeTarget "Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA3EF18915A47744003ABF4E /* Debug */,
DA3EF18A15A47744003ABF4E /* AdHoc */,
DA3EF18B15A47744003ABF4E /* AppStore */,
);
defaultConfigurationIsVisible = 0;
};
DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "iCloudStoreManager" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -28,6 +28,16 @@
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA3EF17815A47744003ABF4E"
BuildableName = "Tests.octest"
BlueprintName = "Tests"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
@@ -44,7 +54,7 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "AppStore"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">

Binary file not shown.

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- PROJECT METADATA -->
<parent>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
</parent>
<name>Master Password Algorithm Implementation</name>
<description>The implementation of the Master Password algorithm</description>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<packaging>jar</packaging>
<!-- DEPENDENCY MANAGEMENT -->
<dependencies>
<!-- PROJECT REFERENCES -->
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId>
<version>GIT-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>GIT-SNAPSHOT</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES -->
<dependency>
<groupId>net.sf.plist</groupId>
<artifactId>property-list</artifactId>
<version>svn-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.lambdaworks</groupId>
<artifactId>scrypt</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,14 @@
package com.lyndir.lhunath.masterpassword;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPElementFeature {
/** Export the key-protected content data. */
ExportContent,
/** Never export content. */
DevicePrivate,
}

View File

@@ -0,0 +1,79 @@
package com.lyndir.lhunath.masterpassword;
import com.google.common.collect.ImmutableSet;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Set;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPElementType {
GeneratedMaximum( "Maximum Security Password", "Maximum", "20 characters, contains symbols.", MPElementTypeClass.Generated ),
GeneratedLong( "Long Password", "Long", "Copy-friendly, 14 characters, contains symbols.", MPElementTypeClass.Generated ),
GeneratedMedium( "Medium Password", "Medium", "Copy-friendly, 8 characters, contains symbols.", MPElementTypeClass.Generated ),
GeneratedShort( "Short Password", "Short", "Copy-friendly, 4 characters, no symbols.", MPElementTypeClass.Generated ),
GeneratedBasic( "Basic Password", "Basic", "8 characters, no symbols.", MPElementTypeClass.Generated ),
GeneratedPIN( "PIN", "PIN", "4 numbers.", MPElementTypeClass.Generated ),
StoredPersonal( "Personal Password", "Personal", "AES-encrypted, exportable.", MPElementTypeClass.Stored, MPElementFeature.ExportContent ),
StoredDevicePrivate( "Device Private Password", "Private", "AES-encrypted, not exported.", MPElementTypeClass.Stored, MPElementFeature.DevicePrivate );
static final Logger logger = Logger.get( MPElementType.class );
private final MPElementTypeClass typeClass;
private final Set<MPElementFeature> typeFeatures;
private final String name;
private final String shortName;
private final String description;
MPElementType(final String name, final String shortName, final String description, final MPElementTypeClass typeClass, final MPElementFeature... typeFeatures) {
this.name = name;
this.shortName = shortName;
this.typeClass = typeClass;
this.description = description;
ImmutableSet.Builder<MPElementFeature> typeFeaturesBuilder = ImmutableSet.builder();
for (final MPElementFeature typeFeature : typeFeatures)
typeFeaturesBuilder.add( typeFeature );
this.typeFeatures = typeFeaturesBuilder.build();
}
public MPElementTypeClass getTypeClass() {
return typeClass;
}
public Set<MPElementFeature> getTypeFeatures() {
return typeFeatures;
}
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
public String getDescription() {
return description;
}
public static MPElementType forName(final String name) {
for (final MPElementType type : values())
if (type.getName().equals( name ))
return type;
throw logger.bug( "Element type not known: %s", name );
}
}

View File

@@ -0,0 +1,27 @@
package com.lyndir.lhunath.masterpassword;
import com.lyndir.lhunath.masterpassword.entity.*;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPElementTypeClass {
Generated(MPElementGeneratedEntity.class),
Stored(MPElementStoredEntity.class);
private final Class<? extends MPElementEntity> entityClass;
MPElementTypeClass(final Class<? extends MPElementEntity> entityClass) {
this.entityClass = entityClass;
}
public Class<? extends MPElementEntity> getEntityClass() {
return entityClass;
}
}

View File

@@ -0,0 +1,41 @@
package com.lyndir.lhunath.masterpassword;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.util.List;
import java.util.Map;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPTemplate extends MetaObject {
private final List<MPTemplateCharacterClass> template;
public MPTemplate(final String template, final Map<Character, MPTemplateCharacterClass> characterClasses) {
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.<MPTemplateCharacterClass>builder();
for (int i = 0; i < template.length(); ++i)
builder.add( characterClasses.get( template.charAt( i ) ) );
this.template = builder.build();
}
public MPTemplate(final List<MPTemplateCharacterClass> template) {
this.template = template;
}
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
return template.get( index );
}
public int length() {
return template.size();
}
}

View File

@@ -0,0 +1,33 @@
package com.lyndir.lhunath.masterpassword;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import com.lyndir.lhunath.opal.system.util.ObjectMeta;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPTemplateCharacterClass extends MetaObject {
private final char identifier;
@ObjectMeta(useFor = { })
private final char[] characters;
public MPTemplateCharacterClass(final char identifier, final char[] characters) {
this.identifier = identifier;
this.characters = characters;
}
public char getIdentifier() {
return identifier;
}
public char getCharacterAtRollingIndex(final int index) {
return characters[index % characters.length];
}
}

View File

@@ -0,0 +1,103 @@
package com.lyndir.lhunath.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Closeables;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import net.sf.plist.*;
import net.sf.plist.io.PropertyListException;
import net.sf.plist.io.PropertyListParser;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPTemplates extends MetaObject {
static final Logger logger = Logger.get( MPTemplates.class );
private final Map<MPElementType, List<MPTemplate>> templates;
public MPTemplates(final Map<MPElementType, List<MPTemplate>> templates) {
this.templates = templates;
}
public static MPTemplates loadFromPList(final String templateResource) {
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
InputStream templateStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( templateResource );
try {
NSObject plistObject = PropertyListParser.parse( templateStream );
Preconditions.checkState( NSDictionary.class.isAssignableFrom( plistObject.getClass() ) );
NSDictionary plist = (NSDictionary) plistObject;
NSDictionary characterClassesDict = (NSDictionary) plist.get( "MPCharacterClasses" );
NSDictionary templatesDict = (NSDictionary) plist.get( "MPElementGeneratedEntity" );
ImmutableMap.Builder<Character, MPTemplateCharacterClass> characterClassesBuilder = ImmutableMap.builder();
for (final Map.Entry<String, NSObject> characterClassEntry : characterClassesDict.entrySet()) {
String key = characterClassEntry.getKey();
NSObject value = characterClassEntry.getValue();
Preconditions.checkState( key.length() == 1 );
Preconditions.checkState( NSString.class.isAssignableFrom( value.getClass() ));
char character = key.charAt( 0 );
char[] characterClass = ((NSString)value).getValue().toCharArray();
characterClassesBuilder.put( character, new MPTemplateCharacterClass( character, characterClass ) );
}
ImmutableMap<Character, MPTemplateCharacterClass> characterClasses = characterClassesBuilder.build();
ImmutableMap.Builder<MPElementType, List<MPTemplate>> templatesBuilder = ImmutableMap.builder();
for (final Map.Entry<String, NSObject> template : templatesDict.entrySet()) {
String key = template.getKey();
NSObject value = template.getValue();
Preconditions.checkState( NSArray.class.isAssignableFrom( value.getClass() ) );
MPElementType type = MPElementType.forName( key );
List<NSObject> templateStrings = ((NSArray) value).getValue();
ImmutableList.Builder<MPTemplate> typeTemplatesBuilder = ImmutableList.<MPTemplate>builder();
for (final NSObject templateString : templateStrings)
typeTemplatesBuilder.add( new MPTemplate( ((NSString) templateString).getValue(), characterClasses ) );
templatesBuilder.put( type, typeTemplatesBuilder.build() );
}
ImmutableMap<MPElementType, List<MPTemplate>> templates = templatesBuilder.build();
return new MPTemplates( templates );
}
catch (PropertyListException e) {
logger.err( e, "Could not parse templates from: %s", templateResource );
throw Throwables.propagate( e );
}
catch (IOException e) {
logger.err( e, "Could not read templates from: %s", templateResource );
throw Throwables.propagate( e );
}
finally {
Closeables.closeQuietly( templateStream );
}
}
public MPTemplate getTemplateForTypeAtRollingIndex(final MPElementType type, final int templateIndex) {
List<MPTemplate> typeTemplates = templates.get( type );
return typeTemplates.get( templateIndex % typeTemplates.size() );
}
public static void main(final String... arguments) {
loadFromPList( "templates.plist" );
}
}

View File

@@ -0,0 +1,128 @@
package com.lyndir.lhunath.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.crypto.CryptUtils;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
/**
* Implementation of the Master Password algorithm.
*
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public abstract class MasterPassword {
static final Logger logger = Logger.get( MasterPassword.class );
private static final int MP_N = 32768;
private static final int MP_r = 8;
private static final int MP_p = 2;
private static final int MP_dkLen = 64;
private static final Charset MP_charset = Charsets.UTF_8;
private static final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
private static final MessageDigests MP_hash = MessageDigests.SHA256;
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
private static final MPTemplates templates = MPTemplates.loadFromPList( "templates.plist" );
public static byte[] keyForPassword(final String password, final String username) {
long start = System.currentTimeMillis();
byte[] nusernameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
.order( MP_byteOrder )
.putInt( username.length() )
.array();
byte[] salt = Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
nusernameLengthBytes, //
username.getBytes( MP_charset ) );
try {
byte[] key = SCrypt.scrypt( password.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
logger.trc( "User: %s, password: %s derives to key ID: %s (took %.2fs)", username, password,
CodeUtils.encodeHex( keyIDForKey( key ) ), (double) (System.currentTimeMillis() - start) / 1000 );
return key;
}
catch (GeneralSecurityException e) {
throw logger.bug( e );
}
}
public static byte[] subkeyForKey(final byte[] key, final int subkeyLength) {
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
System.arraycopy( key, 0, subkey, 0, subkey.length );
return subkey;
}
public static byte[] keyIDForPassword(final String password, final String username) {
return keyIDForKey( keyForPassword( password, username ) );
}
public static byte[] keyIDForKey(final byte[] key) {
return MP_hash.of( key );
}
public static String generateContent(final MPElementType type, final String name, final byte[] key, int counter) {
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
Preconditions.checkArgument( !name.isEmpty() );
Preconditions.checkArgument( key.length > 0 );
if (counter == 0)
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
byte[] nameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( name.length() ).array();
byte[] counterBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( counter ).array();
logger.trc( "seed from: hmac-sha256(%s, 'com.lyndir.masterpassword' | %s | %s | %s)", CryptUtils.encodeBase64( key ),
CodeUtils.encodeHex( nameLengthBytes ), name, CodeUtils.encodeHex( counterBytes ) );
byte[] seed = MP_mac.of( key, Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
nameLengthBytes, //
name.getBytes( MP_charset ), //
counterBytes ) );
logger.trc( "seed is: %s", CryptUtils.encodeBase64( seed ) );
Preconditions.checkState( seed.length > 0 );
int templateIndex = seed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = templates.getTemplateForTypeAtRollingIndex( type, templateIndex );
logger.trc( "type: %s, template: %s", type, template );
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.
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 );
password.append( passwordCharacter );
}
return password.toString();
}
public static void main(final String... arguments) {
String masterPassword = "test-mp";
String username = "test-user";
String siteName = "test-site";
MPElementType siteType = MPElementType.GeneratedLong;
int siteCounter = 42;
String sitePassword = generateContent( siteType, siteName, keyForPassword( masterPassword, username ), siteCounter );
logger.inf( "master password: %s, username: %s\nsite name: %s, site type: %s, site counter: %d\n => site password: %s",
masterPassword, username, siteName, siteType, siteCounter, sitePassword );
}
}

View File

@@ -0,0 +1,10 @@
package com.lyndir.lhunath.masterpassword.entity;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPElementEntity {
}

View File

@@ -0,0 +1,10 @@
package com.lyndir.lhunath.masterpassword.entity;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPElementGeneratedEntity extends MPElementEntity {
}

View File

@@ -0,0 +1,10 @@
package com.lyndir.lhunath.masterpassword.entity;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPElementStoredEntity extends MPElementEntity {
}

View File

@@ -0,0 +1 @@
../../../../../../Resources/ciphers.plist

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MPElementGeneratedEntity</key>
<dict>
<key>Maximum Security Password</key>
<array>
<string>anoxxxxxxxxxxxxxxxxx</string>
<string>axxxxxxxxxxxxxxxxxno</string>
</array>
<key>Long Password</key>
<array>
<string>CvcvnoCvcvCvcv</string>
<string>CvcvCvcvnoCvcv</string>
<string>CvcvCvcvCvcvno</string>
<string>CvccnoCvcvCvcv</string>
<string>CvccCvcvnoCvcv</string>
<string>CvccCvcvCvcvno</string>
<string>CvcvnoCvccCvcv</string>
<string>CvcvCvccnoCvcv</string>
<string>CvcvCvccCvcvno</string>
<string>CvcvnoCvcvCvcc</string>
<string>CvcvCvcvnoCvcc</string>
<string>CvcvCvcvCvccno</string>
<string>CvccnoCvccCvcv</string>
<string>CvccCvccnoCvcv</string>
<string>CvccCvccCvcvno</string>
<string>CvcvnoCvccCvcc</string>
<string>CvcvCvccnoCvcc</string>
<string>CvcvCvccCvccno</string>
<string>CvccnoCvcvCvcc</string>
<string>CvccCvcvnoCvcc</string>
<string>CvccCvcvCvccno</string>
</array>
<key>Medium Password</key>
<array>
<string>CvcnoCvc</string>
<string>CvcCvcno</string>
</array>
<key>Short Password</key>
<array>
<string>Cvcn</string>
</array>
<key>Basic Password</key>
<array>
<string>aaanaaan</string>
<string>aannaaan</string>
<string>aaannaaa</string>
</array>
<key>PIN</key>
<array>
<string>nnnn</string>
</array>
</dict>
<key>MPCharacterClasses</key>
<dict>
<key>V</key>
<string>AEIOU</string>
<key>C</key>
<string>BCDFGHJKLMNPQRSTVWXYZ</string>
<key>v</key>
<string>aeiou</string>
<key>c</key>
<string>bcdfghjklmnpqrstvwxyz</string>
<key>A</key>
<string>AEIOUBCDFGHJKLMNPQRSTVWXYZ</string>
<key>a</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</string>
<key>n</key>
<string>0123456789</string>
<key>o</key>
<string>@&amp;%?,=[]_:-+*$#!'^~;()/.</string>
<key>x</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,5 @@
#Generated by Maven
#Wed Jul 04 23:49:38 CEST 2012
version=GIT-SNAPSHOT
groupId=com.lyndir.lhunath.masterpassword
artifactId=masterpassword-algorithm

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- PROJECT METADATA -->
<parent>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
</parent>
<name>Master Password CLI</name>
<description>A CLI interface to the Master Password algorithm</description>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword-cli</artifactId>
<packaging>jar</packaging>
<!-- BUILD CONFIGURATION -->
<build>
<resources>
<resource>
<directory>src/main/scripts</directory>
<filtering>true</filtering>
<targetPath>${project.build.directory}</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.lyndir.lhunath.masterpassword.CLI</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- DEPENDENCY MANAGEMENT -->
<dependencies>
<!-- PROJECT REFERENCES -->
<dependency>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<version>GIT-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2008, Maarten Billemont
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lyndir.lhunath.masterpassword;
import com.google.common.io.LineReader;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import java.io.*;
import java.util.Arrays;
/**
* <p> <i>Jun 10, 2008</i> </p>
*
* @author mbillemo
*/
public class CLI {
static final Logger logger = Logger.get( CLI.class );
public static void main(final String[] args)
throws IOException {
InputStream in = System.in;
/* Arguments. */
String userName = null, siteName = null;
int counter = 1;
MPElementType type = MPElementType.GeneratedLong;
boolean typeArg = false, counterArg = false, userNameArg = false;
for (final String arg : Arrays.asList( args ))
if ("-t".equals( arg ) || "--type".equals( arg ))
typeArg = true;
else if (typeArg) {
if ("list".equalsIgnoreCase( arg )) {
System.out.format( "%30s | %s\n", "type", "description" );
for (final MPElementType aType : MPElementType.values())
System.out.format( "%30s | %s\n", aType.getName(), aType.getDescription() );
System.exit( 0 );
}
type = MPElementType.forName( arg );
typeArg = false;
} else if ("-c".equals( arg ) || "--counter".equals( arg ))
counterArg = true;
else if (counterArg) {
counter = ConversionUtils.toIntegerNN( arg );
counterArg = false;
} else if ("-u".equals( arg ) || "--username".equals( arg ))
userNameArg = true;
else if (userNameArg) {
userName = arg;
userNameArg = false;
} else if ("-h".equals( arg ) || "--help".equals( arg )) {
System.out.println();
System.out.println( "\tMaster Password CLI" );
System.out.println( "\t\tLyndir" );
System.out.println( "[options] [site name]" );
System.out.println();
System.out.println( "Available options:" );
System.out.println( "\t-t | --type [site password type]" );
System.out.format( "\t\tDefault: %s. The password type to use for this site.\n", type.getName() );
System.out.println( "\t\tUse 'list' to see the available types." );
System.out.println();
System.out.println( "\t-c | --counter [site counter]" );
System.out.format( "\t\tDefault: %d. The counter to use for this site.\n", counter );
System.out.println( "\t\tIncrement the counter if you need a new password." );
System.out.println();
System.out.println( "\t-u | --username [user's name]" );
System.out.println( "\t\tDefault: asked. The name of the current user." );
System.out.println();
return;
} else
siteName = arg;
LineReader lineReader = new LineReader( new InputStreamReader( System.in ) );
if (siteName == null) {
System.out.print( "Site name: " );
siteName = lineReader.readLine();
}
if (userName == null) {
System.out.print( "User's name: " );
userName = lineReader.readLine();
}
System.out.print( "User's master password: " );
String masterPassword = lineReader.readLine();
String sitePassword = MasterPassword.generateContent( type, siteName, MasterPassword.keyForPassword( masterPassword, userName ),
counter );
System.out.println( sitePassword );
}
}

View File

@@ -0,0 +1,19 @@
<configuration scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern>
</layout>
</appender>
<logger name="com.lyndir" level="TRACE" />
<!--
<logger name="org.apache.wicket" level="DEBUG" />
-->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd "${BASH_SOURCE[0]%/*}"
java -jar masterpassword-cli-GIT-SNAPSHOT.jar "$@"

View File

@@ -0,0 +1,5 @@
#Generated by Maven
#Wed Jul 04 23:49:39 CEST 2012
version=GIT-SNAPSHOT
groupId=com.lyndir.lhunath.masterpassword
artifactId=masterpassword-cli

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd "${BASH_SOURCE[0]%/*}"
java -jar masterpassword-cli-GIT-SNAPSHOT.jar "$@"

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- PROJECT METADATA -->
<parent>
<groupId>com.lyndir.lhunath</groupId>
<artifactId>lyndir</artifactId>
<version>GIT-SNAPSHOT</version>
</parent>
<name>Master Password</name>
<description>A Java implementation of the Master Password algorithm.</description>
<groupId>com.lyndir.lhunath.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>masterpassword-algorithm</module>
<module>masterpassword-cli</module>
</modules>
</project>

View File

@@ -11,7 +11,7 @@
@interface MPAppDelegate_Shared (Key)
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password;
- (void)signOut;
- (void)signOutAnimated:(BOOL)animated;
- (void)storeSavedKeyFor:(MPUserEntity *)user;
- (void)forgetSavedKeyFor:(MPUserEntity *)user;

View File

@@ -73,14 +73,15 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
}
}
- (void)signOut {
- (void)signOutAnimated:(BOOL)animated {
if (self.key)
self.key = nil;
if (self.activeUser) {
self.activeUser = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedOut object:self];
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedOut object:self userInfo:
[NSDictionary dictionaryWithObject:PearlBool(animated) forKey:@"animated"]];
}
}

View File

@@ -9,9 +9,9 @@
#import "MPEntities.h"
#define MP_N 131072
#define MP_N 32768
#define MP_r 8
#define MP_p 1
#define MP_p 2
#define MP_dkLen 64
#define MP_hash PearlHashSHA256
@@ -27,7 +27,7 @@ NSData *keyForPassword(NSString *password, NSString *username) {
[username dataUsingEncoding:NSUTF8StringEncoding],
nil] N:MP_N r:MP_r p:MP_p];
trc(@"User: %@, password: %@ derives to key ID: %@ (took %0.2f)", username, password, [keyIDForKey(key) encodeHex], -[start timeIntervalSinceNow]);
trc(@"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", username, password, [keyIDForKey(key) encodeHex], -[start timeIntervalSinceNow]);
return key;
}
@@ -184,16 +184,18 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, ui
withExtension:@"plist"]];
// Determine the seed whose bytes will be used for calculating a password
trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %u | %@ | %u)", key, name.length, name, counter);
uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length);
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof(ncounter)];
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)];
trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key encodeBase64], [nameLengthBytes encodeHex], name, [counterBytes encodeHex]);
NSData *seed = [[NSData dataByConcatenatingDatas:
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
[NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)],
nameLengthBytes,
[name dataUsingEncoding:NSUTF8StringEncoding],
[NSData dataWithBytes:&ncounter length:sizeof(ncounter)],
counterBytes,
nil]
hmacWith:PearlHashSHA256 key:key];
trc(@"seed is: %@", seed);
trc(@"seed is: %@", [seed encodeBase64]);
const char *seedBytes = seed.bytes;
// Determine the cipher from the first seed byte.
@@ -213,7 +215,7 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, ui
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length],
1)];
trc(@"class %@ has characters: %@, selected: %@", cipherClass, cipherClassCharacters, character);
trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character);
[content appendString:character];
}

View File

@@ -182,7 +182,11 @@
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil
usingBlock:^(NSNotification *note) {
[self.navigationController performSegueWithIdentifier:@"MP_Unlock" sender:nil];
if ([[note.userInfo objectForKey:@"animated"] boolValue])
[self.navigationController performSegueWithIdentifier:@"MP_Unlock" sender:nil];
else
[self.navigationController presentViewController:[self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"]
animated:NO completion:nil];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil
usingBlock:^(NSNotification *note) {
@@ -325,7 +329,7 @@
[self saveContext];
if (![[MPiOSConfig get].rememberLogin boolValue])
[self signOut];
[self signOutAnimated:NO];
[TestFlight passCheckpoint:MPCheckpointDeactivated];
}
@@ -487,7 +491,7 @@
inf(@"Unsetting master password for: %@.", user.userID);
user.keyID = nil;
[self forgetSavedKeyFor:user];
[self signOut];
[self signOutAnimated:YES];
[TestFlight passCheckpoint:MPCheckpointChangeMP];
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointChangeMP

View File

@@ -14,7 +14,8 @@
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPSearchResultsDelegate, UIWebViewDelegate, MFMailComposeViewControllerDelegate>
@property (strong, nonatomic) MPElementEntity *activeElement;
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchResultsController;
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate;
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *resetPasswordCounterGesture;
@property (weak, nonatomic) IBOutlet UITextField *contentField;
@property (weak, nonatomic) IBOutlet UIButton *typeButton;
@property (weak, nonatomic) IBOutlet UIWebView *helpView;
@@ -33,7 +34,6 @@
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
@property (weak, nonatomic) IBOutlet UIView *actionsTipContainer;
@property (weak, nonatomic) IBOutlet UIView *typeTipContainer;
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *resetPasswordCounterGesture;
@property (copy) void (^contentTipCleanup)(BOOL finished);

View File

@@ -25,7 +25,7 @@
@implementation MPMainViewController
@synthesize activeElement = _activeElement;
@synthesize searchResultsController = _searchResultsController;
@synthesize searchDelegate = _searchDelegate;
@synthesize typeButton = _typeButton;
@synthesize helpView = _helpView;
@synthesize siteName = _siteName;
@@ -67,6 +67,15 @@
}
- (void)viewDidLoad {
self.searchDelegate = [MPSearchDelegate new];
self.searchDelegate.delegate = self;
self.searchDelegate.searchDisplayController = self.searchDisplayController;
self.searchDelegate.searchTipContainer = self.searchTipContainer;
self.searchDisplayController.searchBar.delegate = self.searchDelegate;
self.searchDisplayController.delegate = self.searchDelegate;
self.searchDisplayController.searchResultsDelegate = self.searchDelegate;
self.searchDisplayController.searchResultsDataSource = self.searchDelegate;
self.resetPasswordCounterGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(resetPasswordCounter:)];
[self.passwordIncrementer addGestureRecognizer:self.resetPasswordCounterGesture];
@@ -136,7 +145,6 @@
[self setContentField:nil];
[self setTypeButton:nil];
[self setSearchResultsController:nil];
[self setHelpView:nil];
[self setSiteName:nil];
[self setPasswordCounter:nil];
@@ -153,6 +161,7 @@
[self setSearchTipContainer:nil];
[self setActionsTipContainer:nil];
[self setTypeTipContainer:nil];
[self setSearchDelegate:nil];
[self setResetPasswordCounterGesture:nil];
[super viewDidUnload];
}
@@ -503,7 +512,7 @@
#endif
{
inf(@"Action: Sign out");
[[MPAppDelegate get] signOut];
[[MPAppDelegate get] signOutAnimated:YES];
break;
}
}

View File

@@ -110,7 +110,6 @@
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
dbg(@"Search ended with: %@", controller.searchBar.text);
controller.searchBar.prompt = nil;
controller.searchBar.searchResultsButtonSelected = NO;
}

View File

@@ -79,7 +79,12 @@
assert(self.navigationController.topViewController == self);
[delegate didSelectType:[self typeAtIndexPath:indexPath]];
MPElementType type = [self typeAtIndexPath:indexPath];
if (type == NSNotFound)
// Selected a non-type row.
return;
[delegate didSelectType:type];
[self.navigationController popViewControllerAnimated:YES];
}

View File

@@ -416,20 +416,10 @@ Your passwords will be AES-encrypted with your master password.</string>
<scene sceneID="U26-Zf-euQ">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="mK2-p1-3zC" userLabel="First Responder" sceneMemberID="firstResponder"/>
<customObject id="0QO-2P-OhD" customClass="MPSearchDelegate">
<connections>
<outlet property="delegate" destination="PQa-Xl-A3x" id="O2f-mW-ab8"/>
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="exk-dS-Ui3"/>
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="lk6-2X-lIb"/>
</connections>
</customObject>
<searchDisplayController id="P8c-gf-nN3">
<connections>
<outlet property="delegate" destination="0QO-2P-OhD" id="gst-Hm-5ja"/>
<outlet property="searchBar" destination="qeo-n2-WVh" id="bFO-FC-Xdj"/>
<outlet property="searchContentsController" destination="PQa-Xl-A3x" id="iEu-t3-hJY"/>
<outlet property="searchResultsDataSource" destination="0QO-2P-OhD" id="XJh-rC-kId"/>
<outlet property="searchResultsDelegate" destination="0QO-2P-OhD" id="Bm8-Q3-lLA"/>
</connections>
</searchDisplayController>
<viewController id="PQa-Xl-A3x" customClass="MPMainViewController" sceneMemberID="viewController">
@@ -621,9 +611,6 @@ L4m3P4sSw0rD</string>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<gestureRecognizers/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/>
<connections>
<outlet property="delegate" destination="0QO-2P-OhD" id="Maj-Iz-6Hb"/>
</connections>
</searchBar>
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="zOR-Du-qRL">
<rect key="frame" x="10" y="15" width="300" height="60"/>
@@ -734,7 +721,6 @@ L4m3P4sSw0rD</string>
<outlet property="passwordEdit" destination="9FS-fS-xH6" id="YeB-HF-ZPk"/>
<outlet property="passwordIncrementer" destination="jec-mu-nPt" id="i9B-lX-zzX"/>
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="CLs-YI-7NC"/>
<outlet property="searchResultsController" destination="0QO-2P-OhD" id="xEC-gV-lHp"/>
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="X7h-Vh-iCE"/>
<outlet property="siteName" destination="gSK-aB-wNI" id="IIe-z8-zy8"/>
<outlet property="typeButton" destination="Cei-5z-uWE" id="4M1-d7-5Bh"/>
@@ -1379,6 +1365,7 @@ L4m3P4sSw0rD</string>
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
<relationship kind="outlet" name="resetPasswordCounterGesture" candidateClass="UILongPressGestureRecognizer"/>
<relationship kind="outlet" name="searchDelegate" candidateClass="MPSearchDelegate"/>
<relationship kind="outlet" name="searchResultsController" candidateClass="MPSearchDelegate"/>
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>

BIN
Resources/AppStore-iOS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

View File

@@ -0,0 +1 @@
../Press/./MasterPassword_PressKit.zip

View File

@@ -85,10 +85,10 @@
</script>
</head>
<body>
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore.png" /></a>
<header>
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore.png" /></a>
<h1><a href="index.html"><img class="logo" src="img/iTunesArtwork-Bare.png" /> Master Password</a></h1>
<div class="divider"></div>
@@ -118,13 +118,13 @@
<p>
Master Password uses a stateless algorithm that relies solely on its implementation and the user's inputs. The user is expected to remember the following information:
<ul>
<li><b>The master password</b> (eg. <em>pink fluffy door frame</em>):<br />
<li><strong>The master password</strong> (eg. <em>pink fluffy door frame</em>):<br />
This is a secret that the user shares with nobody.</li>
<li><b>The site name</b> (eg. <em>apple.com</em>):<br />
<li><strong>The site name</strong> (eg. <em>apple.com</em>):<br />
The user chooses a name for each site. Its domain name is an ideal choice, since it needn't necessarily be remembered.</li>
<li><b>The site's password counter</b> (default: <em>0</em>):<br />
<li><strong>The site's password counter</strong> (default: <em>0</em>):<br />
This is an integer that can be incremented when the user needs a new password for the site.</li>
<li><b>The site's password type</b> (default: <em>Long Password</em>):<br />
<li><strong>The site's password type</strong> (default: <em>Long Password</em>):<br />
This type determines the format of the output password. It can be changed if the site's password policy does not accept passwords of this format.</li>
</ul>
</p>
@@ -132,26 +132,33 @@
In short, the algorithm is comprised of the following steps:
<ul>
<li>Determining the master <code>key</code></li>
<li>Determining the cipher <code>seed</code></li>
<li>Determining the template <code>seed</code></li>
<li>Encoding a user-friendly <code>password</code></li>
</ul>
</p>
<p>
A note on types:
<ul>
<li>Any character string is UTF-8 de- or encoded, depending on context.</li>
<li>Any number is converted to 32-bit network byte order.</li>
</ul>
</p>
<h2>The Master Password</h2>
<p>
The user chooses a single master password, preferably sufficiently long to harden against brute-force attacks. Master Password recommends absurd two or three-word sentences as they're easily remembered and generally sufficiently high in entropy.
The user chooses a single master password, preferably sufficiently long to harden against brute-force attacks. Master Password recommends absurd three or four-word sentences as they're easily remembered and generally sufficiently high in entropy.
</p>
<p>
The application then creates a <a href="http://www.tarsnap.com/scrypt.html" onclick="_gaq.push(['_trackPageview', '/outbound/tarsnap.com/scrypt.html">scrypt</a> key derivative from the user's password. This process takes quite a bit of processing time and memory. This step exists to make brute-force attempts at guessing the master password from a given output password <b>far more difficult</b>, to practically infeasible, even for otherwise vulnerable password strings.
The application then creates a <a href="http://www.tarsnap.com/scrypt.html" onclick="_gaq.push(['_trackPageview', '/outbound/tarsnap.com/scrypt.html">scrypt</a> key derivative from the user's password. This process takes quite a bit of processing time and memory. This step exists to make brute-force attempts at guessing the master password from a given output password <strong>far more difficult</strong>, to practically infeasible, even for otherwise vulnerable password strings.
</p>
<code><pre>
key = scrypt( P, S, N, r, p, dkLen )
where
P = master password (UTF-8)
S = &lt;empty&gt;
N = 16384
P = master password
S = "com.lyndir.masterpassword" . name length . name
N = 32768
r = 8
p = 1
p = 2
dkLen = 64
</pre></code>
@@ -167,8 +174,7 @@
These input values are combined in a byte array, separated by a single <code>NUL</code> byte. In order, the input values are the <code>site name</code> (UTF-8 decoded), the master <code>key</code>, and a <code>salt</code> (this is the password counter, a 32-bit unsigned integer in network byte order). The byte array is hashed using the SHA-1 algorithm to yield the <code>seed</code> as a result.
</p>
<code><pre>
salt = htonl( password counter )
seed = sha1( site name . "\0" . key . "\0" . salt )
seed = hmac-sha256( key, "com.lyndir.masterpassword" . site name length . site name . counter )
</pre></code>
<h2>Generating The Output</h2>
@@ -180,33 +186,58 @@
</p>
<p>
Since the idea is that the output password can be used directly as a password to protect the user's account on the site, it needs to be able to pass the site's password policy.
Master Password addresses this problem by introducing <em>password types</em>. Each password type describes what an output password must look like and maps to a set of <code>ciphers</code>. Ciphers describe the resulting output password using a series of characters that map to character groups of candidate output characters. A cipher has the same length as the output password it yields. Each character in the cipher maps to a specific character group. At each position of the output password, a character is chosen from the character group identified by the character in the cipher at the same position.
Master Password addresses this problem by introducing <em>password types</em>. Each password type describes what an output password must look like and maps to a set of <code>templates</code>. Templates describe the resulting output password using a series of characters that map to character groups of candidate output characters. A template has the same length as the output password it yields. Each character in the template maps to a specific character group. At each position of the output password, a character is chosen from the character group identified by the character in the template at the same position.
</p>
<p>
The following ciphers are defined:
The following templates are defined:
<ul>
<li>Type: <b>Long Password</b></li>
<li>Type: <strong>Maximum Security Password</strong></li>
<li>
<ul>
<li><code>CvcvCvcvnoCvcv</code></li>
<li><code>CvcvnoCvcvCvcv</code></li>
<li><code>CvcvCvcvCvcvno</code></li>
<li><code>anoxxxxxxxxxxxxxxxxx</li></code>
<li><code>axxxxxxxxxxxxxxxxxno</li></code>
</ul>
</li>
<li>Type: <b>Medium Password</b></li>
<li>Type: <strong>Long Password</strong></li>
<li>
<ul>
<li><code>CvcvnoCvcvCvcv</li></code>
<li><code>CvcvCvcvnoCvcv</li></code>
<li><code>CvcvCvcvCvcvno</li></code>
<li><code>CvccnoCvcvCvcv</li></code>
<li><code>CvccCvcvnoCvcv</li></code>
<li><code>CvccCvcvCvcvno</li></code>
<li><code>CvcvnoCvccCvcv</li></code>
<li><code>CvcvCvccnoCvcv</li></code>
<li><code>CvcvCvccCvcvno</li></code>
<li><code>CvcvnoCvcvCvcc</li></code>
<li><code>CvcvCvcvnoCvcc</li></code>
<li><code>CvcvCvcvCvccno</li></code>
<li><code>CvccnoCvccCvcv</li></code>
<li><code>CvccCvccnoCvcv</li></code>
<li><code>CvccCvccCvcvno</li></code>
<li><code>CvcvnoCvccCvcc</li></code>
<li><code>CvcvCvccnoCvcc</li></code>
<li><code>CvcvCvccCvccno</li></code>
<li><code>CvccnoCvcvCvcc</li></code>
<li><code>CvccCvcvnoCvcc</li></code>
<li><code>CvccCvcvCvccno</li></code>
</ul>
</li>
<li>Type: <strong>Medium Password</strong></li>
<li>
<ul>
<li><code>CvcnoCvc</code></li>
<li><code>CvcCvcno</code></li>
</ul>
</li>
<li>Type: <b>Short Password</b></li>
<li>Type: <strong>Short Password</strong></li>
<li>
<ul>
<li><code>Cvcn</code></li>
</ul>
</li>
<li>Type: <b>Basic Password</b></li>
<li>Type: <strong>Basic Password</strong></li>
<li>
<ul>
<li><code>aaanaaan</code></li>
@@ -214,7 +245,7 @@
<li><code>aaannaaa</code></li>
</ul>
</li>
<li>Type: <b>PIN</b></li>
<li>Type: <strong>PIN</strong></li>
<li>
<ul>
<li><code>nnnn</code></li>
@@ -226,70 +257,70 @@
By default, Master Password uses the <em>Long Password</em> type for any new passwords. The user is able to choose a different password type, which is normally only done if the site's password policy is incompatible with the output password produced by this type.
</p>
<p>
To create the create the output password, the bytes in the <code>seed</code> are encoded according to the cipher. The first <code>seed</code> byte is used to determine which of the type's ciphers to use for encoding an output password. We take the byte value of the first <code>seed</code> byte modulo the amount of ciphers set for the chosen password type and use the result as a zero-based index in the cipher list for the password type.
To create the create the output password, the bytes in the <code>seed</code> are encoded according to the template. The first <code>seed</code> byte is used to determine which of the type's templates to use for encoding an output password. We take the byte value of the first <code>seed</code> byte modulo the amount of templates set for the chosen password type and use the result as a zero-based index in the template list for the password type.
</p>
<code><pre>
ciphers = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno" ]
cipher = ciphers[ seed[0] % count( ciphers ) ]
templates = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno", ... ]
template = templates[ seed[0] % count( templates ) ]
</pre></code>
<p>
Now that we know what cipher to use for building our output password, all that's left is to iterate the cipher, and produce a character of password output for each step. When we iterate the cipher (index <code>i</code>), we look in the character group identified by the character (string <code>passChars</code>) in the cipher at index <code>i</code>.
Now that we know what template to use for building our output password, all that's left is to iterate the template, and produce a character of password output for each step. When we iterate the template (index <code>i</code>), we look in the character group identified by the character (string <code>passChars</code>) in the template at index <code>i</code>.
</p>
<p>
The following character groups (<code>passChars</code>) are defined:
<ul>
<li>Cipher character: <code>V</code></li>
<li>Template character: <code>V</code></li>
<li>
<ul>
<li><code>AEIOU</code></li>
</ul>
</li>
<li>Cipher character: <code>C</code></li>
<li>Template character: <code>C</code></li>
<li>
<ul>
<li><code>BCDFGHJKLMNPQRSTVWXYZ</code></li>
</ul>
</li>
<li>Cipher character: <code>v</code></li>
<li>Template character: <code>v</code></li>
<li>
<ul>
<li><code>aeiou</code></li>
</ul>
</li>
<li>Cipher character: <code>c</code></li>
<li>Template character: <code>c</code></li>
<li>
<ul>
<li><code>bcdfghjklmnpqrstvwxyz</code></li>
</ul>
</li>
<li>Cipher character: <code>A</code> (<code>= V . C</code>)</li>
<li>Template character: <code>A</code> (<code>= V . C</code>)</li>
<li>
<ul>
<li><code>AEIOUBCDFGHJKLMNPQRSTVWXYZ</code></li>
</ul>
</li>
<li>Cipher character: <code>a</code> (<code>= V . v . C . c</code>)</li>
<li>Template character: <code>a</code> (<code>= V . v . C . c</code>)</li>
<li>
<ul>
<li><code>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</code></li>
</ul>
</li>
<li>Cipher character: <code>n</code></li>
<li>Template character: <code>n</code></li>
<li>
<ul>
<li><code>0123456789</code></li>
</ul>
</li>
<li>Cipher character: <code>o</code></li>
<li>Template character: <code>o</code></li>
<li>
<ul>
<li><code>!@#$%^&amp;*()</code></li>
<li><code>@&amp;%?,=[]_:-+*$#!'^~;()/.</code></li>
</ul>
</li>
<li>Cipher character: <code>X</code> (<code>= a . n . o</code>)</li>
<li>Template character: <code>X</code> (<code>= a . n . o</code>)</li>
<li>
<ul>
<li><code>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</code></li>
<li><code>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789@&amp;%?,=[]_:-+*$#!'^~;()/.</code></li>
</ul>
</li>
</ul>

View File

@@ -21,7 +21,7 @@ h1 {
font-weight: 100;
}
strong {
font-weight: 400;
font-weight: 600;
}
h1 .sub {
font-size: 0.5em;
@@ -148,7 +148,7 @@ header .divider {
header a, header .link, header :link,
#fixedheader a, #fixedheader .link, #fixedheader :link {
font-family: Exo;
font-weight: 700;
font-weight: 600;
text-decoration: none;
}
header a:hover, header .link:hover,
@@ -258,6 +258,48 @@ header .appstore {
/* appstore.png */
bottom: -25px;
}
.tip {
background: url("../img/tip.png") no-repeat;
color: white;
position: absolute;
display: block;
right: 0;
width: 410px;
height: 60px;
padding: 12px 20px;
margin-top: -60px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font: 14px "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", "Liberation Sans", sans-serif;
}
.footnote {
color: #444;
position: absolute;
width: 100%;
bottom: 2px;
font: 14px "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", "Liberation Sans", sans-serif !important;
}
.footnote:hover {
color: #666;
}
*>.nothover {
display: inline-block;
}
*:hover>.nothover {
display: none;
}
*>.hover {
display: none;
}
*:hover>.hover {
display: inline-block;
}
.columns {
position: relative;
clear: both;

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

BIN
Site/img/iTunesArtwork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

BIN
Site/img/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
Site/img/tip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -9,25 +9,11 @@
<link rel="shortcut icon" href="img/favicon.png" type="image/x-png" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<link rel='stylesheet' type='text/css' href='http://fonts.googleapis.com/css?family=Exo:100,400,600,900,100italic,400italic,600italic' />
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Exo:100,400,600,900,100italic,400italic,600italic" />
<link rel="stylesheet" type="text/css" href="css/ml-shadows.css" />
<link rel="stylesheet" type="text/css" href="css/screen.css" />
<script src="js/jquery-1.6.1.min.js" type="text/javascript"></script>
<script src="js/functions.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$(window).scroll(function() {
if ($(window).scrollTop() > 100) {
$(".appstore").show();
$("header .appstore").hide();
} else {
$(".appstore").hide();
$("header .appstore").show();
}
})
});
</script>
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-90535-15']);
@@ -78,13 +64,43 @@
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(_ue, s);
})();
</script>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<!-- Nivo Slider -->
<link rel="stylesheet" href="js/nivo-slider/nivo-slider.css" type="text/css" media="screen" />
<script src="js/nivo-slider/jquery.nivo.slider.pack.js" type="text/javascript"></script>
<!-- Page JS -->
<script type="text/javascript">
$(document).ready(function() {
$(window).scroll(function() {
if ($(window).scrollTop() > 100) {
$(".appstore").show();
$("header .appstore").hide();
} else {
$(".appstore").hide();
$("header .appstore").show();
}
})
});
</script>
</head>
<body id="frontpage">
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore.png" /></a>
<header>
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984">
<span class="tip">
<span class="nothover">Tell us what you thought and get a free copy for a friend!</span>
<span class="hover">The most constructive feedback earns a promo code.</span>
</span>
<img src="img/appstore.png" />
</a>
<h1><a href="index.html"><img class="logo" src="img/iTunesArtwork-Bare.png" /> Master Password</a></h1>
<a class="footnote" href="mailto:masterpassword@lyndir.com">masterpassword@lyndir.com</a>
<div class="divider"></div>
</header>
@@ -93,6 +109,23 @@
</div>
<!--a href="http://bit.ly/vNN5Zi" onclick="_gaq.push(['_trackPageview', '/outbound/testflight']);" id="ribbon"></a-->
<!--section class="heading">
<div>
<div class="slider-wrapper">
<div id="slider" class="nivoSlider">
<img src="images/slide1.jpg" alt="" />
<a href="http://dev7studios.com"><img src="images/slide2.jpg" alt="" title="#htmlcaption" /></a>
<img src="images/slide3.jpg" alt="" title="This is an example of a caption" />
<img src="images/slide4.jpg" alt="" />
</div>
</div>
<div id="htmlcaption" class="nivo-html-caption">
<strong>This</strong> is an example of a <em>HTML</em> caption with <a href="#">a link</a>.
</div>
</div>
</section-->
<section class="heading">
<div>
<div class="sidebox">
@@ -112,7 +145,7 @@
<p>&nbsp;</p>
<p><b>Master Password is a <em>stateless solution</em></b>, which means <strong>your passwords aren't saved <em>anywhere</em></strong>. Not in your head, not in a notebook, not on your computer and not in the cloud.<br />
<p><strong>Master Password is a <em>stateless solution</em></strong>, which means <strong>your passwords aren't saved <em>anywhere</em></strong>. Not in your head, not in a notebook, not on your computer and not in the cloud.<br />
Nothing to store means nothing to keep safe and nothing to lose.</p>
<p>Master Password just recreates the passwords for your sites whenever you need them: instantly and on-demand. At the same time it makes sure that your accounts are adequately protected with <em>secure and unique</em> passwords.</p>
@@ -120,11 +153,11 @@
</section>
<section>
<p>Master Password is <b>different</b> from other vault-like password solutions. It helps you set <b>secure passwords</b> for your sites, and at the same time makes <b>losing your passwords almost impossible</b>.</p>
<p>Master Password is <strong>different</strong> from other vault-like password solutions. It helps you set <strong>secure passwords</strong> for your sites, and at the same time makes <strong>losing your passwords almost impossible</strong>.</p>
<p>Built on algorithms such as <a href="http://www.bsdcan.org/2009/schedule/events/147.en.html">scrypt</a> and <a href="http://en.wikipedia.org/wiki/HMAC">HMAC-SHA256</a>, your master password is kept safe even if websites you use get hacked.</p>
<p>As to prove a point, <a href="http://www.washingtonpost.com/business/technology/linkedin-eharmony-deal-with-breach-aftermath/2012/06/07/gJQAwqs5KV_story.html"><b>LinkedIn</b>, <b>eHarmony</b></a>, and <a href="http://securitywatch.pcmag.com/none/298865-last-fm-joins-eharmony-linkedin-to-celebrate-breach-week"><b>Last.FM</b></a> have announced breaches that compromise millions of passwords in the past month alone. These breaches have leaked "<em>hashes</em>" of people's passwords, which make it trivial for attackers to find out the actual passwords <em>if they're not secure enough</em>.</p>
<p>As to prove a point, <a href="http://www.washingtonpost.com/business/technology/linkedin-eharmony-deal-with-breach-aftermath/2012/06/07/gJQAwqs5KV_story.html"><strong>LinkedIn</strong>, <strong>eHarmony</strong></a>, and <a href="http://securitywatch.pcmag.com/none/298865-last-fm-joins-eharmony-linkedin-to-celebrate-breach-week"><strong>Last.FM</strong></a> have announced breaches that compromise millions of passwords in the past month alone. These breaches have leaked "<em>hashes</em>" of people's passwords, which make it trivial for attackers to find out the actual passwords <em>if they're not secure enough</em>.</p>
<hr class="clear" />
<!--p>

View File

@@ -0,0 +1 @@
Nivo Slider is "The Most Awesome jQuery Image Slider". See http://nivo.dev7studios.com for more info.

View File

@@ -0,0 +1,35 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<html lang="en">
<head>
<title>Nivo Slider Demo</title>
<link rel="stylesheet" href="../themes/default/default.css" type="text/css" media="screen" />
<link rel="stylesheet" href="../nivo-slider.css" type="text/css" media="screen" />
<link rel="stylesheet" href="style.css" type="text/css" media="screen" />
</head>
<body>
<div id="wrapper">
<a href="http://dev7studios.com" id="dev7link" title="Go to dev7studios">dev7studios</a>
<div class="slider-wrapper theme-default">
<div id="slider" class="nivoSlider">
<img src="images/toystory.jpg" data-thumb="images/toystory.jpg" alt="" />
<a href="http://dev7studios.com"><img src="images/up.jpg" data-thumb="images/up.jpg" alt="" title="This is an example of a caption" /></a>
<img src="images/walle.jpg" data-thumb="images/walle.jpg" alt="" data-transition="slideInLeft" />
<img src="images/nemo.jpg" data-thumb="images/nemo.jpg" alt="" title="#htmlcaption" />
</div>
<div id="htmlcaption" class="nivo-html-caption">
<strong>This</strong> is an example of a <em>HTML</em> caption with <a href="#">a link</a>.
</div>
</div>
</div>
<script type="text/javascript" src="scripts/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="../jquery.nivo.slider.js"></script>
<script type="text/javascript">
$(window).load(function() {
$('#slider').nivoSlider();
});
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,103 @@
/*=================================*/
/* Nivo Slider Demo
/* November 2010
/* By: Gilbert Pellegrom
/* http://dev7studios.com
/*=================================*/
/*====================*/
/*=== Reset Styles ===*/
/*====================*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin:0;
padding:0;
border:0;
outline:0;
font-weight:inherit;
font-style:inherit;
font-size:100%;
font-family:inherit;
vertical-align:baseline;
}
body {
line-height:1;
color:black;
background:white;
}
table {
border-collapse:separate;
border-spacing:0;
}
caption, th, td {
text-align:left;
font-weight:normal;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:"";
}
blockquote, q {
quotes:"" "";
}
/* HTML5 tags */
header, section, footer,
aside, nav, article, figure {
display: block;
}
/*===================*/
/*=== Main Styles ===*/
/*===================*/
body {
font:14px/1.6 Georgia, Palatino, Palatino Linotype, Times, Times New Roman, serif;
color:#333;
background:#eee;
}
a, a:visited {
color:blue;
text-decoration:none;
}
a:hover, a:active {
color:#000;
text-decoration:none;
}
#dev7link {
position:absolute;
top:0;
left:50px;
background:url(images/dev7logo.png) no-repeat;
width:60px;
height:67px;
border:0;
display:block;
text-indent:-9999px;
}
.slider-wrapper {
width: 80%;
margin: 20px auto;
}
.theme-default #slider {
margin:100px auto 0 auto;
}
.theme-pascal.slider-wrapper,
.theme-orman.slider-wrapper {
margin-top:150px;
}
/*====================*/
/*=== Other Styles ===*/
/*====================*/
.clear {
clear:both;
}

View File

@@ -0,0 +1,673 @@
/*
* jQuery Nivo Slider v3.0.1
* http://nivo.dev7studios.com
*
* Copyright 2012, Dev7studios
* Free to use and abuse under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
(function($) {
var NivoSlider = function(element, options){
// Defaults are below
var settings = $.extend({}, $.fn.nivoSlider.defaults, options);
// Useful variables. Play carefully.
var vars = {
currentSlide: 0,
currentImage: '',
totalSlides: 0,
running: false,
paused: false,
stop: false,
controlNavEl: false
};
// Get this slider
var slider = $(element);
slider.data('nivo:vars', vars).addClass('nivoSlider');
// Find our slider children
var kids = slider.children();
kids.each(function() {
var child = $(this);
var link = '';
if(!child.is('img')){
if(child.is('a')){
child.addClass('nivo-imageLink');
link = child;
}
child = child.find('img:first');
}
// Get img width & height
var childWidth = (childWidth === 0) ? child.attr('width') : child.width(),
childHeight = (childHeight === 0) ? child.attr('height') : child.height();
if(link !== ''){
link.css('display','none');
}
child.css('display','none');
vars.totalSlides++;
});
// If randomStart
if(settings.randomStart){
settings.startSlide = Math.floor(Math.random() * vars.totalSlides);
}
// Set startSlide
if(settings.startSlide > 0){
if(settings.startSlide >= vars.totalSlides) { settings.startSlide = vars.totalSlides - 1; }
vars.currentSlide = settings.startSlide;
}
// Get initial image
if($(kids[vars.currentSlide]).is('img')){
vars.currentImage = $(kids[vars.currentSlide]);
} else {
vars.currentImage = $(kids[vars.currentSlide]).find('img:first');
}
// Show initial link
if($(kids[vars.currentSlide]).is('a')){
$(kids[vars.currentSlide]).css('display','block');
}
// Set first background
var sliderImg = $('<img class="nivo-main-image" src="#" />');
sliderImg.attr('src', vars.currentImage.attr('src')).show();
slider.append(sliderImg);
// Detect Window Resize
$(window).resize(function() {
slider.children('img').width(slider.width());
sliderImg.attr('src', vars.currentImage.attr('src'));
sliderImg.stop().height('auto');
$('.nivo-slice').remove();
$('.nivo-box').remove();
});
//Create caption
slider.append($('<div class="nivo-caption"></div>'));
// Process caption function
var processCaption = function(settings){
var nivoCaption = $('.nivo-caption', slider);
if(vars.currentImage.attr('title') != '' && vars.currentImage.attr('title') != undefined){
var title = vars.currentImage.attr('title');
if(title.substr(0,1) == '#') title = $(title).html();
if(nivoCaption.css('display') == 'block'){
setTimeout(function(){
nivoCaption.html(title);
}, settings.animSpeed);
} else {
nivoCaption.html(title);
nivoCaption.stop().fadeIn(settings.animSpeed);
}
} else {
nivoCaption.stop().fadeOut(settings.animSpeed);
}
}
//Process initial caption
processCaption(settings);
// In the words of Super Mario "let's a go!"
var timer = 0;
if(!settings.manualAdvance && kids.length > 1){
timer = setInterval(function(){ nivoRun(slider, kids, settings, false); }, settings.pauseTime);
}
// Add Direction nav
if(settings.directionNav){
slider.append('<div class="nivo-directionNav"><a class="nivo-prevNav">'+ settings.prevText +'</a><a class="nivo-nextNav">'+ settings.nextText +'</a></div>');
// Hide Direction nav
if(settings.directionNavHide){
$('.nivo-directionNav', slider).hide();
slider.hover(function(){
$('.nivo-directionNav', slider).show();
}, function(){
$('.nivo-directionNav', slider).hide();
});
}
$('a.nivo-prevNav', slider).live('click', function(){
if(vars.running) { return false; }
clearInterval(timer);
timer = '';
vars.currentSlide -= 2;
nivoRun(slider, kids, settings, 'prev');
});
$('a.nivo-nextNav', slider).live('click', function(){
if(vars.running) { return false; }
clearInterval(timer);
timer = '';
nivoRun(slider, kids, settings, 'next');
});
}
// Add Control nav
if(settings.controlNav){
vars.controlNavEl = $('<div class="nivo-controlNav"></div>');
slider.after(vars.controlNavEl);
for(var i = 0; i < kids.length; i++){
if(settings.controlNavThumbs){
vars.controlNavEl.addClass('nivo-thumbs-enabled');
var child = kids.eq(i);
if(!child.is('img')){
child = child.find('img:first');
}
if(child.attr('data-thumb')) vars.controlNavEl.append('<a class="nivo-control" rel="'+ i +'"><img src="'+ child.attr('data-thumb') +'" alt="" /></a>');
} else {
vars.controlNavEl.append('<a class="nivo-control" rel="'+ i +'">'+ (i + 1) +'</a>');
}
}
//Set initial active link
$('a:eq('+ vars.currentSlide +')', vars.controlNavEl).addClass('active');
$('a', vars.controlNavEl).bind('click', function(){
if(vars.running) return false;
if($(this).hasClass('active')) return false;
clearInterval(timer);
timer = '';
sliderImg.attr('src', vars.currentImage.attr('src'));
vars.currentSlide = $(this).attr('rel') - 1;
nivoRun(slider, kids, settings, 'control');
});
}
//For pauseOnHover setting
if(settings.pauseOnHover){
slider.hover(function(){
vars.paused = true;
clearInterval(timer);
timer = '';
}, function(){
vars.paused = false;
// Restart the timer
if(timer === '' && !settings.manualAdvance){
timer = setInterval(function(){ nivoRun(slider, kids, settings, false); }, settings.pauseTime);
}
});
}
// Event when Animation finishes
slider.bind('nivo:animFinished', function(){
sliderImg.attr('src', vars.currentImage.attr('src'));
vars.running = false;
// Hide child links
$(kids).each(function(){
if($(this).is('a')){
$(this).css('display','none');
}
});
// Show current link
if($(kids[vars.currentSlide]).is('a')){
$(kids[vars.currentSlide]).css('display','block');
}
// Restart the timer
if(timer === '' && !vars.paused && !settings.manualAdvance){
timer = setInterval(function(){ nivoRun(slider, kids, settings, false); }, settings.pauseTime);
}
// Trigger the afterChange callback
settings.afterChange.call(this);
});
// Add slices for slice animations
var createSlices = function(slider, settings, vars) {
if($(vars.currentImage).parent().is('a')) $(vars.currentImage).parent().css('display','block');
$('img[src="'+ vars.currentImage.attr('src') +'"]', slider).not('.nivo-main-image,.nivo-control img').width(slider.width()).css('visibility', 'hidden').show();
var sliceHeight = ($('img[src="'+ vars.currentImage.attr('src') +'"]', slider).not('.nivo-main-image,.nivo-control img').parent().is('a')) ? $('img[src="'+ vars.currentImage.attr('src') +'"]', slider).not('.nivo-main-image,.nivo-control img').parent().height() : $('img[src="'+ vars.currentImage.attr('src') +'"]', slider).not('.nivo-main-image,.nivo-control img').height();
for(var i = 0; i < settings.slices; i++){
var sliceWidth = Math.round(slider.width()/settings.slices);
if(i === settings.slices-1){
slider.append(
$('<div class="nivo-slice" name="'+i+'"><img src="'+ vars.currentImage.attr('src') +'" style="position:absolute; width:'+ slider.width() +'px; height:auto; display:block !important; top:0; left:-'+ ((sliceWidth + (i * sliceWidth)) - sliceWidth) +'px;" /></div>').css({
left:(sliceWidth*i)+'px',
width:(slider.width()-(sliceWidth*i))+'px',
height:sliceHeight+'px',
opacity:'0',
overflow:'hidden'
})
);
} else {
slider.append(
$('<div class="nivo-slice" name="'+i+'"><img src="'+ vars.currentImage.attr('src') +'" style="position:absolute; width:'+ slider.width() +'px; height:auto; display:block !important; top:0; left:-'+ ((sliceWidth + (i * sliceWidth)) - sliceWidth) +'px;" /></div>').css({
left:(sliceWidth*i)+'px',
width:sliceWidth+'px',
height:sliceHeight+'px',
opacity:'0',
overflow:'hidden'
})
);
}
}
$('.nivo-slice', slider).height(sliceHeight);
sliderImg.stop().animate({
height: $(vars.currentImage).height()
}, settings.animSpeed);
};
// Add boxes for box animations
var createBoxes = function(slider, settings, vars){
if($(vars.currentImage).parent().is('a')) $(vars.currentImage).parent().css('display','block');
$('img[src="'+ vars.currentImage.attr('src') +'"]', slider).not('.nivo-main-image,.nivo-control img').width(slider.width()).css('visibility', 'hidden').show();
var boxWidth = Math.round(slider.width()/settings.boxCols),
boxHeight = Math.round($('img[src="'+ vars.currentImage.attr('src') +'"]', slider).not('.nivo-main-image,.nivo-control img').height() / settings.boxRows);
for(var rows = 0; rows < settings.boxRows; rows++){
for(var cols = 0; cols < settings.boxCols; cols++){
if(cols === settings.boxCols-1){
slider.append(
$('<div class="nivo-box" name="'+ cols +'" rel="'+ rows +'"><img src="'+ vars.currentImage.attr('src') +'" style="position:absolute; width:'+ slider.width() +'px; height:auto; display:block; top:-'+ (boxHeight*rows) +'px; left:-'+ (boxWidth*cols) +'px;" /></div>').css({
opacity:0,
left:(boxWidth*cols)+'px',
top:(boxHeight*rows)+'px',
width:(slider.width()-(boxWidth*cols))+'px'
})
);
$('.nivo-box[name="'+ cols +'"]', slider).height($('.nivo-box[name="'+ cols +'"] img', slider).height()+'px');
} else {
slider.append(
$('<div class="nivo-box" name="'+ cols +'" rel="'+ rows +'"><img src="'+ vars.currentImage.attr('src') +'" style="position:absolute; width:'+ slider.width() +'px; height:auto; display:block; top:-'+ (boxHeight*rows) +'px; left:-'+ (boxWidth*cols) +'px;" /></div>').css({
opacity:0,
left:(boxWidth*cols)+'px',
top:(boxHeight*rows)+'px',
width:boxWidth+'px'
})
);
$('.nivo-box[name="'+ cols +'"]', slider).height($('.nivo-box[name="'+ cols +'"] img', slider).height()+'px');
}
}
}
sliderImg.stop().animate({
height: $(vars.currentImage).height()
}, settings.animSpeed);
};
// Private run method
var nivoRun = function(slider, kids, settings, nudge){
// Get our vars
var vars = slider.data('nivo:vars');
// Trigger the lastSlide callback
if(vars && (vars.currentSlide === vars.totalSlides - 1)){
settings.lastSlide.call(this);
}
// Stop
if((!vars || vars.stop) && !nudge) { return false; }
// Trigger the beforeChange callback
settings.beforeChange.call(this);
// Set current background before change
if(!nudge){
sliderImg.attr('src', vars.currentImage.attr('src'));
} else {
if(nudge === 'prev'){
sliderImg.attr('src', vars.currentImage.attr('src'));
}
if(nudge === 'next'){
sliderImg.attr('src', vars.currentImage.attr('src'));
}
}
vars.currentSlide++;
// Trigger the slideshowEnd callback
if(vars.currentSlide === vars.totalSlides){
vars.currentSlide = 0;
settings.slideshowEnd.call(this);
}
if(vars.currentSlide < 0) { vars.currentSlide = (vars.totalSlides - 1); }
// Set vars.currentImage
if($(kids[vars.currentSlide]).is('img')){
vars.currentImage = $(kids[vars.currentSlide]);
} else {
vars.currentImage = $(kids[vars.currentSlide]).find('img:first');
}
// Set active links
if(settings.controlNav){
$('a', vars.controlNavEl).removeClass('active');
$('a:eq('+ vars.currentSlide +')', vars.controlNavEl).addClass('active');
}
// Process caption
processCaption(settings);
// Remove any slices from last transition
$('.nivo-slice', slider).remove();
// Remove any boxes from last transition
$('.nivo-box', slider).remove();
var currentEffect = settings.effect,
anims = '';
// Generate random effect
if(settings.effect === 'random'){
anims = new Array('sliceDownRight','sliceDownLeft','sliceUpRight','sliceUpLeft','sliceUpDown','sliceUpDownLeft','fold','fade',
'boxRandom','boxRain','boxRainReverse','boxRainGrow','boxRainGrowReverse');
currentEffect = anims[Math.floor(Math.random()*(anims.length + 1))];
if(currentEffect === undefined) { currentEffect = 'fade'; }
}
// Run random effect from specified set (eg: effect:'fold,fade')
if(settings.effect.indexOf(',') !== -1){
anims = settings.effect.split(',');
currentEffect = anims[Math.floor(Math.random()*(anims.length))];
if(currentEffect === undefined) { currentEffect = 'fade'; }
}
// Custom transition as defined by "data-transition" attribute
if(vars.currentImage.attr('data-transition')){
currentEffect = vars.currentImage.attr('data-transition');
}
// Run effects
vars.running = true;
var timeBuff = 0,
i = 0,
slices = '',
firstSlice = '',
totalBoxes = '',
boxes = '';
if(currentEffect === 'sliceDown' || currentEffect === 'sliceDownRight' || currentEffect === 'sliceDownLeft'){
createSlices(slider, settings, vars);
timeBuff = 0;
i = 0;
slices = $('.nivo-slice', slider);
if(currentEffect === 'sliceDownLeft') { slices = $('.nivo-slice', slider)._reverse(); }
slices.each(function(){
var slice = $(this);
slice.css({ 'top': '0px' });
if(i === settings.slices-1){
setTimeout(function(){
slice.animate({opacity:'1.0' }, settings.animSpeed, '', function(){ slider.trigger('nivo:animFinished'); });
}, (100 + timeBuff));
} else {
setTimeout(function(){
slice.animate({opacity:'1.0' }, settings.animSpeed);
}, (100 + timeBuff));
}
timeBuff += 50;
i++;
});
} else if(currentEffect === 'sliceUp' || currentEffect === 'sliceUpRight' || currentEffect === 'sliceUpLeft'){
createSlices(slider, settings, vars);
timeBuff = 0;
i = 0;
slices = $('.nivo-slice', slider);
if(currentEffect === 'sliceUpLeft') { slices = $('.nivo-slice', slider)._reverse(); }
slices.each(function(){
var slice = $(this);
slice.css({ 'bottom': '0px' });
if(i === settings.slices-1){
setTimeout(function(){
slice.animate({opacity:'1.0' }, settings.animSpeed, '', function(){ slider.trigger('nivo:animFinished'); });
}, (100 + timeBuff));
} else {
setTimeout(function(){
slice.animate({opacity:'1.0' }, settings.animSpeed);
}, (100 + timeBuff));
}
timeBuff += 50;
i++;
});
} else if(currentEffect === 'sliceUpDown' || currentEffect === 'sliceUpDownRight' || currentEffect === 'sliceUpDownLeft'){
createSlices(slider, settings, vars);
timeBuff = 0;
i = 0;
var v = 0;
slices = $('.nivo-slice', slider);
if(currentEffect === 'sliceUpDownLeft') { slices = $('.nivo-slice', slider)._reverse(); }
slices.each(function(){
var slice = $(this);
if(i === 0){
slice.css('top','0px');
i++;
} else {
slice.css('bottom','0px');
i = 0;
}
if(v === settings.slices-1){
setTimeout(function(){
slice.animate({opacity:'1.0' }, settings.animSpeed, '', function(){ slider.trigger('nivo:animFinished'); });
}, (100 + timeBuff));
} else {
setTimeout(function(){
slice.animate({opacity:'1.0' }, settings.animSpeed);
}, (100 + timeBuff));
}
timeBuff += 50;
v++;
});
} else if(currentEffect === 'fold'){
createSlices(slider, settings, vars);
timeBuff = 0;
i = 0;
$('.nivo-slice', slider).each(function(){
var slice = $(this);
var origWidth = slice.width();
slice.css({ top:'0px', width:'0px' });
if(i === settings.slices-1){
setTimeout(function(){
slice.animate({ width:origWidth, opacity:'1.0' }, settings.animSpeed, '', function(){ slider.trigger('nivo:animFinished'); });
}, (100 + timeBuff));
} else {
setTimeout(function(){
slice.animate({ width:origWidth, opacity:'1.0' }, settings.animSpeed);
}, (100 + timeBuff));
}
timeBuff += 50;
i++;
});
} else if(currentEffect === 'fade'){
createSlices(slider, settings, vars);
firstSlice = $('.nivo-slice:first', slider);
firstSlice.css({
'width': slider.width() + 'px'
});
firstSlice.animate({ opacity:'1.0' }, (settings.animSpeed*2), '', function(){ slider.trigger('nivo:animFinished'); });
} else if(currentEffect === 'slideInRight'){
createSlices(slider, settings, vars);
firstSlice = $('.nivo-slice:first', slider);
firstSlice.css({
'width': '0px',
'opacity': '1'
});
firstSlice.animate({ width: slider.width() + 'px' }, (settings.animSpeed*2), '', function(){ slider.trigger('nivo:animFinished'); });
} else if(currentEffect === 'slideInLeft'){
createSlices(slider, settings, vars);
firstSlice = $('.nivo-slice:first', slider);
firstSlice.css({
'width': '0px',
'opacity': '1',
'left': '',
'right': '0px'
});
firstSlice.animate({ width: slider.width() + 'px' }, (settings.animSpeed*2), '', function(){
// Reset positioning
firstSlice.css({
'left': '0px',
'right': ''
});
slider.trigger('nivo:animFinished');
});
} else if(currentEffect === 'boxRandom'){
createBoxes(slider, settings, vars);
totalBoxes = settings.boxCols * settings.boxRows;
i = 0;
timeBuff = 0;
boxes = shuffle($('.nivo-box', slider));
boxes.each(function(){
var box = $(this);
if(i === totalBoxes-1){
setTimeout(function(){
box.animate({ opacity:'1' }, settings.animSpeed, '', function(){ slider.trigger('nivo:animFinished'); });
}, (100 + timeBuff));
} else {
setTimeout(function(){
box.animate({ opacity:'1' }, settings.animSpeed);
}, (100 + timeBuff));
}
timeBuff += 20;
i++;
});
} else if(currentEffect === 'boxRain' || currentEffect === 'boxRainReverse' || currentEffect === 'boxRainGrow' || currentEffect === 'boxRainGrowReverse'){
createBoxes(slider, settings, vars);
totalBoxes = settings.boxCols * settings.boxRows;
i = 0;
timeBuff = 0;
// Split boxes into 2D array
var rowIndex = 0;
var colIndex = 0;
var box2Darr = [];
box2Darr[rowIndex] = [];
boxes = $('.nivo-box', slider);
if(currentEffect === 'boxRainReverse' || currentEffect === 'boxRainGrowReverse'){
boxes = $('.nivo-box', slider)._reverse();
}
boxes.each(function(){
box2Darr[rowIndex][colIndex] = $(this);
colIndex++;
if(colIndex === settings.boxCols){
rowIndex++;
colIndex = 0;
box2Darr[rowIndex] = [];
}
});
// Run animation
for(var cols = 0; cols < (settings.boxCols * 2); cols++){
var prevCol = cols;
for(var rows = 0; rows < settings.boxRows; rows++){
if(prevCol >= 0 && prevCol < settings.boxCols){
/* Due to some weird JS bug with loop vars
being used in setTimeout, this is wrapped
with an anonymous function call */
(function(row, col, time, i, totalBoxes) {
var box = $(box2Darr[row][col]);
var w = box.width();
var h = box.height();
if(currentEffect === 'boxRainGrow' || currentEffect === 'boxRainGrowReverse'){
box.width(0).height(0);
}
if(i === totalBoxes-1){
setTimeout(function(){
box.animate({ opacity:'1', width:w, height:h }, settings.animSpeed/1.3, '', function(){ slider.trigger('nivo:animFinished'); });
}, (100 + time));
} else {
setTimeout(function(){
box.animate({ opacity:'1', width:w, height:h }, settings.animSpeed/1.3);
}, (100 + time));
}
})(rows, prevCol, timeBuff, i, totalBoxes);
i++;
}
prevCol--;
}
timeBuff += 100;
}
}
};
// Shuffle an array
var shuffle = function(arr){
for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i, 10), x = arr[--i], arr[i] = arr[j], arr[j] = x);
return arr;
};
// For debugging
var trace = function(msg){
if(this.console && typeof console.log !== 'undefined') { console.log(msg); }
};
// Start / Stop
this.stop = function(){
if(!$(element).data('nivo:vars').stop){
$(element).data('nivo:vars').stop = true;
trace('Stop Slider');
}
};
this.start = function(){
if($(element).data('nivo:vars').stop){
$(element).data('nivo:vars').stop = false;
trace('Start Slider');
}
};
// Trigger the afterLoad callback
settings.afterLoad.call(this);
return this;
};
$.fn.nivoSlider = function(options) {
return this.each(function(key, value){
var element = $(this);
// Return early if this element already has a plugin instance
if (element.data('nivoslider')) { return element.data('nivoslider'); }
// Pass options to plugin constructor
var nivoslider = new NivoSlider(this, options);
// Store plugin object in this element's data
element.data('nivoslider', nivoslider);
});
};
//Default settings
$.fn.nivoSlider.defaults = {
effect: 'random',
slices: 15,
boxCols: 8,
boxRows: 4,
animSpeed: 500,
pauseTime: 3000,
startSlide: 0,
directionNav: true,
directionNavHide: true,
controlNav: true,
controlNavThumbs: false,
pauseOnHover: true,
manualAdvance: false,
prevText: 'Prev',
nextText: 'Next',
randomStart: false,
beforeChange: function(){},
afterChange: function(){},
slideshowEnd: function(){},
lastSlide: function(){},
afterLoad: function(){}
};
$.fn._reverse = [].reverse;
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
Copyright (c) 2010-2012 Dev7studios
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,109 @@
/*
* jQuery Nivo Slider v3.0.1
* http://nivo.dev7studios.com
*
* Copyright 2012, Dev7studios
* Free to use and abuse under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
/* The Nivo Slider styles */
.nivoSlider {
position:relative;
width:100%;
height:auto;
overflow: hidden;
}
.nivoSlider img {
position:absolute;
top:0px;
left:0px;
}
.nivo-main-image {
display: block !important;
position: relative !important;
width: 100% !important;
}
/* If an image is wrapped in a link */
.nivoSlider a.nivo-imageLink {
position:absolute;
top:0px;
left:0px;
width:100%;
height:100%;
border:0;
padding:0;
margin:0;
z-index:6;
display:none;
}
/* The slices and boxes in the Slider */
.nivo-slice {
display:block;
position:absolute;
z-index:5;
height:100%;
top:0;
}
.nivo-box {
display:block;
position:absolute;
z-index:5;
overflow:hidden;
}
.nivo-box img { display:block; }
/* Caption styles */
.nivo-caption {
position:absolute;
left:0px;
bottom:0px;
background:#000;
color:#fff;
width:100%;
z-index:8;
padding: 5px 10px;
opacity: 0.8;
overflow: hidden;
display: none;
-moz-opacity: 0.8;
filter:alpha(opacity=8);
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
}
.nivo-caption p {
padding:5px;
margin:0;
}
.nivo-caption a {
display:inline !important;
}
.nivo-html-caption {
display:none;
}
/* Direction nav styles (e.g. Next & Prev) */
.nivo-directionNav a {
position:absolute;
top:45%;
z-index:9;
cursor:pointer;
}
.nivo-prevNav {
left:0px;
}
.nivo-nextNav {
right:0px;
}
/* Control nav styles (e.g. 1,2,3...) */
.nivo-controlNav {
text-align:center;
padding: 15px 0;
}
.nivo-controlNav a {
cursor:pointer;
}
.nivo-controlNav a.active {
font-weight:bold;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,87 @@
/*
Skin Name: Nivo Slider Default Theme
Skin URI: http://nivo.dev7studios.com
Skin Type: flexible
Description: The default skin for the Nivo Slider.
Version: 1.2
Author: Gilbert Pellegrom
Author URI: http://dev7studios.com
*/
.theme-default .nivoSlider {
position:relative;
background:#fff url(loading.gif) no-repeat 50% 50%;
margin-bottom:50px;
-webkit-box-shadow: 0px 1px 5px 0px #4a4a4a;
-moz-box-shadow: 0px 1px 5px 0px #4a4a4a;
box-shadow: 0px 1px 5px 0px #4a4a4a;
}
.theme-default .nivoSlider img {
position:absolute;
top:0px;
left:0px;
display:none;
}
.theme-default .nivoSlider a {
border:0;
display:block;
}
.theme-default .nivo-controlNav {
text-align: center;
padding: 20px 0;
}
.theme-default .nivo-controlNav a {
display:inline-block;
width:22px;
height:22px;
background:url(bullets.png) no-repeat;
text-indent:-9999px;
border:0;
margin: 0 2px;
}
.theme-default .nivo-controlNav a.active {
background-position:0 -22px;
}
.theme-default .nivo-directionNav a {
display:block;
width:30px;
height:30px;
background:url(arrows.png) no-repeat;
text-indent:-9999px;
border:0;
}
.theme-default a.nivo-nextNav {
background-position:-30px 0;
right:15px;
}
.theme-default a.nivo-prevNav {
left:15px;
}
.theme-default .nivo-caption {
font-family: Helvetica, Arial, sans-serif;
}
.theme-default .nivo-caption a {
color:#fff;
border-bottom:1px dotted #fff;
}
.theme-default .nivo-caption a:hover {
color:#fff;
}
.theme-default .nivo-controlNav.nivo-thumbs-enabled {
width: 100%;
}
.theme-default .nivo-controlNav.nivo-thumbs-enabled a {
width: auto;
height: auto;
background: none;
margin-bottom: 5px;
}
.theme-default .nivo-controlNav.nivo-thumbs-enabled img {
display: block;
width: 120px;
height: auto;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -31,13 +31,13 @@
<body>
<header>
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore.png" /></a>
<h1><a href="index.html"><img class="logo" src="img/iTunesArtwork-Bare.png" /> Master Password</a></h1>
<div class="divider"></div>
</header>
<div id="fixedheader">
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore-small.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore-small.png" /></a>
<h2><a href="index.html">Master Password</a></h2>
</div>
<!--a href="http://bit.ly/vNN5Zi" onclick="_gaq.push(['_trackPageview', '/outbound/testflight']);" id="ribbon"></a-->

View File

@@ -85,10 +85,10 @@
</script>
</head>
<body>
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore.png" /></a>
<header>
<a class="appstore" href="http://itunes.com/apps/MasterPassword"><img src="img/appstore.png" /></a>
<a class="appstore" href="http://itunes.apple.com/app/id510296984"><img src="img/appstore.png" /></a>
<h1><a href="index.html"><img class="logo" src="img/iTunesArtwork-Bare.png" /> Master Password</a></h1>
<div class="divider"></div>

22
Tests/Tests-Info.plist Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

10
Tests/Tests-Prefix.pch Normal file
View File

@@ -0,0 +1,10 @@
//
// Prefix header for all source files of the 'Tests' target in the 'Tests' project
//
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif
#import "MasterPassword-Prefix.pch"

13
Tests/Tests.h Normal file
View File

@@ -0,0 +1,13 @@
//
// Tests.h
// Tests
//
// Created by Maarten Billemont on 04/07/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <SenTestingKit/SenTestingKit.h>
@interface Tests : SenTestCase
@end

45
Tests/Tests.m Normal file
View File

@@ -0,0 +1,45 @@
//
// Tests.m
// Tests
//
// Created by Maarten Billemont on 04/07/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "Tests.h"
@implementation Tests
- (void)setUp
{
dbg(@"======================= TEST SET-UP ======================");
[PearlLogger get].autoprintLevel = PearlLogLevelTrace;
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
dbg(@"===================== TEST TEAR-DOWN =====================");
// Tear-down code here.
[super tearDown];
}
- (void)testAlgorithm
{
NSString *masterPassword = @"test-mp";
NSString *username = @"test-user";
NSString *siteName = @"test-site";
MPElementType siteType = MPElementTypeGeneratedLong;
uint32_t siteCounter = 42;
NSString *sitePassword = MPCalculateContent( siteType, siteName, keyForPassword( masterPassword, username ), siteCounter );
inf( @"master password: %@, username: %@\nsite name: %@, site type: %@, site counter: %d\n => site password: %@",
masterPassword, username, siteName, NSStringFromMPElementType(siteType), siteCounter, sitePassword );
}
@end

Some files were not shown because too many files have changed in this diff Show More