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-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.11" }
|
||||
sshkeys = "0.3.2"
|
||||
pem = "1"
|
||||
rpassword = "6"
|
||||
anyhow = "1"
|
||||
clap = { version = "3.1", features = ["derive"] }
|
||||
|
|
|
@ -109,6 +109,12 @@ pub enum Command {
|
|||
input: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// Attestation management (Yubico)
|
||||
Attestation {
|
||||
#[clap(subcommand)]
|
||||
cmd: AttCommand,
|
||||
},
|
||||
|
||||
/// Completely reset a card (deletes all data, including the keys on the card!)
|
||||
FactoryReset {
|
||||
#[clap(name = "card ident", short = 'c', long = "card")]
|
||||
|
@ -221,3 +227,34 @@ pub enum PinCommand {
|
|||
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 } => {
|
||||
factory_reset(&ident)?;
|
||||
}
|
||||
|
|
|
@ -231,3 +231,14 @@ pub(crate) fn print_gnuk_note(err: Error, card: &Open) -> Result<()> {
|
|||
}
|
||||
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