Skip to main content

confium_deployment/identity/
token.rs

1//! Hardware token descriptors.
2//!
3//! Describes where a key lives in hardware. Standards-only — no vendor
4//! SDKs. Covers YubiKey PIV, YubiKey OpenPGP applet, OpenPGP card v3+,
5//! TPM 2.0.
6
7use serde::{Deserialize, Serialize};
8
9/// A hardware token holding one or more keys.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(tag = "kind", rename_all = "snake_case")]
12pub enum HardwareToken {
13    /// YubiKey PIV applet (PKCS#11 interface).
14    YubiKeyPiv {
15        /// PIV slot identifier (e.g., "9a", "9c", "9e").
16        slot: String,
17        /// PIN policy.
18        pin_policy: PinPolicy,
19    },
20    /// YubiKey OpenPGP applet (OpenPGP card interface).
21    YubiKeyOpenpgp {
22        /// OpenPGP key slot (sig, dec, aut).
23        slot: OpenpgpSlot,
24    },
25    /// Any OpenPGP card v3+ device (YubiKey, Nitrokey, Gnuk).
26    OpenpgpCard {
27        /// Card identifier (typically derived from serial number).
28        card_id: String,
29        /// OpenPGP key slot.
30        slot: OpenpgpSlot,
31    },
32    /// TPM 2.0 sealed key.
33    Tpm {
34        /// TPM persistent handle (e.g., 0x81000001).
35        handle: u32,
36    },
37}
38
39/// PIN entry policy for PIV signing.
40#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
41#[serde(rename_all = "snake_case")]
42pub enum PinPolicy {
43    /// PIN never required (default).
44    #[default]
45    Never,
46    /// PIN required once per session.
47    Once,
48    /// PIN required every sign operation.
49    Always,
50}
51
52/// OpenPGP card key slot.
53#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum OpenpgpSlot {
56    /// Signature slot (SIG).
57    Signature,
58    /// Decryption slot (DEC).
59    Decryption,
60    /// Authentication slot (AUT).
61    Authentication,
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn yubikey_piv_serializes() {
70        let t = HardwareToken::YubiKeyPiv {
71            slot: "9c".into(),
72            pin_policy: PinPolicy::Always,
73        };
74        let json = serde_json::to_string(&t).unwrap();
75        let recovered: HardwareToken = serde_json::from_str(&json).unwrap();
76        match recovered {
77            HardwareToken::YubiKeyPiv {
78                slot,
79                pin_policy: _,
80            } => assert_eq!(slot, "9c"),
81            _ => panic!("wrong variant"),
82        }
83    }
84}