Implement attestation-related functionality in opgpcard
This commit is contained in:
parent
7b3152a88e
commit
22c29262d3
4 changed files with 120 additions and 0 deletions
|
@ -17,6 +17,7 @@ openpgp-card = { path = "../openpgp-card", version = "0.2.4" }
|
||||||
openpgp-card-pcsc = { path = "../pcsc", version = "0.2" }
|
openpgp-card-pcsc = { path = "../pcsc", version = "0.2" }
|
||||||
openpgp-card-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.11" }
|
openpgp-card-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.11" }
|
||||||
sshkeys = "0.3.2"
|
sshkeys = "0.3.2"
|
||||||
|
pem = "1"
|
||||||
rpassword = "6"
|
rpassword = "6"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "3.1", features = ["derive"] }
|
clap = { version = "3.1", features = ["derive"] }
|
||||||
|
|
|
@ -109,6 +109,12 @@ pub enum Command {
|
||||||
input: Option<PathBuf>,
|
input: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Attestation management (Yubico)
|
||||||
|
Attestation {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
cmd: AttCommand,
|
||||||
|
},
|
||||||
|
|
||||||
/// Completely reset a card (deletes all data, including the keys on the card!)
|
/// Completely reset a card (deletes all data, including the keys on the card!)
|
||||||
FactoryReset {
|
FactoryReset {
|
||||||
#[clap(name = "card ident", short = 'c', long = "card")]
|
#[clap(name = "card ident", short = 'c', long = "card")]
|
||||||
|
@ -221,3 +227,34 @@ pub enum PinCommand {
|
||||||
user_pin_new: Option<PathBuf>,
|
user_pin_new: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub enum AttCommand {
|
||||||
|
/// Print the card's "Attestation Certificate"
|
||||||
|
Cert {
|
||||||
|
#[clap(name = "card ident", short = 'c', long = "card")]
|
||||||
|
ident: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Generate "Attestation Statement" for one of the key slots on the card
|
||||||
|
Generate {
|
||||||
|
#[clap(name = "card ident", short = 'c', long = "card")]
|
||||||
|
ident: String,
|
||||||
|
|
||||||
|
#[clap(name = "Key slot (SIG|DEC|AUT)", short = 'k', long = "key")]
|
||||||
|
key: String,
|
||||||
|
|
||||||
|
#[clap(name = "User PIN file", short = 'p', long = "user-pin")]
|
||||||
|
user_pin: Option<PathBuf>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Print a "cardholder certificate" from the card.
|
||||||
|
/// This shows the "Attestation Statement", if one has been generated.
|
||||||
|
Statement {
|
||||||
|
#[clap(name = "card ident", short = 'c', long = "card")]
|
||||||
|
ident: Option<String>,
|
||||||
|
|
||||||
|
#[clap(name = "Key slot (SIG|DEC|AUT)", short = 'k', long = "key")]
|
||||||
|
key: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -78,6 +78,77 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cli::Command::Attestation { cmd } => match cmd {
|
||||||
|
cli::AttCommand::Cert { ident } => {
|
||||||
|
let mut card = pick_card_for_reading(ident)?;
|
||||||
|
|
||||||
|
let mut pgp = OpenPgp::new(&mut *card);
|
||||||
|
let mut open = Open::new(pgp.transaction()?)?;
|
||||||
|
|
||||||
|
if let Ok(ac) = open.attestation_certificate() {
|
||||||
|
let pem = util::pem_encode(ac);
|
||||||
|
println!("{}", pem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cli::AttCommand::Generate {
|
||||||
|
ident,
|
||||||
|
key,
|
||||||
|
user_pin,
|
||||||
|
} => {
|
||||||
|
let mut card = util::open_card(&ident)?;
|
||||||
|
let mut pgp = OpenPgp::new(&mut card);
|
||||||
|
|
||||||
|
let mut open = Open::new(pgp.transaction()?)?;
|
||||||
|
let user_pin = util::get_pin(&mut open, user_pin, ENTER_USER_PIN);
|
||||||
|
|
||||||
|
let mut sign = util::verify_to_sign(&mut open, user_pin.as_deref())?;
|
||||||
|
|
||||||
|
let kt = match key.as_str() {
|
||||||
|
"SIG" => KeyType::Signing,
|
||||||
|
"DEC" => KeyType::Decryption,
|
||||||
|
"AUT" => KeyType::Authentication,
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow!("Unexpected Key Type {}", key).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sign.generate_attestation(kt)?;
|
||||||
|
}
|
||||||
|
cli::AttCommand::Statement { ident, key } => {
|
||||||
|
let mut card = pick_card_for_reading(ident)?;
|
||||||
|
|
||||||
|
let mut pgp = OpenPgp::new(&mut *card);
|
||||||
|
let mut open = Open::new(pgp.transaction()?)?;
|
||||||
|
|
||||||
|
// Load cardholder certificate from card.
|
||||||
|
|
||||||
|
// FIXME/Note: SELECT_DATA seemed to not work as expected on YK5,
|
||||||
|
let cert = match key.as_str() {
|
||||||
|
"AUT" => open.cardholder_certificate()?,
|
||||||
|
"DEC" => {
|
||||||
|
// skip first cardholder certificate
|
||||||
|
let _ = open.cardholder_certificate()?;
|
||||||
|
open.next_cardholder_certificate()?
|
||||||
|
}
|
||||||
|
"SIG" => {
|
||||||
|
// skip first two cardholder certificates
|
||||||
|
let _ = open.cardholder_certificate()?;
|
||||||
|
let _ = open.next_cardholder_certificate()?;
|
||||||
|
open.next_cardholder_certificate()?
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow!("Unexpected Key Type {}", key).into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !cert.is_empty() {
|
||||||
|
let pem = util::pem_encode(cert);
|
||||||
|
println!("{}", pem);
|
||||||
|
} else {
|
||||||
|
println!("Cardholder certificate slot is empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
cli::Command::FactoryReset { ident } => {
|
cli::Command::FactoryReset { ident } => {
|
||||||
factory_reset(&ident)?;
|
factory_reset(&ident)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,3 +231,14 @@ pub(crate) fn print_gnuk_note(err: Error, card: &Open) -> Result<()> {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pem_encode(data: Vec<u8>) -> String {
|
||||||
|
const PEM_TAG: &str = "CERTIFICATE";
|
||||||
|
|
||||||
|
let pem = pem::Pem {
|
||||||
|
tag: String::from(PEM_TAG),
|
||||||
|
contents: data,
|
||||||
|
};
|
||||||
|
|
||||||
|
pem::encode(&pem)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue