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