131 lines
3.9 KiB
Rust
131 lines
3.9 KiB
Rust
//! ## Path data guesswork for BIP-0032 derivation paths.
|
|
|
|
#![allow(clippy::unreadable_literal)]
|
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
|
|
|
/// All common paths for key derivation.
|
|
pub mod paths {
|
|
use super::*;
|
|
|
|
/// The default derivation path for OpenPGP.
|
|
pub static OPENPGP: Lazy<DerivationPath> = Lazy::new(|| {
|
|
DerivationPath::default().chain_push(DerivationIndex::new_unchecked(
|
|
u32::from_be_bytes(*b"\x00pgp"),
|
|
true,
|
|
))
|
|
});
|
|
|
|
/// The derivation path for OpenPGP certificates used for sharding.
|
|
pub static OPENPGP_SHARD: Lazy<DerivationPath> = Lazy::new(|| {
|
|
DerivationPath::default()
|
|
.chain_push(DerivationIndex::new_unchecked(
|
|
u32::from_be_bytes(*b"\x00pgp"),
|
|
true,
|
|
))
|
|
.chain_push(DerivationIndex::new_unchecked(
|
|
u32::from_be_bytes(*b"shrd"),
|
|
true,
|
|
))
|
|
});
|
|
|
|
/// The derivation path for OpenPGP certificates used for disaster recovery.
|
|
pub static OPENPGP_DISASTER_RECOVERY: Lazy<DerivationPath> = Lazy::new(|| {
|
|
DerivationPath::default()
|
|
.chain_push(DerivationIndex::new_unchecked(
|
|
u32::from_be_bytes(*b"\x00pgp"),
|
|
true,
|
|
))
|
|
.chain_push(DerivationIndex::new_unchecked(
|
|
u32::from_be_bytes(*b"\x00\x00dr"),
|
|
true,
|
|
))
|
|
});
|
|
}
|
|
|
|
/// Determine if a prefix matches and whether the next index exists.
|
|
fn prefix_matches(given: &DerivationPath, target: &DerivationPath) -> Option<DerivationIndex> {
|
|
if given.len() <= target.len() {
|
|
return None;
|
|
}
|
|
if target
|
|
.iter()
|
|
.zip(given.iter())
|
|
.all(|(left, right)| left == right)
|
|
{
|
|
given.iter().nth(target.len()).cloned()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// A derivation target.
|
|
#[derive(Debug)]
|
|
#[non_exhaustive]
|
|
pub enum Target {
|
|
/// An OpenPGP key, whose account is the given index.
|
|
OpenPGP(DerivationIndex),
|
|
|
|
/// An OpenPGP key used for sharding.
|
|
OpenPGPShard(DerivationIndex),
|
|
|
|
/// An OpenPGP key used for disaster recovery.
|
|
OpenPGPDisasterRecovery(DerivationIndex),
|
|
}
|
|
|
|
impl std::fmt::Display for Target {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Target::OpenPGP(account) => {
|
|
write!(f, "OpenPGP key (account {account})")
|
|
}
|
|
Target::OpenPGPShard(shard_index) => {
|
|
write!(f, "OpenPGP Shard key (shard index {shard_index})")
|
|
}
|
|
Target::OpenPGPDisasterRecovery(account) => {
|
|
write!(f, "OpenPGP Disaster Recovery key (account {account})")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! test_match {
|
|
($var:ident, $shard:path, $target:path) => {
|
|
if let Some(index) = prefix_matches($var, &$shard) {
|
|
return Some($target(index));
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Determine the closest [`Target`] for the given path. This method is intended to be used by
|
|
/// `keyforkd` to provide an optional textual prompt to what a client is attempting to derive.
|
|
pub fn guess_target(path: &DerivationPath) -> Option<Target> {
|
|
test_match!(path, paths::OPENPGP_SHARD, Target::OpenPGPShard);
|
|
test_match!(
|
|
path,
|
|
paths::OPENPGP_DISASTER_RECOVERY,
|
|
Target::OpenPGPDisasterRecovery
|
|
);
|
|
test_match!(path, paths::OPENPGP, Target::OpenPGP);
|
|
None
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn it_works() {
|
|
let index = DerivationIndex::new(5312, false).unwrap();
|
|
let dr_key = paths::OPENPGP_DISASTER_RECOVERY
|
|
.clone()
|
|
.chain_push(index.clone());
|
|
match guess_target(&dr_key) {
|
|
Some(Target::OpenPGPDisasterRecovery(idx)) if idx == index => (),
|
|
bad => panic!("invalid value: {bad:?}"),
|
|
}
|
|
}
|
|
}
|