Stronghold is an open-source software library that was originally built to protect IOTA Seeds, but can be used to protect any digital secret. It is a secure database for working with cryptography, which ensures that secrets (like private keys) are never revealed. It provides its own peer-to-peer communication layer, so that different instances can securely communicate using the state-of-the-art Noise Protocol. Stronghold will form a secure base for the new IOTA Firefly wallet.
In an increasingly interconnected world of smart devices, whether phone, energy meter, television, or even credit card, the importance of real security will only grow. IoT devices need to securely communicate with each other over hostile networks. It must be clear to everyone reading this, that there is a very real need to keep data, like identity and digital driver’s licences safe from centralized hacks. The problem is not that this risk is unknown. It is just a very hard challenge to solve correctly. We accepted that challenge to protect the IOTA ecosystem and build libraries that anyone can use.
Since the first announcement of Stronghold, we have been growing the team, revising the internals of the engine, and researching applications that can be built with Stronghold. Today, we are happy to announce the Alpha release, which turns the corner of research to development. We dubbed this release “Saint-Malo”, after the wonderful fortified city on the northern French coast of Brittany that was rebuilt after the second world war. That once-pirate hideaway has recovered and survived, its massive walls protecting it from the immense attacks of the tides.
You may be asking yourself, what does “alpha” mean? What makes some software “alpha” and some “beta” - and when will it become “stable”? These stages in software development have to do with code stability and the contract that project engineers have with the consumers. In the case of Stronghold, by declaring our project to be “alpha” quality, we are telling you that we think it is good enough to experiment with. We have put theory into practice, revised our presumptions, and tried to make something that finds the sweet spot between maximum security and usability. We are now at the point where we want your feedback, which we will incorporate into the next stages of development.
During the alpha phase, you can expect that Stronghold will continue to function as it does now. However, we will be changing some of the internal mechanisms and also potentially changing minor aspects of its exposed API. We cannot recommend that Stronghold is used in production today by external parties, because things may change rapidly until we reach the beta phase - particularly during the external security audit of Firefly, and its integration with Stronghold. During the beta phase, we will prepare Stronghold for a complete security audit, after which we will finalise the specification and documentation for the stable release.
If however, you want to get your hands dirty right now, and are comfortable using the command line, here is a quick way to test drive Stronghold:
Visit the GitHub releases page to download the prebuilt Stronghold commandline binary for your platform. If you are more adventurous and Rust-savvy, you can clone the GitHub repo and build it locally for your system:
git clone [email protected]:iotaledger/stronghold.rs.git
cargo build --release
The remainder of this article will outline the Stronghold architecture in greater depth and is of a highly technical nature.
- How Stronghold is different from Other Databases
- Stronghold Engine and Internal Actor Model
- New Client Interface
- Hardening Strongholds
- Dedicated cryptography crate - crypto.rs
- Releases and Safely Consuming Stronghold
What is Stronghold and how is it different from other databases?
Stronghold is a software library written in the Rust programming language. It provides an encrypted, persistent database for performing cryptographic operations in a secure computational zone that can be distributed across multiple devices. We have been asked many times about the difference between Stronghold and other databases. This can be summarized in three major distinctions:
- Stronghold records (and their file backups) are “encrypted-by-nature” for offline-attack mitigation. Most other databases require you to apply encryption yourself, through libraries, plugins, or an underlying encrypted file-system.
- Stronghold allows you to perform operations with those stored digital secrets without revealing them to external processes, and prevents these secrets from ever being exported in decrypted form. (See Hardening Strongholds below)
- Multiple Strongholds can work as a network, communicating and collaborating in a permissioned, decentralized fashion. (Coming in January, 2021).
Stronghold Engine and Internal Actor Model
Before we dive into the details of the client interface, it is important to briefly discuss the underlying architecture.
Strongholds are persisted by a binary file that we call a “snapshot”. These files are encrypted at rest, and can be transported between devices and different operating systems. Once a snapshot has been decrypted, it exists as a vault (or multiple vaults) in memory. In order to gain access to a record in memory and decrypt it, you need to know its path. Once you have the record contents, you can perform an operation with that record and return the results. This is all very complicated and if done improperly, there is a risk of secret leakage, which is what we are trying to avoid with Stronghold in the first place.
The actor model is a type of architecture where individual “actors” send messages to each other instead of directly calling functions and passing around memory. This is an excellent pattern that provides not only process isolation, but also allows us to send messages to other devices. We have designed the interface, as you will see below, in such a way that you will not be forced to use an actor model in your code, but it is available should you prefer.
New Client Interface
The entire client interface has been rebuilt since our initial implementation, using the Riker actor model and the "ask pattern". This allows Stronghold to isolate its internal architecture without forcing any higher level library or application to use Riker (or any actor model for that matter).
- The interface can be called by asynchronous methods which are attached to the Stronghold object.
- On each method call, a temporary actor is created and a future is passed back to the consuming library.
- Upon completion of the call, the actor is garbage-collected and the future resolves.
This is all possible because of Riker’s lightweight actor abstractions.
The Stronghold object internally creates multiple actor systems, which are defined using a client path identifier. Each of these systems is composed of at least 2 actors: a cache actor and an internal Stronghold actor, each with unique identifiers derived from the client path. A snapshot actor is spun up, so that when a snapshot is written, it encapsulates the data in all of the associated client systems together. Conversely, when a snapshot is read into the system, it is cached and read for each cache/internal actor pair individually, allowing the consumer to ensure that the data is laid out in a predictable manner. In this way, it is possible to read part of a snapshot or all of the snapshot.
Under the client abstraction, there is the basic Stronghold engine - a secure versioned key-value storage protocol. This system lives inside of the internal actor along with the zoned runtime. Versioning has been made an opt-in feature via a Location API. Each location defines a relation between each client system and the vaults and records within. A client is a collection of vaults and a vault is a collection of records where each record holds a piece of data.
The Location API allows the consumer to specify a generic location or a counter location. The generic location defines a vault and record path that is fixed. These generic locations do not use versioning and can be overwritten. The location counters on the other hand, make use of a counter to define the record index. When a consumer uses a location counter, they have the ability to either directly specify the counter value or let the system do it for them. If the counter is not explicitly provided, the system will default to the “head” of the vault. The head of the vault is defined depending on the operation; if the operation is a read, the head will be the last record written in the vault and if the operation is a write, the head is the next record to be written based on the linear counter value.
Along with basic database-esque operations, Stronghold provides a slew of cryptographic operations via its runtime. Each of these cryptographic operations is defined as a procedure. Specific inputs and outputs are specified per procedure as needed. For example, say a consumer like Firefly wants to use a mnemonic to generate a cryptographic seed and then use that seed to derive a Ed25519 key pair to sign a piece of data, they may do so by invoking the appropriate procedures. “BIP39 Generate” could be invoked to generate a seed from a provided mnemonic. “SLIP10 Derive” can be called on this seed location to create a key which is again stored in the Stronghold. “Ed25519 Public Key” can now be called on this key location to derive the needed public key and then “Ed25519 Sign” can be called to sign data. In this operation, the only data returned from the Stronghold is the signature and the public key. Every other piece of data is kept inside the Stronghold at the specified locations for security.
Some operating systems and hardware platforms offer unique opportunities for enhancing runtime security. We are therefore developing a secure computational zone within Stronghold, which uses process sandboxing and syscall filtering in order to make a computational enclave. We also make use of a custom memory allocator with guard pages, allowing us to restrict access to memory while not in use. These enclave tools may be used by other projects, even if they do not require the full Stronghold featureset.
For the uninitiated: syscall filtering makes it possible for a process to give up its rights to access operating system resources. As an example: why should a cryptographic algorithm ever need to access the file system or even file descriptors (remember: in Unix everything is a file)? To apply these restrictions, the wrapper forks away from the main process (e.g. the wallet application that contains UI and networking code) and then performs the sensitive operations. This also adds additional benefits with regards to memory isolation and potential maliciously spawned threads.
Secure Zone Attributes
The following features of the secure zone are applied (if the OS provides them) in order to add to the defensive depth of the overall security of the system:
1. process isolation: fork to the Secure Zone (runtime) and apply an acceptlist with a very restrictive set of permitted syscalls
2. memory management: inside of the Secure Zone, only use allocations with guard pages and restrictive memory protection
3. communication: incoming requests (TX) are read from the inherited parent memory, and the outgoing result (RX) is encrypted and authenticated using an ephemeral key generated before forking.
It may also be pointed out that these measures make it harder to probe an already compromised system, i.e. this is defense in depth not perfect security. For that we will need to produce and distribute Strongholds on dedicated hardware, like the USB Armory Mk-II.
With the decision in 2019 by the IOTA Foundation to transition to the Rust programming language, many of the software libraries and products at the Foundation are using Rust. There has been a growing need to have a properly vetted and maintained cryptography library that collects cryptographic primitives and algorithms needed for applications in IOTA. Cryptography must be audited, maintained, and properly reviewed.
Unfortunately, cryptography is easy to get wrong and mistakes can have very real repercussions. Indeed, this is one of the primary goals of the IOTA Stronghold project: Make it easy to safely use cryptography - so we are dogfooding our own philosophy.
To this end, we have already integrated the entire family of cryptography needed by IOTA:
Releases and Safely Consuming
For any library concerned with security and safety, having a solid release strategy is important. Like many other modern package distribution systems, Rust has the Cargo Crates ecosystem. We automatically publish to crates.io using the covector polyglot changelog and release workflow that was developed over at the Tauri community. Covector enables a turnkey releasing procedure, with a minimum of human intervention and a vetted pipeline for testing, building, linting, auditing, changelogging, merging to the main branch, creating git release tags, manufacturing artifacts (like the commandline binary), publishing a new release on Github, and then publishing to crates.io.
However, until Stronghold and Crypto.rs have stabilized and all resources are available purely from crates.io (and not git), our resources will only be available at GitHub. We apologize, but are working night and day to make the crates and their documentation properly available.
While robust and generally more stable than NPM, crates.io is still centralized and it makes it challenging to verify remote crates. To this end, we would like to recommend that your Rust project always consumes Stronghold via git hashes, because that way you can absolutely verify that the code you are receiving is what you expect.
You are amazing for having read through this entire article, and that is why we would love you to work closely with us to help develop the future of Stronghold.
Whether or not you consider yourself to be a member of the IOTA Community, you are invited to bring your experience to Stronghold. To do so, please apply through this form to be added to the X-Team and join the kick-off meeting that will happen in January, 2021.
You can follow the progress at: https://github.com/iota-community/X-Team_IOTA_STRONGHOLD