DevBits header

Accessibility in 1Password for iOS

At AgileBits we believe that everyone should be secure online. That means we want 1Password to be usable by as many people as possible. We have worked hard to implement many features that make 1Password more accessible. In this post I will explain some of the technologies we’ve taken advantage of to improve accessibility in 1Password for iOS.

What We Have Done

While our efforts are ongoing, here are some of the areas in 1Password where we offer accessibility features.

Colour Vision Deficiency and Password Readability

You may have noticed that when viewing a password, 1Password colours numbers and symbols differently. This is not only for convenience. We have carefully chosen colours easily seen by those who have trouble distinguishing different shades of a colour. We have tested for deficiencies related to protan, deutan, and tritan.

Those with achromatopsia (no ability to distinguish colour) must rely on character shapes to tell them apart. 1Password therefore offers a choice of fonts to make it easy to distinguish between the letter o and the number zero; and between lowercase l, capital I, and the number one.

colouredpasswordcropped

Large Type

Sometimes you have to enter a password on another device that cannot run 1Password (e.g. a combination lock, employer’s computer, etc.). For this reason, we provide the large type feature that, when activated, displays the password as large as possible for easy reading when entering it elsewhere.

largetype

Dynamic Type

iOS provides a great feature called dynamic type. This allows apps to dynamically adjust the size of text to user settings. You can experiment with dynamic type in the iOS Settings app in General > Accessibility > Larger Text.

As a developer, it’d be too easy just to turn the feature on and leave it at that. But that would result in text that grows too large for the screen and ends up getting truncated to the point that it is unreadable, or it would push interface elements so far out of the way that they are unusable. Less important text could also crowd out more important text.

We’ve taken care to limit text size where necessary and ensure inevitable truncation happens such that the most important part of the text is still shown. We’re even looking at ways to rework the UI when the text is too large to fit.

dynamictype

VoiceOver

Apple has a fantastically comprehensive voice-over system in iOS. In 1Password we take advantage of that by ensuring all of our interface elements and data values are properly labeled for accessibility so VoiceOver can read them aloud in a context that makes the app more usable. We have also minimized redundancies in describing the interface to make sure relevant interface elements are described quickly and effectively. To keep your passwords secure, 1Password will not read your passwords aloud unless you explicitly request it.

Work in Progress

As interfaces in software change and new features get added, accessibility support can change too. If there’s an accessibility feature you would like to have, or something you feel is not implemented as well as it could be, please let us know! We welcome feedback and want to make 1Password more usable for all of our awesome users!

DevBits header

The endless waltz: making custom UI for Windows Modern

Branding is a promise. It’s a guarantee of what to expect and a derivation of an identity. Here at AgileBits, we have strong brand identity and our customers can immediately recognize our app, no matter the platform on which they see it. As a recent addition to the AgileBits family, working on the latest addition to the 1Password application family, one of my priorities has been to ensure our new app feels like 1Password. But this, like so many other things, is easier said than done.

At first blush (and second, and third, and…), Windows Modern applications look nothing like apps on Mac, iOS, Android or any other platform; right out of the gate, we had our work cut out for us and we had to decide how to balance the unique Windows Modern style with what has come to be recognized as the 1Password style. In the end, we decided to keep the Windows Modern app as true to the 1Password style as possible. Despite having to deal with an overly verbose pattern for overriding the style template (in some cases 100 lines of code just to change a single property for a button) we continued to customize nearly every area of the app; when the dust had finally settled, our shared stylesheet (for phone and desktop) was the largest single file in our entire project (~1200 lines of code).

But we were able to stay true to the 1Password brand, as evident in our lock screen:

So it was well worth the effort, and we certainly learned a few new tricks along the way! But styling with such reckless abandon does have a cost.

There will be blood (and hacks)

Issues first began to arise when we started testing theme-switching for Windows Phone. Windows Phone gives users the ability to switch between a dark and a light theme. This is a hallmark feature of Windows Phone and one that users have come to expect. But of course, this feature is only available on Windows Phone; the iOS and Android platforms don’t have this capability by default, which added a new wrinkle: do we introduce a feature on Windows Phone that is not available on our other mobile platforms? We also noticed very poor performance with accessibility features when using high-contrast themes, and ease-of-access font scaling had a very low ceiling before the majority of text was cut off.

Why did we perform so poorly? In part, it was because of all the customization we had done. We became so focused on achieving the 1Password look and feel that we customized nearly everything, including many things that we never needed to touch. We also used the RequestedTheme property heavily, which allowed us to force a page to always render in a specific theme; this is a big no-no for a universal app. But more than anything else, it’s how the platform handles custom styles. The moment you create a custom style, you’re entirely accountable for it: you must tell it exactly how to behave under every single scenario a user may throw at you; and on the Windows Modern platform, there are a lot of possible scenarios. There are two default themes for Windows Phone (and two other high-contrast themes), and Windows desktop has the default theme (not light, not dark, just right) along with four other high-contrast themes. Yep, that’s eight themes developers need to support when they choose to add some of their own styling and branding to their apps. The moment you decide to change a background colour, adjust a font’s opacity, or change letter spacing, you’re abandoned by the Windows app platform, and left to your own devices. This desertion isn’t too much of an issue for developers working primarily on Windows modern, as the support the platform gives you to handle all of these scenarios right out of the box is excellent. But it’s an unfeasible expectation for Windows to extend this support to developers who customize their apps so heavily, so we got to work and began getting our stylesheets under control.

The taming of the shrew

After all of our customization, we had to take a step back and see how we could make 1Password work with the Windows Modern ecosystem, instead of against it.

We had no desire to rewrite all of the styles and themes that Microsoft had given us for free with the operating system. The code was ready and available; all we had to do was let the platform do its work without losing our brand in the platform.

Our first order of business was getting our stylesheets under control. We went through the entire app, page by page, and removed any properties we could live without, thereby giving responsibility back to Windows. We then rebuilt support for all the missing themes by first including them explicitly in our stylesheets, and only overriding the system colors in the default or base theme; for high contrast and light/dark themes, we once again passed responsibility back to Windows.

Alternatives

Prior to coming to work at AgileBits, I gained a lot of experience working on web accessibility for the Canadian Government, and we often discussed how different platforms handled accessibility.  On the Mac, instead of separate themes, users are given sliders in the OS preferences to adjust contrast and invert colours when necessary. Keeping these preferences at the OS level takes a lot of the responsibility away from app developers. While it can seem a bit daunting to oversee eight themes (at least) on Windows, the aesthetic and practical usefulness of these themes cannot be denied. For example, users can create custom themes that can be saved to their profile and be made available on the different PCs they use. Also, these themes being predefined make selecting appropriate themes much easier for users.

Conclusion

Branding is a promise. This is our endless waltz, and we cannot fail our dance partners. It’s a delicate balance that we must strike between Windows Modern and the 1Password experience, so that we may engender harmony among 1Password and Windows users.

1Password for Mac logo

Adventures in beta testing: 1Password, El Capitan, and iOS 9

Let’s talk about betas. Specifically, let’s talk about Apple’s operating system betas. It used to be that you had to be an active member of the Apple Developer Program to get access to the betas. Last year, Apple launched a beta software program that enables anyone to sign up to test-drive pre-release versions of OS X. This year, for the first time, anyone can sign up to evaluate iOS 9 beta in addition to OS X 10.11 El Capitan beta.

Newshiny

There’s something thrilling about using beta software. It’s exciting to experience the software development process, with frequent updates that fix and improve things before our very eyes. It’s gratifying to participate in that process, seeing our bug reports get resolved and change requests considered and sometimes implemented. I don’t know about you, but I love feeling like I’ve helped make an improvement from which everyone using the software will benefit.

Hark, the cheers from developers far and wide

One of the most difficult things for software developers is getting the feedback they need before an application version goes public. This is because the pool of beta testers is generally so small. We could think everything is just fine, and then it gets out there and—BOOM—suddenly there are all these edge cases that never came up during the beta, because there are so many more people using it.

Public betas can be a real boon to developers, in that they help to increase the size of the beta pool and the degree to which the beta application is tested.

Hard hats required

under construction

Perhaps you remember those “Under construction” images from the early days of web publishing? It’s a very real metaphor for beta software. The most important thing to remember1 is that beta software is incomplete. Some things will not be implemented yet, some will be broken, and some may cause unexpected system kerfufflery.

Here are a few tips to help make your beta experience safe and enjoyable:

Spare a square

Ideally, beta software should be installed on spare hardware. If you have only one Mac, you can install El Capitan beta on separate partition of your Mac’s hard drive. If the iPhone you use every day is your only iOS device, it’s probably best not to install iOS 9 beta. If you have a non-critical iPad or an iPod touch, that would be a good place to install the beta.

Back that thang up

I know some of you are going to ignore me completely and install the betas on your mission-critical devices. Before you do that, please make sure to create a reliable backup!

We hear you

Your feedback is indispensable. If you notice anything wonky, be sure to report it to developers. I’ve seen beta issues reported in App Store reviews. While developers certainly read those and learn from them, they have no way of reaching out to the customer to help. It is best to contact developers directly with your beta feedback.

If you’re using 1Password beta, we have dedicated beta discussion forums. The beta forums are monitored by our developers and our support team is around to help you seven days a week!

If something you report isn’t immediately addressed, don’t worry. Developers may not be able to do anything about it just yet. Rest assured that the issue will be resolved as quickly as possible.

1Password 5, El Capitan, and iOS 9

I’m happy to tell you that we have thus far encountered no major issues in our testing. I have noticed a couple of graphical and layout issues in El Capitan beta, but it’s too early to tell whether the issues are in 1Password 5 for Mac or in El Capitan beta. We don’t want to spend time fixing something that may not actually be broken on our end, so for the moment we’re waiting to see how things pan out. We’ve documented the issues so we don’t lose track of them.

How to test 1Password beta for Mac

You are warmly invited to join our family of beta testers. The more, the merrier! 1Password 5.4 beta for Mac doesn’t require El Capitan beta, but it does require that you use the AgileBits Store version of 1Password, not the Mac App Store version. It’s very easy to switch over, but you will not be able to sync with iCloud.

How to test 1Password beta for iOS

Apple’s TestFlight Beta Testing program enables developers to extend a limited number of invitations to customers. There has been a great deal of interest in 1Password beta for iOS, and we are not looking for additional testers at this time. You can be the first to hear about opportunities to join our beta family for iOS by following @1PasswordBeta on Twitter.

1Password beta for Mac does not require 1Password beta for iOS.

1Password happy face

Have fun!

I lied earlier. The most important thing is to have fun, but keeping in mind the foibles of beta software and protecting yourself against them are a close second. =)

DevBits header

Debugging Next Key View Loops

When you create a window and populate it with views, AppKit automatically links your controls into a tab loop by setting the nextKeyView property on each view. This process is handled by the recalculateKeyViewLoop method inside NSWindow. The order of your views in this loop is based on the geometric location of each view in the window. Most of the time, this automatic mechanism works great, and you don’t have to think about it. But sometimes a window is too complex, and the automatic method produces incorrect results. When this happens, manual adjustment of the nextKeyView loop is required.

This is the situation we faced with the item detail view in 1Password for Mac 5. The detail view consists of a view-based NSTableView with multiple custom cell views. During the development of this view, we were constantly breaking the nextKeyView loop. Every time a new text field was added, or a button moved, some part of the nextKeyView loop would invariably break.

To make this debugging simpler, we wrote a utility class to visually overlay nextKeyView information on top of the detail view. Here is how it works.

The header

Let’s start with the header (.h):

//
//  OPTestKeyLoopView.h
//

#import <Cocoa/Cocoa.h>

@interface OPTestKeyLoopView : NSView

+ (void)enableForView:(NSView *)startView;
+ (void)disable;

@end

The header provides a simple interface to enable and disable key loop debugging on a particular view. OPTestKeyLoopView is a Singleton, so these methods operate on the Class.

Private class to store visual overlay info

Moving on to the implementation (.m):

First, let’s define a simple private class used to store the visual overlay information about a particular view.

//
//  OPTestKeyLoopView.m
//

#import "OPTestKeyLoopView.h"


@interface OPTestKeyView : NSObject

@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;
@property (nonatomic, strong) NSColor *color;
@property (nonatomic, strong) NSFont *font;
@property (nonatomic, assign) NSRect frame;

@end


@implementation OPTestKeyView

@end

This class doesn’t do anything other than store the properties of a view overlay.

OPTestKeyLoopView implementation

Next, we begin implementing OPTestKeyLoopView by writing the Singleton and Class methods.

@interface OPTestKeyLoopView ()

@property (nonatomic, strong) NSView *rootView;
@property (nonatomic, strong) NSMutableArray *keyViews;
@property (nonatomic) NSRect rootFrame;

@end


@implementation OPTestKeyLoopView

+ (OPTestKeyLoopView *)sharedTestKeyLoopView {
    static OPTestKeyLoopView *sharedView = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedView = [[self alloc] init];
        sharedView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
    });
    
    return sharedView;
}

+ (void)enableForView:(NSView *)startView {
    [self sharedTestKeyLoopView].rootView = startView;
    [[NSNotificationCenter defaultCenter] addObserver:[self sharedTestKeyLoopView] selector:@selector(mainWindowFlagsChanged:) name:OPMainWindowFlagsChangedNotification object:nil];
}

+ (void)disable {
    [self sharedTestKeyLoopView].rootView = nil;
    [[self sharedTestKeyLoopView] clear];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Sidebar: OPMainWindowFlagsChangedNotification

This is a good time to pause and talk about the OPMainWindowFlagsChangedNotification notification. We want our overlay to appear when you press the Option key. The standard way to catch an Option key down event is to override

- (void)flagsChanged:(NSEvent *)theEvent

in your NSWindow or NSWindowController subclass. In our case, our window controller overrides this method and posts a notification so that other parts of the UI can easily respond to modifier key events. Here is what it looks like:

// From our NSWindowController subclass
- (void)flagsChanged:(NSEvent *)theEvent {
    [super flagsChanged:theEvent];
    [[NSNotificationCenter defaultCenter] postNotificationName:OPMainWindowFlagsChangedNotification object:nil];
}

Add the notification handler

Moving back to the implementation of OPTestKeyLoopView, next we’ll add the notification handler:

- (void)mainWindowFlagsChanged:(NSNotification *)aNotification {
    NSUInteger modifierFlags = ([NSApp currentEvent].modifierFlags & NSDeviceIndependentModifierFlagsMask);
    if (modifierFlags & NSAlternateKeyMask) {
        NSView *hitView = self.rootView;
        while (YES) {
            NSPoint mouse = [hitView convertPoint:[[self.rootView window] mouseLocationOutsideOfEventStream] fromView:nil];
            BOOL hitSubView = NO;
            for (NSView *subview in hitView.subviews) {
                if (subview == self) {
                    continue;
                }
                if (NSMouseInRect(mouse, subview.frame, hitView.isFlipped)) {
                    hitView = subview;
                    hitSubView = YES;
                    break;
                }
            }
            if (!hitSubView) {
                break;
            }
        }
        
        if (modifierFlags & NSShiftKeyMask) {
            [self showPreviousKeyViewLoopForView:hitView];
        }
        else {
            [self showNextKeyViewLoopForView:hitView];
        }
    }
    else {
        [self clear];
    }
}

This method checks for the Option key down and locates the furthest child view under the mouse. This child view will be the start of our next key view loop. Then we call a method to show the next key view loop overlay. If the shift key is also held down, we reverse the loop and show the previous key view loop.

Insert overlay view into view hierarchy

Next, we implement methods to insert our overlay view into the view hierarchy, and to build our list of keyViews. We do this by following the nextKeyView (or previousKeyView) property of the starting view and recording information about each view we encounter.

- (void)insertIntoViewHirarchy {
    if (self.superview) {
        [self removeFromSuperview];
    }
    self.frame = [[[self.rootView window] contentView] frame];
    [[[self.rootView window] contentView] addSubview:self positioned:NSWindowAbove relativeTo:nil];
}

- (void)showPreviousKeyViewLoopForView:(NSView *)startView {
    [self insertIntoViewHirarchy];
    
    if (!self.keyViews) self.keyViews = [NSMutableArray new];
    [self.keyViews removeAllObjects];
    
    NSLog(@"+++PreviousKeyViewLoop");
    NSView *view = startView;
    NSMutableSet *visitedViews = [NSMutableSet new];
    NSInteger num = 0;
    do {
        NSRect frameInWindow = [view convertRect:view.bounds toView:nil];
        OPTestKeyView *keyView = [[OPTestKeyView alloc] init];
        keyView.frame = [self convertRect:frameInWindow fromView:nil];
        keyView.title = [NSString stringWithFormat:@"%ld", num];
        keyView.color = [NSColor redColor];
        keyView.font = [NSFont fontWithName:@"Menlo" size:12];
        
        NSLog(@"%ld %@", num, [view className]);
        [self.keyViews addObject:keyView];
        
        [visitedViews addObject:view];
        view = view.previousKeyView;
        num--;
    } while (view && ![visitedViews containsObject:view]);
    NSLog(@"---");
    
    self.rootFrame = [self convertRect:[startView convertRect:startView.bounds toView:nil] fromView:nil];
    [self setNeedsDisplay:YES];
}

- (void)showNextKeyViewLoopForView:(NSView *)startView {
    [self insertIntoViewHirarchy];
    
    if (!self.keyViews) self.keyViews = [NSMutableArray new];
    [self.keyViews removeAllObjects];
    
    NSLog(@"+++NextKeyViewLoop");
    NSView *view = startView;
    NSMutableSet *visitedViews = [NSMutableSet new];
    NSInteger num = 0;
    do {
        NSRect frameInWindow = [view convertRect:view.bounds toView:nil];
        OPTestKeyView *keyView = [[OPTestKeyView alloc] init];
        keyView.frame = [self convertRect:frameInWindow fromView:nil];
        keyView.title = [NSString stringWithFormat:@"%ld", num];
        keyView.color = [NSColor blueColor];
        keyView.font = [NSFont fontWithName:@"Menlo" size:12];
        
        NSLog(@"%ld %@", num, [view className]);
        [self.keyViews addObject:keyView];
        
        [visitedViews addObject:view];
        view = view.nextKeyView;
        num++;
    } while (view && ![visitedViews containsObject:view]);
    NSLog(@"---");
    
    self.rootFrame = [self convertRect:[startView convertRect:startView.bounds toView:nil] fromView:nil];
    [self setNeedsDisplay:YES];
}

- (void)clear {
    [self.keyViews removeAllObjects];
    [self removeFromSuperview];
}

Draw the overlay

Now that we have overlay information to display, and our OPTestKeyLoopView is in the view hierarchy, we can draw the overlay.

- (NSDictionary *)attributesWithFont:(NSFont *)font color:(NSColor *)color {
    NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [paragraphStyle setAlignment:NSCenterTextAlignment];
    [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
    [paragraphStyle setLineSpacing:0];
    
    NSDictionary *attributes = [[NSDictionary alloc] initWithObjectsAndKeys:
                                font, NSFontAttributeName,
                                color, NSForegroundColorAttributeName,
                                paragraphStyle, NSParagraphStyleAttributeName, nil];
    return attributes;
}

- (void)drawRect:(NSRect)dirtyRect {
    if (self.keyViews.count > 0) {
        [[NSColor colorWithDeviceRed:1 green:0 blue:0 alpha:0.03] set];
        NSRectFillUsingOperation(self.rootFrame, NSCompositeSourceOver);
    }
    
    for (OPTestKeyView *keyView in self.keyViews) {
        [keyView.color set];
        [NSBezierPath setDefaultLineWidth:0.5];
        [NSBezierPath strokeRect:NSOffsetRect(NSIntegralRect(keyView.frame), 0.25, 0.25)];
        NSDictionary *attributes = [self attributesWithFont:keyView.font color:keyView.color];
        NSSize size = [keyView.title sizeWithAttributes:attributes];
        NSRect textRect = NSIntegralRectWithOptions(NSInsetRect(keyView.frame, (keyView.frame.size.width - size.width)/2, (keyView.frame.size.height - size.height)/2), NSAlignMinXOutward|NSAlignMaxXInward|NSAlignMinYOutward|NSAlignMaxYInward);
        [[NSColor whiteColor] set];
        NSRectFill(textRect);
        [keyView.title drawInRect:textRect withAttributes:attributes];
    }
}

In drawRect: we loop through our keyView array and draw the overlay information for each keyView.

Tidy up

Lastly, we do some housekeeping by making sure our OPTestKeyLoopView is non-opaque and does not respond to mouse clicks.

- (BOOL)isOpaque {
    return NO;
}

- (NSView *)hitTest:(NSPoint)aPoint {
    return nil;
}

@end

Here is what this looks like in action:

Debugging Next Key View Loops

Here is the code contained in this article (under an MIT license):

We’ve found this technique extremely useful when dealing with complex windows, and we hope you find it helpful too.

1Password for Android header

Wi-Fi Sync comes to 1Password for Android

Yesterday evening, after several weeks of collaboration with our beta family, we published the highly anticipated 1Password version 4.5 for Android. The big news, of course, is that it is now possible to sync your primary vault with 1Password for Mac or Windows using the new Wi-Fi Sync feature.

Look, Ma, no hands! (or clouds)

1Password for Android Wi-Fi logo

We’re thrilled to offer you even greater control over how you sync your 1Password data. If you prefer to keep your vault on your local Wi-Fi network and not fuss with manually copying your 1Password vault to and from your Android phone or tablet, you’ll enjoy the convenience of Wi-Fi Sync. You can read about setting up Wi-Fi Sync in our user guide. If you’re interested in reading more about Wi-Fi Sync from a developer’s perspective, we posted a DevBits article about this on our blog last month.

In related news, you know that “pull to refresh” gesture, where you tap your screen and drag it down? Well, we kind of love it. But that’s not a good enough reason to use it. After all, we don’t just throw things into 1Password, all willy nilly-like. While we were working on improvements for 4.5, we thought, What if you could use that easy, convenient gesture to manually trigger a sync? That would be cool! So now you can.

Polyglottery, levelled up.

One of the ways in which we make 1Password more accessible to folks is by localizing it, and we were immensely proud when we began doing that last year. Today, we add Korean to the list of available localizations.
안녕하세요!

You are awesome and we love you.

You are some seriously passionate people! Every day, our inbox and forums are filled with new conversations. The time you take to submit feature requests and bug reports means the world to us. It helps our developers prioritize resource allocation so we can make 1Password even better for you. The complete rundown of the improvements in version 4.5 can be found on our version history page.

If you use 1Password beta for Android, we have to extend an extra special thank you to you. We love our beta families on every platform, but especially on Android! There are so many different Android devices and without your help, it would be exponentially more difficult for us to make sure every 1Password update is a solid one. Thank you for all that you do.

1Password 4.5 for Android is available now in your local Google Play Store. If you’d like to join our beta team, you’re most welcome! Please sign up on our website. Got feedback? That’s fantastic, we love to hear from you. Please share your thoughts with us in our Android forum.

Windows v4 blog

1Password 4.6 for Windows: Fine-tuning the air conditioner

I’m not sure what it’s like where you are, but it’s been HOT lately, here in Germany. Recent days have had many on the team wishing they could work in their underwear. (Spoiler alert: I believe many already do.)
And just like fine-tuning an air conditioner in the house can make the difference between melting like ice in the sunshine and constantly looking like a plucked goose, we’re using version 4.6 of 1Password for Windows to make some useful improvements and fix a few bugs, to make things extra comfy for you.

Here’s the new coolness:

1Password + Yandex = best pals

The 1Password extension now works nicely in the privacy-focused Yandex browser. To install it, simply visit our browser extensions page in Yandex. Yandex will identify itself as Google Chrome (it’s based on the Chromium project) and from there you can install the extension like you usually would in Google Chrome.

Usernames column in the Logins category

You asked for it, we’ve listened. Now you can have a username column in the Logins category. To enable it, select View > Columns > Show Username in Logins in the menu bar.

Reordering Favorites

Don’t like the order of your favorites? Now you can reorder them by right-clicking the item you want to move up or down and selecting the appropriate menu option.

New custom field type: Phone

Once you add a custom phone field to an item, clicking the phone number will allow you to dial it with your favorite VoIP application or hand off the call to your smartphone by using a remote phone app for Android or Windows Phone.
phone-field-type

More cool stuff…

  • The reliability of 1Click Bookmarks in Internet Explorer has been vastly improved.
  • The date picker in all categories now includes month digits next to the month name.
  • Our translators have further refined the localization of the app.

You can find the entire list of new features, improvements, and bug fixes in the release notes.

1Password 4.6 for Windows is available now as a free update for all existing owners. Choose Help > Check for New Version in the menu bar, or grab the new version from our downloads page.

Security

1Password inter-process communication: a discussion

Recently, security researcher Luyi Xing of Indiana University at Bloomington and his co-authors released the details of their research revealing security vulnerabilities in Apple’s Mac OS X and iOS that allow “a malicious app to gain unauthorised access to other apps’ sensitive data such as passwords and tokens for iCloud, Mail app and all web passwords stored by Google Chrome.”  It has since been described in the technology press, including an article in the Register with a somewhat hyperbolic title. I should point out that even in the worst case, the attack described does not get at data you have stored in 1Password.

The fact of the matter is that specialized malware can capture some of the information sent by the 1Password browser extension and 1Password mini on the Mac under certain circumstances.  But roughly speaking, such malware can do no more (and actually considerably less) than what a malicious browser extension could do in your browser.

For 1Password, the difficulty is in fully authenticating the communication between the 1Password browser extension and 1Password mini; however, this problem is not unique to 1Password. The difficulty of securing inter-process communication on the operating system is a problem system-wide. A recent paper, “Unauthorized Cross-App Resource Access on MAC OS X and iOS” (PDF),  by Luyi Xing (Li) and his colleagues shows just how difficult securing such communication can be. Since November 2014, we’ve been engaged in discussion with Li about what, if anything, we can do about such attacks. He and his team have been excellent at providing us with details and information upfront.

As always, we are limited in what we can do in the face of malware running on the local machine. It may be useful to quote at length the introduction of that article

I have said it before, and I’ll say it again: 1Password […] cannot provide complete protection against a compromised operating system. There is a saying […] “Once an attacker has broken into your computer […], it is no longer your computer.” So in principle, there is nothing that 1Password can do to protect you if your computer is compromised.

In practice, however, there are steps we can and do take which dramatically reduce the chances that some malware running on your computer [could obtain your 1Password data].

That was written more specifically about  keystroke loggers, and there are some things that set the new attack apart. Like superficial keystroke loggers it doesn’t require “admin” or “root” access, but they were able to sneak a proof of concept past Apple reviewers.

The threat

The threat is that a malicious Mac app can pretend to be 1Password mini as far as the 1Password browser extension is concerned if it gets the timing right. In these cases, the malicious app can collect Login details sent from the 1Password browser extension to the fake 1Password mini. The researchers have demonstrated that it is possible to install a malicious app that might be able to put itself in a position to capture passwords sent from the browser to 1Password.

Note that their attack does not gain full access to your 1Password data but only to those passwords being sent from the browser to 1Password mini. In this sense, it is getting the same sort of information that a malicious browser extension might get if you weren’t using 1Password.

Background

1Password provides its own security. What I mean by this is that for the bulk of what we do, we don’t generally rely upon security mechanisms like sandboxing or iOS Keychain. So it doesn’t matter whether those sorts of security measures provided by the operating system fail.

The careful reader will note, however, that I used phrases like “for the bulk of what we do” and “don’t generally rely upon” in the previous paragraph. There are some features and aspects for which some of 1Password’s security makes use of those mechanisms, and so vulnerabilities in those mechanisms can allow for harm to us and our customers.

1Password mini listens to the extension

Application sandboxing is a good thing for security. But it limits how the 1Password browser extension can actually exchange data with 1Password itself. Indeed, the extension (correctly) has no direct access to your data. Keeping your data out of the browser (a relatively hostile environment) is one of our security design choices. But this does mean that the 1Password browser extension needs to find a way to talk to something that does actually manage your data. 1Password mini (originally the 1Password Helper) was invented for this purpose.

One of the few ways that a browser extension can communicate locally is through a websocket. Browser extensions are free to talk to the Internet as a whole, but we certainly don’t want our browser extension doing that; we only want it talking to 1Password locally. So we restrict the browser extension to only talking to 1Password mini via a local websocket.

Mutual authentication

Obviously we would want 1Password mini and the browser extension to only talk to bona fide versions of each other, so this becomes a problem of mutual authentication. There should be some way for 1Password mini to prove to the extension that it is the real one, and there should be a way for the browser extension to prove to 1Password mini that it is a real 1Password browser extension.

The difficulty that we face is that we have no completely reliable mechanism for that mutual authentication. Instead, we employ a number of separate mechanisms of authentication, but each has its own limitations. We have no way to guarantee that when the browser extension reaches out to 1Password mini it is really talking to the genuine one.

There are a number of checks that we can (and do) perform to see if everyone is talking to who they think they are talking to, but those checks are not perfect. As a result, malware running on your Mac under your username can sometimes defeat those checks. In this case, it can pretend to be 1Password mini when talking to the browser extension and thus capture any information sent from the 1Password browser extension that is intended for the mini.

What can be done

Neither we nor Luyi Xing and his team have been able to figure out a completely reliable way to solve this problem. We thank them for their help and suggestions during these discussions. But, although there is no perfect solution, there are things that can be done to make such attacks more difficult.

What you can do

1. Check “Always Keep 1Password Mini Running” in Preferences > General

In the specific attack that Luyi Xing demonstrates, the malicious malware needs to be launched before the genuine 1Password mini is launched. By setting 1Password mini to always run, you reduce the opportunity for that particular attack.

keep mini running

 

 

2. Keep using the 1Password browser extension

Although what is described is an attack against the communication between 1Password mini and the browser extension through specialized malware, using the 1Password browser extension protects you from a more typical malware attack of pasteboard/clipboard sniffers. Likewise, the 1Password extension helps fend off phishing attacks because it will refuse to fill into pages that don’t match the domain for your saved Logins.

Quite simply, the 1Password extension not only makes life easier for you, but it is an important safety feature on its own.

3. Pay attention to what you install

As always be careful about what software you run and install on your system. On your Mac, open System Preferences > Security & Privacy > General. You’ll see an Allow apps downloaded from: setting there. We strongly recommend confirming that this setting is configured so that only apps from trusted sources can be opened. You can read more about the setting and its options on Apple’s support site.

Now Xing and his team point out that this isn’t a guaranteed way to prevent malware being installed. They were able to get a malicious app approved by the Mac App Store review process. However, I think it is reasonable to assume that now that Apple reviewers know what to look for, it will be much harder for that specific kind of malware to get through.

What we can do

There are additional (defeasible) mechanisms that we can add to our attempts at mutual authentication between the extension and 1Password mini. I will briefly mention a few that we’ve considered over the years.

Encryption with an obfuscated key

One option is to have a shared obfuscated key in both 1Password mini and the extension. (Remember that the browser extension never sees your Master Password so any secret it stores for authentication cannot be protected by your Master Password.)

Obfuscation only makes things harder for attackers until someone breaks the obfuscation, and every system designer should assume that obfuscation will be broken. See our discussion of Kerckhoffs’ Principle in our article, “You have secrets; we don’t,” for some background on why we tend to be reluctant to use obfuscation. Of course, it may be warranted in the absence of a more effective alternative, so this remains under consideration.

In anticipation of a likely suggestion, I should point out that even the magic of public key encryption wouldn’t save us from having to rely on obfuscation here; but I will save that discussion for our forums.

Using the OS X keychain

Another option would be to store authentication secrets in the OS X keychain, so that both our browser extension and 1Password mini would have access to it. This could be made to work for authenticating 1Password mini to the extension for those browsers that allow easy use of the OS X keychain.

This might solve half the problem for some browsers, but to date we’ve been focusing on solutions that work across all of the browsers we support.

An extreme solution

In the extreme case, we could have some explicit pairing (sort of like Bluetooth) between 1Password mini and the extension.  That is, the browser extension may display some number that you have to type into 1Password mini (or the other way around).  With this user intervention we can provide solid mutual authentication, but that user action would need to be done every time either the browser or 1Password mini is launched.

Quite frankly, there is no really good solution for this. To date, our approach has been to put in those authentication checks that we have and keep an eye out for any hints of malware that exploits the known limitations of what we do.

Is 1Password for iOS affected?

The research paper isn’t limited to discussing inter-process communication (IPC) that is done through websockets, but covers a wide range of mechanisms used on Apple systems. This includes some mechanisms that we may use for some features in 1Password for iOS.

Shared data security

1Password for iOS shares some of its data with the 1Password app extension. As most of that data is encrypted with your Master Password, it is not a substantial problem if that data becomes available to attackers. The exception, of course, is the TouchID secret.

As yet, we have not had a chance to test whether there is any exposure there, but watch this space for updates.

Conclusion

We truly are grateful for the active security community, including Luyi Xing and his team, who take the time to test existing security measures and challenge us to do better. Our analysis of the researchers’ findings will continue and we will post an update if further action is necessary.

Anniversary sale polaroids

9th Anniversary Sale-abration!

Update: The 9th Anniversary Sale ended on June 20th, 2015.

Let me take you back in time to the days when AgileBits was known as Agile Web Solutions. About 10 years ago, Dave Teare and Roustem Karimov decided to spend a month writing a quick little password management app to help them share data more efficiently while they worked to build their Palm app empire. Response to the little tool was great, and soon the pair had made the app available for download in their store.

Our intrepid co-founders quickly realized that they had a Thing on their hands. A really Real Thing! At the same time, Dave & Roustem had begun a little love affair with the Mac, and decided it was the perfect platform for their ambitious application. And so, nine years ago, on June 18, 2006, 1Password 1.0 for Mac was born. (For a real blast from the past, check out the original 1Passwd website!)

1Password_1___2_for_Mac_Release_Notes

Palm may be a thing of the past, but Dave and Roustem’s “hobby” has grown into a powerful and secure data management tool for most major desktop and mobile operating systems, including Mac OS X, Windows, iOS, and Android. What began as two coders and one support person has grown into a team of almost 60.

polaroidsWe’re so proud of how much 1Password has evolved over the past nine years, largely due to feedback and support from millions of customers just like you. To celebrate, we’re offering a 30% discount on 1Password for Mac and Windows. You can get both 1Password for Mac and Windows in the AgileBits Store, and 1Password for Mac is in available from the Mac App Store.

This is a limited-time offer, so take advantage of the celebration and get your 1Password license today!

DevBits header

Wi-Fi Sync in 1Password for Android: Design Overview

Today, I’m happy to tell you that Wi-Fi Sync is coming to 1Password for Android! In fact, it is already available in the latest beta, so you can join our beta family and try it out right now. In this edition of our DevBits series, I am going to talk about how we implemented Wi-Fi Sync in 1Password for Android.

Wi-Fi Sync in 1Password for Android uses only standard Android APIs. We don’t use any third-party libraries. All the required communication logic was written in-house (although inspirational ideas for WebSocket implementation were taken from elsewhere). Using Android APIs keeps the .apk file small and eliminates version incompatibility, licensing issues, or any other trouble that might arise when incorporating third-party code into the app.

Wi-Fi Sync in 1Password for Android consists of three parts: Network Service Discovery (NSD), Network Service Resolution, and the actual sync itself. Both the Network Service Discovery and Network Service Resolution are based on the NSD framework built in to Android. The sync is implemented using synchronous Websocket communication with a service provided by 1Password for Mac or 1Password for Windows.

Network Service Discovery

When you choose to sync using Wi-Fi in 1Password for Android, Network Service Discovery is launched asynchronously and continues to run in the background until you stop it. The service looks for all network services matching the type used by 1Password (in our case “_1password4._tcp.”).

This network service type matches the type used by the latest versions of 1Password on both Mac and Windows when Wi-Fi Sync is enabled. Any discovered Wi-Fi services are displayed in a list for you to select from in order to set up the initial sync. It is important to note that the service info found by NSD contains no information other than the service name and type.

Network Service Resolution

Once you have decided which service you want to use, the Network Service Resolution process is launched asynchronously for the chosen service. 1Password for Android is given the service credentials, including the IP address and port, so that communication with the server can be established. The service name is stored in 1Password preferences and used for subsequent communication sessions. This allows service discovery during incremental sync to automatically stop when a service matching the one stored in preferences is found.

Next, 1Password proceeds with service resolution. If the connection is successful, the actual sync process is launched using the provided service IP address and port. If service discovery is unable to discover the service in two seconds, or if the resolution is invalid, you will be asked to ensure that 1Password is running on the computer you are trying to sync with, and the sync attempt is aborted.

The actual sync

1Password for Android Wi-Fi logo

The actual sync process is handled by a subclass of Android’s AsyncTask that establishes synchronous communication with the server using the WebSocket protocol. In order to establish a connection, this task first requires valid service credentials (address and port) and a reference to the database manager. Once connected with the service, communication proceeds according to a proprietary JSON-based command protocol which is itself based on the WebSocket protocol.

Once 1Password for Android is successfully authenticated by the server it receives an item/folder list. Next, a request is made for items from the list which have been updated on the server, and these are then decrypted and saved in the 1Password for Android internal database.

In order to decrypt these items, your Master Password is requested during initial sync. Although the communication secret is stored in 1Password preferences, it should be noted that your Master Password is never stored in the system preferences or in the database.

Once the initial sync is complete and an incremental sync has begun, you may notice some minor differences between syncing with 1Password for Mac and 1Password for Windows. These differences are the result of architectural differences between the two versions, namely that 1Password for Windows doesn’t rely on an internal database. This results in slightly faster syncing with 1Password for Windows and the need to enter your Master Password on each incremental sync.

When the Wi-Fi Sync server has transmitted all of its updated items to 1Password for Android, and it has transmitted all of its updated items back to the Wi-Fi Sync server, the communication session is terminated and the network socket is closed. Detailed sync results of the latest session are written to the Diagnostics Report, which you can generate from the Settings > Advanced screen and review at any time.

At present, Wi-Fi Sync is designed to work between one computer and one or more mobile devices. We do not recommend switching between multiple desktops when syncing using Wi-Fi. Note that the sync method cannot be changed once it has been selected. For example, if your initial sync uses Wi-Fi, you cannot later switch to Dropbox. Because 1Password for Android supports sync with only the primary vault at this time, it is not possible to switch to a different vault once the Wi-Fi Sync connection to the chosen server has been established.

The addition of Wi-Fi Sync to 1Password for Android furthers our goal of placing you in control of your data. In addition to local storage and sync with Dropbox, you now have a third option for syncing your vault from your Android devices to your other devices. We hope you enjoy using it and welcome your feedback in our beta forums.

DevBits header

Filling with 1Password for Android

1Password is all about bringing convenience to security. Of course, there are always challenges to overcome. On Android, one particular challenge we have been working on is how to make it both secure and convenient for you to use your login credentials. Until recently, your options for filling these credentials were limited to either using the 1Browser built into 1Password or using the clipboard to copy and paste.

While 1Browser helps you fill your login credentials into your favorite sites, it probably isn’t as fully featured as your favorite browser. 1Browser also isn’t much help when you want to use your login credentials to sign into an app. In these situations, you were previously limited to using copy and paste to get your login information out of 1Password and into that browser or app. Unfortunately, using the clipboard for this purpose is not at all convenient, and as we have mentioned before, not particularly secure.

Something better

When evaluating ways to provide a Login filling solution, we wanted to address the following concerns:

  • It needed to be more secure and more convenient than using the clipboard.
  • It needed to provide login filling for both third-party apps and browsers.

In order to make this happen, we needed to implement a service that could detect login fields when displayed in apps and browsers, and insert text directly into those fields. So, we split this functionality across two different services: the 1Password Automatic Filling service detects login fields and gives them focus when appropriate, while the 1Password Keyboard displays the interface for selecting the right Login and sends the credentials for that Login to the appropriate text fields.

Login detection

Twitter Login PageThe first step in filling your credentials is determining when there is a login form on screen that 1Password can fill into. To do this, we take advantage of the Accessibility APIs included in Android to get information about the elements displayed on screen in the form of an AccessibilityEvent.

Our implementation of the 1Password Automatic Filling service starts with this callback:

 @Override
 public void onAccessibilityEvent(AccessibilityEvent event) {
 // Insert magic here
 }

The onAccessibilityEvent callback is fired whenever a user interface event occurs for which we have registered. In our case, we are interested in events which indicate that the elements on the screen have changed. In particular, we register to receive typeViewFocused, typeWindowStateChanged, and typeWindowContentChanged events. By monitoring these events, we can keep an eye out for potential login screens or other opportunities for 1Password to fill.

When the callback is fired for one of these events, our next step is to see if we can identify login fields on the updated screen. We can determine which user interface elements are displayed on screen by invoking AccessibilityEvent.getRootInActiveWindow(). From the root AccessibilityNodeInfo object returned by this method, we can obtain information about all the user interface elements displayed in the active window. In particular, we look for arrangements of text fields matching the pattern for login entry. Once login fields have been identified, the 1Password Keyboard is notified that automatic filling is available. The keyboard is also passed the package name of the application or the URL of the website in which the login fields were detected.

Login selection

1Password keyboard Login sectionKeyboards on Android are built upon the InputMethodService APIs provided by the OS and, in this sense, the 1Password Keyboard is similar to other third-party keyboards. However, the benefit of creating a custom keyboard is that it can be tweaked to do a whole lot more than simple text entry. In the case of 1Password, our keyboard also allows you to view and select the Login items contained in your vault. When you tap the 1Password button on the keyboard, we expand the keyboard to full screen in order to display a list of relevant Logins.

If the 1Password Keyboard has been notified that automatic filling is available, it will look at the package name or URL provided by the 1Password Automatic Filling service and attempt to match it with the Logins contained in your vault. We display any matching Logins and offer the ability to browse for additional logins when appropriate. From here, you can tap on a Login to select it for filling.

Login filling

1Password keyboard filling completeOnce you have selected the appropriate Login for filling, the 1Password Keyboard exits fullscreen mode and once again shows the keyboard keys. You will now see two buttons displayed above the keyboard for filling the username and password corresponding to the selected Login. These buttons provide the ability to manually fill Login credentials in those instances when the 1Password Automatic Filling service isn’t enabled or when it doesn’t correctly identify the login fields in question.

However, when the 1Password Automatic Filling service is enabled and has detected the login fields, the 1Password Keyboard will do all of that work for you. The keyboard asks the 1Password Automatic Filling service to select the appropriate login fields by invoking:

 AccessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);

Once each login field has been focused by the 1Password Automatic Filling service, the 1Password Keyboard is notified. It then inputs the username or password text directly into that field. Once this is done, all that is required of you is to tap the “Sign In” button.

Security and convenience

By combining the 1Password Keyboard with the 1Password Automatic Filling service, we are able to provide a filling solution that avoids use of the clipboard entirely and doesn’t rely on passing your credentials through a third party. Whether you use the 1Password Keyboard as your main keyboard or in addition to your favorite keyboard, securely filling Logins into apps and browsers is only a couple of taps away.

If you would like to read more about enabling the 1Password Keyboard and Automatic Filling service on your Android device, please see our helpful documentation.