blob: 21b89678a380f23beaf20ab37e8674d17cbd1eda [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use erased_serde::Serialize;
use std::any::Any;
use std::path::PathBuf;
use structopt::StructOpt;
use opentitanlib::app::command::CommandDispatch;
use opentitanlib::app::TransportWrapper;
use opentitanlib::crypto::rsa::{
Exponent, Modulus, N0Inv, RsaPrivateKey, RsaPublicKey, Signature, RR,
};
use opentitanlib::crypto::sha256::Sha256Digest;
use opentitanlib::util::parse_int::ParseInt;
#[derive(serde::Serialize)]
pub struct RsaKeyInfo {
pub key_num_bits: usize,
pub modulus: Modulus,
pub public_exponent: Exponent,
pub n0_inv: N0Inv,
pub rr: RR,
}
#[derive(serde::Serialize)]
pub struct RsaKeyInfoInWords {
pub key_num_bits: usize,
pub modulus: Vec<String>,
pub public_exponent: Vec<String>,
pub n0_inv: Vec<String>,
pub rr: Vec<String>,
}
/// Show public information of a private or public RSA key
#[derive(Debug, StructOpt)]
pub struct RsaKeyShowCommand {
#[structopt(
name = "DER_FILE",
help = "RSA public or private key file in DER format"
)]
der_file: PathBuf,
}
impl CommandDispatch for RsaKeyShowCommand {
fn run(
&self,
_context: &dyn Any,
_transport: &TransportWrapper,
) -> Result<Option<Box<dyn Serialize>>> {
let key = match RsaPublicKey::from_pkcs1_der_file(&self.der_file) {
Ok(key) => Ok(key),
Err(_) => match RsaPrivateKey::from_pkcs8_der_file(&self.der_file) {
Ok(key) => Ok(RsaPublicKey::from_private_key(&key)),
Err(e) => Err(e),
},
}?;
Ok(Some(Box::new(RsaKeyInfo {
key_num_bits: key.modulus_num_bits(),
modulus: key.modulus(),
public_exponent: key.exponent(),
n0_inv: key.n0_inv()?,
rr: key.rr(),
})))
}
}
/// Generate a 3072-bit RSA public private key pair with exponent 65537. RSA private key will
/// be written to <OUTPUT_DIR>/<BASENAME>.der and RSA public key will be written to
/// <OUTPUT_DIR>/<BASENAME>.pub.der
#[derive(Debug, StructOpt)]
pub struct RsaKeyGenerateCommand {
#[structopt(name = "OUTPUT_DIR", help = "Output directory")]
output_dir: PathBuf,
#[structopt(name = "BASENAME", help = "Basename for the generated key pair")]
basename: String,
}
impl CommandDispatch for RsaKeyGenerateCommand {
fn run(
&self,
_context: &dyn Any,
_transport: &TransportWrapper,
) -> Result<Option<Box<dyn Serialize>>> {
let private_key = RsaPrivateKey::new()?;
let mut der_file = self.output_dir.to_owned();
der_file.push(&self.basename);
der_file.set_extension("der");
private_key.to_pkcs8_der_file(&der_file)?;
der_file.set_extension("pub.der");
RsaPublicKey::from_private_key(&private_key).to_pkcs1_der_file(&der_file)?;
Ok(None)
}
}
#[derive(Debug, StructOpt, CommandDispatch)]
pub enum RsaKeySubcommands {
Show(RsaKeyShowCommand),
Generate(RsaKeyGenerateCommand),
}
#[derive(serde::Serialize)]
pub struct RsaSignResult {
pub digest: String,
pub signature: String,
}
#[derive(Debug, StructOpt)]
pub struct RsaSignCommand {
#[structopt(
name = "DER_FILE",
parse(try_from_str=RsaPrivateKey::from_pkcs8_der_file),
help = "RSA private key file in PKCS#1 DER format"
)]
private_key: RsaPrivateKey,
#[structopt(
name = "SHA256_DIGEST",
parse(try_from_str=ParseInt::from_str),
help = "SHA256 digest of the message"
)]
digest: Sha256Digest,
#[structopt(
name = "output",
short,
long,
help = "File name to write the signature to"
)]
output: Option<PathBuf>,
}
impl CommandDispatch for RsaSignCommand {
fn run(
&self,
_context: &dyn Any,
_transport: &TransportWrapper,
) -> Result<Option<Box<dyn Serialize>>> {
let signature = self.private_key.sign(&self.digest)?;
if let Some(output) = &self.output {
signature.write_to_file(output)?;
}
Ok(Some(Box::new(RsaSignResult {
digest: self.digest.to_string(),
signature: signature.to_string(),
})))
}
}
#[derive(Debug, StructOpt)]
pub struct RsaVerifyCommand {
#[structopt(name = "KEY", help = "Key file in DER format")]
der_file: PathBuf,
#[structopt(
name = "SHA256_DIGEST",
help = "SHA256 digest of the message as a hex string (big-endian), i.e. 0x..."
)]
digest: String,
#[structopt(
name = "SIGNATURE",
help = "Signature to be verified as a hex string (big-endian), i.e. 0x..."
)]
signature: String,
}
impl CommandDispatch for RsaVerifyCommand {
fn run(
&self,
_context: &dyn Any,
_transport: &TransportWrapper,
) -> Result<Option<Box<dyn Serialize>>> {
let key = RsaPublicKey::from_pkcs1_der_file(&self.der_file)?;
let digest = Sha256Digest::from_str(&self.digest)?;
let signature = Signature::from_str(&self.signature)?;
key.verify(&digest, &signature)?;
Ok(None)
}
}
#[derive(Debug, StructOpt, CommandDispatch)]
/// RSA commands.
pub enum Rsa {
Key(RsaKeySubcommands),
Sign(RsaSignCommand),
Verify(RsaVerifyCommand),
}