hsmtool/util/
wrap.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 wrapping and unwrapping cryptographic
6// keys using different mechanisms. It supports AES key wrap, AES key wrap with
7// padding, and RSA PKCS OAEP.
8
9use anyhow::{Ok, Result};
10use clap::ValueEnum;
11use cryptoki::mechanism::rsa::{PkcsMgfType, PkcsOaepParams, PkcsOaepSource};
12use cryptoki::mechanism::vendor_defined::{CKM_VENDOR_DEFINED, VendorDefinedMechanism};
13use cryptoki::mechanism::{Mechanism, MechanismType};
14use cryptoki::object::{Attribute, ObjectHandle};
15use cryptoki::session::Session;
16use serde::{Deserialize, Serialize};
17use strum::{Display, EnumString};
18
19use crate::error::HsmError;
20use crate::util::attribute::{AttributeMap, AttributeType, KeyType, ObjectClass};
21use crate::util::helper;
22
23/// The wrapping mechanism to use.
24#[derive(
25    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, EnumString, ValueEnum,
26)]
27#[strum(ascii_case_insensitive)]
28pub enum Wrap {
29    AesKeyWrap,
30    AesKeyWrapPad,
31    RsaPkcs,
32    RsaPkcsOaep,
33    VendorThalesAesKw,
34    VendorThalesAesKwp,
35}
36
37/// The wrapping mechanism to use for private keys.
38#[derive(
39    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, EnumString, ValueEnum,
40)]
41#[strum(ascii_case_insensitive)]
42pub enum WrapPrivateKey {
43    AesKeyWrap,
44    AesKeyWrapPad,
45    VendorThalesAesKw,
46    VendorThalesAesKwp,
47}
48
49impl From<WrapPrivateKey> for Wrap {
50    fn from(wrap: WrapPrivateKey) -> Self {
51        match wrap {
52            WrapPrivateKey::AesKeyWrap => Wrap::AesKeyWrap,
53            WrapPrivateKey::AesKeyWrapPad => Wrap::AesKeyWrapPad,
54            WrapPrivateKey::VendorThalesAesKw => Wrap::VendorThalesAesKw,
55            WrapPrivateKey::VendorThalesAesKwp => Wrap::VendorThalesAesKwp,
56        }
57    }
58}
59
60impl Wrap {
61    // Vendor defined mechanisms for Thales AES key wrap.
62    const CKM_THALES_AES_KW: u64 = CKM_VENDOR_DEFINED | 0x00000170;
63    const CKM_THALES_AES_KWP: u64 = CKM_VENDOR_DEFINED | 0x00000171;
64
65    pub fn mechanism(&self) -> Result<Mechanism<'_>> {
66        match self {
67            Wrap::AesKeyWrap => Ok(Mechanism::AesKeyWrap),
68            Wrap::AesKeyWrapPad => Ok(Mechanism::AesKeyWrapPad),
69            Wrap::RsaPkcs => Ok(Mechanism::RsaPkcs),
70            Wrap::RsaPkcsOaep => Ok(Mechanism::RsaPkcsOaep(PkcsOaepParams::new(
71                MechanismType::SHA256,
72                PkcsMgfType::MGF1_SHA256,
73                PkcsOaepSource::empty(),
74            ))),
75            Wrap::VendorThalesAesKw => {
76                Ok(Mechanism::VendorDefined(VendorDefinedMechanism::new::<()>(
77                    MechanismType::new_vendor_defined(Wrap::CKM_THALES_AES_KW)?,
78                    None,
79                )))
80            }
81            Wrap::VendorThalesAesKwp => {
82                Ok(Mechanism::VendorDefined(VendorDefinedMechanism::new::<()>(
83                    MechanismType::new_vendor_defined(Wrap::CKM_THALES_AES_KWP)?,
84                    None,
85                )))
86            }
87        }
88    }
89
90    pub fn wrapping_key(&self, session: &Session, label: Option<&str>) -> Result<ObjectHandle> {
91        let mut attrs = helper::search_spec(None, label)?;
92        attrs.push(Attribute::Wrap(true));
93        match self {
94            Wrap::AesKeyWrap
95            | Wrap::AesKeyWrapPad
96            | Wrap::VendorThalesAesKw
97            | Wrap::VendorThalesAesKwp => {
98                attrs.push(Attribute::KeyType(KeyType::Aes.try_into()?));
99                attrs.push(Attribute::Class(ObjectClass::SecretKey.try_into()?));
100                helper::find_one_object(session, &attrs)
101            }
102            Wrap::RsaPkcs | Wrap::RsaPkcsOaep => {
103                attrs.push(Attribute::KeyType(KeyType::Rsa.try_into()?));
104                attrs.push(Attribute::Class(ObjectClass::PublicKey.try_into()?));
105                helper::find_one_object(session, &attrs)
106            }
107        }
108    }
109
110    pub fn unwrapping_key(&self, session: &Session, label: Option<&str>) -> Result<ObjectHandle> {
111        let mut attrs = helper::search_spec(None, label)?;
112        attrs.push(Attribute::Unwrap(true));
113        match self {
114            Wrap::AesKeyWrap
115            | Wrap::AesKeyWrapPad
116            | Wrap::VendorThalesAesKw
117            | Wrap::VendorThalesAesKwp => {
118                attrs.push(Attribute::KeyType(KeyType::Aes.try_into()?));
119                attrs.push(Attribute::Class(ObjectClass::SecretKey.try_into()?));
120                helper::find_one_object(session, &attrs)
121            }
122            Wrap::RsaPkcs | Wrap::RsaPkcsOaep => {
123                attrs.push(Attribute::KeyType(KeyType::Rsa.try_into()?));
124                attrs.push(Attribute::Class(ObjectClass::PrivateKey.try_into()?));
125                helper::find_one_object(session, &attrs)
126            }
127        }
128    }
129
130    pub fn check_key(&self, session: &Session, key: ObjectHandle) -> Result<()> {
131        let map = AttributeMap::from_object(session, key)?;
132
133        let extractable: bool = map
134            .get(&AttributeType::Extractable)
135            .ok_or_else(|| HsmError::KeyError("Key does not have an extractable attribute".into()))?
136            .try_into()
137            .map_err(HsmError::AttributeError)?;
138        if !extractable {
139            Err(HsmError::KeyError("Key is not extractable".into()))?;
140        }
141
142        let key_type: KeyType = map
143            .get(&AttributeType::KeyType)
144            .ok_or_else(|| HsmError::KeyError("Key does not have a key type attribute".into()))?
145            .try_into()
146            .map_err(HsmError::AttributeError)?;
147
148        if *self == Wrap::RsaPkcsOaep || *self == Wrap::RsaPkcs {
149            let result = match key_type {
150                KeyType::Aes => Ok(()),
151                KeyType::GenericSecret => Ok(()),
152                _ => Err(HsmError::KeyError(format!(
153                    "Unsupported key type: {key_type:?}"
154                )))?,
155            };
156            result?;
157        }
158
159        Ok(())
160    }
161
162    pub fn wrap(
163        &self,
164        session: &Session,
165        key: ObjectHandle,
166        wrapping_key_label: Option<&str>,
167    ) -> Result<Vec<u8>> {
168        self.check_key(session, key)?;
169        let wrapping_key = self.wrapping_key(session, wrapping_key_label)?;
170        let mechanism = self.mechanism()?;
171        Ok(session.wrap_key(&mechanism, wrapping_key, key)?)
172    }
173
174    pub fn unwrap(
175        &self,
176        session: &Session,
177        wrapped_key: &[u8],
178        wrapping_key_label: Option<&str>,
179        template: &AttributeMap,
180    ) -> Result<ObjectHandle> {
181        let wrapping_key = self.unwrapping_key(session, wrapping_key_label)?;
182        let mechanism = self.mechanism()?;
183        Ok(session.unwrap_key(
184            &mechanism,
185            wrapping_key,
186            wrapped_key,
187            template.to_vec()?.as_slice(),
188        )?)
189    }
190}