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",
|
||||
"hkdf",
|
||||
"keyfork-derive-openpgp",
|
||||
"keyfork-derive-util",
|
||||
"keyfork-mnemonic-util",
|
||||
"keyfork-prompt",
|
||||
"keyfork-qrcode",
|
||||
|
|
|
@ -78,9 +78,7 @@ fn validate(
|
|||
subkey_format: &str,
|
||||
default_userid: &str,
|
||||
) -> Result<(DerivationPath, Vec<KeyType>, UserID), Box<dyn std::error::Error>> {
|
||||
let mut pgp_u32 = [0u8; 4];
|
||||
pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::<Vec<u8>>());
|
||||
let index = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
|
||||
let index = DerivationIndex::new(u32::from_be_bytes(*b"\x00pgp"), true)?;
|
||||
|
||||
let path = DerivationPath::from_str(path)?;
|
||||
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 = { version = "0.4.0", optional = true }
|
||||
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,
|
||||
};
|
||||
use hkdf::Hkdf;
|
||||
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||
use keyfork_mnemonic_util::{English, Mnemonic};
|
||||
use keyfork_prompt::{
|
||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||
|
@ -60,6 +61,52 @@ pub trait Format {
|
|||
/// A type representing the parsed, but encrypted, Shard data.
|
||||
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
|
||||
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
|||
};
|
||||
|
||||
use keyfork_derive_openpgp::{
|
||||
derive_util::{DerivationPath, VariableLengthSeed},
|
||||
derive_util::{DerivationIndex, DerivationPath, VariableLengthSeed},
|
||||
XPrv,
|
||||
};
|
||||
use openpgp::{
|
||||
|
@ -216,6 +216,18 @@ impl Format for OpenPGP {
|
|||
type SigningKey = Cert;
|
||||
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.
|
||||
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey {
|
||||
let seed = VariableLengthSeed::new(seed);
|
||||
|
|
Loading…
Reference in New Issue