hsmtool/
module.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
5use anyhow::{Context, Result};
6use cryptoki::context::CInitializeArgs;
7use cryptoki::context::Pkcs11;
8use cryptoki::session::Session;
9use cryptoki::session::UserType;
10use cryptoki::slot::Slot;
11use cryptoki::types::AuthPin;
12use serde::de::{Deserialize, Deserializer};
13use std::rc::Rc;
14use std::str::FromStr;
15
16use crate::error::HsmError;
17use crate::spxef::SpxEf;
18use acorn::{Acorn, SpxInterface};
19
20#[derive(Debug, Clone)]
21pub enum SpxModule {
22    Acorn(String),
23    Pkcs11Ef,
24}
25
26impl SpxModule {
27    pub const HELP: &'static str =
28        "Type of sphincs+ module [allowed values: acorn:<libpath>, pkcs11-ef]";
29}
30
31impl FromStr for SpxModule {
32    type Err = HsmError;
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        if s[..6].eq_ignore_ascii_case("acorn:") {
35            Ok(SpxModule::Acorn(s[6..].into()))
36        } else if s.eq_ignore_ascii_case("pkcs11-ef") {
37            Ok(SpxModule::Pkcs11Ef)
38        } else {
39            Err(HsmError::ParseError(format!("unknown SpxModule {s:?}")))
40        }
41    }
42}
43
44pub struct Module {
45    pub pkcs11: Pkcs11,
46    pub session: Option<Rc<Session>>,
47    pub spx: Option<Box<dyn SpxInterface>>,
48    pub token: Option<String>,
49}
50
51impl Module {
52    pub fn initialize(module: &str) -> Result<Self> {
53        let pkcs11 = Pkcs11::new(module)?;
54        pkcs11.initialize(CInitializeArgs::OsThreads)?;
55        Ok(Module {
56            pkcs11,
57            session: None,
58            spx: None,
59            token: None,
60        })
61    }
62
63    pub fn initialize_spx(&mut self, module: &SpxModule) -> Result<()> {
64        let module = match module {
65            SpxModule::Acorn(libpath) => Acorn::new(libpath)? as Box<dyn SpxInterface>,
66            SpxModule::Pkcs11Ef => {
67                let session = self
68                    .session
69                    .as_ref()
70                    .map(Rc::clone)
71                    .ok_or(HsmError::SessionRequired)?;
72                SpxEf::new(session) as Box<dyn SpxInterface>
73            }
74        };
75        self.spx = Some(module);
76        Ok(())
77    }
78
79    pub fn get_session(&self) -> Option<&Session> {
80        self.session.as_ref().map(Rc::as_ref)
81    }
82
83    pub fn get_token(&self, label: &str) -> Result<Slot> {
84        let slots = self.pkcs11.get_slots_with_token()?;
85        for slot in slots {
86            let info = self.pkcs11.get_token_info(slot)?;
87            if label == info.label() {
88                return Ok(slot);
89            }
90        }
91        Err(HsmError::TokenNotFound(label.into()).into())
92    }
93
94    pub fn connect(
95        &mut self,
96        token: &str,
97        user: Option<UserType>,
98        pin: Option<&str>,
99    ) -> Result<()> {
100        let slot = self.get_token(token)?;
101        let session = self.pkcs11.open_rw_session(slot)?;
102        if let Some(user) = user {
103            let pin = pin.map(|x| AuthPin::new(x.to_owned()));
104            session
105                .login(user, pin.as_ref())
106                .context("Failed HSM Login")?;
107        }
108        self.token = Some(token.into());
109        self.session = Some(Rc::new(session));
110        Ok(())
111    }
112}
113
114pub fn parse_user_type(val: &str) -> Result<UserType> {
115    match val {
116        "So" | "SO" | "so" | "security_officer" => Ok(UserType::So),
117        "User" | "USER" | "user" => Ok(UserType::User),
118        _ => Err(HsmError::UnknownUser(val.into()).into()),
119    }
120}
121
122pub fn deserialize_user<'de, D>(deserializer: D) -> std::result::Result<UserType, D::Error>
123where
124    D: Deserializer<'de>,
125{
126    let user = String::deserialize(deserializer)?;
127    parse_user_type(&user).map_err(serde::de::Error::custom)
128}