nethsm_tests/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::fs::File;
4use std::path::PathBuf;
5
6use chrono::Utc;
7use nethsm::{
8    Connection,
9    ConnectionSecurity,
10    Credentials,
11    KeyId,
12    KeyMechanism,
13    KeyType,
14    NetHsm,
15    Passphrase,
16    Url,
17    UserRole,
18};
19use rstest::fixture;
20// Publicly re-export, so that consumers do not have to rely on rustainers directly .
21pub use rustainers::Container;
22use rustainers::runner::Runner;
23use testresult::TestResult;
24
25/// Identifier for an admin user.
26pub static ADMIN_USER_ID: &str = "admin";
27
28/// Sample admin passphrase.
29pub static ADMIN_USER_PASSPHRASE: &str = "just-an-admin-passphrase";
30
31/// Sample unlock passphrase.
32pub static UNLOCK_PASSPHRASE: &str = "just-an-unlock-passphrase";
33
34/// Default user ID for an operator.
35pub static DEFAULT_OPERATOR_USER_ID: &str = "operator1";
36
37/// Default real name for an operator.
38pub static DEFAULT_OPERATOR_USER_REAL_NAME: &str = "Some Operator";
39
40/// Sample operator passphrase.
41pub static DEFAULT_OPERATOR_USER_PASSPHRASE: &str = "just-an-operator-passphrase";
42
43/// User ID for a different user.
44pub static OTHER_OPERATOR_USER_ID: &str = "operator2";
45
46/// Real name for a different user.
47pub static OTHER_OPERATOR_USER_REAL_NAME: &str = "Some Other Operator";
48
49/// Sample passphrase for a different user.
50pub static OTHER_OPERATOR_USER_PASSPHRASE: &str = "just-another-operator-passphrase";
51
52/// User ID for backup purposes.
53pub static BACKUP_USER_ID: &str = "backup1";
54
55/// Real name for the backup user.
56pub static BACKUP_USER_REAL_NAME: &str = "Some Backup";
57
58/// Sample passphrase for the backup user.
59pub static BACKUP_USER_PASSPHRASE: &str = "just-a-backup-passphrase";
60
61/// User ID for the metrics user.
62pub static METRICS_USER_ID: &str = "metrics1";
63
64/// Real name for the metrics user.
65pub static METRICS_USER_REAL_NAME: &str = "Some Metrics";
66
67/// Sample passphrase for the metrics user.
68pub static METRICS_USER_PASSPHRASE: &str = "just-a-metrics-passphrase";
69
70/// Default size of the RSA key in bits.
71pub static DEFAULT_RSA_BITS: u32 = 2048;
72
73/// Default ID for a key.
74pub static DEFAULT_KEY_ID: &str = "key1";
75
76/// Default ID for a different key.
77pub static OTHER_KEY_ID: &str = "key2";
78
79/// Default tag.
80pub static DEFAULT_TAG: &str = "tag1";
81
82/// Different tag.
83pub static OTHER_TAG: &str = "tag2";
84
85/// Default ID for the encryption key.
86pub static ENC_KEY_ID: &str = "enckey1";
87
88/// Default tag for the encryption key.
89pub static ENC_TAG: &str = "enctag1";
90
91/// User ID for the operator user who can access the encryption key.
92pub static ENC_OPERATOR_USER_ID: &str = "encoperator1";
93
94/// Real name for the operator user who can access the encryption key.
95pub static ENC_OPERATOR_USER_REAL_NAME: &str = "Some Encryption Operator";
96
97/// Sample passphrase for the operator user who can access the encryption key.
98pub static ENC_OPERATOR_USER_PASSPHRASE: &str = "just-an-encryption-passphrase";
99
100/// Default size for the AES key in bits.
101pub static DEFAULT_AES_BITS: u32 = 128;
102
103/// Sample namespace.
104pub static NAMESPACE1: &str = "namespace1";
105
106/// Administrator's user ID for `namespace1`.
107pub static NAMESPACE1_ADMIN_USER_ID: &str = "namespace1~admin";
108
109/// Sample passphrase for `namespace1`'s administrator.
110pub static NAMESPACE1_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace-admin-passphrase";
111
112/// Real name for `namespace1`'s administrator.
113pub static NAMESPACE1_ADMIN_REAL_NAME: &str = "Namespace1 Admin";
114
115/// User ID of an operator in `namespace1`.
116pub static NAMESPACE1_OPERATOR_USER_ID: &str = "namespace1~operator";
117
118/// Sample passphrase of an operator in `namespace1`.
119pub static NAMESPACE1_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace-operator-passphrase";
120
121/// Real name of an operator in `namespace1`.
122pub static NAMESPACE1_OPERATOR_REAL_NAME: &str = "Namespace1 Operator";
123
124/// Second namespace.
125pub static NAMESPACE2: &str = "namespace2";
126
127/// Administrator's user ID for `namespace2`.
128pub static NAMESPACE2_ADMIN_USER_ID: &str = "namespace2~admin";
129
130/// Sample passphrase for `namespace2`'s administrator.
131pub static NAMESPACE2_ADMIN_USER_PASSPHRASE: &str = "just-a-namespace2-admin-passphrase";
132
133/// Real name for `namespace2`'s administrator.
134pub static NAMESPACE2_ADMIN_REAL_NAME: &str = "Namespace2 Admin";
135
136/// User ID of an operator in `namespace2`.
137pub static NAMESPACE2_OPERATOR_USER_ID: &str = "namespace2~operator";
138
139/// Sample passphrase of an operator in `namespace2`.
140pub static NAMESPACE2_OPERATOR_USER_PASSPHRASE: &str = "just-a-namespace2-operator-passphrase";
141
142/// Real name of an operator in `namespace2`.
143pub static NAMESPACE2_OPERATOR_REAL_NAME: &str = "Namespace2 Operator";
144
145mod container;
146pub use container::NetHsmImage;
147
148/// Creates and starts a new NetHSM container.
149pub async fn create_container() -> TestResult<Container<NetHsmImage>> {
150    let runner = Runner::podman()?;
151    let image = NetHsmImage::default();
152    println!("image: {:#?}", image.image);
153    let container = runner.start(image).await?;
154    println!("serving URL: {}", container.url().await?);
155    Ok(container)
156}
157
158/// Creates a new [NetHsm] object configured with administrator credentials.
159pub fn create_nethsm(url: Url) -> TestResult<NetHsm> {
160    Ok(NetHsm::new(
161        Connection::new(url, ConnectionSecurity::Unsafe),
162        Some(Credentials::new(
163            ADMIN_USER_ID.parse()?,
164            Some(Passphrase::new(ADMIN_USER_PASSPHRASE.to_string())),
165        )),
166        None,
167        None,
168    )?)
169}
170
171/// Returns a new [NetHsm] object pointing to an unprovisioned NetHSM.
172#[fixture]
173pub async fn unprovisioned_nethsm() -> TestResult<(NetHsm, rustainers::Container<NetHsmImage>)> {
174    let container = create_container().await?;
175
176    Ok((create_nethsm(container.url().await?)?, container))
177}
178
179fn provision_nethsm(nethsm: &NetHsm) -> TestResult {
180    nethsm.provision(
181        Passphrase::new(UNLOCK_PASSPHRASE.to_string()),
182        Passphrase::new(ADMIN_USER_PASSPHRASE.to_string()),
183        Utc::now(),
184    )?;
185    Ok(())
186}
187
188fn add_users_to_nethsm(nethsm: &NetHsm) -> TestResult {
189    let users = [
190        (
191            UserRole::Operator,
192            DEFAULT_OPERATOR_USER_ID,
193            DEFAULT_OPERATOR_USER_PASSPHRASE,
194            DEFAULT_OPERATOR_USER_REAL_NAME,
195        ),
196        (
197            UserRole::Operator,
198            OTHER_OPERATOR_USER_ID,
199            OTHER_OPERATOR_USER_PASSPHRASE,
200            OTHER_OPERATOR_USER_REAL_NAME,
201        ),
202        (
203            UserRole::Operator,
204            ENC_OPERATOR_USER_ID,
205            ENC_OPERATOR_USER_PASSPHRASE,
206            ENC_OPERATOR_USER_REAL_NAME,
207        ),
208        (
209            UserRole::Metrics,
210            METRICS_USER_ID,
211            METRICS_USER_PASSPHRASE,
212            METRICS_USER_REAL_NAME,
213        ),
214        (
215            UserRole::Backup,
216            BACKUP_USER_ID,
217            BACKUP_USER_PASSPHRASE,
218            BACKUP_USER_REAL_NAME,
219        ),
220        (
221            UserRole::Administrator,
222            NAMESPACE1_ADMIN_USER_ID,
223            NAMESPACE1_ADMIN_USER_PASSPHRASE,
224            NAMESPACE1_ADMIN_REAL_NAME,
225        ),
226        (
227            UserRole::Operator,
228            NAMESPACE1_OPERATOR_USER_ID,
229            NAMESPACE1_OPERATOR_USER_PASSPHRASE,
230            NAMESPACE1_OPERATOR_REAL_NAME,
231        ),
232        (
233            UserRole::Administrator,
234            NAMESPACE2_ADMIN_USER_ID,
235            NAMESPACE2_ADMIN_USER_PASSPHRASE,
236            NAMESPACE2_ADMIN_REAL_NAME,
237        ),
238        (
239            UserRole::Operator,
240            NAMESPACE2_OPERATOR_USER_ID,
241            NAMESPACE2_OPERATOR_USER_PASSPHRASE,
242            NAMESPACE2_OPERATOR_REAL_NAME,
243        ),
244    ];
245
246    println!("Adding users to NetHSM...");
247    for (role, user_id, passphrase, real_name) in users.into_iter() {
248        println!("Adding user: {}", user_id);
249        nethsm.add_user(
250            real_name.to_string(),
251            role,
252            Passphrase::new(passphrase.to_string()),
253            Some(user_id.parse()?),
254        )?;
255    }
256    println!("users: {:?}", nethsm.get_users()?);
257    println!("Creating namespaces...");
258    for namespace in [NAMESPACE1, NAMESPACE2] {
259        println!("Creating namespace: {}", namespace);
260        nethsm.add_namespace(&namespace.parse()?)?;
261    }
262    println!("namespaces: {:?}", nethsm.get_namespaces()?);
263    Ok(())
264}
265
266fn add_keys_to_nethsm(nethsm: &NetHsm) -> TestResult {
267    let keys = [
268        (
269            vec![KeyMechanism::EdDsaSignature],
270            KeyType::Curve25519,
271            None,
272            DEFAULT_KEY_ID,
273            DEFAULT_TAG,
274            DEFAULT_OPERATOR_USER_ID,
275        ),
276        (
277            vec![
278                KeyMechanism::RsaSignaturePkcs1,
279                KeyMechanism::RsaDecryptionPkcs1,
280            ],
281            KeyType::Rsa,
282            Some(DEFAULT_RSA_BITS),
283            OTHER_KEY_ID,
284            OTHER_TAG,
285            OTHER_OPERATOR_USER_ID,
286        ),
287        (
288            vec![
289                KeyMechanism::AesDecryptionCbc,
290                KeyMechanism::AesEncryptionCbc,
291            ],
292            KeyType::Generic,
293            Some(DEFAULT_AES_BITS),
294            ENC_KEY_ID,
295            ENC_TAG,
296            ENC_OPERATOR_USER_ID,
297        ),
298    ];
299
300    println!("Adding keys to NetHSM...");
301    for (mechanisms, key_type, length, key_id, tag, user_id) in keys {
302        let key_id: &KeyId = &key_id.parse()?;
303        nethsm.generate_key(key_type, mechanisms, length, Some((*key_id).clone()), None)?;
304        nethsm.add_key_tag(key_id, tag)?;
305        nethsm.add_user_tag(&user_id.parse()?, tag)?;
306        // skip symmetric keys, as for those we do not have a public key
307        if key_type != KeyType::Generic {
308            nethsm.import_key_certificate(key_id, nethsm.get_public_key(key_id)?.into_bytes())?;
309        }
310    }
311
312    println!("users: {:?}", nethsm.get_users()?);
313    println!("keys: {:?}", nethsm.get_keys(None)?);
314    Ok(())
315}
316
317/// Creates a new [NetHsm] object pointing at a provisioned NetHSM container.
318#[fixture]
319pub async fn provisioned_nethsm() -> TestResult<(NetHsm, Container<NetHsmImage>)> {
320    let container = create_container().await?;
321    let nethsm = create_nethsm(container.url().await?)?;
322    println!("Provisioning container...");
323    provision_nethsm(&nethsm)?;
324
325    Ok((nethsm, container))
326}
327
328/// Creates a new [NetHsm] object pointing at a NetHSM container with users.
329#[fixture]
330pub async fn nethsm_with_users() -> TestResult<(NetHsm, Container<NetHsmImage>)> {
331    let container = create_container().await?;
332    let nethsm = create_nethsm(container.url().await?)?;
333    println!("Provisioning container...");
334    provision_nethsm(&nethsm)?;
335    println!("Adding users to container...");
336    add_users_to_nethsm(&nethsm)?;
337
338    Ok((nethsm, container))
339}
340
341/// Adds users and keys to an already provisioned NetHSM container.
342#[fixture]
343pub async fn nethsm_with_keys(
344    #[future] provisioned_nethsm: TestResult<(NetHsm, Container<NetHsmImage>)>,
345) -> TestResult<(NetHsm, Container<NetHsmImage>)> {
346    let (nethsm, container) = provisioned_nethsm.await?;
347
348    println!("Adding users and keys to container...");
349    add_users_to_nethsm(&nethsm)?;
350    add_keys_to_nethsm(&nethsm)?;
351
352    Ok((nethsm, container))
353}
354
355/// Downloads an update file if it's not already present.
356#[fixture]
357pub fn update_file() -> TestResult<PathBuf> {
358    let file_name = "update.img.bin";
359    let update_link = format!(
360        "https://raw.githubusercontent.com/Nitrokey/nethsm-sdk-py/main/tests/{}",
361        file_name
362    );
363    let download_dir = PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("/tmp".into()));
364    let file = download_dir.join(file_name);
365
366    if !file.exists() {
367        let mut file_bytes = ureq::get(&update_link).call()?.into_reader();
368        let mut file_writer = File::create(&file)?;
369        std::io::copy(&mut file_bytes, &mut file_writer)?;
370        assert!(file.exists());
371    }
372
373    println!("Update file downloaded: {:?}", file);
374    Ok(file)
375}