Skip to main content

signstar_yubihsm2/
mock.rs

1//! Utilities related to mocking.
2
3use signstar_crypto::{
4    openpgp::{OpenPgpKeyUsageFlags, OpenPgpUserId, OpenPgpVersion},
5    signer::openpgp::{Timestamp, generate_certificate},
6    traits::UserWithPassphrase as _,
7};
8use yubihsm::{
9    Capability,
10    Connector,
11    Credentials as YubiCredentials,
12    Domain,
13    asymmetric::Algorithm as AsymmetricAlgorithm,
14    authentication::{Algorithm as AuthenticationAlgorithm, Key},
15    client::Client,
16    object::Id,
17    opaque::Algorithm as OpaqueAlgorithm,
18};
19
20use crate::{Credentials, Error, YubiHsm2SigningKey};
21
22/// Sets up a single signing key for use with OpenPGP signing in a MockHSM.
23///
24/// The `credentials` are used to setup authentication for a signing key with `key_id`.
25/// Both signing and authentication key are located in domain one.
26///
27/// # Warning
28///
29/// The signing key created by this function should be used only for tests as the signing
30/// material is exposed in memory!
31///
32/// # Errors
33///
34/// When automatic provisioning of the connector fails this function can return [`Error`].
35pub fn setup_single_key_in_mockhsm(
36    key_id: Id,
37    credentials: &Credentials,
38) -> Result<Connector, Error> {
39    let connector = Connector::mockhsm();
40    let client =
41        Client::open(connector, Default::default(), true).map_err(|source| Error::Client {
42            context: "connecting to mockhsm",
43            source,
44        })?;
45    let auth_key = Key::derive_from_password(credentials.passphrase().expose_borrowed().as_bytes());
46    let domain = Domain::DOM1;
47    client
48        .put_authentication_key(
49            credentials.id(),
50            Default::default(),
51            domain,
52            Capability::empty(),
53            Capability::SIGN_EDDSA,
54            AuthenticationAlgorithm::YubicoAes,
55            auth_key.clone(),
56        )
57        .map_err(|source| Error::Client {
58            context: "putting authentication key",
59            source,
60        })?;
61
62    let client = Client::open(
63        client.connector().clone(),
64        YubiCredentials::new(credentials.id(), auth_key),
65        true,
66    )
67    .map_err(|source| Error::Client {
68        context: "connecting to mockhsm",
69        source,
70    })?;
71
72    client
73        .generate_asymmetric_key(
74            key_id,
75            Default::default(),
76            domain,
77            Capability::SIGN_EDDSA,
78            AsymmetricAlgorithm::Ed25519,
79        )
80        .map_err(|source| Error::Client {
81            context: "generating asymmetric key",
82            source,
83        })?;
84
85    let flags = {
86        let mut flags = OpenPgpKeyUsageFlags::default();
87        flags.set_sign();
88        flags
89    };
90
91    let signer = YubiHsm2SigningKey::new(client, key_id);
92
93    let cert = generate_certificate(
94        &signer,
95        flags,
96        &[OpenPgpUserId::new("Test".to_owned()).expect("static user ID to be valid")],
97        Default::default(),
98        Timestamp::now(),
99        OpenPgpVersion::V4,
100    )
101    .map_err(|source| Error::CertificateGeneration {
102        context: "generating OpenPGP certificate",
103        source,
104    })?;
105
106    signer
107        .client()
108        .put_opaque(
109            key_id,
110            Default::default(),
111            domain,
112            Capability::empty(),
113            OpaqueAlgorithm::Data,
114            cert,
115        )
116        .map_err(|source| Error::Client {
117            context: "putting generated certificate on the device",
118            source,
119        })?;
120
121    Ok(signer.client().connector().clone())
122}