diff --git a/Cargo.lock b/Cargo.lock index 869539f..0aae23a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,7 +384,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.1.9" +version = "1.1.11" dependencies = [ "authenticator", "base64 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 4ecf263..cfdbd2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.1.9" +version = "1.1.11" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_chall.rs b/src/cmd_chall.rs index 4564f67..4a4f134 100644 --- a/src/cmd_chall.rs +++ b/src/cmd_chall.rs @@ -46,7 +46,7 @@ impl Command for CommandImpl { let mut yubi = Yubico::new(); if let Ok(device) = yubi.find_yubikey() { - success!("Found key, Vendor ID: {:?} Product ID {:?}", device.vendor_id, device.product_id); + success!("Found key, Vendor ID: {:?}, Product ID: {:?}", device.vendor_id, device.product_id); let config = Config::default() .set_vendor_id(device.vendor_id) diff --git a/src/cmd_pgpcardsign.rs b/src/cmd_pgpcardsign.rs index 3628cea..cba01dd 100644 --- a/src/cmd_pgpcardsign.rs +++ b/src/cmd_pgpcardsign.rs @@ -84,7 +84,7 @@ impl Command for CommandImpl { if let Some(sha256) = sha256 { let sha256_hex = opt_result!(hex::decode(sha256.trim()), "Decode sha256 failed: {}"); - let sha256_hex = copy_sha256(&sha256_hex)?; + let sha256_hex = crate::digest::copy_sha256(&sha256_hex)?; opt_result!(trans.verify_pw1_sign(pin.as_ref()), "User sign pin verify failed: {}"); success!("User sign pin verify success!"); let sig = trans.signature_for_hash(Hash::SHA256(sha256_hex))?; @@ -99,7 +99,7 @@ impl Command for CommandImpl { } if let Some(sha384) = sha384 { let sha384_hex = opt_result!(hex::decode(sha384.trim()), "Decode sha384 failed: {}"); - let sha384_hex = copy_sha384(&sha384_hex)?; + let sha384_hex = crate::digest::copy_sha384(&sha384_hex)?; opt_result!(trans.verify_pw1_sign(pin.as_ref()), "User sign pin verify failed: {}"); success!("User sign pin verify success!"); let sig = trans.signature_for_hash(Hash::SHA384(sha384_hex))?; @@ -114,7 +114,7 @@ impl Command for CommandImpl { } if let Some(sha512) = sha512 { let sha512_hex = opt_result!(hex::decode(sha512.trim()), "Decode sha512 failed: {}"); - let sha512_hex = copy_sha512(&sha512_hex)?; + let sha512_hex = crate::digest::copy_sha512(&sha512_hex)?; opt_result!(trans.verify_pw1_sign(pin.as_ref()), "User sign pin verify failed: {}"); success!("User sign pin verify success!"); let sig = trans.signature_for_hash(Hash::SHA512(sha512_hex))?; @@ -152,22 +152,3 @@ fn calc_file_digest(file_name: &str) -> XResult> where D: Digest { hasher.update(&buf[..len]); } } - -macro_rules! define_copy_array { - ($fn_name: ident, $len: tt) => ( - fn $fn_name(in_arr: &[u8]) -> XResult<[u8; $len]> { - if in_arr.len() != $len { - return simple_error!("Array length is not: {}, but is: {}", $len, in_arr.len()); - } - let mut out_arr = [0_u8; $len]; - for i in 0..$len { - out_arr[i] = in_arr[i]; - } - Ok(out_arr) - } - ) -} - -define_copy_array!(copy_sha256, 0x20); -define_copy_array!(copy_sha384, 0x30); -define_copy_array!(copy_sha512, 0x40); diff --git a/src/cmd_pivgenerate.rs b/src/cmd_pivgenerate.rs new file mode 100644 index 0000000..8566be5 --- /dev/null +++ b/src/cmd_pivgenerate.rs @@ -0,0 +1,35 @@ +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::util_clap::{Command, CommandError}; +use yubikey::{PinPolicy, TouchPolicy, YubiKey}; +use yubikey::piv::{AlgorithmId, SlotId}; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { "piv-generate" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("PIV Generate subcommand") + .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("123456").help("OpenPGP card user pin")) + .arg(Arg::with_name("json").long("json").help("JSON output")) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let json_output = sub_arg_matches.is_present("json"); + if json_output { rust_util::util_msg::set_logger_std_out(false); } + + warning!("This feature is not works"); + let pin = opt_value_result!(sub_arg_matches.value_of("pin"), "User pin must be assigned"); + + let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); + opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}"); + + let public_key_info = opt_result!(yubikey::piv::generate(&mut yk,SlotId::Signature, AlgorithmId::Rsa2048, + PinPolicy::Default, TouchPolicy::Default), "Generate key failed: {}"); + + success!("Generate key success: {:?}", public_key_info); + + + Ok(None) + } +} diff --git a/src/cmd_pivsign.rs b/src/cmd_pivsign.rs index d89f8ea..509e983 100644 --- a/src/cmd_pivsign.rs +++ b/src/cmd_pivsign.rs @@ -2,6 +2,8 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; +use rust_util::util_msg::MessageType; +use rust_util::XResult; use yubikey::piv::{AlgorithmId, SlotId}; use yubikey::YubiKey; @@ -28,14 +30,21 @@ impl Command for CommandImpl { let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}"); - // TODO make PKCS#1_5 padding signature - let raw_in = [1_u8; 256]; + + let hash = hex::decode( + "3031300d060960864801650304020105000420".to_string() + + "66d8dff9cbf819183700ff6d08349d9472af54031e8e297f8ab341e307f5387c").unwrap(); + rust_util::util_msg::when(MessageType::DEBUG, || debugging!("Hash: {}", hex::encode(&hash))); + let hash_padding = pkcs1_padding_for_sign(&hash, 2048).unwrap(); + rust_util::util_msg::when(MessageType::DEBUG, || debugging!("PKCS1 padding: {}", hex::encode(&hash_padding))); + let raw_in = crate::digest::copy_rsa2048(&hash_padding).unwrap(); let sign_result = yubikey::piv::sign_data(&mut yk, &raw_in, AlgorithmId::Rsa2048, SlotId::Signature); let sign = opt_result!(sign_result, "Sign data failed: {}"); let sign_bytes = sign.as_slice(); if json_output { let mut json = BTreeMap::<&'_ str, String>::new(); + json.insert("hash_hex", hex::encode(&hash)); json.insert("sign_hex", hex::encode(&sign_bytes)); json.insert("sign_base64", base64::encode(&sign_bytes)); println!("{}", serde_json::to_string_pretty(&json).unwrap()); @@ -46,3 +55,21 @@ impl Command for CommandImpl { Ok(None) } } + +fn pkcs1_padding_for_sign(bs: &[u8], bit_len: usize) -> XResult> { + let byte_len = bit_len / 8; + let max_len = byte_len - (1 + 1 + 8 + 2); + if bs.len() > max_len { + return simple_error!("Length is too large: {} > {}", bs.len(), max_len); + } + let mut output = Vec::::with_capacity(byte_len); + output.push(0x00); + output.push(0x01); + let ps_len = byte_len - bs.len() - (1 + 1 + 1); + for _ in 0..ps_len { + output.push(0xff); + } + output.push(0x00); + output.extend_from_slice(bs); + Ok(output) +} diff --git a/src/cmd_rsaverify.rs b/src/cmd_rsaverify.rs index 167bfcf..77031ff 100644 --- a/src/cmd_rsaverify.rs +++ b/src/cmd_rsaverify.rs @@ -1,11 +1,13 @@ use std::fs::File; use clap::{App, Arg, ArgMatches, SubCommand}; +use openssl::bn::{BigNum, BigNumContext}; use openssl::hash::MessageDigest; use openssl::pkey::PKey; use openssl::rsa::Rsa; use openssl::sign::Verifier; use rust_util::util_clap::{Command, CommandError}; +use rust_util::util_msg::MessageType; use rust_util::XResult; use crate::digest::sha256_bytes; @@ -47,6 +49,16 @@ impl Command for CommandImpl { return simple_error!("Signature is required, --signature argument!"); }; + rust_util::util_msg::when(MessageType::DEBUG, || { + let rsa = keypair.rsa().clone().unwrap(); + let n = rsa.n(); + let e = rsa.e(); + let m = BigNum::from_slice(&signature).unwrap(); + let mut r = BigNum::new().unwrap(); + r.mod_exp(&m, &e, &n, &mut BigNumContext::new().unwrap()).unwrap(); + debugging!("Signature raw HEX: {}", hex::encode(&r.to_vec())); + }); + let file_in = opt_value_result!(sub_arg_matches.value_of("in"), "File in --in required"); information!("File in: {}", file_in); information!("Public key fingerprint: {}", pub_key_fingerprint); diff --git a/src/digest.rs b/src/digest.rs index ae68531..10948cb 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -35,4 +35,25 @@ pub fn sha512_bytes(input: &[u8]) -> Vec { let mut challenge = Sha512::default(); Digest::update(&mut challenge, input); challenge.finalize().to_vec() -} \ No newline at end of file +} + +macro_rules! define_copy_array { + ($fn_name: ident, $len: tt) => ( + pub fn $fn_name(in_arr: &[u8]) -> rust_util::XResult<[u8; $len]> { + if in_arr.len() != $len { + return simple_error!("Array length is not: {}, but is: {}", $len, in_arr.len()); + } + let mut out_arr = [0_u8; $len]; + for i in 0..$len { + out_arr[i] = in_arr[i]; + } + Ok(out_arr) + } + ) +} + +define_copy_array!(copy_sha256, 0x20); +define_copy_array!(copy_sha384, 0x30); +define_copy_array!(copy_sha512, 0x40); + +define_copy_array!(copy_rsa2048, 0x100); diff --git a/src/main.rs b/src/main.rs index 8a823ec..9e0208b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod cmd_pgpcarddecrypt; mod cmd_pgpcardmake; mod cmd_piv; mod cmd_pivsign; +mod cmd_pivgenerate; mod cmd_chall; mod cmd_challconfig; @@ -58,6 +59,7 @@ fn inner_main() -> CommandError { Box::new(cmd_pgpcardmake::CommandImpl), Box::new(cmd_piv::CommandImpl), Box::new(cmd_pivsign::CommandImpl), + Box::new(cmd_pivgenerate::CommandImpl), Box::new(cmd_u2fregister::CommandImpl), Box::new(cmd_u2fsign::CommandImpl), ];