feat: update yubikey to v0.8
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
__external_gitignore/
|
||||||
__*.pem
|
__*.pem
|
||||||
test.txt
|
test.txt
|
||||||
enc.txt
|
enc.txt
|
||||||
@@ -25,6 +26,7 @@ target/
|
|||||||
# Icon must end with two \r
|
# Icon must end with two \r
|
||||||
Icon
|
Icon
|
||||||
|
|
||||||
|
|
||||||
# Thumbnails
|
# Thumbnails
|
||||||
._*
|
._*
|
||||||
|
|
||||||
|
|||||||
948
Cargo.lock
generated
948
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.5.8"
|
version = "1.6.0"
|
||||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@@ -25,8 +25,8 @@ chrono = "0.4"
|
|||||||
simpledateformat = "0.1"
|
simpledateformat = "0.1"
|
||||||
ring = "0.16"
|
ring = "0.16"
|
||||||
openssl = "0.10"
|
openssl = "0.10"
|
||||||
pem = "2.0"
|
pem = "3.0"
|
||||||
yubikey = { version = "0.7", features = ["untested"] }
|
yubikey = { version = "0.8", features = ["untested"] }
|
||||||
yubico_manager = "0.9"
|
yubico_manager = "0.9"
|
||||||
x509 = "0.2"
|
x509 = "0.2"
|
||||||
x509-parser = "0.13"
|
x509-parser = "0.13"
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ use digest::Digest;
|
|||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
use spki::der::Encode;
|
||||||
use x509_parser::parse_x509_certificate;
|
use x509_parser::parse_x509_certificate;
|
||||||
use yubikey::{Certificate, YubiKey};
|
use yubikey::{Certificate, YubiKey};
|
||||||
use yubikey::piv::SlotId;
|
use yubikey::piv::SlotId;
|
||||||
|
|
||||||
|
use crate::pivutil::get_algorithm_id;
|
||||||
use crate::pkiutil::{bytes_to_pem, get_pki_algorithm};
|
use crate::pkiutil::{bytes_to_pem, get_pki_algorithm};
|
||||||
|
|
||||||
pub struct CommandImpl;
|
pub struct CommandImpl;
|
||||||
@@ -78,13 +80,17 @@ fn print_cert_info(yubikey: &mut YubiKey, slot: SlotId, detail_output: bool) ->
|
|||||||
return simple_error!("error reading certificate in slot {:?}: {}", slot, e);
|
return simple_error!("error reading certificate in slot {:?}: {}", slot, e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let buf = cert.as_ref();
|
let buf_vec = cert.cert.to_der()?;
|
||||||
|
let buf: &[u8] = buf_vec.as_ref();
|
||||||
if !buf.is_empty() {
|
if !buf.is_empty() {
|
||||||
information!("{}", "-".repeat(88));
|
information!("{}", "-".repeat(88));
|
||||||
let certificate_fingerprint_sha256 = Sha256::digest(buf);
|
let certificate_fingerprint_sha256 = Sha256::digest(buf);
|
||||||
|
|
||||||
let slot_id: u8 = slot.into();
|
let slot_id: u8 = slot.into();
|
||||||
success!("Slot: {:?}, id: {:x}, algorithm: {:?}", slot, slot_id, cert.subject_pki().algorithm());
|
let algorithm_id = get_algorithm_id(&cert.cert.tbs_certificate.subject_public_key_info)
|
||||||
|
.map(|aid| format!("{:?}", aid))
|
||||||
|
.unwrap_or_else(|e| format!("Error: {}", e));
|
||||||
|
success!("Slot: {:?}, id: {:x}, algorithm: {}", slot, slot_id, algorithm_id);
|
||||||
|
|
||||||
if detail_output {
|
if detail_output {
|
||||||
information!("{}", bytes_to_pem("CERTIFICATE", buf));
|
information!("{}", bytes_to_pem("CERTIFICATE", buf));
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ use rand::rngs::OsRng;
|
|||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use rust_util::util_msg;
|
use rust_util::util_msg;
|
||||||
use yubikey::{PinPolicy, YubiKey};
|
use yubikey::{PinPolicy, YubiKey};
|
||||||
use yubikey::certificate::PublicKeyInfo;
|
|
||||||
use yubikey::piv::{AlgorithmId, decrypt_data, metadata, RetiredSlotId, SlotId};
|
use yubikey::piv::{AlgorithmId, decrypt_data, metadata, RetiredSlotId, SlotId};
|
||||||
|
|
||||||
|
use crate::pivutil::get_algorithm_id;
|
||||||
|
|
||||||
pub struct CommandImpl;
|
pub struct CommandImpl;
|
||||||
|
|
||||||
impl Command for CommandImpl {
|
impl Command for CommandImpl {
|
||||||
@@ -108,19 +109,18 @@ impl Command for CommandImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(public_key) = &meta.public {
|
if let Some(public_key) = &meta.public {
|
||||||
match public_key {
|
let algorithm_id = opt_result!(get_algorithm_id(&public_key), "Get algorithm id failed: {}");
|
||||||
PublicKeyInfo::Rsa { algorithm, pubkey } => {
|
match algorithm_id {
|
||||||
failure_and_exit!("RSA not supported, {:?}, {:?}", algorithm, pubkey);
|
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 | AlgorithmId::EccP384 => {
|
||||||
|
failure_and_exit!("Not supported algorithm: {:?}", algorithm_id);
|
||||||
}
|
}
|
||||||
PublicKeyInfo::EcP256(pubkey) => {
|
AlgorithmId::EccP256 => {
|
||||||
if json_output {
|
match algorithm_id {
|
||||||
json.insert("pk_point_hex", hex::encode(pubkey.as_bytes()));
|
AlgorithmId::EccP256 => if let Some(public) = &meta.public {
|
||||||
} else {
|
json.insert("pk_point_hex", hex::encode(public.subject_public_key.raw_bytes()));
|
||||||
information!("EC-P256, {}", hex::encode(pubkey.as_bytes()));
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
PublicKeyInfo::EcP384(pubkey) => {
|
|
||||||
failure_and_exit!("EC-P384 not supported, {}", hex::encode(pubkey.as_bytes()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use hex::ToHex;
|
use p256::pkcs8::der::Encode;
|
||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use rust_util::util_msg;
|
use rust_util::util_msg;
|
||||||
use rust_util::util_msg::MessageType;
|
use rust_util::util_msg::MessageType;
|
||||||
use x509::SubjectPublicKeyInfo;
|
|
||||||
use x509_parser::parse_x509_certificate;
|
use x509_parser::parse_x509_certificate;
|
||||||
use yubikey::{Key, PinPolicy, TouchPolicy, YubiKey};
|
use yubikey::{Key, PinPolicy, TouchPolicy, YubiKey};
|
||||||
use yubikey::certificate::PublicKeyInfo;
|
|
||||||
use yubikey::piv::{AlgorithmId, ManagementAlgorithmId, metadata, Origin};
|
use yubikey::piv::{AlgorithmId, ManagementAlgorithmId, metadata, Origin};
|
||||||
|
|
||||||
use crate::pivutil;
|
use crate::pivutil;
|
||||||
use crate::pivutil::slot_equals;
|
use crate::pivutil::{get_algorithm_id, slot_equals};
|
||||||
use crate::pkiutil::bytes_to_pem;
|
use crate::pkiutil::bytes_to_pem;
|
||||||
|
|
||||||
pub struct CommandImpl;
|
pub struct CommandImpl;
|
||||||
@@ -87,28 +85,6 @@ impl Command for CommandImpl {
|
|||||||
} else {
|
} else {
|
||||||
information!("Origin: {}", origin_str);
|
information!("Origin: {}", origin_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(public_key) = &meta.public {
|
|
||||||
match public_key {
|
|
||||||
PublicKeyInfo::Rsa { algorithm, pubkey } => {
|
|
||||||
failure_and_exit!("RSA not supported, {:?}, {:?}", algorithm, pubkey);
|
|
||||||
}
|
|
||||||
PublicKeyInfo::EcP256(pubkey) => {
|
|
||||||
if json_output {
|
|
||||||
json.insert("pk_point_hex", hex::encode(pubkey.as_bytes()));
|
|
||||||
} else {
|
|
||||||
information!("EC-P256, {}", hex::encode(pubkey.as_bytes()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PublicKeyInfo::EcP384(pubkey) => {
|
|
||||||
if json_output {
|
|
||||||
json.insert("pk_point_hex", hex::encode(pubkey.as_bytes()));
|
|
||||||
} else {
|
|
||||||
information!("EC-P384, {}", hex::encode(pubkey.as_bytes()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
warning!("Get slot: {} meta data failed", slot);
|
warning!("Get slot: {} meta data failed", slot);
|
||||||
}
|
}
|
||||||
@@ -116,42 +92,51 @@ impl Command for CommandImpl {
|
|||||||
match Key::list(&mut yk) {
|
match Key::list(&mut yk) {
|
||||||
Err(e) => warning!("List keys failed: {}", e),
|
Err(e) => warning!("List keys failed: {}", e),
|
||||||
Ok(keys) => for k in &keys {
|
Ok(keys) => for k in &keys {
|
||||||
|
let cert = &k.certificate().cert.tbs_certificate;
|
||||||
let slot_str = format!("{:x}", Into::<u8>::into(k.slot()));
|
let slot_str = format!("{:x}", Into::<u8>::into(k.slot()));
|
||||||
if slot_equals(&slot_id, &slot_str) {
|
if slot_equals(&slot_id, &slot_str) {
|
||||||
if !json.contains_key("pk_point_hex") {
|
if let Ok(algorithm_id) = get_algorithm_id(&k.certificate().cert.tbs_certificate.subject_public_key_info) {
|
||||||
let public_key_hex = &k.certificate().subject_pki().public_key();
|
let algorithm_str = match algorithm_id {
|
||||||
json.insert("pk_point_hex", hex::encode(&public_key_hex));
|
|
||||||
let algorithm_str = match k.certificate().subject_pki().algorithm() {
|
|
||||||
AlgorithmId::Rsa1024 => "rsa1024",
|
AlgorithmId::Rsa1024 => "rsa1024",
|
||||||
AlgorithmId::Rsa2048 => "rsa2048",
|
AlgorithmId::Rsa2048 => "rsa2048",
|
||||||
AlgorithmId::EccP256 => "p256",
|
AlgorithmId::EccP256 => "p256",
|
||||||
AlgorithmId::EccP384 => "p384",
|
AlgorithmId::EccP384 => "p384",
|
||||||
};
|
};
|
||||||
json.insert("algorithm", algorithm_str.to_string());
|
json.insert("algorithm", algorithm_str.to_string());
|
||||||
}
|
|
||||||
json.insert("subject", k.certificate().subject().to_string());
|
|
||||||
json.insert("issuer", k.certificate().issuer().to_string());
|
|
||||||
json.insert("serial", k.certificate().serial().to_string());
|
|
||||||
json.insert("certificate_hex", k.certificate().encode_hex::<String>());
|
|
||||||
json.insert("certificate_pem", bytes_to_pem("CERTIFICATE", k.certificate().as_ref()));
|
|
||||||
|
|
||||||
let x509_certificate = parse_x509_certificate(k.certificate().as_ref()).unwrap().1;
|
let public_key_bit_string = &cert.subject_public_key_info.subject_public_key;
|
||||||
|
match algorithm_id {
|
||||||
|
AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
|
||||||
|
json.insert("pk_point_hex", hex::encode(public_key_bit_string.raw_bytes()));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let serial_lower = cert.serial_number.to_string().to_lowercase();
|
||||||
|
json.insert("serial", if serial_lower.starts_with("00:") { serial_lower.chars().skip(3).collect() } else { serial_lower });
|
||||||
|
let cert_der = k.certificate().cert.to_der()?;
|
||||||
|
json.insert("certificate_hex", hex::encode(&cert_der));
|
||||||
|
json.insert("certificate_pem", bytes_to_pem("CERTIFICATE", cert_der.as_slice()));
|
||||||
|
|
||||||
|
let x509_certificate = parse_x509_certificate(cert_der.as_slice()).unwrap().1;
|
||||||
let public_key_bytes = x509_certificate.public_key().raw;
|
let public_key_bytes = x509_certificate.public_key().raw;
|
||||||
|
json.insert("subject", x509_certificate.subject.to_string());
|
||||||
|
json.insert("issuer", x509_certificate.issuer.to_string());
|
||||||
json.insert("public_key_hex", hex::encode(public_key_bytes));
|
json.insert("public_key_hex", hex::encode(public_key_bytes));
|
||||||
json.insert("public_key_pem", bytes_to_pem("PUBLIC KEY", public_key_bytes));
|
json.insert("public_key_pem", bytes_to_pem("PUBLIC KEY", public_key_bytes));
|
||||||
|
|
||||||
if !json_output {
|
if !json_output {
|
||||||
information!("Subject: {}", k.certificate().subject());
|
information!("Subject: {}", x509_certificate.subject.to_string());
|
||||||
information!("Certificate: {}", bytes_to_pem("CERTIFICATE", k.certificate().as_ref()));
|
information!("Certificate: {}", bytes_to_pem("CERTIFICATE", cert_der.as_slice()));
|
||||||
information!("Public key: {}", bytes_to_pem("PUBLIC KEY", public_key_bytes));
|
information!("Public key: {}", bytes_to_pem("PUBLIC KEY", public_key_bytes));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
util_msg::when(MessageType::DEBUG, || {
|
util_msg::when(MessageType::DEBUG, || {
|
||||||
|
let cert_der = cert.to_der().unwrap();
|
||||||
debugging!("Slot: {:x}", Into::<u8>::into(k.slot()));
|
debugging!("Slot: {:x}", Into::<u8>::into(k.slot()));
|
||||||
let cert_hex = k.certificate().encode_hex::<String>();
|
let public_key_bytes = cert.subject_public_key_info.subject_public_key.raw_bytes();
|
||||||
let public_key_hex = &k.certificate().subject_pki().public_key();
|
debugging!("Certificate: {}", hex::encode(&cert_der));
|
||||||
debugging!("Certificate: {}", &cert_hex);
|
debugging!("Public key: {}", hex::encode(public_key_bytes));
|
||||||
debugging!("Public key: {}", hex::encode(&public_key_hex));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,55 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use yubikey::piv::RetiredSlotId;
|
use spki::der::{Decode, Encode};
|
||||||
|
use spki::{ObjectIdentifier, SubjectPublicKeyInfoOwned};
|
||||||
|
use x509_parser::prelude::FromDer;
|
||||||
|
use x509_parser::public_key::RSAPublicKey;
|
||||||
|
use yubikey::piv::{AlgorithmId, RetiredSlotId};
|
||||||
use yubikey::piv::SlotId;
|
use yubikey::piv::SlotId;
|
||||||
|
|
||||||
|
const RSA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
|
||||||
|
const ECC: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
|
||||||
|
|
||||||
|
// NIST recommended curves
|
||||||
|
// secp192r1 – {1.2.840.10045.3.1.1}
|
||||||
|
// secp224r1 – {1.3.132.0.33}
|
||||||
|
// secp256r1 – {1.2.840.10045.3.1.7}
|
||||||
|
// secp384r1 – {1.3.132.0.34}
|
||||||
|
// secp521r1 – {1.3.132.0.35}
|
||||||
|
const ECC_P256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
|
||||||
|
const ECC_P384: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.132.0.34");
|
||||||
|
|
||||||
|
pub fn get_algorithm_id(public_key_info: &SubjectPublicKeyInfoOwned) -> XResult<AlgorithmId> {
|
||||||
|
if public_key_info.algorithm.oid == RSA {
|
||||||
|
let rsa_public_key = opt_result!(
|
||||||
|
RSAPublicKey::from_der(public_key_info.subject_public_key.raw_bytes()), "Parse public key failed: {}");
|
||||||
|
let starts_with_0 = rsa_public_key.1.modulus.starts_with(&[0]);
|
||||||
|
let public_key_bits = (rsa_public_key.1.modulus.len() - if starts_with_0 { 1 } else { 0 }) * 8;
|
||||||
|
if public_key_bits == 1024 {
|
||||||
|
return Ok(AlgorithmId::Rsa1024);
|
||||||
|
}
|
||||||
|
if public_key_bits == 2048 {
|
||||||
|
return Ok(AlgorithmId::Rsa2048);
|
||||||
|
}
|
||||||
|
return simple_error!("Unknown rsa bits: {}", public_key_bits);
|
||||||
|
}
|
||||||
|
if public_key_info.algorithm.oid == ECC {
|
||||||
|
if let Some(any) = &public_key_info.algorithm.parameters {
|
||||||
|
let any_parameter_der = opt_result!(any.to_der(), "Bad any parameter: {}");
|
||||||
|
let any_parameter_oid = opt_result!(ObjectIdentifier::from_der(&any_parameter_der), "Bad any parameter der: {}");
|
||||||
|
if any_parameter_oid == ECC_P256 {
|
||||||
|
return Ok(AlgorithmId::EccP256);
|
||||||
|
}
|
||||||
|
if any_parameter_oid == ECC_P384 {
|
||||||
|
return Ok(AlgorithmId::EccP384);
|
||||||
|
}
|
||||||
|
return simple_error!("Unknown any parameter oid: {}", any_parameter_oid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simple_error!("Unknown algorithm: {}", public_key_info.algorithm.oid)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn slot_equals(slot_id: &SlotId, slot: &str) -> bool {
|
pub fn slot_equals(slot_id: &SlotId, slot: &str) -> bool {
|
||||||
get_slot_id(slot).map(|sid| &sid == slot_id).unwrap_or(false)
|
get_slot_id(slot).map(|sid| &sid == slot_id).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user