diff --git a/MasterPassword/MPAppDelegate_Key.h b/MasterPassword/MPAppDelegate_Key.h index 2306edb5..a75f3ed5 100644 --- a/MasterPassword/MPAppDelegate_Key.h +++ b/MasterPassword/MPAppDelegate_Key.h @@ -10,13 +10,11 @@ @interface MPAppDelegate_Shared (Key) -- (void)loadSavedKey; -- (IBAction)signOut:(id)sender; +- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password; +- (void)signOut; -- (BOOL)tryMasterPassword:(NSString *)tryPassword forUser:(MPUserEntity *)user; -- (void)storeSavedKey; -- (void)forgetSavedKey; -- (void)unsetKey; +- (void)storeSavedKeyFor:(MPUserEntity *)user; +- (void)forgetSavedKeyFor:(MPUserEntity *)user; - (NSData *)keyWithLength:(NSUInteger)keyLength; diff --git a/MasterPassword/MPAppDelegate_Key.m b/MasterPassword/MPAppDelegate_Key.m index b050f233..62a4473d 100644 --- a/MasterPassword/MPAppDelegate_Key.m +++ b/MasterPassword/MPAppDelegate_Key.m @@ -12,7 +12,7 @@ @implementation MPAppDelegate_Shared (Key) static NSDictionary *keyQuery(MPUserEntity *user) { - + return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:[NSDictionary dictionaryWithObjectsAndKeys: @"Saved Master Password", (__bridge id)kSecAttrService, @@ -21,113 +21,131 @@ static NSDictionary *keyQuery(MPUserEntity *user) { matches:nil]; } -- (void)forgetSavedKey { - - if ([PearlKeyChain deleteItemForQuery:keyQuery(self.activeUser)] != errSecItemNotFound) { - inf(@"Removed key from keychain."); - - [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self]; -#ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPForgotten]; -#endif +- (NSData *)loadSavedKeyFor:(MPUserEntity *)user { + + NSData *key = [PearlKeyChain dataOfItemForQuery:keyQuery(user)]; + if (key) + inf(@"Found key (for: %@) in keychain.", user.name); + + else { + user.saveKey = NO; + inf(@"No key found (for: %@) in keychain.", user.name); } + + return key; } -- (IBAction)signOut:(id)sender { +- (void)storeSavedKeyFor:(MPUserEntity *)user { - [self forgetSavedKey]; - [self unsetKey]; -} + if (user.saveKey) { + NSData *existingKey = [PearlKeyChain dataOfItemForQuery:keyQuery(user)]; -- (void)loadSavedKey { - - if (self.activeUser.saveKey) { - // Key should be saved in keychain. Load it. - self.key = [PearlKeyChain dataOfItemForQuery:keyQuery(self.activeUser)]; - inf(@"Looking for key in keychain: %@.", self.key? @"found": @"missing"); - if (self.key) - [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self]; - } else { - // Key should not be stored in keychain. Delete it. - if ([PearlKeyChain deleteItemForQuery:keyQuery(self.activeUser)] != errSecItemNotFound) - inf(@"Removed key from keychain."); -#ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored]; + if (![existingKey isEqualToData:self.key]) { + inf(@"Updating key in keychain."); + [PearlKeyChain addOrUpdateItemForQuery:keyQuery(user) + withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: + self.key, (__bridge id) kSecValueData, +#if TARGET_OS_IPHONE + kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id) kSecAttrAccessible, #endif - } -} - -- (BOOL)tryMasterPassword:(NSString *)tryPassword forUser:(MPUserEntity *)user { - - if (![tryPassword length]) - return NO; - - NSData *tryKey = keyForPassword(tryPassword, user.name); - NSData *tryKeyID = keyIDForKey(tryKey); - inf(@"Key ID was known? %@.", user.keyID? @"YES": @"NO"); - if (user.keyID) { - // A key ID is known -> a master password is set. - // Make sure the user's entered master password matches it. - if (![user.keyID isEqual:tryKeyID]) { - wrn(@"Key ID mismatch. Expected: %@, answer: %@.", [user.keyID encodeHex], [tryKeyID encodeHex]); - -#ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch]; -#endif - return NO; + nil]]; } - } else { - // A key ID is not known -> recording a new master password. - user.keyID = tryKeyID; - [[MPAppDelegate_Shared get] saveContext]; } - user.lastUsed = [NSDate date]; +} + +- (void)forgetSavedKeyFor:(MPUserEntity *)user { + + OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery(user)]; + if (result == noErr || result == errSecItemNotFound) { + user.saveKey = NO; + + if (result == noErr) { + inf(@"Removed key (for: %@) from keychain.", user.name); + + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self]; +#ifdef TESTFLIGHT_SDK_VERSION + [TestFlight passCheckpoint:MPTestFlightCheckpointForgetSavedKey]; +#endif + } + } +} + +- (void)signOut { + + self.key = nil; + self.activeUser = nil; + + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedOut object:self]; +} + +- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password { + + NSData *tryKey = nil; + + // Method 1: When the user has no keyID set, set a new key from the given master password. + if (!user.keyID) { + if ([password length]) + if ((tryKey = keyForPassword(password, user.name))) { + user.keyID = keyIDForKey(tryKey); + [[MPAppDelegate_Shared get] saveContext]; + } + } + + // Method 2: Depending on the user's saveKey, load or remove the key from the keychain. + if (!user.saveKey) + // Key should not be stored in keychain. Delete it. + [self forgetSavedKeyFor:user]; + + else if (!tryKey) { + // Key should be saved in keychain. Load it. + if ((tryKey = [self loadSavedKeyFor:user])) + if (![user.keyID isEqual:keyIDForKey(tryKey)]) { + // Loaded password doesn't match user's keyID. Forget saved password: it is incorrect. + tryKey = nil; + [self forgetSavedKeyFor:user]; #ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:MPTestFlightCheckpointMPEntered]; + [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch]; #endif - - if (self.key != tryKey) { - self.key = tryKey; - [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self]; + } } - + + // Method 3: Check the given master password string. + if (!tryKey) { + if ([password length]) + if ((tryKey = keyForPassword(password, user.name))) + if (![user.keyID isEqual:keyIDForKey(tryKey)]) { + tryKey = nil; + + #ifdef TESTFLIGHT_SDK_VERSION + [TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch]; + #endif + } + } + + // No more methods left, fail if key still not known. + if (!tryKey) + return NO; + + if (![self.key isEqualToData:tryKey]) { + self.key = tryKey; + [self storeSavedKeyFor:user]; + } + + user.lastUsed = [NSDate date]; self.activeUser = user; - + [[MPAppDelegate_Shared get] saveContext]; + + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedIn object:self]; #ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:MPTestFlightCheckpointSetKey]; + [TestFlight passCheckpoint:MPTestFlightCheckpointSignedIn]; #endif - + return YES; } -- (void)storeSavedKey { - - if (self.activeUser.saveKey) { - NSData *existingKey = [PearlKeyChain dataOfItemForQuery:keyQuery(self.activeUser)]; - - if (![existingKey isEqualToData:self.key]) { - inf(@"Updating key in keychain."); - [PearlKeyChain addOrUpdateItemForQuery:keyQuery(self.activeUser) - withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: - self.key, (__bridge id)kSecValueData, -#if TARGET_OS_IPHONE - kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id)kSecAttrAccessible, -#endif - nil]]; - } - } -} - -- (void)unsetKey { - - self.key = nil; - self.activeUser = nil; - [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self]; -} - - (NSData *)keyWithLength:(NSUInteger)keyLength { - + return [self.key subdataWithRange:NSMakeRange(0, MIN(keyLength, self.key.length))]; } diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h index cee0f018..aaa1fb68 100644 --- a/MasterPassword/MPTypes.h +++ b/MasterPassword/MPTypes.h @@ -59,22 +59,21 @@ typedef enum { #define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated" #define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated" #define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide" -#define MPTestFlightCheckpointMPForgotten @"MPTestFlightCheckpointMPForgotten" -#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged" -#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored" +#define MPTestFlightCheckpointForgetSavedKey @"MPTestFlightCheckpointForgetSavedKey" +#define MPTestFlightCheckpointChangeMP @"MPTestFlightCheckpointChangeMP" #define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch" -#define MPTestFlightCheckpointMPEntered @"MPTestFlightCheckpointMPEntered" +#define MPTestFlightCheckpointMPValid @"MPTestFlightCheckpointMPValid" #define MPTestFlightCheckpointLocalStoreIncompatible @"MPTestFlightCheckpointLocalStoreIncompatible" #define MPTestFlightCheckpointCloudStoreIncompatible @"MPTestFlightCheckpointCloudStoreIncompatible" -#define MPTestFlightCheckpointSetKey @"MPTestFlightCheckpointSetKey" +#define MPTestFlightCheckpointSignedIn @"MPTestFlightCheckpointSetKey" #define MPTestFlightCheckpointCloudEnabled @"MPTestFlightCheckpointCloudEnabled" #define MPTestFlightCheckpointCloudDisabled @"MPTestFlightCheckpointCloudDisabled" #define MPTestFlightCheckpointSitesImported @"MPTestFlightCheckpointSitesImported" #define MPTestFlightCheckpointSitesExported @"MPTestFlightCheckpointSitesExported" #define MPNotificationStoreUpdated @"MPNotificationStoreUpdated" -#define MPNotificationKeySet @"MPNotificationKeySet" -#define MPNotificationKeyUnset @"MPNotificationKeyUnset" +#define MPNotificationSignedIn @"MPNotificationKeySet" +#define MPNotificationSignedOut @"MPNotificationKeyUnset" #define MPNotificationKeyForgotten @"MPNotificationKeyForgotten" #define MPNotificationElementUsed @"MPNotificationElementUsed" diff --git a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents index 9e3fd78f..8c463b83 100644 --- a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents +++ b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword.xcdatamodel/contents @@ -3,7 +3,7 @@ - + @@ -18,8 +18,8 @@ - - + + diff --git a/MasterPassword/iOS/MPAppDelegate.h b/MasterPassword/iOS/MPAppDelegate.h index 48e421bf..d4caccfd 100644 --- a/MasterPassword/iOS/MPAppDelegate.h +++ b/MasterPassword/iOS/MPAppDelegate.h @@ -14,8 +14,8 @@ + (MPAppDelegate *)get; +- (void)checkConfig; - (void)showGuide; -- (void)loadKey:(BOOL)animated; - (void)export; - (void)changeMP; diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m index 66e0f0f5..e8a95ccb 100644 --- a/MasterPassword/iOS/MPAppDelegate.m +++ b/MasterPassword/iOS/MPAppDelegate.m @@ -50,6 +50,12 @@ return (MPAppDelegate *)[super get]; } +- (void)checkConfig { + + if ([[MPConfig get].iCloud boolValue] != [self.storeManager iCloudEnabled]) + [self.storeManager useiCloudStore:[[MPConfig get].iCloud boolValue] alertUser:YES]; +} + - (void)showGuide { [self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self]; @@ -57,21 +63,6 @@ [TestFlight passCheckpoint:MPTestFlightCheckpointShowGuide]; } -- (void)loadKey:(BOOL)animated { - - if (!self.key) - // Try and load the key from the keychain. - [self loadSavedKey]; - - if (!self.key) - // Ask the user to set the key through his master password. - PearlMainThread(^{ - [self.navigationController presentViewController: - [self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"] - animated:animated completion:nil]; - }); -} - - (void)export { [PearlAlert showNotice: @@ -131,10 +122,10 @@ if (buttonIndex == [alert cancelButtonIndex]) return; - [[MPAppDelegate get] forgetSavedKey]; - [[MPAppDelegate get] loadKey:YES]; + self.activeUser.keyID = nil; + [self signOut]; - [TestFlight passCheckpoint:MPTestFlightCheckpointMPChanged]; + [TestFlight passCheckpoint:MPTestFlightCheckpointChangeMP]; } cancelTitle:[PearlStrings get].commonButtonAbort otherTitles:[PearlStrings get].commonButtonContinue, nil]; @@ -147,12 +138,6 @@ [self checkConfig]; } -- (void)checkConfig { - - if ([[MPConfig get].iCloud boolValue] != [self.storeManager iCloudEnabled]) - [self.storeManager useiCloudStore:[[MPConfig get].iCloud boolValue] alertUser:YES]; -} - #pragma mark - UIApplicationDelegate @@ -287,7 +272,11 @@ [[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];*/ - + + [[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil + usingBlock:^(NSNotification *note) { + [self.navigationController performSegueWithIdentifier:@"MP_Unlock" sender:nil]; + }]; [[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil usingBlock:^(NSNotification *note) { [self checkConfig]; @@ -379,11 +368,7 @@ if ([[MPiOSConfig get].showQuickStart boolValue]) [self showGuide]; - else { - [self loadKey:NO]; - [self checkConfig]; - } - + [TestFlight passCheckpoint:MPTestFlightCheckpointActivated]; [super applicationDidBecomeActive:application]; @@ -421,11 +406,9 @@ [self saveContext]; - if (![[MPiOSConfig get].rememberLogin boolValue]) { - [self unsetKey]; - [self loadKey:NO]; - } - + if (![[MPiOSConfig get].rememberLogin boolValue]) + [self signOut]; + [TestFlight passCheckpoint:MPTestFlightCheckpointDeactivated]; } diff --git a/MasterPassword/iOS/MPGuideViewController.m b/MasterPassword/iOS/MPGuideViewController.m index ec05d097..5031a3c9 100644 --- a/MasterPassword/iOS/MPGuideViewController.m +++ b/MasterPassword/iOS/MPGuideViewController.m @@ -32,14 +32,6 @@ [MPiOSConfig get].showQuickStart = [NSNumber numberWithBool:NO]; } -- (void)viewDidDisappear:(BOOL)animated { - - [super viewDidDisappear:animated]; - - [[MPAppDelegate get] loadKey:animated]; -} - - - (void)viewDidUnload { [self setScrollView:nil]; diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index 51bab77a..e378a074 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -71,6 +71,8 @@ [super viewWillAppear:animated]; + if (![MPAppDelegate get].activeUser) + [self.navigationController performSegueWithIdentifier:@"MP_Unlock" sender:self]; if (self.activeElement.user != [MPAppDelegate get].activeUser) self.activeElement = nil; self.searchDisplayController.searchBar.text = nil; @@ -103,6 +105,8 @@ } }]; + [[MPAppDelegate get] checkConfig]; + [super viewDidAppear:animated]; } @@ -220,7 +224,7 @@ - (void)webViewDidFinishLoad:(UIWebView *)webView { NSString *error = [self.helpView stringByEvaluatingJavaScriptFromString: - PearlString(@"setClass('%@');", ClassNameFromMPElementType(self.activeElement.type))]; + PearlString(@"setClass('%@');", ClassNameFromMPElementType(self.activeElement.type))]; if (error.length) err(@"helpView.setClass: %@", error); } @@ -419,8 +423,8 @@ case 5: #endif { - [[MPAppDelegate get] signOut:self]; - [[MPAppDelegate get] loadKey:YES]; + [[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser]; + [[MPAppDelegate get] signOut]; break; } } diff --git a/MasterPassword/iOS/MPPreferencesViewController.m b/MasterPassword/iOS/MPPreferencesViewController.m index cdfbf71b..01fa795b 100644 --- a/MasterPassword/iOS/MPPreferencesViewController.m +++ b/MasterPassword/iOS/MPPreferencesViewController.m @@ -9,6 +9,7 @@ #import #import "MPPreferencesViewController.h" #import "MPAppDelegate.h" +#import "MPAppDelegate_Key.h" @interface MPPreferencesViewController () @@ -114,7 +115,10 @@ - (IBAction)didToggleSwitch:(UISwitch *)sender { - [MPAppDelegate get].activeUser.saveKey = sender.on; + if (([MPAppDelegate get].activeUser.saveKey = sender.on)) + [[MPAppDelegate get] storeSavedKeyFor:[MPAppDelegate get].activeUser]; + else + [[MPAppDelegate get] forgetSavedKeyFor:[MPAppDelegate get].activeUser]; } @end diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m index 36227759..927ba778 100644 --- a/MasterPassword/iOS/MPUnlockViewController.m +++ b/MasterPassword/iOS/MPUnlockViewController.m @@ -119,12 +119,6 @@ [self.avatarsView autoSizeContentIgnoreHidden:YES ignoreInvisible:YES limitPadding:NO ignoreSubviews:nil]; [self updateLayoutAnimated:YES allowScroll:YES completion:nil]; - - self.deleteTip.alpha = 0; - if ([users count] > 1) - [UIView animateWithDuration:0.5f animations:^{ - self.deleteTip.alpha = 1; - }]; } - (UIButton *)setupAvatar:(UIButton *)avatar forUser:(MPUserEntity *)user { @@ -169,7 +163,11 @@ if (!self.selectedUser) [self.passwordField resignFirstResponder]; - + else if ([[MPAppDelegate get] signInAsUser:self.selectedUser usingMasterPassword:nil]) { + [self dismissModalViewControllerAnimated:YES]; + return; + } + [self updateLayoutAnimated:YES allowScroll:YES completion:^(BOOL finished) { if (finished) if (self.selectedUser) @@ -230,6 +228,7 @@ self.nameLabel.backgroundColor = [UIColor blackColor]; self.oldNameLabel.center = self.nameLabel.center; self.avatarShadowColor = [UIColor whiteColor]; + self.deleteTip.alpha = 0; } else if (!self.selectedUser && self.passwordView.alpha == 1) { self.passwordView.alpha = 0; self.avatarsView.center = CGPointMake(160, 240); @@ -238,6 +237,7 @@ self.nameLabel.backgroundColor = [UIColor clearColor]; self.oldNameLabel.center = self.nameLabel.center; self.avatarShadowColor = [UIColor lightGrayColor]; + self.deleteTip.alpha = [self.avatarToUser count] > 2? 1: 0; } MPUserEntity *targetedUser = self.selectedUser; @@ -294,14 +294,14 @@ [self setSpinnerActive:YES]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - BOOL unlocked = [[MPAppDelegate get] tryMasterPassword:self.passwordField.text forUser:self.selectedUser]; + BOOL unlocked = [[MPAppDelegate get] signInAsUser:self.selectedUser usingMasterPassword:self.passwordField.text]; dispatch_async(dispatch_get_main_queue(), ^{ if (unlocked) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (long) (NSEC_PER_SEC * 0.5f)), dispatch_get_main_queue(), ^{ [self dismissModalViewControllerAnimated:YES]; }); - } else + } else if (self.passwordField.text.length) [self setPasswordTip:@"Incorrect password."]; [self setSpinnerActive:NO]; diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index 7cacd1cd..9135fd1a 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -925,6 +925,7 @@ L4m3P4sSw0rD + diff --git a/Site/css/screen.css b/Site/css/screen.css index e38ed2d5..8fda2a20 100644 --- a/Site/css/screen.css +++ b/Site/css/screen.css @@ -7,7 +7,7 @@ body { color: black; - font: 105% Exo, sans-serif; + font: 105% "Hoefler Text", Garamond, Baskerville, "Baskerville Old Face", "Times New Roman", serif; font-weight: 100; } h1, h2, h3, h4 { @@ -15,6 +15,11 @@ h1, h2, h3, h4 { font-weight: 600; } +h1 { + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", "Liberation Sans", sans-serif; + + font-weight: 100; +} strong { font-weight: 400; } @@ -62,12 +67,28 @@ img { /* Classes */ .stripe { background: rgba(255, 255, 255, 0.3); - border: 1px solid rgba(255, 255, 255, 0.7); + border: 1px solid rgba(255, 255, 255, 0.5); border-width: 1px 0; -webkit-box-shadow: inset 0 0 15px rgba(255, 255, 255, 0.5), 2px 2px 6px rgba(200, 200, 200, 0.5); -moz-box-shadow: inset 0 0 15px rgba(255, 255, 255, 0.5), 2px 2px 6px rgba(200, 200, 200, 0.5); box-shadow: inset 0 0 15px rgba(255, 255, 255, 0.5), 2px 2px 6px rgba(200, 200, 200, 0.5); } +.button { + display: inline-block; + background: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(255, 255, 255, 1); + border-radius: 5px; + -webkit-box-shadow: 1px 1px 6px rgba(200, 200, 200, 0.5); + -moz-box-shadow: 1px 1px 6px rgba(200, 200, 200, 0.5); + box-shadow: 1px 1px 6px rgba(200, 200, 200, 0.5); + padding: 1em; + + text-decoration: none; +} +.button:hover { + background: rgba(240, 240, 240, 0.5); + border-color: white; +} /* Page */ header { @@ -79,11 +100,15 @@ header { -moz-box-shadow: 0 0 50px #666; box-shadow: 0 0 50px #666; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; top: 0; left: 0; width: 100%; + height: 150px; margin: 0 0 5em; - padding: 1em 0; + padding: 1em 0 0; text-align: center; } @@ -111,11 +136,15 @@ header .divider { -moz-box-shadow: 0 0 10px #000; box-shadow: 0 0 10px #000; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; top: 0; left: 0; width: 100%; + height: 50px; margin: 0 0 5em; - padding: 0.5em 0; + padding: 10px 0 0; text-align: center; } @@ -127,6 +156,8 @@ header .divider { } header a, header .link, header :link, #fixedheader a, #fixedheader .link, #fixedheader :link { + font-family: Exo; + font-weight: 700; text-decoration: none; } header a:hover, header .link:hover, @@ -187,9 +218,40 @@ blockquote:before { z-index: -1; } .appstore { - position: absolute; - right: 10px; + position: fixed; + display: none; + z-index: 100; font-size: 0; + + /* appstore-bubble.png * + top: 8px; + right: 5px; + */ + /* appstore.png */ + top: 25px; + right: 25px; +} +.appstore img { + border-radius: 5px; + -webkit-box-shadow: 0 0 30px #AAA; + -moz-box-shadow: 0 0 30px #AAA; + box-shadow: 0 0 30px #AAA; +} +.appstore:hover img { + -webkit-box-shadow: 0 0 30px #FFF; + -moz-box-shadow: 0 0 30px #FFF; + box-shadow: 0 0 30px #FFF; +} +header .appstore { + position: absolute; + display: block; + + top: auto; + /* appstore-bubble.png * + bottom: -73px; + */ + /* appstore.png */ + bottom: -25px; } .columns { position: relative; diff --git a/Site/img/appstore-bubble.png b/Site/img/appstore-bubble.png new file mode 100644 index 00000000..c867e3e0 Binary files /dev/null and b/Site/img/appstore-bubble.png differ diff --git a/Site/img/bubble.png b/Site/img/bubble.png new file mode 100644 index 00000000..b4ff44d4 Binary files /dev/null and b/Site/img/bubble.png differ diff --git a/Site/img/frontpage_phone.png b/Site/img/frontpage_phone.png index fa19cded..af93be7a 100644 Binary files a/Site/img/frontpage_phone.png and b/Site/img/frontpage_phone.png differ diff --git a/Site/index.html b/Site/index.html index 646ac32d..432fd03e 100644 --- a/Site/index.html +++ b/Site/index.html @@ -14,6 +14,19 @@ + +
@@ -75,7 +89,6 @@
@@ -87,19 +100,23 @@ -

Stop worrying about passwords

+

Stop worrying
+ about passwords

-

Memorizing passwords or even saving them in our browser, an application or the cloud just isn't good enough.

+

Admit it, you're terrible at memorizing passwords.

-

- Master Password is a solution that voids the need to keep your passwords anywhere. Not in your head, not on your computer and not in the cloud. -

-

- Nothing to store means nothing to lose. At the same time it makes sure that your accounts are adequately protected with exclusive passwords. -

-

- Learn how below. -

+

Just like the rest of the world, your passwords are too simple or reused between many sites. To security specialists or hackers, your accounts are like empty houses with the door wide open.

+

Even the sites you use that hold nothing of value can easily be used by attackers to impersonate you.

+ +

Master Password is a stateless solution, which means your passwords don't need to be saved anywhere. Not in your head, not on your computer and not in the cloud.
+ Nothing to store means nothing to lose. At the same time it makes sure that your accounts are adequately protected with secure and unique passwords.

+ +

Master Password is different from other vault-like password solutions. It helps you set secure passwords for your sites, and at the same time makes losing your passwords almost impossible.

+ +

Built on algorithms such as scrypt and HMAC-SHA256, your master password is kept safe even if websites you use get hacked.

+ +

On that topic, if you're not using Master Password yet and you have a LinkedIn account, you should worry (click for details).
+ Change your password now, and change it into something secure and unique.