use crate::{utils::wrap_err, TinkError};
use std::{convert::TryFrom, sync::Arc};
use tink_proto::{key_data::KeyMaterialType, prost::Message, Keyset, KeysetInfo};
pub struct Handle {
ks: Keyset,
}
impl Handle {
pub fn new(kt: &tink_proto::KeyTemplate) -> Result<Self, TinkError> {
let mut ksm = super::Manager::new();
ksm.rotate(kt)
.map_err(|e| wrap_err("keyset::Handle: cannot generate new keyset", e))?;
ksm.handle()
.map_err(|e| wrap_err("keyset::Handle: cannot get keyset handle", e))
}
pub fn new_with_no_secrets(ks: Keyset) -> Result<Self, TinkError> {
let h = Handle {
ks: validate_keyset(ks)?,
};
if h.has_secrets()? {
return Err("importing unencrypted secret key material is forbidden".into());
}
Ok(h)
}
pub fn read<T>(reader: &mut T, master_key: Box<dyn crate::Aead>) -> Result<Self, TinkError>
where
T: crate::keyset::Reader,
{
Self::read_with_associated_data(reader, master_key, &[])
}
pub fn read_with_associated_data<T>(
reader: &mut T,
master_key: Box<dyn crate::Aead>,
associated_data: &[u8],
) -> Result<Self, TinkError>
where
T: crate::keyset::Reader,
{
let encrypted_keyset = reader.read_encrypted()?;
let ks = decrypt(&encrypted_keyset, master_key, associated_data)?;
Ok(Handle {
ks: validate_keyset(ks)?,
})
}
pub fn read_with_no_secrets<T>(reader: &mut T) -> Result<Self, TinkError>
where
T: crate::keyset::Reader,
{
let ks = reader.read()?;
Handle::new_with_no_secrets(ks)
}
pub fn public(&self) -> Result<Self, TinkError> {
let priv_keys = &self.ks.key;
let mut pub_keys = Vec::with_capacity(priv_keys.len());
for priv_key in priv_keys {
let priv_key_data = priv_key
.key_data
.as_ref()
.ok_or_else(|| TinkError::new("keyset::Handle: invalid keyset"))?;
let pub_key_data =
public_key_data(priv_key_data).map_err(|e| wrap_err("keyset::Handle", e))?;
pub_keys.push(tink_proto::keyset::Key {
key_data: Some(pub_key_data),
status: priv_key.status,
key_id: priv_key.key_id,
output_prefix_type: priv_key.output_prefix_type,
});
}
let ks = Keyset {
primary_key_id: self.ks.primary_key_id,
key: pub_keys,
};
Ok(Handle { ks })
}
pub fn write<T>(
&self,
writer: &mut T,
master_key: Box<dyn crate::Aead>,
) -> Result<(), TinkError>
where
T: super::Writer,
{
self.write_with_associated_data(writer, master_key, &[])
}
pub fn write_with_associated_data<T>(
&self,
writer: &mut T,
master_key: Box<dyn crate::Aead>,
associated_data: &[u8],
) -> Result<(), TinkError>
where
T: super::Writer,
{
let encrypted = encrypt(&self.ks, master_key, associated_data)?;
writer.write_encrypted(&encrypted)
}
pub fn write_with_no_secrets<T>(&self, w: &mut T) -> Result<(), TinkError>
where
T: super::Writer,
{
if self.has_secrets()? {
Err("exporting unencrypted secret key material is forbidden".into())
} else {
w.write(&self.ks)
}
}
pub fn primitives(&self) -> Result<crate::primitiveset::PrimitiveSet, TinkError> {
self.primitives_with_key_manager(None)
}
pub fn primitives_with_key_manager(
&self,
km: Option<Arc<dyn crate::registry::KeyManager>>,
) -> Result<crate::primitiveset::PrimitiveSet, TinkError> {
super::validate(&self.ks)
.map_err(|e| wrap_err("primitives_with_key_manager: invalid keyset", e))?;
let mut primitive_set = crate::primitiveset::PrimitiveSet::new();
for key in &self.ks.key {
if key.status != tink_proto::KeyStatusType::Enabled as i32 {
continue;
}
let key_data = key
.key_data
.as_ref()
.ok_or_else(|| TinkError::new("primitives_with_key_manager: no key_data"))?;
let primitive = match &km {
Some(km) if km.does_support(&key_data.type_url) => km.primitive(&key_data.value),
Some(_) | None => crate::registry::primitive_from_key_data(key_data),
}
.map_err(|e| {
wrap_err(
"primitives_with_key_manager: cannot get primitive from key",
e,
)
})?;
let entry = primitive_set
.add(primitive, key)
.map_err(|e| wrap_err("primitives_with_key_manager: cannot add primitive", e))?;
if key.key_id == self.ks.primary_key_id {
primitive_set.primary = Some(entry.clone());
}
}
Ok(primitive_set)
}
fn has_secrets(&self) -> Result<bool, TinkError> {
let mut result = false;
for k in &self.ks.key {
match &k.key_data {
None => return Err("invalid keyset".into()),
Some(kd) => match KeyMaterialType::try_from(kd.key_material_type) {
Ok(KeyMaterialType::UnknownKeymaterial) => result = true,
Ok(KeyMaterialType::Symmetric) => result = true,
Ok(KeyMaterialType::AsymmetricPrivate) => result = true,
Ok(KeyMaterialType::AsymmetricPublic) => {}
Ok(KeyMaterialType::Remote) => {}
Err(_) => return Err("invalid key material type".into()),
},
}
}
Ok(result)
}
pub fn keyset_info(&self) -> KeysetInfo {
get_keyset_info(&self.ks)
}
pub(crate) fn into_inner(self) -> Keyset {
self.ks
}
#[cfg(feature = "insecure")]
#[cfg_attr(docsrs, doc(cfg(feature = "insecure")))]
pub(crate) fn clone_keyset(&self) -> Keyset {
self.ks.clone()
}
pub(crate) fn from_keyset(ks: Keyset) -> Result<Self, TinkError> {
Ok(Handle {
ks: validate_keyset(ks)?,
})
}
}
fn validate_keyset(ks: Keyset) -> Result<Keyset, TinkError> {
for k in &ks.key {
match &k.key_data {
None if k.status == tink_proto::KeyStatusType::Destroyed as i32 => {}
None => return Err("invalid keyset".into()),
Some(kd) => match KeyMaterialType::try_from(kd.key_material_type) {
Ok(_) => {}
Err(_) => return Err("invalid key material type".into()),
},
}
}
Ok(ks)
}
fn public_key_data(priv_key_data: &tink_proto::KeyData) -> Result<tink_proto::KeyData, TinkError> {
if priv_key_data.key_material_type
!= tink_proto::key_data::KeyMaterialType::AsymmetricPrivate as i32
{
return Err("keyset::Handle: keyset contains a non-private key".into());
}
let km = crate::registry::get_key_manager(&priv_key_data.type_url)?;
if !km.supports_private_keys() {
return Err(format!(
"keyset::Handle: {} does not belong to a KeyManager that handles private keys",
priv_key_data.type_url
)
.into());
}
km.public_key_data(&priv_key_data.value)
}
fn decrypt(
encrypted_keyset: &tink_proto::EncryptedKeyset,
master_key: Box<dyn crate::Aead>,
associated_data: &[u8],
) -> Result<Keyset, TinkError> {
let decrypted = master_key
.decrypt(&encrypted_keyset.encrypted_keyset, associated_data)
.map_err(|e| wrap_err("keyset::Handle: decryption failed", e))?;
Keyset::decode(&decrypted[..]).map_err(|_| TinkError::new("keyset::Handle:: invalid keyset"))
}
fn encrypt(
keyset: &Keyset,
master_key: Box<dyn crate::Aead>,
associated_data: &[u8],
) -> Result<tink_proto::EncryptedKeyset, TinkError> {
let mut serialized_keyset = vec![];
keyset
.encode(&mut serialized_keyset)
.map_err(|e| wrap_err("keyset::Handle: invalid keyset", e))?;
let encrypted = master_key
.encrypt(&serialized_keyset, associated_data)
.map_err(|e| wrap_err("keyset::Handle: encrypted failed", e))?;
Ok(tink_proto::EncryptedKeyset {
encrypted_keyset: encrypted,
keyset_info: Some(get_keyset_info(keyset)),
})
}
fn get_keyset_info(keyset: &Keyset) -> KeysetInfo {
let n_key = keyset.key.len();
let mut key_infos = Vec::with_capacity(n_key);
for key in &keyset.key {
key_infos.push(get_key_info(key));
}
KeysetInfo {
primary_key_id: keyset.primary_key_id,
key_info: key_infos,
}
}
fn get_key_info(key: &tink_proto::keyset::Key) -> tink_proto::keyset_info::KeyInfo {
tink_proto::keyset_info::KeyInfo {
type_url: match &key.key_data {
Some(kd) => kd.type_url.clone(),
None => "".to_string(),
},
status: key.status,
key_id: key.key_id,
output_prefix_type: key.output_prefix_type,
}
}
impl std::fmt::Debug for Handle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", get_keyset_info(&self.ks))
}
}