use crate::algorithm::{Algorithm, Curve, Hash, KeyUsage};
use crate::context::Context;
use crate::error::{self, check, Result};
use crate::ffi;
use crate::key::Key;
use std::ffi::CString;
use std::ptr;
use super::ProtectConfig;
pub struct SubkeyBuilder {
pub(crate) alg_str: String,
pub(crate) bits: Option<u32>,
pub(crate) hash: Option<Hash>,
pub(crate) dsa_qbits: Option<u32>,
pub(crate) curve: Option<Curve>,
pub(crate) expiration: Option<u32>,
pub(crate) usages: Vec<KeyUsage>,
pub(crate) protection: Option<ProtectConfig>,
pub(crate) request_password: bool,
}
impl SubkeyBuilder {
pub fn new(alg: Algorithm) -> Self {
Self::new_str(alg.as_str())
}
pub fn new_str(alg: &str) -> Self {
SubkeyBuilder {
alg_str: alg.to_string(),
bits: None,
hash: None,
dsa_qbits: None,
curve: None,
expiration: None,
usages: Vec::new(),
protection: None,
request_password: false,
}
}
pub fn bits(mut self, n: u32) -> Self {
self.bits = Some(n);
self
}
pub fn hash(mut self, h: Hash) -> Self {
self.hash = Some(h);
self
}
pub fn dsa_qbits(mut self, n: u32) -> Self {
self.dsa_qbits = Some(n);
self
}
pub fn curve(mut self, c: Curve) -> Self {
self.curve = Some(c);
self
}
pub fn expiration(mut self, seconds: u32) -> Self {
self.expiration = Some(seconds);
self
}
pub fn add_usage(mut self, u: KeyUsage) -> Self {
self.usages.push(u);
self
}
pub fn build<'ctx>(self, ctx: &'ctx Context, primary: &Key<'_>) -> Result<Key<'ctx>> {
self.build_with_ffi(ctx, primary.handle)
}
pub(crate) fn build_with_ffi<'ctx>(
self,
ctx: &'ctx Context,
primary_handle: ffi::rnp_key_handle_t,
) -> Result<Key<'ctx>> {
let alg_c = CString::new(self.alg_str.as_str()).unwrap();
let mut op: ffi::rnp_op_generate_t = ptr::null_mut();
unsafe {
check(ffi::rnp_op_generate_subkey_create(
&mut op,
ctx.ffi,
primary_handle,
alg_c.as_ptr(),
))?;
}
if let Err(e) = unsafe { apply_subkey_setters(op, &self) } {
unsafe {
let _ = ffi::rnp_op_generate_destroy(op);
}
return Err(e);
}
unsafe {
check(ffi::rnp_op_generate_execute(op))?;
let mut handle: ffi::rnp_key_handle_t = ptr::null_mut();
check(ffi::rnp_op_generate_get_key(op, &mut handle))?;
let _ = ffi::rnp_op_generate_destroy(op);
if handle.is_null() {
return Err(error::Error::NullPointer);
}
Ok(Key::from_handle(handle))
}
}
}
unsafe fn apply_subkey_setters(op: ffi::rnp_op_generate_t, b: &SubkeyBuilder) -> Result<()> {
unsafe {
if let Some(n) = b.bits {
check(ffi::rnp_op_generate_set_bits(op, n))?;
}
if let Some(h) = b.hash {
let c = CString::new(h.as_str()).unwrap();
check(ffi::rnp_op_generate_set_hash(op, c.as_ptr()))?;
}
if let Some(q) = b.dsa_qbits {
check(ffi::rnp_op_generate_set_dsa_qbits(op, q))?;
}
if let Some(c) = b.curve {
let cs = CString::new(c.as_str()).unwrap();
check(ffi::rnp_op_generate_set_curve(op, cs.as_ptr()))?;
}
if let Some(exp) = b.expiration {
check(ffi::rnp_op_generate_set_expiration(op, exp))?;
}
for u in &b.usages {
let c = CString::new(u.as_str()).unwrap();
check(ffi::rnp_op_generate_add_usage(op, c.as_ptr()))?;
}
if let Some(cfg) = &b.protection {
super::apply_protection(op, cfg)?;
}
if b.request_password {
check(ffi::rnp_op_generate_set_request_password(op, true))?;
}
Ok(())
}
}