Rust in Production: 1Password
Rust has taken the programming language world by storm. Since its 1.0 release in 2015, it has been one of the most loved programming languages with a loyal following of developers and contributors.
To learn why this language is favored so much between developers, we have started a new series on Rust in production. In it, we’ll interview people that have used Rust for significant projects: apps, services, startup MVPs, and others.
For the first installment of the series, we interview Michael Fey, VP of Engineering at 1Password. Read further to find out why they chose Rust for their product, the benefits of Rust for security-centered applications, and what cool libraries you should look into if you’re developing something similar in Rust.
Could you tell us a little about your company and your role there?
1Password is a password manager trusted by millions of people and 70,000 businesses to secure their sensitive data. It remembers all your passwords so you don’t have to, and comes with apps for all major browsers, plus desktop and mobile.
I am the VP of Engineering for Client Apps here at 1Password. If you have ever had the pleasure to use 1Password on your Mac, Windows PC, iPhone, iPad, Android phone, or tablet, or in your browser, then you’ve been lucky enough to use something my team has built. We’ve been around since 2004, and we take a lot of pride in building a well-crafted experience and keeping people safe online.
Can you talk about the stack of 1Password? How big a part of your codebase is written in Rust?
We’ve been using Rust in production at 1Password for a few years now. Our Windows team was the frontrunner on this effort to the point where about 70% of 1Password 7 for Windows is written in Rust. We also ported the 1Password Brain – the engine that powers our browser filling logic – from Go to Rust at the end of 2019 so that we could take advantage of the speed and performance of deploying Rust to WebAssembly in our browser extension.
These have been in production for the last few years and we’ve seen great success. So much so that we’re now in the midst of a complete rewrite of nearly our entire product lineup, and Rust is a major part of that story. We are using Rust to create a headless 1Password app that encompasses all of the business logic, cryptography, database access, server communication, and more wrapped in a thin UI layer that is native to the system on which we’re deploying.
Did any of Rust’s advantages like speed or type/memory-safety influence the choice of using Rust for 1Password?
One of the main things that drew us to Rust initially was the memory safety; it definitely excites us knowing that Rust helps us maximize our confidence in the safety of our customers’ secrets. Beyond memory safety, though, there’s so much more we love about the Rust ecosystem. There is a significant performance benefit to the lack of a traditional runtime; we don’t have to worry about the overhead of a garbage collector, for instance. Rust offers a form of “program correctness” and many guarantees against undefined behaviour at runtime. The strong type system enforces these rules at compile-time. Carefully aligning application logic with Rust’s strong type rules makes APIs difficult to use incorrectly and results in simpler code that’s free from runtime checking of constraints and invariants; the compiler can guarantee there are no invalid runtime code paths that will lead your program astray before it executes. Having to perform less runtime state validation leads to cleaner, more efficient, more focused, and higher quality code. Rust requires very little runtime debugging compared to other languages. If it compiles, you can be fairly sure it won’t exhibit unexpected behaviour. It may not be what you want but it will be “correct”. 🙂
Another very powerful (and often overlooked) feature of Rust is its procedural macro system, which has allowed us to write a tool that automatically shares types defined in Rust with our client-side languages (Swift, Kotlin, and TypeScript). The output from this tool handles the serialization/deserialization process automatically, meaning our client-side devs can continue to work in their language of choice while interacting with the Rust library and can be free from the concerns of JSON parsing over the foreign function interface (FFI). We get all of this while enjoying the benefits of compile-time type checking in every one of our target languages. We’ve integrated this tool into our continuous integration server as well, meaning that changes to the Rust models can result in compilation failures in the client applications that are caught during our review process.
This tool has been an integral component in our development process, allowing us to move much more quickly than ever before. Once our types are defined in Rust, we are able to immediately generate equivalent types in our client-side languages. This allows our devs to focus on solving problems without having to hand-roll boilerplate code to communicate over the FFI.
How good is Rust’s support (library and otherwise) for developing security-centric applications like 1Password?
There is more than enough to build a majority of the base that security-centered applications require. There are two large, prominent cryptography platforms (ring and the Rust Crypto group) that together provide a wealth of functionality. As I mentioned above, writing with Rust itself gives you much greater confidence in your memory usage and makes it much harder to accidentally introduce a memory-related exploit into your application. There is also a wonderful system in place for keeping track of vulnerabilities that do show up from time to time in Rust crates: the RustSec database, which is community-sourced by other Rust developers and is updated frequently with new information that can be consumed in CI audit scans. The batteries-included test framework that Rust and Cargo also include mean that you always have an easy way to write unit-test suites for correct behavior in critical code, like any cryptographic functions that you write.
While safe native Rust libraries for everything are the dream (and they will come in time), there is always the option to dip down and easily consume something in C or from native platform libraries. We use this to great effect in our Rust code for things like calling out to the native implementations of biometric unlock (Touch ID, Face ID, Windows Hello) and platform-specific settings implementations like NSUserDefaults on Apple platforms.
Any particular Rust libraries that you want to feature?
Absolutely. Tokio, Hyper/Reqwest, Ring, and Neon all have a home in 1Password and are fundamental in allowing us to tackle this ambitious project at all. You should also check out our password-rules-parser on crates.io, which is based on a spec primarily being backed by Apple. Their tools and docs can be found here.
Where is Rust great to use, and where does it fall short in your stack?
Rust has fulfilled 90% of what we were hoping for when we started this project. We’ve been able to deploy it to nearly every one of our target platforms in some way shape or form (with the exception of Apple Watch). The language itself has been designed with modern sensibilities and is improving with every release. It has great documentation and an active community.
Although there are countless crates available for use, we did have to roll our own logging and tracing tools to ensure that they were safe to use in 1Password. Additionally, we constructed a substantial localization implementation to meet the requirements of our products.
It did fall short for us in one key area: We were hoping WebAssembly would take us further in the browser and our browser extension than it has. WebAssembly has been great as a function library, but attempting to stand up an entire runtime in WASM has been a challenge. Many of the issues we ran into, however, were not limitations of Rust but of WebAssembly as a deployment platform.
What was the biggest challenge while developing 1Password with Rust?
Many of the folks on our team were new to Rust, and they experienced the typical learning curve that comes with its memory management and ownership model. We are also finding the compile times to be pretty beefy; our CPUs and fans are definitely getting a workout. 😄
Are you satisfied with the result?
Any key takeaways that you would like to share with our audience?
If you’re new to Rust, start small and build on top of that. We ran a large number of experiments when we were getting started to try and find the edges of what a Rust-based solution could provide. When your experiments pan out, try to reimagine the ways you used to work with other languages and see if your code can benefit from Rust’s philosophy.
If you’re new to 1Password, you can sign up today with this link and save 50% off your first year for family and individual accounts. If you’re working on an open source project, you can get a 1Password Teams account for free. Head on over to our GitHub repo to learn more.
I would like to thank Michael for the interview, and wish 1Password the best of luck in creating the most awesome password manager out there!
To read more about programming languages like Rust, be sure to follow us on Medium, DEV, or Twitter. If you are looking for the next read, we have an article on 9 other Rust production stories that you might want to check out.
- Could you tell us a little about your company and your role there?
- Can you talk about the stack of 1Password? How big a part of your codebase is written in Rust?
- Did any of Rust’s advantages like speed or type/memory-safety influence the choice of using Rust for 1Password?
- How good is Rust’s support (library and otherwise) for developing security-centric applications like 1Password?
- Any particular Rust libraries that you want to feature?
- Where is Rust great to use, and where does it fall short in your stack?
- What was the biggest challenge while developing 1Password with Rust?
- Are you satisfied with the result?
- Any key takeaways that you would like to share with our audience?