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

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