use std::{fs::File, io::prelude::*, path::PathBuf};
use clap::Parser;
use cosmian_kms_client::{
KmsClient,
cosmian_kmip::kmip_2_1::{kmip_operations::Encrypt, kmip_types::UniqueIdentifier},
read_bytes_from_file,
reexport::cosmian_kms_client_utils::rsa_utils::{HashFn, RsaEncryptionAlgorithm},
};
use zeroize::Zeroizing;
use crate::{
actions::kms::{console, labels::CERTIFICATE_ID, shared::get_key_uid},
error::result::{KmsCliResult, KmsCliResultHelper},
};
#[derive(Parser, Default, Debug)]
pub struct EncryptCertificateAction {
#[clap(required = true, name = "FILE")]
pub(crate) input_file: PathBuf,
#[clap(long = CERTIFICATE_ID, short = 'c', group = "key-tags")]
pub(crate) certificate_id: Option<String>,
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
pub(crate) tags: Option<Vec<String>>,
#[clap(required = false, long, short = 'o')]
pub(crate) output_file: Option<PathBuf>,
#[clap(required = false, long, short = 'a')]
pub(crate) authentication_data: Option<String>,
#[clap(long, short = 'e', verbatim_doc_comment)]
pub(crate) encryption_algorithm: Option<RsaEncryptionAlgorithm>,
}
impl EncryptCertificateAction {
pub async fn run(&self, kms_rest_client: KmsClient) -> KmsCliResult<()> {
let data = Zeroizing::from(read_bytes_from_file(&self.input_file)?);
let id = get_key_uid(
self.certificate_id.as_ref(),
self.tags.as_ref(),
CERTIFICATE_ID,
)?;
let authenticated_encryption_additional_data = self
.authentication_data
.as_ref()
.map(|auth_data| auth_data.as_bytes().to_vec());
let cryptographic_parameters =
self.encryption_algorithm
.as_ref()
.map(|encryption_algorithm| {
encryption_algorithm.to_cryptographic_parameters(HashFn::Sha256)
});
let encrypt_request = Encrypt {
unique_identifier: Some(UniqueIdentifier::TextString(id.clone())),
data: Some(data),
authenticated_encryption_additional_data,
cryptographic_parameters,
..Encrypt::default()
};
let encrypt_response = kms_rest_client
.encrypt(encrypt_request)
.await
.with_context(|| "Can't execute the query on the kms server")?;
let ciphertext = encrypt_response
.data
.context("The encrypted data are empty")?;
let output_file = self
.output_file
.clone()
.unwrap_or_else(|| self.input_file.with_extension("enc"));
let mut buffer =
File::create(&output_file).with_context(|| "failed to write the encrypted file")?;
buffer
.write_all(&ciphertext)
.with_context(|| "failed to write the encrypted file")?;
console::Stdout::new(&format!(
"The encrypted file is available at {}",
output_file.display()
))
.write()?;
Ok(())
}
}