diff --git a/Cargo.lock b/Cargo.lock index 2dd5843..20c0eb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,6 +1055,7 @@ dependencies = [ "rand", "rand_core 0.6.4", "rpassword", + "rsa", "serde", "serde_json", "serde_with", diff --git a/Cargo.toml b/Cargo.toml index 8833303..740f3bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,4 @@ yubihsm = { git = "https://github.com/oxidecomputer/yubihsm.rs", branch = "sessi zeroize = "1.8.1" zeroize_derive = "1.4.2" glob = "0.3.2" +rsa = "0.9.3" diff --git a/src/bin/dcsr-pubkey.rs b/src/bin/dcsr-pubkey.rs new file mode 100644 index 0000000..3d8a78d --- /dev/null +++ b/src/bin/dcsr-pubkey.rs @@ -0,0 +1,87 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use anyhow::{Context, Result}; +use clap::{Parser, ValueEnum}; +use oks::config::DcsrSpec; +use rsa::{ + pkcs1::{EncodeRsaPublicKey, LineEnding}, + pkcs8::EncodePublicKey, +}; +use std::{ + fs, + io::{self, Write}, + path::PathBuf, +}; + +#[derive(ValueEnum, Copy, Clone, Debug, Default)] +enum Format { + Pkcs1, + #[default] + Spki, +} + +#[derive(ValueEnum, Copy, Clone, Debug, Default)] +enum Encoding { + Der, + #[default] + Pem, +} + +/// Extract the RSA public key from a DcsrSpec and dump it to stdout in the +/// requested format and encoding. +#[derive(Parser, Debug)] +struct Args { + /// The encoding used to serialize the public key. + #[clap(default_value_t, long, value_enum)] + encoding: Encoding, + + /// The format use to represent the public key. + #[clap(default_value_t, long, value_enum)] + format: Format, + + /// Path to a DcsrSpec file. + dcsr_file: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + let json = fs::read_to_string(&args.dcsr_file) + .context("Reading file name argument to string")?; + + let spec: DcsrSpec = serde_json::from_str(&json) + .context("Failed to deserialize DcsrSpec from json")?; + + let pub_key = spec.dcsr.debug_public_key; + + match args.encoding { + Encoding::Der => { + let der = match args.format { + Format::Pkcs1 => pub_key.to_pkcs1_der().context( + "Get DER encoded, PKCS#1 formatted RSA public key", + )?, + Format::Spki => pub_key.to_public_key_der().context( + "Get DER encoded, SPKI formatted RSA public key", + )?, + }; + io::stdout() + .write_all(der.as_bytes()) + .context("write encoded public key to stdout") + } + Encoding::Pem => { + let pem = match args.format { + Format::Pkcs1 => pub_key + .to_pkcs1_pem(LineEnding::default()) + .context("Get PEM encoded PKCS#1 from RSA public key")?, + Format::Spki => pub_key + .to_public_key_pem(LineEnding::default()) + .context("Get SPKI PEM from RSA public key")?, + }; + io::stdout() + .write_all(pem.as_bytes()) + .context("write encoded public key to stdout") + } + } +}