Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ wasm_experimental = ["ring/wasm32_unknown_unknown_js"]
# if you need to work with fido data, without needing to generate it.
fido-support = ["ctap-hid-fido2", "fido-lite"]
fido-support-mozilla = ["authenticator", "fido-lite"]
fido-lite = ["minicbor", "x509-parser"]
fido-lite = ["minicbor", "x509-cert"]
rsa-signing = ["simple_asn1", "num-bigint"]
x509-support = ["der-parser", "x509", "x509-parser"]
yubikey-support = ["rcgen", "yubikey", "yubikey-lite"]
yubikey-lite = ["x509-support", "der", "x509-cert"]
yubikey-lite = ["x509-support", "x509-cert", "p256", "p384"]

[dependencies]
base64 = "0.13"
Expand All @@ -67,8 +67,9 @@ rcgen = { version = "0.11", optional = true }
x509 = { version = "0.2", optional = true }
x509-parser = { version = "0.15", features = ["verify"], optional = true }
der-parser = { version = "5", optional = true }
der = { version = "0.7", optional = true }
x509-cert = { version = "0.2", optional = true }
p256 = { version = "0.13.2", optional = true }
p384 = { version = "0.13.1", optional = true }

# Dependencies for encrypted-keys
aes = { version = "0.7", features = ["ctr"], optional = true }
Expand Down Expand Up @@ -97,8 +98,6 @@ env_logger = "0.8.2"
hex = "0.4.2"
clap = "3.0.5"
criterion = "0.3"
p256 = "*"
p384 = "*"

[[bench]]
name = "certs_per_second"
Expand All @@ -113,6 +112,10 @@ required-features = ["yubikey-support"]
name = "yk-provision"
required-features = ["yubikey-support"]

[[example]]
name = "yk-generate-csr"
required-features = ["yubikey-support"]

[[example]]
name = "sign-cert-with-yubikey"
required-features = ["yubikey-support"]
Expand Down
89 changes: 89 additions & 0 deletions examples/yk-generate-csr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::env;

use clap::{Arg, Command};

use sshcerts::yubikey::piv::Yubikey;
use sshcerts::yubikey::piv::{RetiredSlotId, SlotId};

use x509_parser::prelude::*;

use std::convert::TryFrom;

fn slot_parser(slot: &str) -> Option<SlotId> {
// If first character is R, then we need to parse the nice
// notation
if (slot.len() == 2 || slot.len() == 3) && slot.starts_with('R') {
let slot_value = slot[1..].parse::<u8>();
match slot_value {
Ok(v) if v <= 20 => Some(SlotId::try_from(0x81_u8 + v).unwrap()),
_ => None,
}
} else if slot.len() == 4 && slot.starts_with("0x") {
let slot_value = hex::decode(&slot[2..]).unwrap()[0];
Some(SlotId::try_from(slot_value).unwrap())
} else {
None
}
}

fn slot_validator(slot: &str) -> Result<(), String> {
match slot_parser(slot) {
Some(_) => Ok(()),
None => Err(String::from(
"Provided slot was not valid. Should be R1 - R20 or a raw hex identifier",
)),
}
}

/// This routine will generate a new PIV key and use the new key to create a CSR signer.
/// Then use this CSR signer to sign a random blob and then verify the signature.
fn main() {
env_logger::init();
let matches = Command::new("yk-generate-csr")
.version(env!("CARGO_PKG_VERSION"))
.author("Thanh Nguyen <thanh@timweri.me")
.about("A tool to create certificate signer on a Yubikey")
.arg(
Arg::new("slot")
.help("Numerical value for the slot on the yubikey to use for your private key")
.long("slot")
.short('s')
.required(true)
.validator(slot_validator)
.takes_value(true),
)
.arg(
Arg::new("management-key")
.help("Provision this slot with a new private key. The pin number must be passed as parameter here")
.default_value("010203040506070801020304050607080102030405060708")
.long("mgmkey")
.short('m')
.takes_value(true),
)
.arg(
Arg::new("pin")
.help("Provision this slot with a new private key. The pin number must be passed as parameter here")
.default_value("123456")
.long("pin")
.short('p')
.takes_value(true),
)
.get_matches();

let slot = match matches.value_of("slot") {
// We unwrap here because we have already run the validator above
Some(x) => slot_parser(x).unwrap(),
None => SlotId::Retired(RetiredSlotId::R17),
};
let pin = matches.value_of("pin").unwrap();
let mgm_key = &hex::decode(matches.value_of("management-key").unwrap()).unwrap();

let mut yk = Yubikey::new().unwrap();
yk.unlock(pin.as_bytes(), mgm_key).unwrap();

let csr = yk.generate_csr(&slot, "TestCSR").unwrap();

let (_, parsed_csr) =
x509_parser::certification_request::X509CertificationRequest::from_der(&csr).unwrap();
parsed_csr.verify_signature().unwrap();
}
13 changes: 8 additions & 5 deletions examples/yk-provision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ fn provision_new_key(
let mut yk = Yubikey::new().unwrap();
yk.unlock(pin.as_bytes(), mgm_key).unwrap();
match alg {
"p256" => match yk.provision::<p256::NistP256>(&slot, subject, policy, PinPolicy::Never) {
Ok(pk) => {
println!("New hardware backed SSH Public Key: {}", pk);
"p256" => {
println!("Using P256");
match yk.provision_p256(&slot, subject, policy, PinPolicy::Never) {
Ok(pk) => {
println!("New hardware backed SSH Public Key: {}", pk);
}
Err(e) => panic!("Could not provision device with new key: {:?}", e),
}
Err(e) => panic!("Could not provision device with new key: {:?}", e),
},
_ => {
println!("Using P384");
match yk.provision::<p384::NistP384>(&slot, subject, policy, PinPolicy::Never) {
match yk.provision_p384(&slot, subject, policy, PinPolicy::Never) {
Ok(pk) => {
println!("New hardware backed SSH Public Key: {}", pk);
}
Expand Down
20 changes: 14 additions & 6 deletions src/fido/generate/mozilla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::{
PrivateKey,
};

use ring::rand::SecureRandom;
use ring::rand::SystemRandom;
use ring::digest;
use ring::rand::{SecureRandom, SystemRandom};

use crate::fido::Error as FidoError;

Expand Down Expand Up @@ -47,12 +47,20 @@ pub fn generate_new_ssh_key(
};
manager.add_u2f_usb_hid_platform_transports();

let mut chall_bytes = [0u8; 32];
SystemRandom::new().fill(&mut chall_bytes).unwrap();
// This forms the challenge
let mut client_data = [0u8; 32];
// Fill it with random data because we don't support taking in
// challenge data at this point.
SystemRandom::new().fill(&mut client_data).unwrap();

// Hash the data because that is what will actually be signed
let client_data_digest = digest::digest(&digest::SHA256, &client_data);
let mut client_data_hash = [0u8; 32];
client_data_hash.copy_from_slice(client_data_digest.as_ref());

let origin = application.to_string();
let ctap_args = RegisterArgs {
client_data_hash: chall_bytes,
client_data_hash,
relying_party: RelyingParty {
id: origin.clone(),
name: None,
Expand Down Expand Up @@ -170,7 +178,7 @@ pub fn generate_new_ssh_key(
auth_data: raw_auth_data,
auth_data_sig,
intermediate,
challenge: chall_bytes.to_vec(),
challenge: client_data_hash.into(),
alg,
};

Expand Down
Loading