Crate tpfs_krypt
source ·Expand description
Tpfs Krypt
The tpfs_krypt
crate provides an implementation-agnostic interface used to manage
(e.g. generate, list or import keypairs) and work with (e.g. sign and later
encrypt) cryptographic secrets.
The common apis are KeyManagement
and Signature
traits, and the Address
struct.
How to configure a specific KeyManager can be found within the module. Two sample ones are
the file_key_manager
and the in_memory_key_manager
modules.
Example: Performing Signing
Here’s a basic example of using a directory and the FileKeyManager to create a key and then perform a signing.
use std::fs;
use std::path::PathBuf;
use tpfs_krypt::{
config::{KeyManagerConfig, KryptConfig},
errors::KeyManagementError,
from_config, FileKeyManagerConfig, KeyType
};
let path = PathBuf::from("/tmp/krypt/keypairs/");
if !path.exists() {
fs::create_dir_all(&path).unwrap();
}
let config = KryptConfig {
key_manager_config: KeyManagerConfig::FileKeyManager(FileKeyManagerConfig {
keypair_directory_path: path.into_os_string().into_string().unwrap(),
}),
};
let mut key_manager = from_config(config)?;
let address = key_manager.generate_keypair(KeyType::SubstrateSr25519)?;
let signature = key_manager.sign(address.as_ref(), b"My important message that can be verified was done by me via my public address.")?;
let signature_bytes: &[u8] = signature.as_ref().as_ref();
Example: Performing shared encryption, generating a deterministic key per-message.
Shared Encryption allows for two parties to share a secret with each other. Diffie-Hellman is used for this purpose, but we deviate from the standard interactive protocol in order to avoid additional storage overhead in the context of on-chain transactions. Rather than having the initiator send some encrypted randomness to the receiver, we use something that is unique and publicly available (as part of the transaction, typically) to take the place of this randomness. This also makes it convenient for the initiator/sender to decrypt old messages, since the process is always the same. IE: They don’t have to store the randomness that would normally be transmitted to the receiver somewhere.
This does mean that, if either party’s private key is stolen, the owner will be able to decrypt the exchanged data. Periodic rotation of encryption keys is recommended.
use secrecy::ExposeSecret;
use std::fs;
use std::path::PathBuf;
use tpfs_krypt::{
config::{KeyManagerConfig, KryptConfig},
errors::KeyManagementError,
from_config, FileKeyManagerConfig, KeyManagement, KeyType, SharedEncryption, DecryptionStrategy
};
fn create_file_key_manager(path: PathBuf) -> Result<Box<dyn KeyManagement>, KeyManagementError> {
if !path.exists() {
fs::create_dir_all(&path).unwrap();
}
let config = KryptConfig {
key_manager_config: KeyManagerConfig::FileKeyManager(FileKeyManagerConfig {
keypair_directory_path: path.into_os_string().into_string().unwrap(),
}),
};
from_config(config)
}
let mut sender_key_manager = create_file_key_manager(PathBuf::from("/tmp/krypt/sender/keypairs/"))?;
let mut receiver_key_manager = create_file_key_manager(PathBuf::from("/tmp/krypt/receiver/keypairs/"))?;
let sender = sender_key_manager.generate_keypair(KeyType::SharedEncryptionX25519)?;
let receiver = receiver_key_manager.generate_keypair(KeyType::SharedEncryptionX25519)?;
let message = b"My super super secret message.";
let public_input = b"Some public data";
let encrypted = sender_key_manager.shared_encrypt(sender.as_ref(),
&receiver.pubkey,
message,
public_input)?;
let decrypted = receiver_key_manager.shared_decrypt(receiver.as_ref(),
DecryptionStrategy::Receiver(encrypted))?;
assert_eq!(message, decrypted.expose_secret().as_slice());
Substrate DEV_PHRASE with derived keys.
You’ll notice the use of the Secrecy
crate and that it is to make sure that the phrase’s
contents are zeroed out from memory once the secret is imported into the key manager.
The sp_core
crate is used for pulling in substrate keys and for checking the address
returned from the key manager match.
use std::{path::PathBuf, fs};
use tpfs_krypt::{
config::{KeyManagerConfig, KryptConfig},
errors::KeyManagementError,
from_config, FileKeyManagerConfig, KeyType, secrecy::Secret,
sp_core::{crypto::{DEV_PHRASE, Pair, Ss58Codec}, sr25519}
};
let path = PathBuf::from("/tmp/krypt/keypairs/");
if !path.exists() {
fs::create_dir_all(&path).unwrap();
}
let config = KryptConfig {
key_manager_config: KeyManagerConfig::FileKeyManager(FileKeyManagerConfig {
keypair_directory_path: path.into_os_string().into_string().unwrap(),
}),
};
let mut key_manager = from_config(config)?;
let secret_phrase = Secret::new(format!("{}//Xand", DEV_PHRASE));
let address = key_manager.import_keypair(secret_phrase, KeyType::SubstrateSr25519)?;
let signature = key_manager.sign(address.as_ref(), b"My important message that can be verified was done by me via my public address.")?;
let signature_bytes: &[u8] = signature.as_ref().as_ref();
// You can generate the address yourself with the code below.
let pair = sr25519::Pair::from_string("//Xand", None)?;
assert_eq!(pair.public().to_ss58check(), address.id.value);
Example: For use in Tests
Since it can be kind of heavy to set up a directory with keys to just run a series of tests. This is what the InMemoryKeyManager was built for to allow for quick uses of the key manager without having to set up a way to actually persist the keys. It allows for importing a set of initial keys to avoid having to run import and manage them as secrets.
use sp_core::crypto::DEV_PHRASE;
use tpfs_krypt::{
config::{KeyManagerConfig, KryptConfig},
from_config, InMemoryKeyManagerConfig, KeyType,
};
let config = KryptConfig {
key_manager_config: KeyManagerConfig::InMemoryKeyManager(InMemoryKeyManagerConfig {
initial_keys: get_initial_keys(),
}),
};
let key_manager = from_config(config).unwrap();
/// Creates an initial set of keys for tests to work with.
pub fn get_initial_keys() -> Vec<(KeyType, String)> {
let paths = vec!["Biggie", "2Pac", "Xand", "Trust"];
let mut keys: Vec<(KeyType, String)> = paths
.iter()
.map(|path| {
(
KeyType::SubstrateSr25519,
format!("{}//{}", DEV_PHRASE, path),
)
})
.collect();
keys.push((KeyType::SubstrateSr25519, String::from(DEV_PHRASE)));
keys
}
Re-exports
pub use self::file_key_manager::FileKeyManagerConfig;
pub use self::in_memory_key_manager::InMemoryKeyManagerConfig;
pub use secrecy;
pub use sp_core;
pub use typed::Encrypted;
Modules
- This module contains any non key manager specific configuration for the
tpfs_krypt
crate. - This module contains error related items for the
tpfs_krypt
crate. - The File Key Manager Module which should contain the config and struct for working with a file based key manager.
- The In Memory Key Manager Module which should contain the config and struct for working with a key manager that is only intended to work in situations where the keys are to not be persisted. Please take care to not use this in a production running application.
- Tools to encrypt and decrypt values while keeping their type around
Structs
- Contains an encrypted message along with the sender Diffie-Hellman key.
- Uniquely identifies (the public portion of) a key
- This struct exists to allow dispatch of the KeyPair trait for various implementors. Holding an instance of this means you are holding the complete key material including the secret
- Key which authorizes Limited Agent actions
- Returned specifically from calls to generate new keypairs. Includes the public key bytes, which is useful for shared encryption, for example.
- Contains an encrypted message sent by you, along with the parameters used to generate the original encrypted message.
- Keys used to authorize creations and redemptions.
- The key that validators use to control their own settings, including changing their session keys.
- This key is used by validators to identify peer nodes and encrypt gossip
- This key is used by validators to testify they voted for finalizing blocks
- This key is used by validators to produce blocks
Enums
- Key pair types used across the Xand platform
- The decryption strategy provides the context needed to decrypt a message based on whether you are the sender or receiver of the message, as they are slightly different protocols.
- The complete keypair material, including the secret information. Generally you don’t want to work with this in an app - but during testing or configuration you may need manual control.
- The different sorts of keys that will be managed via this crate. This will be persisted with the key for when we may want to be able to return the type of the key after it’s been imported or generated.
- Auto-generated discriminant enum variants
Constants
- By default all keypair files that are written without at explicit filename are prefixed with this constant
Traits
- The ability to validate the address using this trait.
- The trait for KeyManagement that is organized based on addresses created based on the key type.
- This defines what we expect a KeyPair to be defined with in order to make a simple interface for KeyManagers. This interface can be worked with in trait objects since the lengths of the bytes aren’t enforced. See crypto/translator for performing translations to the raw types that a RawKeyPair expects to work with.
- A trait that just performs encryption that is intended to be both ways via SharedEncryption which is accessible via KeyManagement trait.
- A trait for the signature that may be expanded on later.
- Implementors are capable of signing a message
- A trait that specifies any of the signing portions which is shared publicly via KeyManagement trait.
- Interface all Xand key pair types must implement
Functions
- Creates a KeyManager based on the
KryptConfig
struct that is passed in. - Creates a KeyManager based on a file named krypt.toml from the path passed in.
Type Definitions
- A fixed sized type alias for a public key