Lock Keeper helps people store, retrieve and use the private keys associated with their digital assets. We're building a security-first system with layers of cryptography, hardware protection, and a misuse-resistant design to prevent theft and misuse of keys.
Lock Keeper aims to provide a flexible system of components for managing digital assets and composed of the following:
-
A client library: The client allows a user to generate and store a secret key in a distributed way, across multiple servers, and provides generic functionality for requesting a signature under the stored key, and reconstructs a full signature from a set of partial signatures.
The client also includes the cryptographic functionality for:- authentication,
- networking with servers,
- integration as a self-contained library.
-
A key server is responsible for the generation, the secure storage for secret keys and distributed operations on those keys with cryptographic-based guarantees. The server has the following properties:
-
The server may be run by either an external cloud provider, e.g, Microsoft Azure, AWS, or directly by the service provider. This allows for a flexible distribution of trust; in particular, the compromise of a single server (or some group of servers below a designated threshold) does not allow for theft or misuse of the given secret key.
-
Integrates with an extensible external policy engine client API for approval & rejection of requested signing operations by asset fiduciaries with a stake in the use and sale of the underlying digital asset.
-
Leverages enclaves for increased security against key theft and misuse; in this model, a server is unable to participate in signing without authorization by the user. That is, the key share is held by the enclave and is not accessible outside of the enclave environment; the enclave authenticates the client before any partial signatures are produced. Similarly, the enclave may enforce transaction approval by a designated party as well, thereby enforcing the policy restrictions on signing.
-
This server either returns a partial signature, if the signature request meets the designated policy, or returns an appropriate rejection message.
-
Refer to the current design specification for Lock Keeper.
- LockKeeperKeyServer v0.3 introduces a breaking change with respect to a database that was generated using LockKeeperKeyServer v0.2. Remotely generated signing keys are now encrypted using a remote storage key.
- LockKeeperClient v0.3 introduces a breaking change with respect to v0.2. Storage keys are encrypted inside the database using a different key, therefore, old storage keys in the database cannot be used anymore.
- LockKeeperClient v0.3 is backwards incompatible with LockKeeperServer v0.2, given that authenticated traffic will now use an extra layer of encryption using the session key.
- A recent version of stable Rust to build the Lock Keeper project. Version 1.65 is the minimum required version.
- OpenSSL. You should be able to install this using your package manager of choice.
protoc
is required to build .proto files. It can be installed usingbrew
for MacOS orapt install
for Linux. Further instructions here.- cargo-make can be installed with
cargo install cargo-make
. - Docker.
- On Linux, you may need to install Docker Compose separately.
In order to use the cargo make
tasks on Linux, you need to be able to run Docker without sudo
. You can find instructions for this here.
Once the required dependencies are installed, build the project as follows:
cargo build --all-features --all-targets
We follow test-driven development practices and the test suite should be a close mapping to the functionality we currently implement at any given stage of development.
To run unit tests:
cargo test --all-features
To run the doctests:
cargo test --all-features --doc
Integration tests are separated into two categories, end-to-end tests and general integration tests. Both categories require the key server to be running.
Before running Lock Keeper for the first time, you will need to generate the necessary certs and keys for the key server.
cargo make init
Any time you need to refresh your certs and keys, you can run this command again.
Then, start the server running in the background. This will compile the project from scratch the first time you run it so it will take a while. It should be faster for future runs.
cargo make start
To run the end-to-end tests:
cargo make e2e
To run the integration tests:
cargo make integration
To run all tests including unit tests with a single command:
cargo make all-tests
The server will be running in the background so you can continue to run integration tests without starting the server again. To stop the server, run:
cargo make stop
If you want to watch server output in real-time, you can run the server in the foreground with:
cargo make run
Running the test binary directly offers some extra command line options.
To only run tests whose name contains certain words, use the --filter
option
cargo run --bin lock-keeper-tests -- --filter generate --filter retrieve
Then run:
cargo run --bin key-server-cli ./dev/config/local/Binary.toml
Client authentication can be enabled in server and client configs. See the config files in dev/config/docker-client-auth
or dev/config/local-client-auth
for examples.
The key server always requires a private key.
The private key can optionally be provided via a file path in the server config.
Alternatively, the raw bytes for a private key can be passed to the lock_keeper_key_server::Config
constructors.
This alternative allows the server to secure its private key however it chooses.
The key-server-cli
binary included with the lock-keeper-key-server
crate provides a command-line argument to accept a private
key as a base64 string.
Example:
cargo run --bin key-server-cli dev/config/local-client-auth/Binary.toml --private-key "$(cat dev/test-pki/gen/certs/server.key | base64)"
LockKeeperClient
requires a private key if client authentication is enabled.
The private key can optionally be provided via a file path in the client config.
Alternatively, the raw bytes for a private key can be passed to the lock_keeper_client::Config
constructors.
This alternative allows the client to secure its private key however it chooses.
The key server always requires a remote storage key. The remote storage key can be provided in a similar fashion as the private key (see above).
The command line argument for the key-server-cli
binary included with the lock-keeper-key-server
is remote-storage-key
.
Example:
cargo run --bin key-server-cli dev/config/local-client-auth/Binary.toml --remote_storage_key "$(cat dev/remote-storage-key/gen/remote_storage.key | base64)"
Important to note is that this key encrypts all signing keys on the server, therefore, loss of this key would mean loss of all signing keys. Therefore, we recommend to store this key in a secure fashion, e.g. by using an HSM. Access to this key should be well guarded to obtain very high security. Ultimately, the key server will provide a different way to keep signing keys secure using an enclave.
Lock Keeper comes with an interactive client CLI that can be used to interact with a key server for basic testing and troubleshooting.
See the lock-keeper-client-cli
crate for more information.
First start the key server:
cargo make start
Then run the client CLI:
cargo make cli
There are a few cargo make
tasks that run specific CLI scripts for quick testing. Check the CLI section of Makefile.toml
for available tasks.
The server writes logs to a few locations:
- Standard Output:
INFO
level logs get written out to standard output for any events originating from any lock-keeper crates. - Sever-only logs:
INFO
level logs written to a file based on user config (see below) for any events originating from any lock-keeper crates. - All logs:
TRACE
or higher level logs written to a file based on user config (see below) for any event from any crate including dependencies.
The following may be configured via the server configuration file:
- The log directory may be specified via the
log_directory
field in the server config file. - The sever-only log file may be specified via the
lock_keeper_logs_file_name
field. - The all log file may be specified via the
all_logs_file_name
field.
If you get a no space left on device
error from Docker, try running:
docker image prune -a
docker volume prune
If this doesn't help, you can do a full system prune. This will delete your cache and your next build will take a long time.
docker system prune -a --volumes
In our client and server we make use of the zeroize library to make sure secrets get properly removed from memory after dropping. This library will make sure that the memory is set to zero after the secret is of no use anymore. Most of the secrets are annotated with the trait ZeroizeOnDrop which assures the allocated memory gets set to zero as soon as the object gets dropped.
The zeroize crate guarantees the following:
- The zeroing operation can’t be “optimized away” by the compiler.
- All subsequent reads to memory will see “zeroized” values.
For more information about this carte and its usage, we refer the reader to the zeroize documentation.
To build the API documentation for the project:
RUSTDOCFLAGS="-Dwarnings" cargo doc --all-features --no-deps --open
You can find the API docs in the source of the client and policy engine.
Documentation from the main
and develop
branches is automatically deployed to GitHub Pages any time code is merged. There is a very basic index page for published documentation here.