keyfork-derive-openpgp: convert to lib+bin
This commit is contained in:
parent
0a84d47a4d
commit
0119e58d2d
|
@ -893,6 +893,7 @@ dependencies = [
|
||||||
name = "keyfork-derive-openpgp"
|
name = "keyfork-derive-openpgp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"keyforkd-client",
|
"keyforkd-client",
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
ed25519-dalek = "2.0.0"
|
ed25519-dalek = "2.0.0"
|
||||||
keyfork-derive-util = { version = "0.1.0", path = "../keyfork-derive-util", default-features = false, features = ["ed25519"] }
|
keyfork-derive-util = { version = "0.1.0", path = "../keyfork-derive-util", default-features = false, features = ["ed25519"] }
|
||||||
keyforkd-client = { version = "0.1.0", path = "../keyforkd-client", default-features = false, features = ["ed25519"] }
|
keyforkd-client = { version = "0.1.0", path = "../keyforkd-client", default-features = false, features = ["ed25519"] }
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
use std::time::{Duration, SystemTime, SystemTimeError};
|
||||||
|
|
||||||
|
use ed25519_dalek::SigningKey;
|
||||||
|
use keyfork_derive_util::{
|
||||||
|
request::{DerivationResponse, TryFromDerivationResponseError},
|
||||||
|
DerivationIndex, ExtendedPrivateKey, PrivateKey,
|
||||||
|
};
|
||||||
|
use sequoia_openpgp::{
|
||||||
|
packet::{
|
||||||
|
key::{Key4, PrimaryRole, SubordinateRole},
|
||||||
|
signature::SignatureBuilder,
|
||||||
|
Key, UserID,
|
||||||
|
},
|
||||||
|
types::{KeyFlags, SignatureType},
|
||||||
|
Cert, Packet,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("{0}")]
|
||||||
|
Anyhow(#[from] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Incorrect derived data: {0}")]
|
||||||
|
IncorrectDerivedData(#[from] TryFromDerivationResponseError),
|
||||||
|
|
||||||
|
#[error("Could not create derivation index: {0}")]
|
||||||
|
Index(#[from] keyfork_derive_util::index::Error),
|
||||||
|
|
||||||
|
#[error("Could not perform operation against private key: {0}")]
|
||||||
|
PrivateKey(#[from] keyfork_derive_util::extended_key::private_key::Error),
|
||||||
|
|
||||||
|
#[error("Invalid system time: {0}")]
|
||||||
|
SystemTime(#[from] SystemTimeError),
|
||||||
|
|
||||||
|
#[error("First key in certificate must have certify capability")]
|
||||||
|
NotCert,
|
||||||
|
|
||||||
|
#[error("Index out of bounds: {0}")]
|
||||||
|
IndexOutOfBounds(#[from] std::num::TryFromIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: UserID) -> Result<Cert> {
|
||||||
|
let primary_key_flags = match keys.get(0) {
|
||||||
|
Some(kf) if kf.for_certification() => kf,
|
||||||
|
_ => return Err(Error::NotCert),
|
||||||
|
};
|
||||||
|
|
||||||
|
let epoch = SystemTime::UNIX_EPOCH + Duration::from_secs(1);
|
||||||
|
let one_day = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
|
||||||
|
|
||||||
|
// Create certificate with initial key and signature
|
||||||
|
let xprv = ExtendedPrivateKey::<SigningKey>::try_from(data)?;
|
||||||
|
let derived_primary_key = xprv.derive_child(&DerivationIndex::new(0, true)?)?;
|
||||||
|
let primary_key = Key::from(Key4::<_, PrimaryRole>::import_secret_ed25519(
|
||||||
|
&PrivateKey::to_bytes(derived_primary_key.private_key()),
|
||||||
|
epoch,
|
||||||
|
)?);
|
||||||
|
let cert = Cert::from_packets(vec![Packet::SecretKey(primary_key.clone())].into_iter())?;
|
||||||
|
|
||||||
|
// Sign and attach primary key and primary userid
|
||||||
|
let builder = SignatureBuilder::new(SignatureType::PositiveCertification)
|
||||||
|
.set_key_validity_period(one_day.duration_since(epoch)?)?
|
||||||
|
.set_signature_creation_time(epoch)?
|
||||||
|
.set_key_flags(primary_key_flags.clone())?;
|
||||||
|
let binding = userid.bind(&mut primary_key.clone().into_keypair()?, &cert, builder)?;
|
||||||
|
let cert = cert.insert_packets(vec![Packet::from(userid.clone()), binding.into()])?;
|
||||||
|
let policy = sequoia_openpgp::policy::StandardPolicy::new();
|
||||||
|
|
||||||
|
// Set certificate expiration to one day
|
||||||
|
let mut keypair = primary_key.clone().into_keypair()?;
|
||||||
|
let signatures = cert.set_expiration_time(&policy, None, &mut keypair, Some(one_day))?;
|
||||||
|
let cert = cert.insert_packets(signatures)?;
|
||||||
|
|
||||||
|
let mut cert = cert;
|
||||||
|
for (index, subkey_flags) in keys.iter().enumerate().skip(1) {
|
||||||
|
// Generate subkey
|
||||||
|
let index = u32::try_from(index)?;
|
||||||
|
let derived_key = xprv.derive_child(&DerivationIndex::new(index, true)?)?;
|
||||||
|
let subkey = Key::from(
|
||||||
|
Key4::<_, SubordinateRole>::import_secret_ed25519(
|
||||||
|
&PrivateKey::to_bytes(derived_key.private_key()),
|
||||||
|
epoch,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// As per OpenPGP spec, signing keys must backsig the primary key
|
||||||
|
let builder = if subkey_flags.for_signing() {
|
||||||
|
SignatureBuilder::new(SignatureType::SubkeyBinding)
|
||||||
|
.set_key_flags(subkey_flags.clone())?
|
||||||
|
.set_signature_creation_time(epoch)?
|
||||||
|
.set_key_validity_period(one_day.duration_since(epoch)?)?
|
||||||
|
.set_embedded_signature(
|
||||||
|
SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
|
||||||
|
.set_signature_creation_time(epoch)?
|
||||||
|
.sign_primary_key_binding(
|
||||||
|
&mut subkey.clone().into_keypair()?,
|
||||||
|
&primary_key,
|
||||||
|
&subkey,
|
||||||
|
)?,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
SignatureBuilder::new(SignatureType::SubkeyBinding)
|
||||||
|
.set_key_flags(subkey_flags.clone())?
|
||||||
|
.set_signature_creation_time(epoch)?
|
||||||
|
.set_key_validity_period(one_day.duration_since(epoch)?)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sign subkey with primary key and attach to cert
|
||||||
|
let binding =
|
||||||
|
builder.sign_subkey_binding(&mut primary_key.clone().into_keypair()?, None, &subkey)?;
|
||||||
|
cert = cert.insert_packets(vec![
|
||||||
|
Packet::SecretSubkey(subkey.clone()),
|
||||||
|
Packet::Signature(binding),
|
||||||
|
])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cert)
|
||||||
|
}
|
|
@ -121,75 +121,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
||||||
let derived_data = Client::discover_socket()?.request(&request)?;
|
let derived_data = Client::discover_socket()?.request(&request)?;
|
||||||
let xprv = ExtendedPrivateKey::<SigningKey>::try_from(&derived_data)?;
|
let subkeys = subkey_format.iter().map(|kt| kt.inner().clone()).collect::<Vec<_>>();
|
||||||
|
|
||||||
let derived_key = xprv.derive_child(&DerivationIndex::new(0, true)?)?;
|
let cert = keyfork_derive_openpgp::derive(derived_data, subkeys.as_slice(), default_userid)?;
|
||||||
let epoch = SystemTime::UNIX_EPOCH + Duration::from_secs(1);
|
|
||||||
let one_day = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
|
|
||||||
let primary_key = Key::from(
|
|
||||||
Key4::<_, PrimaryRole>::import_secret_ed25519(
|
|
||||||
&PrivateKey::to_bytes(derived_key.private_key()),
|
|
||||||
epoch,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let cert =
|
|
||||||
Cert::from_packets(vec![Packet::SecretKey(primary_key.clone())].into_iter()).unwrap();
|
|
||||||
let builder = SignatureBuilder::new(SignatureType::PositiveCertification)
|
|
||||||
.set_key_validity_period(one_day.duration_since(epoch)?)?
|
|
||||||
.set_signature_creation_time(epoch)?
|
|
||||||
.set_key_flags(subkey_format[0].inner().clone())?;
|
|
||||||
let binding = default_userid.bind(&mut primary_key.clone().into_keypair()?, &cert, builder)?;
|
|
||||||
let cert = cert.insert_packets(vec![Packet::from(default_userid), binding.into()])?;
|
|
||||||
|
|
||||||
let policy = sequoia_openpgp::policy::StandardPolicy::new();
|
|
||||||
let mut keypair = cert
|
|
||||||
.primary_key()
|
|
||||||
.key()
|
|
||||||
.clone()
|
|
||||||
.parts_into_secret()?
|
|
||||||
.into_keypair()?;
|
|
||||||
let signatures = cert.set_expiration_time(&policy, None, &mut keypair, Some(one_day))?;
|
|
||||||
let mut cert = cert.insert_packets(signatures)?;
|
|
||||||
|
|
||||||
for (index, subkey_flags) in subkey_format.iter().enumerate().skip(1) {
|
|
||||||
let index = u32::try_from(index)?;
|
|
||||||
let derived_key = xprv.derive_child(&DerivationIndex::new(index, true)?)?;
|
|
||||||
let subkey = Key::from(
|
|
||||||
Key4::<_, SubordinateRole>::import_secret_ed25519(
|
|
||||||
&PrivateKey::to_bytes(derived_key.private_key()),
|
|
||||||
epoch,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
let builder = if subkey_flags.inner().for_signing() {
|
|
||||||
SignatureBuilder::new(SignatureType::SubkeyBinding)
|
|
||||||
.set_key_flags(subkey_flags.inner().clone())?
|
|
||||||
.set_signature_creation_time(epoch)?
|
|
||||||
.set_key_validity_period(one_day.duration_since(epoch)?)?
|
|
||||||
.set_embedded_signature(
|
|
||||||
SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
|
|
||||||
.set_signature_creation_time(epoch)?
|
|
||||||
.sign_primary_key_binding(
|
|
||||||
&mut subkey.clone().into_keypair()?,
|
|
||||||
&primary_key,
|
|
||||||
&subkey,
|
|
||||||
)?,
|
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
SignatureBuilder::new(SignatureType::SubkeyBinding)
|
|
||||||
.set_key_flags(subkey_flags.inner().clone())?
|
|
||||||
.set_signature_creation_time(epoch)?
|
|
||||||
.set_key_validity_period(one_day.duration_since(epoch)?)?
|
|
||||||
};
|
|
||||||
let binding =
|
|
||||||
builder.sign_subkey_binding(&mut primary_key.clone().into_keypair()?, None, &subkey)?;
|
|
||||||
cert = cert.insert_packets(vec![
|
|
||||||
Packet::SecretSubkey(subkey.clone()),
|
|
||||||
Packet::Signature(binding),
|
|
||||||
])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
use sequoia_openpgp::{
|
use sequoia_openpgp::{
|
||||||
armor::{Kind, Writer},
|
armor::{Kind, Writer},
|
||||||
|
|
Loading…
Reference in New Issue