diff --git a/Default.png b/Default.png
index af1b56ab..620ec364 100644
Binary files a/Default.png and b/Default.png differ
diff --git a/Default@2x.png b/Default@2x.png
index dacaed30..e975220d 100644
Binary files a/Default@2x.png and b/Default@2x.png differ
diff --git a/MasterPassword.xcodeproj/project.pbxproj b/MasterPassword.xcodeproj/project.pbxproj
index 7a892a27..150f6fd2 100644
--- a/MasterPassword.xcodeproj/project.pbxproj
+++ b/MasterPassword.xcodeproj/project.pbxproj
@@ -19,6 +19,16 @@
DA41A40B14DB3BF100638533 /* guide_page_0.png in Resources */ = {isa = PBXBuildFile; fileRef = DA41A40914DB3BF100638533 /* guide_page_0.png */; };
DA41A40C14DB3BF100638533 /* guide_page_0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA41A40A14DB3BF100638533 /* guide_page_0@2x.png */; };
DA55B2A214B4EB47001131B7 /* MPSearchDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA55B2A114B4EB46001131B7 /* MPSearchDelegate.m */; };
+ DA566D2914F8EAF200A6EB2E /* lock_blue.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2014F8EAF200A6EB2E /* lock_blue.png */; };
+ DA566D2A14F8EAF200A6EB2E /* lock_blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2114F8EAF200A6EB2E /* lock_blue@2x.png */; };
+ DA566D2B14F8EAF200A6EB2E /* lock_green.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2214F8EAF200A6EB2E /* lock_green.png */; };
+ DA566D2C14F8EAF200A6EB2E /* lock_green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2314F8EAF200A6EB2E /* lock_green@2x.png */; };
+ DA566D2D14F8EAF200A6EB2E /* lock_idle.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2414F8EAF200A6EB2E /* lock_idle.png */; };
+ DA566D2E14F8EAF200A6EB2E /* lock_idle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2514F8EAF200A6EB2E /* lock_idle@2x.png */; };
+ DA566D2F14F8EAF200A6EB2E /* lock_red.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2614F8EAF200A6EB2E /* lock_red.png */; };
+ DA566D3014F8EAF200A6EB2E /* lock_red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D2714F8EAF200A6EB2E /* lock_red@2x.png */; };
+ DA566D3514F8EB0700A6EB2E /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D3314F8EB0700A6EB2E /* background@2x.png */; };
+ DA566D3714F8EB3B00A6EB2E /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = DA566D3614F8EB3B00A6EB2E /* background.png */; };
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; };
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; };
@@ -671,8 +681,6 @@
DAC781331482AAD800BCF976 /* WebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC780F01482AAD700BCF976 /* WebViewController.m */; };
DADC3C4D14C62B350091CB4D /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DADC3C4C14C62B350091CB4D /* Settings.bundle */; };
DAE2C648148247E500BA6B10 /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE2C646148247E500BA6B10 /* MPTypeViewController.m */; };
- DAE998D214C1D2A0002D7C22 /* Content-Backdrop.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */; };
- DAE998D314C1D2A0002D7C22 /* Content-Backdrop@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987A14C1D2A0002D7C22 /* Content-Backdrop@2x.png */; };
DAE998D414C1D2A0002D7C22 /* Bold_Lines.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987C14C1D2A0002D7C22 /* Bold_Lines.png */; };
DAE998D514C1D2A0002D7C22 /* Box.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987D14C1D2A0002D7C22 /* Box.png */; };
DAE998D614C1D2A0002D7C22 /* Dashed_Divider.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987E14C1D2A0002D7C22 /* Dashed_Divider.png */; };
@@ -711,6 +719,8 @@
DAE998F714C1D2A0002D7C22 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9989F14C1D2A0002D7C22 /* Icon.png */; };
DAE998F814C1D2A0002D7C22 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE998A014C1D2A0002D7C22 /* Icon@2x.png */; };
DAE998F914C1D2A0002D7C22 /* iTunesArtwork.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE998A114C1D2A0002D7C22 /* iTunesArtwork.png */; };
+ DAEBC45114F5A4E800987BF6 /* MPUnlockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEBC45014F5A4E800987BF6 /* MPUnlockViewController.m */; };
+ DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -783,6 +793,16 @@
DA41A40A14DB3BF100638533 /* guide_page_0@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "guide_page_0@2x.png"; sourceTree = ""; };
DA55B2A014B4EB46001131B7 /* MPSearchDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSearchDelegate.h; sourceTree = ""; };
DA55B2A114B4EB46001131B7 /* MPSearchDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSearchDelegate.m; sourceTree = ""; };
+ DA566D2014F8EAF200A6EB2E /* lock_blue.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lock_blue.png; sourceTree = ""; };
+ DA566D2114F8EAF200A6EB2E /* lock_blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "lock_blue@2x.png"; sourceTree = ""; };
+ DA566D2214F8EAF200A6EB2E /* lock_green.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lock_green.png; sourceTree = ""; };
+ DA566D2314F8EAF200A6EB2E /* lock_green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "lock_green@2x.png"; sourceTree = ""; };
+ DA566D2414F8EAF200A6EB2E /* lock_idle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lock_idle.png; sourceTree = ""; };
+ DA566D2514F8EAF200A6EB2E /* lock_idle@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "lock_idle@2x.png"; sourceTree = ""; };
+ DA566D2614F8EAF200A6EB2E /* lock_red.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lock_red.png; sourceTree = ""; };
+ DA566D2714F8EAF200A6EB2E /* lock_red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "lock_red@2x.png"; sourceTree = ""; };
+ DA566D3314F8EB0700A6EB2E /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = ""; };
+ DA566D3614F8EB3B00A6EB2E /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = background.png; sourceTree = ""; };
DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA48147E415C00F98B1E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -1442,8 +1462,6 @@
DAC780F01482AAD700BCF976 /* WebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewController.m; sourceTree = ""; };
DADC3C4C14C62B350091CB4D /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; };
DAE2C646148247E500BA6B10 /* MPTypeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTypeViewController.m; sourceTree = ""; };
- DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Content-Backdrop.png"; path = "Resources/Content-Backdrop.png"; sourceTree = ""; };
- DAE9987A14C1D2A0002D7C22 /* Content-Backdrop@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Content-Backdrop@2x.png"; path = "Resources/Content-Backdrop@2x.png"; sourceTree = ""; };
DAE9987C14C1D2A0002D7C22 /* Bold_Lines.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Bold_Lines.png; sourceTree = ""; };
DAE9987D14C1D2A0002D7C22 /* Box.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Box.png; sourceTree = ""; };
DAE9987E14C1D2A0002D7C22 /* Dashed_Divider.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Dashed_Divider.png; sourceTree = ""; };
@@ -1482,6 +1500,9 @@
DAE9989F14C1D2A0002D7C22 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = Resources/Icon.png; sourceTree = ""; };
DAE998A014C1D2A0002D7C22 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon@2x.png"; path = "Resources/Icon@2x.png"; sourceTree = ""; };
DAE998A114C1D2A0002D7C22 /* iTunesArtwork.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = iTunesArtwork.png; path = Resources/iTunesArtwork.png; sourceTree = ""; };
+ DAEBC44F14F5A4E800987BF6 /* MPUnlockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUnlockViewController.h; sourceTree = ""; };
+ DAEBC45014F5A4E800987BF6 /* MPUnlockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUnlockViewController.m; sourceTree = ""; };
+ DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1489,6 +1510,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
DA95D5ED14DF08AF008D1B94 /* libInAppSettingsKit.a in Frameworks */,
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
@@ -1900,6 +1922,32 @@
name = Products;
sourceTree = "";
};
+ DA566D1E14F8EAF200A6EB2E /* Lock */ = {
+ isa = PBXGroup;
+ children = (
+ DA566D2014F8EAF200A6EB2E /* lock_blue.png */,
+ DA566D2114F8EAF200A6EB2E /* lock_blue@2x.png */,
+ DA566D2214F8EAF200A6EB2E /* lock_green.png */,
+ DA566D2314F8EAF200A6EB2E /* lock_green@2x.png */,
+ DA566D2414F8EAF200A6EB2E /* lock_idle.png */,
+ DA566D2514F8EAF200A6EB2E /* lock_idle@2x.png */,
+ DA566D2614F8EAF200A6EB2E /* lock_red.png */,
+ DA566D2714F8EAF200A6EB2E /* lock_red@2x.png */,
+ );
+ name = Lock;
+ path = Resources/Lock;
+ sourceTree = "";
+ };
+ DA566D3114F8EB0700A6EB2E /* Background */ = {
+ isa = PBXGroup;
+ children = (
+ DA566D3614F8EB3B00A6EB2E /* background.png */,
+ DA566D3314F8EB0700A6EB2E /* background@2x.png */,
+ );
+ name = Background;
+ path = Resources/Background;
+ sourceTree = "";
+ };
DA5BFA39147E415C00F98B1E = {
isa = PBXGroup;
children = (
@@ -1931,6 +1979,7 @@
DA5BFA47147E415C00F98B1E /* Frameworks */ = {
isa = PBXGroup;
children = (
+ DAEBC45214F6364500987BF6 /* QuartzCore.framework */,
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */,
DAC632871486D95D0075AEA5 /* Security.framework */,
DA5BFA48147E415C00F98B1E /* UIKit.framework */,
@@ -1945,8 +1994,10 @@
isa = PBXGroup;
children = (
DA8E8E4714DDA62D0044257E /* MasterPassword.entitlements */,
+ DA5BFA51147E415C00F98B1E /* Supporting Files */,
DA7C28A214AF02A000491972 /* Models */,
DA7C28A314AF02B100491972 /* Data */,
+ DADC3C4C14C62B350091CB4D /* Settings.bundle */,
DA5BFA59147E415C00F98B1E /* MPAppDelegate.h */,
DA5BFA5A147E415C00F98B1E /* MPAppDelegate.m */,
DA5BFA65147E415C00F98B1E /* MPMainViewController.h */,
@@ -1957,12 +2008,12 @@
DA55B2A114B4EB46001131B7 /* MPSearchDelegate.m */,
DA95D5FC14DF295F008D1B94 /* MPTypeViewController.h */,
DAE2C646148247E500BA6B10 /* MPTypeViewController.m */,
+ DAEBC44F14F5A4E800987BF6 /* MPUnlockViewController.h */,
+ DAEBC45014F5A4E800987BF6 /* MPUnlockViewController.m */,
DA007F5014B24DCC00251337 /* MPConfig.h */,
DA95D5F914DF295E008D1B94 /* MPConfig.m */,
DA95D5FA14DF295E008D1B94 /* MPTypes.h */,
DA95D5FB14DF295F008D1B94 /* MPTypes.m */,
- DADC3C4C14C62B350091CB4D /* Settings.bundle */,
- DA5BFA51147E415C00F98B1E /* Supporting Files */,
);
path = MasterPassword;
sourceTree = "";
@@ -1970,6 +2021,8 @@
DA5BFA51147E415C00F98B1E /* Supporting Files */ = {
isa = PBXGroup;
children = (
+ DA566D3114F8EB0700A6EB2E /* Background */,
+ DA566D1E14F8EAF200A6EB2E /* Lock */,
DA8E8E4514DD7C1D0044257E /* logo-bare.png */,
DA6556F714D730B700841C99 /* Guide */,
DAA3B80414CDBBC600F35AF6 /* jquery-1.6.1.min.js */,
@@ -1977,8 +2030,6 @@
DA0B951214C37486001D4EB1 /* Insignia */,
DA0B951014C2D69E001D4EB1 /* help.html */,
DAE998FA14C1D3F9002D7C22 /* Automaton */,
- DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */,
- DAE9987A14C1D2A0002D7C22 /* Content-Backdrop@2x.png */,
DAE9987B14C1D2A0002D7C22 /* Dividers */,
DAE9989B14C1D2A0002D7C22 /* Icon-72.png */,
DAE9989C14C1D2A0002D7C22 /* Icon-Small-50.png */,
@@ -2671,7 +2722,7 @@
DA5BFA3B147E415C00F98B1E /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0420;
+ LastUpgradeCheck = 0430;
ORGANIZATIONNAME = Lyndir;
};
buildConfigurationList = DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword" */;
@@ -2738,8 +2789,6 @@
DA007F5514B25EE100251337 /* ciphers.plist in Resources */,
DA5DB7A614BE4B19002DD256 /* Default.png in Resources */,
DA5DB7A814BE4B4B002DD256 /* Default@2x.png in Resources */,
- DAE998D214C1D2A0002D7C22 /* Content-Backdrop.png in Resources */,
- DAE998D314C1D2A0002D7C22 /* Content-Backdrop@2x.png in Resources */,
DAE998D414C1D2A0002D7C22 /* Bold_Lines.png in Resources */,
DAE998D514C1D2A0002D7C22 /* Box.png in Resources */,
DAE998D614C1D2A0002D7C22 /* Dashed_Divider.png in Resources */,
@@ -3299,6 +3348,16 @@
DA95D5F614DF0B9F008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib in Resources */,
DA95D5F714DF0B9F008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib in Resources */,
DA95D5F814DF0B9F008D1B94 /* IASKSpecifierValuesView.xib in Resources */,
+ DA566D2914F8EAF200A6EB2E /* lock_blue.png in Resources */,
+ DA566D2A14F8EAF200A6EB2E /* lock_blue@2x.png in Resources */,
+ DA566D2B14F8EAF200A6EB2E /* lock_green.png in Resources */,
+ DA566D2C14F8EAF200A6EB2E /* lock_green@2x.png in Resources */,
+ DA566D2D14F8EAF200A6EB2E /* lock_idle.png in Resources */,
+ DA566D2E14F8EAF200A6EB2E /* lock_idle@2x.png in Resources */,
+ DA566D2F14F8EAF200A6EB2E /* lock_red.png in Resources */,
+ DA566D3014F8EAF200A6EB2E /* lock_red@2x.png in Resources */,
+ DA566D3514F8EB0700A6EB2E /* background@2x.png in Resources */,
+ DA566D3714F8EB3B00A6EB2E /* background.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3315,8 +3374,8 @@
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
- shellPath = "/usr/bin/env bash";
- shellScript = "PATH+=:/usr/libexec\nset -e\n\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n\n PlistBuddy -c \"Set :$key $value\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :$key\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n\n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\nbuild=$(git describe --tags --always --dirty --long)\ntag=$(git describe --tags | sed 's/-[^-]*-[^-]*$//')\n\nsetPlistWithKey CFBundleVersion \"$build\"\nsetPlistWithKey CFBundleShortVersionString \"$tag\"\n\nsetSettingWithTitle \"Build\" \"$build\"\nsetSettingWithTitle \"Version\" \"$tag\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n";
+ shellPath = "/bin/bash -e";
+ shellScript = "PATH+=:/usr/libexec\n\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n\n PlistBuddy -c \"Set :$key $value\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :$key\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n\n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\nbuild=$(git describe --tags --always --dirty --long)\ntag=$(git describe --tags | sed 's/-\\([^-]*\\)-[^-]*$/.\\1/')\n\nsetPlistWithKey CFBundleVersion \"$build\"\nsetPlistWithKey CFBundleShortVersionString \"$tag\"\n\nsetSettingWithTitle \"Build\" \"$build\"\nsetSettingWithTitle \"Version\" \"$tag\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -3339,6 +3398,7 @@
DA65570614D731F000841C99 /* MPGuideViewController.m in Sources */,
DA95D5FD14DF295F008D1B94 /* MPConfig.m in Sources */,
DA95D5FE14DF295F008D1B94 /* MPTypes.m in Sources */,
+ DAEBC45114F5A4E800987BF6 /* MPUnlockViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme b/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme
index fdcd64d2..b1131020 100644
--- a/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme
+++ b/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme
@@ -1,5 +1,6 @@
diff --git a/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme b/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme
index 04bf3a74..6ecbf969 100644
--- a/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme
+++ b/MasterPassword.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme
@@ -1,5 +1,6 @@
@@ -56,6 +58,11 @@
+
+
a key phrase is set.
- // Make sure the user's entered key phrase matches it.
- if (![keyPhraseHash isEqual:answerHash]) {
- dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, answerHash);
-
-#ifndef PRODUCTION
- [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
-#endif
- [AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
- message:
- @"Incorrect master password.\n\n"
- @"If you are trying to use the app with a different master password, "
- @"flip the 'Change my password' option in Settings."
- viewStyle:UIAlertViewStyleDefault
- tappedButtonBlock:
- ^(UIAlertView *alert, NSInteger buttonIndex) {
- exit(0);
- } cancelTitle:@"Quit" otherTitles:nil];
-
- return;
- }
-
-#ifndef PRODUCTION
- [TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked];
-#endif
-
- self.keyPhrase = answerKeyPhrase;
- } cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
- });
+ [self.navigationController presentViewController:[self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"] animated:NO completion:nil];
}
- (void)applicationWillResignActive:(UIApplication *)application {
@@ -337,10 +286,45 @@
}];
}
+- (BOOL)tryMasterPassword:(NSString *)tryPassword {
+
+ NSData *keyPhraseHash = [KeyChain dataOfItemForQuery:[MPAppDelegate keyPhraseHashQuery]];
+ dbg(@"Key phrase hash %@.", keyPhraseHash? @"known": @"NOT known");
+
+ if (![tryPassword length])
+ return NO;
+
+ NSData *tryKeyPhrase = keyPhraseForPassword(tryPassword);
+ NSData *tryKeyPhraseHash = keyPhraseHashForKeyPhrase(tryKeyPhrase);
+ if (keyPhraseHash)
+ // A key phrase hash is known -> a key phrase is set.
+ // Make sure the user's entered key phrase matches it.
+ if (![keyPhraseHash isEqual:tryKeyPhraseHash]) {
+ dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, tryKeyPhraseHash);
+
+#ifndef PRODUCTION
+ [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
+#endif
+ return NO;
+ }
+
+#ifndef PRODUCTION
+ [TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked];
+#endif
+
+ self.keyPhrase = tryKeyPhrase;
+ return YES;
+}
+
- (void)setKeyPhrase:(NSData *)keyPhrase {
_keyPhrase = keyPhrase;
+ if (keyPhrase)
+ [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self];
+ else
+ [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self];
+
if (keyPhrase) {
self.keyPhraseHash = keyPhraseHashForKeyPhrase(keyPhrase);
self.keyPhraseHashHex = [self.keyPhraseHash encodeHex];
diff --git a/MasterPassword/MPElementStoredEntity.m b/MasterPassword/MPElementStoredEntity.m
index c1b856c0..33cf98e9 100644
--- a/MasterPassword/MPElementStoredEntity.m
+++ b/MasterPassword/MPElementStoredEntity.m
@@ -46,7 +46,7 @@
- (void)setContent:(id)content {
- NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[MPAppDelegate get].keyPhrase
+ NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[[MPAppDelegate get] keyPhraseWithLength:kCipherKeySize]
usePadding:YES];
if (self.type == MPElementTypeStoredDevicePrivate) {
diff --git a/MasterPassword/MPMainViewController.m b/MasterPassword/MPMainViewController.m
index 522dee7a..638f8055 100644
--- a/MasterPassword/MPMainViewController.m
+++ b/MasterPassword/MPMainViewController.m
@@ -114,6 +114,9 @@
}
}];
+ self.alertBody.text = nil;
+ self.contentTipEditIcon.alpha = 0;
+
[super viewDidLoad];
}
@@ -167,7 +170,7 @@
self.contentField.enabled = NO;
if ([self.activeElement isKindOfClass:[MPElementGeneratedEntity class]])
- self.passwordCounter.text = [NSString stringWithFormat:@"%d", ((MPElementGeneratedEntity *) self.activeElement).counter];
+ self.passwordCounter.text = [NSString stringWithFormat:@"%u", ((MPElementGeneratedEntity *) self.activeElement).counter];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *description = self.activeElement.description;
@@ -194,7 +197,7 @@
if (hidden) {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 373);
- self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 415);
+ self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 416);
[MPConfig get].helpHidden = [NSNumber numberWithBool:YES];
} else {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 175);
@@ -296,7 +299,7 @@
[self updateAnimated:YES];
// Show new and old password.
- if (oldPassword && ![oldPassword isEqualToString:newPassword])
+ if ([oldPassword length] && ![oldPassword isEqualToString:newPassword])
[self showAlertWithTitle:@"Password Changed!" message:l(@"The password for %@ has changed.\n\n"
@"Don't forget to update the site with your new password! "
@"Your old password was:\n"
diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h
index ba405a48..cb862e9a 100644
--- a/MasterPassword/MPTypes.h
+++ b/MasterPassword/MPTypes.h
@@ -33,30 +33,35 @@ typedef enum {
} MPElementType;
#ifndef PRODUCTION
-#define MPTestFlightCheckpointAction @"MPTestFlightCheckpointAction"
-#define MPTestFlightCheckpointHelpChapter @"MPTestFlightCheckpointHelpChapter_%@"
-#define MPTestFlightCheckpointCopyToPasteboard @"MPTestFlightCheckpointCopyToPasteboard"
-#define MPTestFlightCheckpointIncrementPasswordCounter @"MPTestFlightCheckpointIncrementPasswordCounter"
-#define MPTestFlightCheckpointEditPassword @"MPTestFlightCheckpointEditPassword"
-#define MPTestFlightCheckpointCloseAlert @"MPTestFlightCheckpointCloseAlert"
-#define MPTestFlightCheckpointSelectType @"MPTestFlightCheckpointSelectType_%@"
-#define MPTestFlightCheckpointSelectElement @"MPTestFlightCheckpointSelectElement"
-#define MPTestFlightCheckpointDeleteElement @"MPTestFlightCheckpointDeleteElement"
-#define MPTestFlightCheckpointCancelSearch @"MPTestFlightCheckpointCancelSearch"
-#define MPTestFlightCheckpointExternalLink @"MPTestFlightCheckpointExternalLink"
-#define MPTestFlightCheckpointLaunched @"MPTestFlightCheckpointLaunched"
-#define MPTestFlightCheckpointActivated @"MPTestFlightCheckpointActivated"
-#define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated"
-#define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated"
-#define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide"
-#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged"
-#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored"
-#define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch"
-#define MPTestFlightCheckpointMPAsked @"MPTestFlightCheckpointMPAsked"
-#define MPTestFlightCheckpointStoreIncompatible @"MPTestFlightCheckpointStoreIncompatible"
-#define MPTestFlightCheckpointSetKeyphraseLength @"MPTestFlightCheckpointSetKeyphraseLength_%d"
+#define MPTestFlightCheckpointAction @"MPTestFlightCheckpointAction"
+#define MPTestFlightCheckpointHelpChapter @"MPTestFlightCheckpointHelpChapter_%@"
+#define MPTestFlightCheckpointCopyToPasteboard @"MPTestFlightCheckpointCopyToPasteboard"
+#define MPTestFlightCheckpointIncrementPasswordCounter @"MPTestFlightCheckpointIncrementPasswordCounter"
+#define MPTestFlightCheckpointEditPassword @"MPTestFlightCheckpointEditPassword"
+#define MPTestFlightCheckpointCloseAlert @"MPTestFlightCheckpointCloseAlert"
+#define MPTestFlightCheckpointSelectType @"MPTestFlightCheckpointSelectType_%@"
+#define MPTestFlightCheckpointSelectElement @"MPTestFlightCheckpointSelectElement"
+#define MPTestFlightCheckpointDeleteElement @"MPTestFlightCheckpointDeleteElement"
+#define MPTestFlightCheckpointCancelSearch @"MPTestFlightCheckpointCancelSearch"
+#define MPTestFlightCheckpointExternalLink @"MPTestFlightCheckpointExternalLink"
+#define MPTestFlightCheckpointLaunched @"MPTestFlightCheckpointLaunched"
+#define MPTestFlightCheckpointActivated @"MPTestFlightCheckpointActivated"
+#define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated"
+#define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated"
+#define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide"
+#define MPTestFlightCheckpointMPForgotten @"MPTestFlightCheckpointMPForgotten"
+#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged"
+#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored"
+#define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch"
+#define MPTestFlightCheckpointMPAsked @"MPTestFlightCheckpointMPAsked"
+#define MPTestFlightCheckpointStoreIncompatible @"MPTestFlightCheckpointStoreIncompatible"
+#define MPTestFlightCheckpointSetKeyphraseLength @"MPTestFlightCheckpointSetKeyphraseLength_%d"
#endif
+#define MPNotificationKeySet @"MPNotificationKeySet"
+#define MPNotificationKeyUnset @"MPNotificationKeyUnset"
+#define MPNotificationKeyForgotten @"MPNotificationKeyForgotten"
+
NSData *keyPhraseForPassword(NSString *password);
NSData *keyPhraseHashForPassword(NSString *password);
NSData *keyPhraseHashForKeyPhrase(NSData *keyPhrase);
diff --git a/MasterPassword/MPTypes.m b/MasterPassword/MPTypes.m
index 0c66e628..1f9ebb21 100644
--- a/MasterPassword/MPTypes.m
+++ b/MasterPassword/MPTypes.m
@@ -10,15 +10,16 @@
#import "MPElementGeneratedEntity.h"
#import "MPElementStoredEntity.h"
-#define MP_salt nil
-#define MP_N 16384
-#define MP_r 8
-#define MP_p 1
-#define MP_hash PearlDigestSHA256
+#define MP_salt nil
+#define MP_N 16384
+#define MP_r 8
+#define MP_p 1
+#define MP_dkLen 64
+#define MP_hash PearlDigestSHA256
NSData *keyPhraseForPassword(NSString *password) {
- return [SCrypt deriveKeyWithLength:64 fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
+ return [SCrypt deriveKeyWithLength:MP_dkLen fromPassword:[password dataUsingEncoding:NSUTF8StringEncoding]
usingSalt:MP_salt N:MP_N r:MP_r p:MP_p];
}
NSData *keyPhraseHashForPassword(NSString *password) {
@@ -109,10 +110,11 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *keyPhra
// Determine the hash whose bytes will be used for calculating a password: md4(name-keyPhrase)
assert(name && keyPhrase);
+ uint16_t ncounter = htons(counter);
NSData *keyHash = [[NSData dataByConcatenatingWithDelimitor:'-' datas:
[name dataUsingEncoding:NSUTF8StringEncoding],
keyPhrase,
- htonl(counter),
+ [NSData dataWithBytes:&ncounter length:sizeof(ncounter)],
nil] hashWith:PearlDigestSHA1];
const char *keyBytes = keyHash.bytes;
diff --git a/MasterPassword/MPUnlockViewController.h b/MasterPassword/MPUnlockViewController.h
new file mode 100644
index 00000000..703b44e3
--- /dev/null
+++ b/MasterPassword/MPUnlockViewController.h
@@ -0,0 +1,21 @@
+//
+// MBUnlockViewController.h
+// MasterPassword
+//
+// Created by Maarten Billemont on 22/02/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import
+
+@interface MPUnlockViewController : UIViewController
+
+@property (weak, nonatomic) IBOutlet UIImageView *lock;
+@property (weak, nonatomic) IBOutlet UIImageView *spinner;
+@property (weak, nonatomic) IBOutlet UITextField *field;
+@property (weak, nonatomic) IBOutlet UILabel *messageLabel;
+@property (weak, nonatomic) IBOutlet UIView *changeMPView;
+
+- (IBAction)changeMP;
+
+@end
diff --git a/MasterPassword/MPUnlockViewController.m b/MasterPassword/MPUnlockViewController.m
new file mode 100644
index 00000000..26b55672
--- /dev/null
+++ b/MasterPassword/MPUnlockViewController.m
@@ -0,0 +1,198 @@
+//
+// MBUnlockViewController.m
+// MasterPassword
+//
+// Created by Maarten Billemont on 22/02/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import
+
+#import "MPUnlockViewController.h"
+#import "MPAppDelegate.h"
+
+typedef enum {
+ MPLockscreenIdle,
+ MPLockscreenError,
+ MPLockscreenSuccess,
+ MPLockscreenProgress,
+} MPLockscreen;
+
+@interface MPUnlockViewController ()
+
+@end
+
+@implementation MPUnlockViewController
+@synthesize lock;
+@synthesize spinner;
+@synthesize field;
+@synthesize messageLabel;
+@synthesize changeMPView;
+
+- (void)showMessage:(NSString *)message state:(MPLockscreen)state {
+
+ __block void(^showMessageAnimation)(void) = ^{
+ self.lock.alpha = 0.0f;
+ switch (state) {
+ case MPLockscreenIdle:
+ [self.lock setImage:[UIImage imageNamed:@"lockscreen_idle"]];
+ break;
+ case MPLockscreenError:
+ [self.lock setImage:[UIImage imageNamed:@"lockscreen_red"]];
+ break;
+ case MPLockscreenSuccess:
+ [self.lock setImage:[UIImage imageNamed:@"lockscreen_green"]];
+ break;
+ case MPLockscreenProgress:
+ [self.lock setImage:[UIImage imageNamed:@"lockscreen_blue"]];
+ break;
+ }
+
+ self.lock.alpha = 0.0f;
+ [UIView animateWithDuration:1.0f animations:^{
+ self.lock.alpha = 1.0f;
+ } completion:^(BOOL finished) {
+ if (finished)
+ [UIView animateWithDuration:1.0f delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
+ self.lock.alpha = 0.5f;
+ } completion:nil];
+ }];
+
+ [UIView animateWithDuration:0.5f animations:^{
+ self.messageLabel.alpha = 1.0f;
+ self.messageLabel.text = message;
+ }];
+ };
+
+ if (self.messageLabel.alpha)
+ [UIView animateWithDuration:0.3f animations:^{
+ self.messageLabel.alpha = 0.0f;
+ } completion:^(BOOL finished) {
+ if (finished)
+ showMessageAnimation();
+ }];
+ else
+ showMessageAnimation();
+}
+
+- (void)hideMessage {
+
+ [UIView animateWithDuration:0.5f animations:^{
+ self.messageLabel.alpha = 0.0f;
+ }];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+- (void)viewDidLoad {
+
+ self.messageLabel.text = nil;
+ self.messageLabel.alpha = 0;
+ self.changeMPView.alpha = 0;
+ self.spinner.alpha = 0;
+ self.field.text = nil;
+
+ [[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationKeyForgotten
+ object:nil queue:nil usingBlock:^(NSNotification *note) {
+ [self.field becomeFirstResponder];
+ }];
+
+ [super viewDidLoad];
+}
+
+- (void)viewDidUnload {
+
+ [self setSpinner:nil];
+ [self setField:nil];
+
+ [self setMessageLabel:nil];
+ [self setLock:nil];
+ [self setChangeMPView:nil];
+ [super viewDidUnload];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+
+ [[UIApplication sharedApplication] setStatusBarHidden:YES
+ withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
+
+ [super viewWillAppear:animated];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+
+ [[UIApplication sharedApplication] setStatusBarHidden:NO
+ withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
+
+ [super viewWillDisappear:animated];
+}
+
+- (BOOL)textFieldShouldReturn:(UITextField *)textField {
+
+ if ([textField.text length]) {
+ [textField resignFirstResponder];
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)textFieldDidEndEditing:(UITextField *)textField {
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ @try {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
+ rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
+ rotate.fromValue = [NSNumber numberWithFloat:0];
+ rotate.toValue = [NSNumber numberWithFloat:2 * M_PI];
+ rotate.repeatCount = MAXFLOAT;
+ rotate.duration = 3.0;
+
+ [self.spinner.layer removeAllAnimations];
+ [self.spinner.layer addAnimation:rotate forKey:@"transform"];
+
+ [UIView animateWithDuration:0.3f animations:^{
+ self.spinner.alpha = 1.0f;
+ }];
+
+ [self showMessage:@"Checking password..." state:MPLockscreenProgress];
+ });
+
+ if ([[MPAppDelegate get] tryMasterPassword:textField.text])
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self showMessage:@"Success!" state:MPLockscreenSuccess];
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1.5f), dispatch_get_main_queue(), ^{
+ [self dismissModalViewControllerAnimated:YES];
+ });
+ });
+ else
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self showMessage:@"Not valid." state:MPLockscreenError];
+ [UIView animateWithDuration:0.5f animations:^{
+ self.changeMPView.alpha = 1.0f;
+ }];
+ });
+ }
+ @finally {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [UIView animateWithDuration:0.3f animations:^{
+ self.spinner.alpha = 0.0f;
+ } completion:^(BOOL finished) {
+ [self.spinner.layer removeAllAnimations];
+ }];
+ });
+ }
+ });
+}
+
+- (IBAction)changeMP {
+
+ [[MPAppDelegate get] forgetKeyPhrase];
+}
+
+@end
diff --git a/MasterPassword/MainStoryboard_iPhone.storyboard b/MasterPassword/MainStoryboard_iPhone.storyboard
index bb0edccc..45ef885f 100644
--- a/MasterPassword/MainStoryboard_iPhone.storyboard
+++ b/MasterPassword/MainStoryboard_iPhone.storyboard
@@ -1,10 +1,11 @@
-
+
-
+
+
@@ -52,9 +53,9 @@
-
+
-
+
@@ -96,9 +97,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -129,9 +130,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -162,9 +163,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -195,9 +196,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -251,9 +252,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -284,9 +285,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -308,6 +309,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
+
@@ -340,7 +342,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -373,7 +375,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -383,7 +385,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -392,7 +394,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -415,7 +417,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -433,9 +435,9 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
-
+
@@ -461,7 +463,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -471,7 +473,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
-
+
@@ -601,6 +603,7 @@ L4m3P4sSw0rD
+
@@ -669,6 +672,113 @@ L4m3P4sSw0rD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -690,8 +800,9 @@ L4m3P4sSw0rD
-
+
+
@@ -702,6 +813,7 @@ L4m3P4sSw0rD
+
@@ -711,6 +823,8 @@ L4m3P4sSw0rD
+
+
diff --git a/MasterPassword/Resources/Background/background.png b/MasterPassword/Resources/Background/background.png
new file mode 100644
index 00000000..37349beb
Binary files /dev/null and b/MasterPassword/Resources/Background/background.png differ
diff --git a/MasterPassword/Resources/Background/background@2x.png b/MasterPassword/Resources/Background/background@2x.png
new file mode 100644
index 00000000..45426eff
Binary files /dev/null and b/MasterPassword/Resources/Background/background@2x.png differ
diff --git a/MasterPassword/Resources/Content-Backdrop.png b/MasterPassword/Resources/Content-Backdrop.png
deleted file mode 100644
index 835c54ed..00000000
Binary files a/MasterPassword/Resources/Content-Backdrop.png and /dev/null differ
diff --git a/MasterPassword/Resources/Content-Backdrop@2x.png b/MasterPassword/Resources/Content-Backdrop@2x.png
deleted file mode 100644
index 26a5afee..00000000
Binary files a/MasterPassword/Resources/Content-Backdrop@2x.png and /dev/null differ
diff --git a/MasterPassword/Resources/Lock/lock_blue.png b/MasterPassword/Resources/Lock/lock_blue.png
new file mode 100644
index 00000000..fc508203
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_blue.png differ
diff --git a/MasterPassword/Resources/Lock/lock_blue@2x.png b/MasterPassword/Resources/Lock/lock_blue@2x.png
new file mode 100644
index 00000000..9cd9bcc4
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_blue@2x.png differ
diff --git a/MasterPassword/Resources/Lock/lock_green.png b/MasterPassword/Resources/Lock/lock_green.png
new file mode 100644
index 00000000..a78a8baa
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_green.png differ
diff --git a/MasterPassword/Resources/Lock/lock_green@2x.png b/MasterPassword/Resources/Lock/lock_green@2x.png
new file mode 100644
index 00000000..ad4de112
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_green@2x.png differ
diff --git a/MasterPassword/Resources/Lock/lock_idle.png b/MasterPassword/Resources/Lock/lock_idle.png
new file mode 100644
index 00000000..407a5f51
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_idle.png differ
diff --git a/MasterPassword/Resources/Lock/lock_idle@2x.png b/MasterPassword/Resources/Lock/lock_idle@2x.png
new file mode 100644
index 00000000..82fcdade
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_idle@2x.png differ
diff --git a/MasterPassword/Resources/Lock/lock_red.png b/MasterPassword/Resources/Lock/lock_red.png
new file mode 100644
index 00000000..dc34c95e
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_red.png differ
diff --git a/MasterPassword/Resources/Lock/lock_red@2x.png b/MasterPassword/Resources/Lock/lock_red@2x.png
new file mode 100644
index 00000000..60bc5303
Binary files /dev/null and b/MasterPassword/Resources/Lock/lock_red@2x.png differ
diff --git a/Scripts/convertImages b/Scripts/convertImages
index ec6a3c31..d82a24d2 100755
--- a/Scripts/convertImages
+++ b/Scripts/convertImages
@@ -15,6 +15,7 @@ cd "${0%/*}/../MasterPassword/Resources"
for size in "${!icons[@]}"; do
file=${icons[size]}
+ [[ iTunesArtwork.png -nt $file ]] || continue
emit "$file ($size px)" --
convert "iTunesArtwork.png" -resize "${size}x${size}" "$file"
@@ -24,8 +25,9 @@ done
echo
emit "Converting @2x artwork"
-for file in ./{,Guide/}*@2x.png; do
+for file in ./{,Guide,Lock,Background}/*@2x.png; do
inArray "${file##*/}" "${icons[@]}" && continue
+ [[ $file -nt ${file/@2x} ]] || continue
emit "${file/@2x}" --
convert "$file" -filter box -resize 50% -unsharp 0x1 "${file/@2x}"
diff --git a/Site/1/css/screen.css b/Site/1/css/screen.css
index c8876e00..511f3651 100644
--- a/Site/1/css/screen.css
+++ b/Site/1/css/screen.css
@@ -10,16 +10,16 @@ body {
color: black;
- font: 120% ExoLight, sans-serif;
+ font: 120% Exo, sans-serif;
font-weight: 100;
}
h1, h2, h3, h4 {
text-shadow: #FFF 0 -1px 1px, #AAA 0 0 5px;
- font-family: ExoBold;
+ font-weight: 600;
}
strong {
- font-family: ExoDemiBold;
+ font-weight: 400;
}
h1 .sub {
font-size: 0.5em;
@@ -37,20 +37,18 @@ p {
text-align: justify;
}
ul {
- font-family: ExoDemiBold;
font-size: 90%;
- font-weight: 500;
+ font-weight: 400;
}
blockquote {
- font-family: ExoDemiBold;
font-size: 90%;
- font-weight: bold;
+ font-weight: 400;
}
a, .link, :link {
color: inherit;
text-decoration: underline;
cursor: pointer;
- font-weight: 500;
+ font-weight: 400;
}
a:hover, .link:hover {
text-decoration: none;
@@ -130,7 +128,6 @@ blockquote:before {
position: absolute;
margin: -0.5ex 0 0 -1em;
font-size: 300%;
- font-family: ExoLight;
font-weight: 100;
}
@@ -182,21 +179,20 @@ section.active {
a.previous, a.next {
display: block;
position: absolute;
- width: 100%;
margin: -2.5em 0 0 0;
font-size: 150%;
- font-family: ExoDemiBold;
+ font-weight: 400;
text-decoration: none;
}
a.previous {
- margin-left: -3em;
+ left: 0;
}
a.previous:before {
content: "< ";
}
a.next {
- margin-left: 3em;
+ right: 0;
text-align: right;
}
diff --git a/Site/1/index.html b/Site/1/index.html
index 104ced5e..d2a77d17 100644
--- a/Site/1/index.html
+++ b/Site/1/index.html
@@ -9,9 +9,7 @@
-
-
-
+
@@ -67,7 +65,7 @@
- It aims to secure your online (and offline!) life by changing the way you deal with passwords.
+ It aims to secure your online (and offline!) life by changing the way you deal with passwords .
@@ -76,20 +74,20 @@
You already know the problem:
- Passwords are confidential information between you and a site. They should never be shared with anyone else, definitely not other sites. Yet that's exactly what happens with most of us: Hundereds of online accounts to manage and authenticate, we can't help but reuse one, two or five passwords that we can remember. Maybe we keep a paper stuck to our monitor with a list of passwords on them, because we realize the truth:
+ Passwords are confidential information between you and a site. They should never be shared with anyone else, definitely not other sites. Yet that's exactly what happens with most of us: Hundereds of online accounts to manage and authenticate, we can't help but reuse one, two or five passwords that we can remember . Maybe we keep a paper stuck to our monitor with a list of passwords on them, because we realize the truth:
It is impossible to remember a secure password for each of our accounts and still keep those passwords both exclusive and confidential .
Multiple solutions exist:
- Sites that realize that passwords aren't the end-all of authentication usually implement some sort of alternative authentication mechanism: OpenID, SAML, some form of mobile authentication, secure tokens, etc.
+ Sites that realize that passwords aren't the end-all of authentication usually implement some sort of alternative authentication mechanism: OpenID, SAML, some form of mobile authentication, secure tokens, etc .
The problem here is that these solutions only work for the select few sites that have chosen to implement them; and then you, the user, are stuck with whatever mechanism the site has chosen for you.
- To solve the problem for other sites, there are programs that remember our passwords for us.
- The problem with these is that they do not actually help us with setting exclusive and confidential passwords for our accounts. They just offload the work of remembering passwords, and at a great expense: If you loose your data, you loose your online identity and are locked out of everything.
+ To solve the problem for other sites, there are programs that remember our passwords for us .
+ The problem with these is that they do not actually help us with setting exclusive and confidential passwords for our accounts. They just offload the work of remembering passwords, and at a great expense: If you loose your data, you loose your online identity and are locked out of everything .
@@ -98,7 +96,7 @@
Master Password aims to turn the tables in favor of the user, you.
- In the end, what we really want is a way of dealing with passwords in an exclusive and confidential way without having to remember them, and without running the risk of losing our online identity to fraudsters.
+ In the end, what we really want is a way of dealing with passwords in an exclusive and confidential way without having to remember them, and without running the risk of losing our online identity to fraudsters.
@@ -119,7 +117,7 @@
For those cases where you cannot change your account's password, the application will encrypt passwords with your master password and store them securely (as explained, stored passwords can get lost).
Integrates with iCloud to synchronize and back up your site history and stored passwords.
- For those that care to know, the password generation algorithm is open and documented within the application.
+ For those that care to know, the password generation algorithm is open and fully documented, so you aren't tied down to this application.
@@ -134,7 +132,7 @@
Participation in the beta is free of charge, but does come with the expectation that you will contribute. Comment constructively, report issues and propose improvements.
- Post-beta, Master Password is expected to sell for somewhere around 7 USD. The most helpful testers will receive the final version (and all future updates) free of charge.
+ Post-beta, Master Password is expected to sell for somewhere around 10 USD. The most helpful testers will receive the final version (and all future updates) free of charge.
@@ -158,22 +156,31 @@
Alright, let's describe the process in detail. This part will likely make sense to you only if you're well versed in computer security jargon. If you're the kind of person who likes to know how the clock ticks before deciding that it can be trusted to keep ticking, read on.
- The user chooses a single master password, preferably sufficiently long to harden against brute-force attacks. When the user requests a password be generated for a site, the application composes a string consisting of the site name, the master password, and a password counter, delimited in that order by a dash character, and hashes those UTF-8
bytes using the SHA-1
algorithm. The bytes resulting from this hashing operation are called the keyBytes
in the next steps.
+ The user chooses a single master password, preferably sufficiently long to harden against brute-force attacks. Before usage, a masterKey
is derived from this master password using the scrypt key derivation function . This makes it impossibly expensive and time-consuming to attempt brute-forcing a properly sized master password.
- keyBytes = sha1( site name "-" master password "-" password counter )
+ masterKey = scrypt( P, S, N, r, p, dkLen )
+ where
+ P = master password
+ S = <empty>
+ N = 16384
+ r = 8
+ p = 1
+ dkLen = 64
+
+
+
+ When the user requests a password be generated for a site, the application composes a byte string consisting of the site name, the master key, and a password counter, delimited in that order by a dash character (characters are UTF-8 encoded, numbers in 32-bit network byte order), and hashes it using the SHA-1
algorithm. The result is called the cipherKey
.
+
+
+ cipherKey = sha1( site name "-" masterKey "-" password counter )
- Next, we need the password type that the user has chosen to use for the site. Password types determine the
- cipher that will be used to encrypt keyBytes
into a readable password. For
- instance, the standard password type Long Password activates one of three pre-set ciphers:
- CvcvCvcvnoCvcv
, CvcvnoCvcvCvcv
or CvcvCvcvCvcvno
. Which of those
- will be used, depends on the first of the keyBytes
. Take the byte value modulo the amount of
- pre-set ciphers (in this case, three), and the result tells you which of the three ciphers to use.
+ Next up is to merge this key with the password type that the user has chosen to use for the site. Password types determine the cipher that will be used to encrypt cipherKey
bytes into a readable password. For instance, the standard password type Long Password activates one of three pre-set ciphers: CvcvCvcvnoCvcv
, CvcvnoCvcvCvcv
or CvcvCvcvCvcvno
. Which of those will be used, depends on the first byte of the cipherKey
. Take the byte value modulo the amount of pre-set ciphers (in this case, three), and the result tells you which of the pre-set ciphers to use.
ciphers = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno" ]
- cipher = ciphers[ keyBytes[0] % count( ciphers ) ]
+ cipher = ciphers[ cipherKey[0] % count( ciphers ) ]
Now that we know what cipher to use for building our final password, all that's left is to iterate the
@@ -181,13 +188,13 @@
character in the cipher represents a set of possible output characters (passChars
). For instance, a C
character in the cipher indicates that we need to choose a capital consonant character. An o
character in the cipher indicates that we need to choose an other (symbol) character. Exactly which
- character to choose in that set for the password output depends on the next byte from keyBytes
.
- Like before, take the next unused keyByte
's byte value modulo the amount of characters in the
+ character to choose in that set for the password output depends on the next byte from the cipherKey
.
+ Like before, take the next unused cipherKey
byte value modulo the amount of characters in the
set of possible output characters for the cipher iteration and use the result to choose the output
character (passChar
). Repeat until you've iterated the whole cipher.
- passChar = passChars[ keyBytes[i + 1] % count( passChars ) ]
+ passChar = passChars[ cipherKey[i + 1] % count( passChars ) ]
passWord += passChar