tink_core/keyset/
handle.rs

1// Copyright 2020 The Tink-Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17//! Handle wrapper for keysets.
18
19use crate::{utils::wrap_err, TinkError};
20use std::{convert::TryFrom, sync::Arc};
21use tink_proto::{key_data::KeyMaterialType, prost::Message, Keyset, KeysetInfo};
22
23/// `Handle` provides access to a [`Keyset`] protobuf, to limit the exposure
24/// of actual protocol buffers that hold sensitive key material.
25pub struct Handle {
26    ks: Keyset,
27}
28
29impl Handle {
30    /// Create a keyset handle that contains a single fresh key generated according
31    /// to the given [`KeyTemplate`](tink_proto::KeyTemplate).
32    pub fn new(kt: &tink_proto::KeyTemplate) -> Result<Self, TinkError> {
33        let mut ksm = super::Manager::new();
34        ksm.rotate(kt)
35            .map_err(|e| wrap_err("keyset::Handle: cannot generate new keyset", e))?;
36        ksm.handle()
37            .map_err(|e| wrap_err("keyset::Handle: cannot get keyset handle", e))
38    }
39
40    /// Create a new instance of [`Handle`] using the given [`Keyset`] which does not contain any
41    /// secret key material.
42    pub fn new_with_no_secrets(ks: Keyset) -> Result<Self, TinkError> {
43        let h = Handle {
44            ks: validate_keyset(ks)?,
45        };
46        if h.has_secrets()? {
47            // If you need to do this, you have to use `tink_core::keyset::insecure::read()`
48            // instead.
49            return Err("importing unencrypted secret key material is forbidden".into());
50        }
51        Ok(h)
52    }
53
54    /// Attempt to create a [`Handle`] from an encrypted keyset obtained via a
55    /// [`Reader`](crate::keyset::Reader).
56    pub fn read<T>(reader: &mut T, master_key: Box<dyn crate::Aead>) -> Result<Self, TinkError>
57    where
58        T: crate::keyset::Reader,
59    {
60        Self::read_with_associated_data(reader, master_key, &[])
61    }
62
63    /// Attempt to create a [`Handle`] from an encrypted keyset obtained via a
64    /// [`Reader`](crate::keyset::Reader) using the provided associated data.
65    pub fn read_with_associated_data<T>(
66        reader: &mut T,
67        master_key: Box<dyn crate::Aead>,
68        associated_data: &[u8],
69    ) -> Result<Self, TinkError>
70    where
71        T: crate::keyset::Reader,
72    {
73        let encrypted_keyset = reader.read_encrypted()?;
74        let ks = decrypt(&encrypted_keyset, master_key, associated_data)?;
75        Ok(Handle {
76            ks: validate_keyset(ks)?,
77        })
78    }
79
80    /// Attempt to create a [`Handle`] from a keyset obtained via a
81    /// [`Reader`](crate::keyset::Reader).
82    pub fn read_with_no_secrets<T>(reader: &mut T) -> Result<Self, TinkError>
83    where
84        T: crate::keyset::Reader,
85    {
86        let ks = reader.read()?;
87        Handle::new_with_no_secrets(ks)
88    }
89
90    /// Return a [`Handle`] of the public keys if the managed keyset contains private keys.
91    pub fn public(&self) -> Result<Self, TinkError> {
92        let priv_keys = &self.ks.key;
93        let mut pub_keys = Vec::with_capacity(priv_keys.len());
94        for priv_key in priv_keys {
95            let priv_key_data = priv_key
96                .key_data
97                .as_ref()
98                .ok_or_else(|| TinkError::new("keyset::Handle: invalid keyset"))?;
99            let pub_key_data =
100                public_key_data(priv_key_data).map_err(|e| wrap_err("keyset::Handle", e))?;
101            pub_keys.push(tink_proto::keyset::Key {
102                key_data: Some(pub_key_data),
103                status: priv_key.status,
104                key_id: priv_key.key_id,
105                output_prefix_type: priv_key.output_prefix_type,
106            });
107        }
108        let ks = Keyset {
109            primary_key_id: self.ks.primary_key_id,
110            key: pub_keys,
111        };
112        Ok(Handle { ks })
113    }
114
115    /// Encrypts and writes the enclosed [`Keyset`].
116    pub fn write<T>(
117        &self,
118        writer: &mut T,
119        master_key: Box<dyn crate::Aead>,
120    ) -> Result<(), TinkError>
121    where
122        T: super::Writer,
123    {
124        self.write_with_associated_data(writer, master_key, &[])
125    }
126
127    /// Encrypts and writes the enclosed [`Keyset`] using the provided associated data.
128    pub fn write_with_associated_data<T>(
129        &self,
130        writer: &mut T,
131        master_key: Box<dyn crate::Aead>,
132        associated_data: &[u8],
133    ) -> Result<(), TinkError>
134    where
135        T: super::Writer,
136    {
137        let encrypted = encrypt(&self.ks, master_key, associated_data)?;
138        writer.write_encrypted(&encrypted)
139    }
140
141    /// Export the keyset in `h` to the given [`Writer`](super::Writer) returning an error if the
142    /// keyset contains secret key material.
143    pub fn write_with_no_secrets<T>(&self, w: &mut T) -> Result<(), TinkError>
144    where
145        T: super::Writer,
146    {
147        if self.has_secrets()? {
148            Err("exporting unencrypted secret key material is forbidden".into())
149        } else {
150            w.write(&self.ks)
151        }
152    }
153
154    /// Create a set of primitives corresponding to the keys with status=ENABLED in the keyset of
155    /// the given keyset [`Handle`], assuming all the corresponding key managers are present (keys
156    /// with status!=ENABLED are skipped).
157    ///
158    /// The returned set is usually later "wrapped" into a class that implements the corresponding
159    /// [`Primitive`](crate::Primitive) interface.
160    pub fn primitives(&self) -> Result<crate::primitiveset::PrimitiveSet, TinkError> {
161        self.primitives_with_key_manager(None)
162    }
163
164    /// Create a set of primitives corresponding to the keys with status=ENABLED in the keyset of
165    /// the given keyset [`Handle`], using the given key manager (instead of registered key
166    /// managers) for keys supported by it.  Keys not supported by the key manager are handled
167    /// by matching registered key managers (if present), and keys with status!=ENABLED are
168    /// skipped.
169    ///
170    /// This enables custom treatment of keys, for example providing extra context (e.g. credentials
171    /// for accessing keys managed by a KMS), or gathering custom monitoring/profiling
172    /// information.
173    ///
174    /// The returned set is usually later "wrapped" into a class that implements the corresponding
175    /// [`Primitive`](crate::Primitive)-interface.
176    pub fn primitives_with_key_manager(
177        &self,
178        km: Option<Arc<dyn crate::registry::KeyManager>>,
179    ) -> Result<crate::primitiveset::PrimitiveSet, TinkError> {
180        super::validate(&self.ks)
181            .map_err(|e| wrap_err("primitives_with_key_manager: invalid keyset", e))?;
182        let mut primitive_set = crate::primitiveset::PrimitiveSet::new();
183        for key in &self.ks.key {
184            if key.status != tink_proto::KeyStatusType::Enabled as i32 {
185                continue;
186            }
187            let key_data = key
188                .key_data
189                .as_ref()
190                .ok_or_else(|| TinkError::new("primitives_with_key_manager: no key_data"))?;
191            let primitive = match &km {
192                Some(km) if km.does_support(&key_data.type_url) => km.primitive(&key_data.value),
193                Some(_) | None => crate::registry::primitive_from_key_data(key_data),
194            }
195            .map_err(|e| {
196                wrap_err(
197                    "primitives_with_key_manager: cannot get primitive from key",
198                    e,
199                )
200            })?;
201
202            let entry = primitive_set
203                .add(primitive, key)
204                .map_err(|e| wrap_err("primitives_with_key_manager: cannot add primitive", e))?;
205            if key.key_id == self.ks.primary_key_id {
206                primitive_set.primary = Some(entry.clone());
207            }
208        }
209        Ok(primitive_set)
210    }
211
212    /// Check if the keyset handle contains any key material considered secret.  Both symmetric keys
213    /// and the private key of an asymmetric crypto system are considered secret keys. Also
214    /// returns true when encountering any errors.
215    fn has_secrets(&self) -> Result<bool, TinkError> {
216        let mut result = false;
217        for k in &self.ks.key {
218            match &k.key_data {
219                None => return Err("invalid keyset".into()),
220                Some(kd) => match KeyMaterialType::try_from(kd.key_material_type) {
221                    Ok(KeyMaterialType::UnknownKeymaterial) => result = true,
222                    Ok(KeyMaterialType::Symmetric) => result = true,
223                    Ok(KeyMaterialType::AsymmetricPrivate) => result = true,
224                    Ok(KeyMaterialType::AsymmetricPublic) => {}
225                    Ok(KeyMaterialType::Remote) => {}
226                    Err(_) => return Err("invalid key material type".into()),
227                },
228            }
229        }
230        Ok(result)
231    }
232
233    /// Return [`KeysetInfo`] representation of the managed keyset. The result does not
234    /// contain any sensitive key material.
235    pub fn keyset_info(&self) -> KeysetInfo {
236        get_keyset_info(&self.ks)
237    }
238
239    /// Consume the `Handle` and return the enclosed [`Keyset`].
240    pub(crate) fn into_inner(self) -> Keyset {
241        self.ks
242    }
243
244    /// Return a copy of the enclosed [`Keyset`]; for internal
245    /// use only.
246    #[cfg(feature = "insecure")]
247    #[cfg_attr(docsrs, doc(cfg(feature = "insecure")))]
248    pub(crate) fn clone_keyset(&self) -> Keyset {
249        self.ks.clone()
250    }
251
252    /// Create a `Handle` from a [`Keyset`].  Implemented as a standalone method rather than
253    /// as an `impl` of the `From` trait so visibility can be restricted.
254    pub(crate) fn from_keyset(ks: Keyset) -> Result<Self, TinkError> {
255        Ok(Handle {
256            ks: validate_keyset(ks)?,
257        })
258    }
259}
260
261/// Check that a [`Keyset`] is valid.
262fn validate_keyset(ks: Keyset) -> Result<Keyset, TinkError> {
263    for k in &ks.key {
264        match &k.key_data {
265            None if k.status == tink_proto::KeyStatusType::Destroyed as i32 => {}
266            None => return Err("invalid keyset".into()),
267            Some(kd) => match KeyMaterialType::try_from(kd.key_material_type) {
268                Ok(_) => {}
269                Err(_) => return Err("invalid key material type".into()),
270            },
271        }
272    }
273    Ok(ks)
274}
275
276/// Extract the public key data corresponding to private key data.
277fn public_key_data(priv_key_data: &tink_proto::KeyData) -> Result<tink_proto::KeyData, TinkError> {
278    if priv_key_data.key_material_type
279        != tink_proto::key_data::KeyMaterialType::AsymmetricPrivate as i32
280    {
281        return Err("keyset::Handle: keyset contains a non-private key".into());
282    }
283    let km = crate::registry::get_key_manager(&priv_key_data.type_url)?;
284
285    if !km.supports_private_keys() {
286        return Err(format!(
287            "keyset::Handle: {} does not belong to a KeyManager that handles private keys",
288            priv_key_data.type_url
289        )
290        .into());
291    }
292    km.public_key_data(&priv_key_data.value)
293}
294
295/// Decrypt a keyset with a master key.
296fn decrypt(
297    encrypted_keyset: &tink_proto::EncryptedKeyset,
298    master_key: Box<dyn crate::Aead>,
299    associated_data: &[u8],
300) -> Result<Keyset, TinkError> {
301    let decrypted = master_key
302        .decrypt(&encrypted_keyset.encrypted_keyset, associated_data)
303        .map_err(|e| wrap_err("keyset::Handle: decryption failed", e))?;
304    Keyset::decode(&decrypted[..]).map_err(|_| TinkError::new("keyset::Handle:: invalid keyset"))
305}
306
307/// Encrypt a keyset with a master key.
308fn encrypt(
309    keyset: &Keyset,
310    master_key: Box<dyn crate::Aead>,
311    associated_data: &[u8],
312) -> Result<tink_proto::EncryptedKeyset, TinkError> {
313    let mut serialized_keyset = vec![];
314    keyset
315        .encode(&mut serialized_keyset)
316        .map_err(|e| wrap_err("keyset::Handle: invalid keyset", e))?;
317    let encrypted = master_key
318        .encrypt(&serialized_keyset, associated_data)
319        .map_err(|e| wrap_err("keyset::Handle: encrypted failed", e))?;
320    Ok(tink_proto::EncryptedKeyset {
321        encrypted_keyset: encrypted,
322        keyset_info: Some(get_keyset_info(keyset)),
323    })
324}
325
326/// Return a [`KeysetInfo`] from a [`Keyset`] protobuf.
327fn get_keyset_info(keyset: &Keyset) -> KeysetInfo {
328    let n_key = keyset.key.len();
329    let mut key_infos = Vec::with_capacity(n_key);
330    for key in &keyset.key {
331        key_infos.push(get_key_info(key));
332    }
333    KeysetInfo {
334        primary_key_id: keyset.primary_key_id,
335        key_info: key_infos,
336    }
337}
338
339/// Return a [`KeyInfo`](tink_proto::keyset_info::KeyInfo) from a
340/// [`Key`](tink_proto::keyset::Key) protobuf.
341fn get_key_info(key: &tink_proto::keyset::Key) -> tink_proto::keyset_info::KeyInfo {
342    tink_proto::keyset_info::KeyInfo {
343        type_url: match &key.key_data {
344            Some(kd) => kd.type_url.clone(),
345            None => "".to_string(),
346        },
347        status: key.status,
348        key_id: key.key_id,
349        output_prefix_type: key.output_prefix_type,
350    }
351}
352
353impl std::fmt::Debug for Handle {
354    /// Return a string representation of the managed keyset.
355    /// The result does not contain any sensitive key material.
356    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357        write!(f, "{:?}", get_keyset_info(&self.ks))
358    }
359}