Typelock
Enforce security boundaries at the Type level
typelock is a Rust framework for creating type-safe secured data models. It uses a procedural macro to generate locked and unlocked versions of your data models so sensitive data is only accessible through explicit model conversion steps.
Features
Given a single input definition, typelock generates the following:
- An unlocked representation
- A locked representation
- Explicit
lock()andunlock()transitions, based on the current model - Field policies for
encrypt,secret,digest,sign, andmac - Optional ToBytes/FromBytes derive macros via the
wincode-codecfeature - Optional Diesel integration for direct database storage of locked fields
Example
Custom types
Secured fields must define how they are converted to and from bytes. This is intentional, so that typelock does not force you into using a specific serialization library.
typelock includes ToBytes/FromBytes implementations for String and Vec<u8> out of the box.
You can also enable the wincode-codec feature to use the #[derive(ToBytes, FromBytes)] macros. Those derives generate byte conversion implementations using wincode, so you can avoid writing manual conversion code for custom data types.
Security providers
To stay flexible, typelock is intentionally backend agnostic. You provide your own implementation for the policy traits your model uses:
;
// CryptoProvider for encrypt/decrypt fields
// SecretProvider for one-way secret hashing
Additional traits are available for other policies:
DigestProviderfordigestSignProviderforsign/verify_signatureMacProviderformac/verify_mac
typelock does not implement cryptography itself; you always provide your own backend.
Converting between models
typelock automatically provides the following model conversions:
- OriginalModel -> LockedModel // call
.lock() - LockedModel -> UnlockedModel // call
.unlock() - UnlockedModel -> LockedModel // call
.lock()
For one-way policies (secret and digest), the unlocked model still carries wrapped bytes (Secret<T> / Digested<T>) because the original plaintext cannot be reconstructed.
You are encouraged to always instantiate your own models starting from OriginalModel and never from LockedModel or UnlockedModel. This is because otherwise typelock is not able to guarantee that you actually secured your data correctly.
use ;
let provider = MySecurityProvider;
let user = User ;
let locked_user = user.lock;
let unlocked_user = locked_user.unlock.expect;
Calling .lock() and .unlock() move the previous model into the function call. This means that trying to access user after calling .lock() is not allowed. typelock also provides default implementations to call .lock() and .unlock() on collections of Vec.
Warning: you are discouraged from deriving Clone on your original model or any of the generated models. This is because doing the following could lead to ghost copies that stay in your program's memory, which could compromise data.
let user = User
let locked_user = user.clone.lock;
In cases you need to do so, please take a look at the zeroize crate for example.
Diesel Integration
When the diesel feature is enabled, typelock provides FromSql and ToSql implementations for Encrypted<T> and Secret<T>. This allows those locked fields to be stored directly as Diesel Binary columns.
Locked fields are stored as Binary in the database.
Why use Typelock?
Without typelock, you typically end up writing code that looks like the following:
DecryptedUser
This is verbose, easy to get wrong, and typically repeated among multiple models. typelock generates that boundary once, correctly.
Use cases
typelock is useful when you need:
- Type-safe boundaries between plaintext and secured states (encrypted, secret-hashed, digested, signed, or MAC-tagged)
- Compile-time guarantees that sensitive data can't be accidentally used in the wrong state
- Automatic transitions between domain models and their storage representations
- To eliminate boilerplate for policy-based security transformations across multiple models
- To prevent accidentally serializing or logging sensitive data in plaintext form
- A strict separation between domain logic and persistence layer with security enforced at compile time
typelock complements your cryptography library by modeling security state transitions at the type level. It does not implement cryptography itself.
Status
typelock is still in an early-stage. Therefore some features are still missing and the API might change.
License
This project is licensed under the MIT License.
This crate provides optional integration with wincode via the wincode-codec feature, and with Diesel via the diesel feature. Therefore the respective licenses of these dependencies apply.