keyfork-shard: begin work on (re)provisioning shardholder keys
This commit is contained in:
parent
b15d088905
commit
1b30b17691
|
@ -1829,6 +1829,7 @@ dependencies = [
|
||||||
"card-backend-pcsc",
|
"card-backend-pcsc",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"keyfork-derive-openpgp",
|
"keyfork-derive-openpgp",
|
||||||
|
"keyfork-derive-util",
|
||||||
"keyfork-mnemonic-util",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-prompt",
|
"keyfork-prompt",
|
||||||
"keyfork-qrcode",
|
"keyfork-qrcode",
|
||||||
|
|
|
@ -78,9 +78,7 @@ fn validate(
|
||||||
subkey_format: &str,
|
subkey_format: &str,
|
||||||
default_userid: &str,
|
default_userid: &str,
|
||||||
) -> Result<(DerivationPath, Vec<KeyType>, UserID), Box<dyn std::error::Error>> {
|
) -> Result<(DerivationPath, Vec<KeyType>, UserID), Box<dyn std::error::Error>> {
|
||||||
let mut pgp_u32 = [0u8; 4];
|
let index = DerivationIndex::new(u32::from_be_bytes(*b"\x00pgp"), true)?;
|
||||||
pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::<Vec<u8>>());
|
|
||||||
let index = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
|
|
||||||
|
|
||||||
let path = DerivationPath::from_str(path)?;
|
let path = DerivationPath::from_str(path)?;
|
||||||
assert_eq!(2, path.len(), "Expected path of m/{index}/account_id'");
|
assert_eq!(2, path.len(), "Expected path of m/{index}/account_id'");
|
||||||
|
|
|
@ -36,3 +36,4 @@ card-backend-pcsc = { version = "0.5.0", optional = true }
|
||||||
openpgp-card-sequoia = { version = "0.2.0", optional = true, default-features = false }
|
openpgp-card-sequoia = { version = "0.2.0", optional = true, default-features = false }
|
||||||
openpgp-card = { version = "0.4.0", optional = true }
|
openpgp-card = { version = "0.4.0", optional = true }
|
||||||
sequoia-openpgp = { version = "1.17.0", optional = true, default-features = false }
|
sequoia-openpgp = { version = "1.17.0", optional = true, default-features = false }
|
||||||
|
keyfork-derive-util = { version = "0.1.0", path = "../derive/keyfork-derive-util", default-features = false }
|
||||||
|
|
|
@ -7,6 +7,7 @@ use aes_gcm::{
|
||||||
Aes256Gcm, KeyInit, Nonce,
|
Aes256Gcm, KeyInit, Nonce,
|
||||||
};
|
};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyfork_mnemonic_util::{English, Mnemonic};
|
use keyfork_mnemonic_util::{English, Mnemonic};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||||
|
@ -60,6 +61,52 @@ pub trait Format {
|
||||||
/// A type representing the parsed, but encrypted, Shard data.
|
/// A type representing the parsed, but encrypted, Shard data.
|
||||||
type EncryptedData;
|
type EncryptedData;
|
||||||
|
|
||||||
|
/// Provision hardware with a deterministic key based on a shardholder's DerivationIndex.
|
||||||
|
///
|
||||||
|
/// The derivation path for provisioned shardholder keys is built using the following template:
|
||||||
|
/// `m / purpose ' / shard_index ' / shardholder_index '`.
|
||||||
|
///
|
||||||
|
/// Purpose is defined by the Format, and can be a four-byte sequence transformed into a u32
|
||||||
|
/// using `u32::from_be_bytes(*purpose)`. For OpenPGP, for legacy reasons, this purpose is
|
||||||
|
/// "\x00pgp". The purpose can be _any_ sequence of four bytes so long as the _first_ byte is
|
||||||
|
/// not higher than 0x80 (meaning, all ASCII / 7-bit characters are allowed).
|
||||||
|
///
|
||||||
|
/// The shard index is provided by Keyfork, and is equivalent to b"shrd".
|
||||||
|
///
|
||||||
|
/// The shardholder index is how Keyfork is able to recreate keys for specific shardholders -
|
||||||
|
/// the only necessary information is which shardholder is not accounted for. Shardholders are
|
||||||
|
/// encouraged to mark hardware with the shardholder number so shardholders can verify their
|
||||||
|
/// index.
|
||||||
|
fn provision_shardholder_key(
|
||||||
|
&self,
|
||||||
|
derivation_path: DerivationPath,
|
||||||
|
seed: &[u8],
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Return a DerivationIndex for the Format.
|
||||||
|
///
|
||||||
|
/// The derivation path for provisioned shardholder keys is built using the following template:
|
||||||
|
/// `m / purpose ' / shard_index ' / shardholder_index '`.
|
||||||
|
///
|
||||||
|
/// Purpose is defined by the Format, and can be a four-byte sequence transformed into a u32
|
||||||
|
/// using `u32::from_be_bytes(*purpose)`. For OpenPGP, for legacy reasons, this purpose is
|
||||||
|
/// "\x00pgp". The purpose can be _any_ sequence of four bytes so long as the _first_ byte is
|
||||||
|
/// not higher than 0x80 (meaning, all ASCII / 7-bit characters are allowed).
|
||||||
|
fn purpose_derivation_index(&self) -> DerivationIndex;
|
||||||
|
|
||||||
|
/// Create a shardholder derivation path for the given format.
|
||||||
|
///
|
||||||
|
/// The derivation path for provisioned shardholder keys is built using the following template:
|
||||||
|
/// `m / purpose ' / shard_index ' / shardholder_index '`.
|
||||||
|
fn create_derivation_path(&self, shardholder_index: DerivationIndex) -> DerivationPath {
|
||||||
|
let purpose = self.purpose_derivation_index();
|
||||||
|
let shard_index = DerivationIndex::new(u32::from_be_bytes(*b"shrd"), true).unwrap();
|
||||||
|
DerivationPath::default()
|
||||||
|
.chain_push(purpose)
|
||||||
|
.chain_push(shard_index)
|
||||||
|
.chain_push(shardholder_index)
|
||||||
|
}
|
||||||
|
|
||||||
/// Derive a signer
|
/// Derive a signer
|
||||||
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey;
|
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
derive_util::{DerivationPath, VariableLengthSeed},
|
derive_util::{DerivationIndex, DerivationPath, VariableLengthSeed},
|
||||||
XPrv,
|
XPrv,
|
||||||
};
|
};
|
||||||
use openpgp::{
|
use openpgp::{
|
||||||
|
@ -216,6 +216,18 @@ impl Format for OpenPGP {
|
||||||
type SigningKey = Cert;
|
type SigningKey = Cert;
|
||||||
type EncryptedData = EncryptedMessage;
|
type EncryptedData = EncryptedMessage;
|
||||||
|
|
||||||
|
fn provision_shardholder_key(
|
||||||
|
&self,
|
||||||
|
derivation_path: DerivationPath,
|
||||||
|
seed: &[u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn purpose_derivation_index(&self) -> DerivationIndex {
|
||||||
|
DerivationIndex::new(u32::from_be_bytes(*b"\x00pgp"), true).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Derive an OpenPGP Shard certificate from the given seed.
|
/// Derive an OpenPGP Shard certificate from the given seed.
|
||||||
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey {
|
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey {
|
||||||
let seed = VariableLengthSeed::new(seed);
|
let seed = VariableLengthSeed::new(seed);
|
||||||
|
|
Loading…
Reference in New Issue