nstilt1 / crypto-on-the-edge

A WIP Rust crate for generating private keys from IDs based on an HKDF, and eliminates the need to store private keys.
Apache License 2.0
0 stars 0 forks source link

Private Key Management

The private_key_generator library provides the core functionality of generating private keys from a user-provided ID, and the key_manager library provides functionality of handling API requests using these IDs. The IDs can contain an embedded expiration time and a truncated message authentication code (MAC) to determine probable authenticity. The HMAC can be computed with associated data, which can allow for an ID to only be used by a specific client. The associated data is also used for computing the private key to ensure that it is actually specific to the client, and that another client cannot expect an associated key to work if they don't provide the same associated data.

You might be thinking, "What?! IDs for private keys? How can that be secure???" The IDs behave similarly to having an ID for a locally saved private key. In my use case, I publish some public keys and some related IDs once a month, and set them to expire after a year. Before a client makes their first request, they grab an ECDH public key and an ID, as well as an ECDSA public key and ID, and send the IDs in the request. The server uses the ID to regenerate the private keys using an HKDF that was keyed with a securely generated, uniformly pseudorandom key, verifies the client's signature, then responds to the request, encrypting the data with the generated ECDH key and the client's public key, then signs the data with the expected ECDSA key.

The main differences between saving every private key locally and associating them with an ID, versus publishing and providing clients with these IDs are: 1) We don't have to save every generated private key 2) The IDs have a variable-length MAC at the end, so if a client sends a bogus Client ID or key ID, we will (most likely) instantly find out and reject the request.

Features

Aside from the ID MACs and associated IDs, this library also provides the following features:

Limitations

The current limitations that can be changed include:

The current limitations that cannot be changed is primarily the fact that some configurations of the VersioningConfig can break. This is the definition of the versioning config:

pub struct VersioningConfig<
    const EPOCH: u64,
    const VERSION_LIFETIME: u64,
    const VERSION_BITS: u8,
    const TIMESTAMP_BITS: u8,
    const TIMESTAMP_PRECISION_LOSS: u8,
    const MAX_EXPIRATION_TIME: u64,
>;

Every VERSION_LIFETIME seconds, the version increments. There is a lower bound on this set to 10 minutes, but if you set it to 10 minutes, and the VERSION_BITS is set to 3, the code will break after 80 minutes.

The versioning is primarily supposed to handle rekeying the HKDF and MAC periodically. It does not NEED to change every 10 minutes, but if you want it to in a personal project where you know that you will only be alive for less than 80 more years, then you could make the program expire in 80 years.

Security and Compatibility Notice

This library has not received an audit, and it is still a work in progress. If any changes are made in the core functionality of generating keys from IDs, or how the IDs or metadata are represented, it can and will break your code. I will add that I've tested all of this code and it works. There is only one change I might put out that might change things (aside from restructuring or adding functionality), which is supporting up to 57 bits for the length of the version in the private_key_generator. The only thing that this might break is encrypt_resource, where the version is encoded in the encrypted data as 4 bytes, and it would need to be able to encode it as either 4 or 8 bytes to avoid breaking changes.

Requirements

private_key_generator is compatible with no-std, but without std, this library's validate_id functions will not be able to validate the timestamps of IDs. However, if you are able to determine the current time in seconds since UNIX_EPOCH, you can call get_expiration_time() and manually validate the expiration time.

key_manager is dependent on std.

key_manager also depends on RustCrypto's elliptic_curves with the following features enabled:

You might need to ensure that those features are enabled when you use RustCrypto libraries' curves.

License

All crates licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.