hsmtool/util/
secret.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5// This module provides functionality for handling secret keys in an HSM
6// (Hardware Security Module). It supports AES and generic secret keys,
7// allowing for key generation, import, export, wrapping, and unwrapping.
8//
9// The `Secret` enum defines the types of secrets supported, and methods are
10// provided to manage these keys using the PKCS#11 interface through the
11// `cryptoki` crate.
12
13use anyhow::{Ok, Result};
14use cryptoki::mechanism::Mechanism;
15use cryptoki::object::ObjectHandle;
16use cryptoki::session::Session;
17use std::str::FromStr;
18
19use crate::error::HsmError;
20use crate::util::attribute::{AttrData, AttributeMap, AttributeType, KeyType, ObjectClass};
21use crate::util::wrap::Wrap;
22
23pub enum Secret {
24    Aes,
25    GenericSecret,
26}
27
28impl Secret {
29    const TEMPLATE_AES: &str = r#"{
30        "CKA_CLASS": "CKO_SECRET_KEY",
31        "CKA_DERIVE": true,
32        "CKA_ENCRYPT": true,
33        "CKA_DECRYPT": true,
34        "CKA_KEY_TYPE": "CKK_AES",
35        "CKA_SENSITIVE": true,
36        "CKA_TOKEN": true,
37    }"#;
38
39    const TEMPLATE_GENERIC_SECRET: &str = r#"{
40        "CKA_CLASS": "CKO_SECRET_KEY",
41        "CKA_DERIVE": true,
42        "CKA_KEY_TYPE": "CKK_GENERIC_SECRET",
43        "CKA_SENSITIVE": true,
44        "CKA_TOKEN": true,
45    }"#;
46
47    fn key_template(&self) -> Result<AttributeMap> {
48        let template = match self {
49            Secret::Aes => Self::TEMPLATE_AES,
50            Secret::GenericSecret => Self::TEMPLATE_GENERIC_SECRET,
51        };
52        Ok(AttributeMap::from_str(template).expect("error in TEMPLATE"))
53    }
54
55    fn key_type(&self) -> Result<KeyType> {
56        match self {
57            Secret::Aes => Ok(KeyType::Aes),
58            Secret::GenericSecret => Ok(KeyType::GenericSecret),
59        }
60    }
61
62    fn keygen_mechanism(&self) -> Result<Mechanism<'_>> {
63        match self {
64            Secret::Aes => Ok(Mechanism::AesKeyGen),
65            Secret::GenericSecret => Ok(Mechanism::GenericSecretKeyGen),
66        }
67    }
68
69    fn check(&self, map: &AttributeMap) -> Result<()> {
70        let class: ObjectClass = map
71            .get(&AttributeType::Class)
72            .ok_or_else(|| HsmError::KeyError("Key does not have a class attribute".into()))?
73            .try_into()
74            .map_err(HsmError::AttributeError)?;
75        let key_type: KeyType = map
76            .get(&AttributeType::KeyType)
77            .ok_or_else(|| HsmError::KeyError("Key does not have a key type attribute".into()))?
78            .try_into()
79            .map_err(HsmError::AttributeError)?;
80        if class != ObjectClass::SecretKey || key_type != self.key_type()? {
81            Err(HsmError::KeyError("Key is not a secret key".into()))?;
82        }
83
84        let extractable: bool = map
85            .get(&AttributeType::Extractable)
86            .ok_or_else(|| HsmError::KeyError("Key does not have an extractable attribute".into()))?
87            .try_into()
88            .map_err(HsmError::AttributeError)?;
89        if !extractable {
90            Err(HsmError::KeyError("Key is not extractable".into()))?;
91        }
92        Ok(())
93    }
94
95    pub fn generate(
96        &self,
97        session: &Session,
98        id: AttrData,
99        label: AttrData,
100        attrs: Option<AttributeMap>,
101    ) -> Result<ObjectHandle> {
102        let mut template = self.key_template()?;
103        template.insert(AttributeType::Id, id);
104        template.insert(AttributeType::Label, label);
105        template.insert(AttributeType::ValueLen, 32u64.into());
106
107        if let Some(tpl) = &attrs {
108            template.merge(tpl.clone());
109        }
110
111        log::info!("template = {}", serde_json::to_string_pretty(&template)?);
112
113        let template = template.to_vec()?;
114        Ok(session.generate_key(&self.keygen_mechanism()?, &template)?)
115    }
116
117    pub fn import(
118        &self,
119        session: &Session,
120        id: AttrData,
121        label: AttrData,
122        key: Vec<u8>,
123        attrs: Option<AttributeMap>,
124    ) -> Result<ObjectHandle> {
125        let mut template = self.key_template()?;
126        template.insert(AttributeType::Id, id);
127        template.insert(AttributeType::Label, label);
128
129        if let Some(tpl) = &attrs {
130            template.merge(tpl.clone());
131        }
132
133        log::info!("template = {}", serde_json::to_string_pretty(&template)?);
134
135        template.insert(AttributeType::Value, AttrData::from(key.as_slice()));
136        let template = template.to_vec()?;
137        Ok(session.create_object(&template)?)
138    }
139
140    pub fn export(&self, session: &Session, object: ObjectHandle) -> Result<Vec<u8>> {
141        let map = AttributeMap::from_object(session, object)?;
142        self.check(&map)?;
143        let sensitive: bool = map
144            .get(&AttributeType::Sensitive)
145            .ok_or_else(|| HsmError::KeyError("Key does not have a sensitive attribute".into()))?
146            .try_into()
147            .map_err(HsmError::AttributeError)?;
148        if sensitive {
149            Err(HsmError::KeyError("Key is marked as sensitive".into()))?;
150        }
151
152        let key: Vec<u8> = map
153            .get(&AttributeType::Value)
154            .ok_or_else(|| HsmError::KeyError("Key does not have a value attribute".into()))?
155            .try_into()
156            .map_err(HsmError::AttributeError)?;
157        Ok(key)
158    }
159
160    pub fn wrap_key(
161        &self,
162        session: &Session,
163        object: ObjectHandle,
164        wrap_key_label: Option<&str>,
165        wrap_mechanism: &Wrap,
166    ) -> Result<Vec<u8>> {
167        let map = AttributeMap::from_object(session, object)?;
168        self.check(&map)?;
169        Ok(wrap_mechanism.wrap(session, object, wrap_key_label)?)
170    }
171
172    #[allow(clippy::too_many_arguments)]
173    pub fn unwrap_key(
174        &self,
175        session: &Session,
176        id: AttrData,
177        label: AttrData,
178        key: Vec<u8>,
179        attrs: Option<AttributeMap>,
180        wrap_key_label: Option<&str>,
181        wrap_mechanism: &Wrap,
182    ) -> Result<ObjectHandle> {
183        let mut template = self.key_template()?;
184        template.insert(AttributeType::Id, id);
185        template.insert(AttributeType::Label, label);
186
187        if let Some(tpl) = &attrs {
188            template.merge(tpl.clone());
189        }
190
191        log::info!("template = {}", serde_json::to_string_pretty(&template)?);
192        Ok(wrap_mechanism.unwrap(session, key.as_slice(), wrap_key_label, &template)?)
193    }
194}