Bitwarden - From Vision to Architecture
Main architectural style
Bitwarden’s main vision translated into a architectural relevant sentence is as follows: Build a safe and secure password manager that everyone can use 1. In the same slide and presentation, the Bitwarden company states that this lead to four early architectural decisions:
- Cloud native
- Scalable
- Flexible
- Enterprise friendly
These architectural decisions go hand in hand. Making a the system scalable requires making it native to the cloud. To make the system enterprise friendly is to make the system flexible. We have been unable to find a high level discussion of architectural patterns that implement these 4 decisions. In the crowdcast 1 on this topic, they immediately link these statements to certain programming languages and implementations. E.g. SQL server, Azure and Docker are named.
Larger higher level architectural decisions are explicitly stated in regards to security implementation 1.
Zero Knowledge Architecture Bitwarden implements a full zero-knowledge architecture. All information that ever leaves the computer of a client is encrypted. The cloud service of Bitwarden only ever saves these encrypted files or passwords. All decryption of these files is also done on the client-side. With this architectural decision all Bitwarden (or self-hosted) servers never touch unencrypted information.
DRY (Don’t Repeat Yourself) Many programmers are lazy and rarely enjoy repeating themselves or tasks they have coded previously. Bitwarden reiterates how important it is to use code effectively in their codebase. By reusing as much code as possible you ensure you have faster iterations, releases and an easier time maintaining your code base. Besides that, reusing well written code reduces the footprint of bugs and security vulnerabilities in the application.
Containers view
The software system of Bitwarden consists of a few different elements which can be called a container by using the c4 model2. “Essentially, a container is a separately runnable/deployable unit (e.g. a separate process space) that executes code or stores data.”3 If we take a look at the repositories of Bitwarden, we can divide the software system into a few different containers. The different containers are, Server backend application, web application, browser extension, desktop application, mobile application, command-line interface application and the directory connector. Bitwarden has a separate API documentation as well as help guide websites. As these systems are not a part of the actual functionality of the Bitwarden software system, they will be left out of the container analysis. These two websites are however very important for customer service and for developers that want to contribute.
A container overview of the different container in the Bitwarden software system can be seen in the figure below 4.
Components view
In this section, a structural decomposition of the Bitwarden system into components with explicit interfaces and their inter-dependencies will be explained. The Bitwarden project in divided into multiple programs, as explained in the container view section. To narrow the scope, this decomposition focuses only on the modules of the Bitwarden backend (the bitwarden/server repository 5). The server is divided into a number of services, which can be seen as containers in the C4model 2. The rest of the folders inside the server source map can be seen as components 6. Essentially, a component is a collection of implementation classes behind an interface. Components inside a container typically execute in the same process space, in the C4 model, components are not separately deployable units 2. An overview of the folder structure is given in Development View section, and an overview of the components and containers in the Bitwarden server can be seen below 5.
Some of the parts of the Bitwarden backend can be seen as containers rather than components, since they are dockerized and can be deployed and run on their own. Exceptions to this are the Core component, the billing component and the EventsProcessor component. The containers use the Models Controllers Views design pattern, although only the Admin container actually has a view component. This is not surprising, as a backend often does not have a GUI.
To narrow the scope further, we are going to inspect the core component only. The composition of this component can be seen in the figure below 5
Aside from these components, there are some other components such as mail templates, custom exceptions and a resources map. The model component contains data models.
Connectors view
The connectors view provides an overview of the tools and methods used to enable communication between components and containers. Connector types vary between components and containers. On the more high-level view of containers, Bitwarden has its main selling point that all of the users passwords are globally accessible on all devices. This means for their architecture that all containers are connected to the Bitwarden backend. That is, the database, where all the data is stored centrally, is connected to a server backend that handles all communication with the database. All containers running any kind of client-side application is then connected to the server backend to make syncing across devices possible. All communication between clients is then done via secure HTTP requests over a HTTPS connection, so the inter-container connector is HTTPS (requests). Documentation about this connector and its features can be found in the API documentation of Bitwarden 7. This container structure can be seen in the section ‘Containers view’ and the inter-container connector can be seen in the figure below8.
Between components the communication happens through function calls as these components have access to each others methods and classes.
No extra data formats or network requests are needed for this. Specifically since we are zooming in on the server backend repository as an example, server backend programmed in .NET using C#, uses standard .NET function function calls and accesses the database using sql connections.
Development view
Instead of spreading this blog post thin with multiple development views of all the different repositories we want to focus this development view on the bitwarden/server repository. The dependencies of the bitwarden/server repository are easily explained in the README9. Requirements:
- .NET Core 3.1 SDK
- SQL Server 2017
Both of these dependencies are free to use. This ensures any contributor can access them and contribute to the Bitwarden project without other costs.
The server repository is divided into seven different services10. Each service is a Visual Studio project. Please note Visual Studio is the recommended development tooling which is also free of charge. Six services
- Admin
- Api
- Icons
- Identity
- Notifications
- SQL
- Events
Each service is built and run separately. Clients can use a different physical server for each of the different services. This includes development of the application, if you are fixing an issue in the API, you only need to build the API service. A snapshot of the file structure as taken from the repository 5 that accompanies this can be found below.
Run time view
In the run time view we will show how the different components described interact during runtime. The key scenarios that will be highlighted here, are the process of saving the master password and the extraction of passwords that are in the Bitwarden vault.
The saving process of the master password manly takes place at the client-side of the software systems. As the master password is never sent over the network in its decrypted form. As can be seen in the diagram below. The master password and email address are used to create a stretched master key, a somewhat longer key to make weak passwords more difficult to brute-force. Together with a random generated symmetric key and initialization vector the client application can generate the protected symmetric key and this will be sent to the server. The master password hash is used for account authentication on the server. The protected symmetric key is pulled from the server to decrypted the Bitwarden vault locally 8. In this manner, the Client applications and backend server interact with each other.
Key quality attributes
In our previous blogpost, we explained three key quality attributes of Bitwarden: Availability, Security and Transparency. in this section, we will explain how the architecture realizes these key quality attributes, and how potential trade-offs between them have been resolved.
Availability
Bitwarden can be used in two ways, self-hosted or using the Bitwarden servers. The Bitwarden servers make use of Microsoft Azure Cloud Computing Services 11 infrastructure to ensure availability. All uptime, scalability, and security updates and guarantees are backed by Microsoft and their cloud infrastructure12.
The Bitwarden backend is dockerized, which allows for easy deployment. When using a self-hosted server, the availability of the server is the responsibility of the user hosting the server. Docker also makes it easy to deploy Bitwarden on your own server, regardless of its operating system and environment.
Another way Bitwarden’s architecture contributes to its availability is through offering 5 different clients, a mobile app, a browser, a command line interface and a web client.
Security
To ensure good data security, Bitwarden decided to use what is known as Zero Knowledge architecture13. As explained in the main architectural style, Bitwarden uses End-to-End encryption to make sure that all data is encrypted before is it sent 14.
This architecture can be seen in the Figure below 1.
Transparency
The transparent architecture of Bitwarden contributes greatly to its security. Bitwarden’s two main architectural decisions on its security model are “don’t invent your own crypto”, and “work conform industry standards”. The transparent open-source architecture of Bitwarden contributes to both of these design goals by using the power of the community and preexisting cryptography methods, to prevent the need for inventing their own crypto. Furthermore, because of the transparency, Bitwarden’s features and infrastructure security are vetted and improved by their global community.
API design principles applied
Bitwarden has an extensive public API as well as extensive API documentation 7 and even help pages on the API documentation 15. Although these resources do not contain information about the API design principles, they are immensely helpful for users and developers. The fact that there is no article about the API design principles does not mean that Bitwarden did not adhere to them, on the contrary, here we will list which principles were followed and how it was done.
MECE principle
The MECE principle stands for mutually exclusive and collective exhaustive. What does this mean for an API? An API following this principle covers everything, but does not have overlap between its request-URIs. This design principle is applied in the public API of the bitwarden server backend as can be seen in their documentation 7. Different URLs lead to different API functionality, as do the different request that have their own unique use.
Explicit interface principle
By providing their API documentation using Swagger, Bitwarden inherently follows this principle, but documenting the URI, the parameters, the use and the expected response. All potential API calls are explicitly documented here 7.
Uniform access principle
All available APIs are exposed through a uniform notation, which is easily derivable from other API if known and does not reveal more information than necessary to the consumer of the API.
RESTful API
The Bitwarden API adheres to the RESTful API design principle and this in turn automatically fulfills many API design principles like the seperation concern. RESTful API dictates seperate resource addresses and distinct HTTP verbs for different actions to different resources 16. Examples of the implementation of this principle is laid out in their documentation 7.
References
-
Spearrin, K., Marshal, A., & Orenstein, G. (2020, August 20). Building a zero knowledge architecture[Slides]. Crowdcast. https://www.crowdcast.io/e/zero-knowledge-architecture ↩︎
-
Simon Brown. (n.d.-a). The C4 model for visualising software architecture. Retrieved March 15, 2021, from https://c4model.com/ ↩︎
-
The C4 model Container View. (2021). The C4 Model. https://c4model.com/#ContainerDiagram ↩︎
-
Bitwarden. (n.d.). Bitwarden server repo. GitHub. Retrieved March 29, 2021, from https://github.com/bitwarden/ ↩︎
-
Bitwarden. (n.d.). Bitwarden server repo. GitHub. Retrieved March 29, 2021, from https://github.com/bitwarden/server ↩︎
-
The C4 model Component View. (2021). The C4 Model. https://c4model.com/#ComponentDiagram ↩︎
-
Bitwarden. (n.d.-a). Bitwarden Public API. Retrieved March 15, 2021, from https://bitwarden.com/help/api/ ↩︎
-
The Bitwarden white paper. (October 2020). Bitwarden. https://bitwarden.com/images/resources/security-white-paper-download.pdf ↩︎
-
Bitwarden. (n.d.). bitwarden/server/README. GitHub. Retrieved March 15, 2021, from https://github.com/bitwarden/server/blob/master/README.md ↩︎
-
Bitwarden. (n.d.-a). bitwarden/server/contributing. GitHub. Retrieved March 15, 2021, from https://github.com/bitwarden/server/blob/master/CONTRIBUTING.md ↩︎
-
Microsoft. (n.d.-b). Microsoft Azure. Retrieved March 15, 2021, from https://azure.microsoft.com/ ↩︎
-
Bitwarden. (n.d.-d). Storage. Retrieved March 29, 2021, from https://bitwarden.com/help/article/data-storage/ ↩︎
-
Orenstein, G. (2020b, August 6). How End-to-End Encryption Paves the Way for Zero Knowledge. Bitwarden Blog. https://bitwarden.com/blog/post/end-to-end-encryption-and-zero-knowledge/ ↩︎
-
Orenstein, G. (2020, June 13). Vault Security in the Bitwarden Password Manager. Bitwarden Blog. https://bitwarden.com/blog/post/vault-security-bitwarden-password-manager ↩︎
-
Bitwarden. (n.d.-b). Bitwarden Public API. Retrieved March 15, 2021, from https://bitwarden.com/help/article/public-api/ ↩︎
-
Rodriguez, A. (2008). Restful web services: The basics. IBM developerWorks, 33, 18. ↩︎