cryptoki/slot/
token_info.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3//! PKCS11 Token info and associated flags
4
5use crate::error::{Error, Result};
6use crate::string_from_blank_padded;
7use crate::types::convert_utc_time;
8use crate::types::{UtcTime, Version};
9use bitflags::bitflags;
10use cryptoki_sys::*;
11use std::convert::TryFrom;
12use std::fmt::Debug;
13
14bitflags! {
15    /// Collection of flags defined for [`CK_TOKEN_INFO`]
16    struct TokenInfoFlags: CK_FLAGS {
17        const RNG = CKF_RNG;
18        const WRITE_PROTECTED = CKF_WRITE_PROTECTED;
19        const LOGIN_REQUIRED = CKF_LOGIN_REQUIRED;
20        const USER_PIN_INITIALIZED = CKF_USER_PIN_INITIALIZED;
21        const RESTORE_KEY_NOT_NEEDED = CKF_RESTORE_KEY_NOT_NEEDED;
22        const CLOCK_ON_TOKEN = CKF_CLOCK_ON_TOKEN;
23        const PROTECTED_AUTHENTICATION_PATH = CKF_PROTECTED_AUTHENTICATION_PATH;
24        const DUAL_CRYPTO_OPERATIONS = CKF_DUAL_CRYPTO_OPERATIONS;
25        const TOKEN_INITIALIZED = CKF_TOKEN_INITIALIZED;
26        const SECONDARY_AUTHENTICATION = CKF_SECONDARY_AUTHENTICATION;
27        const USER_PIN_COUNT_LOW = CKF_USER_PIN_COUNT_LOW;
28        const USER_PIN_FINAL_TRY = CKF_USER_PIN_FINAL_TRY;
29        const USER_PIN_LOCKED = CKF_USER_PIN_LOCKED;
30        const USER_PIN_TO_BE_CHANGED = CKF_USER_PIN_TO_BE_CHANGED;
31        const SO_PIN_COUNT_LOW = CKF_SO_PIN_COUNT_LOW;
32        const SO_PIN_FINAL_TRY = CKF_SO_PIN_FINAL_TRY;
33        const SO_PIN_LOCKED = CKF_SO_PIN_LOCKED;
34        const SO_PIN_TO_BE_CHANGED = CKF_SO_PIN_TO_BE_CHANGED;
35        const ERROR_STATE = CKF_ERROR_STATE;
36    }
37}
38
39#[derive(Debug, Clone, Copy)]
40/// A limiting value for the token that may or may not take an explicit value
41pub enum Limit {
42    /// There is an explicit value for this limit
43    Max(u64),
44    /// The token does not provide information about this limit
45    Unavailable,
46    /// The limit is "effectively infinite" and may be treated as such
47    Infinite,
48}
49
50/// Information about a token
51#[derive(Debug, Clone)]
52pub struct TokenInfo {
53    // The following four strings are limited in size based on
54    // the originating struct definition. Sizes are in *bytes*
55    // but UTF-8 data may represent fewer characters.
56    // Original buffers were space (0x20) padded.
57    label: String,           // len <= 32 bytes
58    manufacturer_id: String, // len <= 32 bytes
59    model: String,           // len <= 16 bytes
60    serial_number: String,   // len <= 16 bytes
61    flags: TokenInfoFlags,
62    max_session_count: Limit,
63    session_count: Option<u64>,
64    max_rw_session_count: Limit,
65    rw_session_count: Option<u64>,
66    max_pin_len: usize,
67    min_pin_len: usize,
68    total_public_memory: Option<usize>,
69    free_public_memory: Option<usize>,
70    total_private_memory: Option<usize>,
71    free_private_memory: Option<usize>,
72    hardware_version: Version,
73    firmware_version: Version,
74    utc_time: Option<UtcTime>,
75}
76trait MaybeUnavailable: Sized {
77    fn maybe_unavailable(value: CK_ULONG) -> Option<Self>;
78}
79
80impl MaybeUnavailable for usize {
81    fn maybe_unavailable(value: CK_ULONG) -> Option<usize> {
82        if value == CK_UNAVAILABLE_INFORMATION {
83            None
84        } else {
85            Some(value as usize)
86        }
87    }
88}
89
90impl MaybeUnavailable for u64 {
91    fn maybe_unavailable(value: CK_ULONG) -> Option<u64> {
92        if value == CK_UNAVAILABLE_INFORMATION {
93            None
94        } else {
95            // Must have cast for when ulong is 32 bits
96            Some(value.into())
97        }
98    }
99}
100
101/// Flattens both `Infinite` and `Unavailable` to `None`,
102impl From<Limit> for Option<u64> {
103    fn from(limit: Limit) -> Self {
104        match limit {
105            Limit::Unavailable | Limit::Infinite => None,
106            Limit::Max(n) => Some(n),
107        }
108    }
109}
110
111fn maybe_unlimited(value: CK_ULONG) -> Limit {
112    match value {
113        CK_UNAVAILABLE_INFORMATION => Limit::Unavailable,
114        CK_EFFECTIVELY_INFINITE => Limit::Infinite,
115        // Must have cast for when ulong is 32 bits
116        _ => Limit::Max(value.into()),
117    }
118}
119
120impl TokenInfo {
121    /// An application-defined label, assigned during token initialization
122    ///
123    /// **[Conformance](crate#conformance-notes):**
124    /// This string is maximally 32 bytes (*not* chars) as UTF-8
125    pub fn label(&self) -> &str {
126        &self.label
127    }
128
129    /// The ID of the device manufacturer
130    ///
131    /// **[Conformance](crate#conformance-notes):**
132    /// This string is maximally 32 bytes (*not* chars) as UTF-8
133    pub fn manufacturer_id(&self) -> &str {
134        &self.manufacturer_id
135    }
136
137    /// The model of the device
138    ///
139    /// **[Conformance](crate#conformance-notes):**
140    /// This string is maximally 16 bytes (*not* chars) as UTF-8
141    pub fn model(&self) -> &str {
142        &self.model
143    }
144
145    /// The character-string serial number of the device
146    ///
147    /// **[Conformance](crate#conformance-notes):**
148    /// This string is maximally 16 bytes (*not* chars) as UTF-8
149    pub fn serial_number(&self) -> &str {
150        &self.serial_number
151    }
152
153    /// True if the token has its own random number generator
154    pub fn rng(&self) -> bool {
155        self.flags.contains(TokenInfoFlags::RNG)
156    }
157
158    /// True if the token is write-protected
159    ///
160    /// **[Conformance](crate#conformance-notes):**
161    /// Exactly what this value means is determined by the application. An
162    /// application may be unable to perform certain actions on a write-
163    /// protected token. These actions can include any of the following (non-
164    /// exhaustive):
165    /// * Creating/modifying/deleting any object on the token
166    /// * Creating/modifying/deleting a token object on the token
167    /// * Changing the Security Officer's PIN
168    /// * Changing the normal user's PIN
169    ///
170    /// The token may change its write-protected status depending on the
171    /// session state to implement its object management policy. For instance,
172    /// the token may report write-protection unless the session state is R/W
173    /// SO or R/W User to implement a policy that does not allow any objects,
174    /// public or private, to be created, modified, or deleted unless the user
175    /// has successfully called [`Session::login`](crate::session::Session::login).
176    pub fn write_protected(&self) -> bool {
177        self.flags.contains(TokenInfoFlags::WRITE_PROTECTED)
178    }
179
180    /// True if there are some cryptographic functions that a user *must* be
181    /// logged in to perform
182    pub fn login_required(&self) -> bool {
183        self.flags.contains(TokenInfoFlags::LOGIN_REQUIRED)
184    }
185
186    /// True of the normal user's PIN has been initialized
187    pub fn user_pin_initialized(&self) -> bool {
188        self.flags.contains(TokenInfoFlags::USER_PIN_INITIALIZED)
189    }
190
191    /// True if a successful save of a session's cryptographic operations state
192    /// *always* contains all keys needed to restore the state of the session.
193    pub fn restore_key_not_needed(&self) -> bool {
194        self.flags.contains(TokenInfoFlags::RESTORE_KEY_NOT_NEEDED)
195    }
196
197    /// True if the token has its own hardware clock
198    pub fn clock_on_token(&self) -> bool {
199        self.flags.contains(TokenInfoFlags::CLOCK_ON_TOKEN)
200    }
201
202    /// True if the token has a "protected authentication path" whereby a user
203    /// can log into the token without passing a PIN
204    pub fn protected_authentication_path(&self) -> bool {
205        self.flags
206            .contains(TokenInfoFlags::PROTECTED_AUTHENTICATION_PATH)
207    }
208
209    /// True if a single session with the token can perform dual cryptographic
210    /// operations
211    // TODO: Requires Session callbacks to access
212    // * digest_encrypt_update
213    // * decrypt_digest_update
214    // * sign_encrypt_update
215    // * decrypt_verify_update
216    pub fn dual_crypto_operations(&self) -> bool {
217        self.flags.contains(TokenInfoFlags::DUAL_CRYPTO_OPERATIONS)
218    }
219
220    /// True if the token has been initialized with
221    /// [`Pkcs11::init_token](crate::context::Pkcs11::init_token) or an
222    /// equivalent mechanism outside the scope of the PKCS#11 standard
223    ///
224    /// **[Conformance](crate#conformance-notes):**
225    /// Calling [`Pkcs11::init_token`](crate::context::Pkcs11::init_token) when
226    /// this flag is set will cause the token to be reinitialized.
227    pub fn token_initialized(&self) -> bool {
228        self.flags.contains(TokenInfoFlags::TOKEN_INITIALIZED)
229    }
230
231    /// True if the token supports secondary authentication for private key
232    /// objects
233    /// **[Conformance](crate#conformance-notes):**
234    /// This field is deprecated and new providers *must not* set it. I.e., this function must always return `false`.
235    pub fn secondary_authentication(&self) -> bool {
236        self.flags
237            .contains(TokenInfoFlags::SECONDARY_AUTHENTICATION)
238    }
239
240    /// True if an incorrect user login PIN has been entered at least once
241    /// since the last successful authentication
242    ///
243    /// **[Conformance](crate#conformance-notes):**
244    /// This value may be set to always be false if the token either does not
245    /// support the functionality or will not reveal the information because of
246    /// its security policy.
247    pub fn user_pin_count_low(&self) -> bool {
248        self.flags.contains(TokenInfoFlags::USER_PIN_COUNT_LOW)
249    }
250
251    /// True if supplying an incorrect user PIN will cause it to become locked
252    ///
253    /// **[Conformance](crate#conformance-notes):**
254    /// This value may be set to always be false if the token either does not
255    /// support the functionality or will not reveal the information because of
256    /// its security policy.
257    pub fn user_pin_final_try(&self) -> bool {
258        self.flags.contains(TokenInfoFlags::USER_PIN_FINAL_TRY)
259    }
260
261    /// True if the user PIN has been locked; user login to the token is not
262    /// possible
263    pub fn user_pin_locked(&self) -> bool {
264        self.flags.contains(TokenInfoFlags::USER_PIN_LOCKED)
265    }
266
267    /// True if the user PIN value is the default value set by the token
268    /// initialization or manufacturing, or the PIN has been expired by the
269    /// card
270    ///
271    /// **[Conformance](crate#conformance-notes):**
272    /// This may be always false if the token either does not support the
273    /// functionality or will not reveal the information because of its
274    /// security policy.
275    ///
276    /// If a PIN is set to the default value or has expired, this function
277    /// returns `true`. When true, logging in with a PIN will succeed, but only
278    /// the [`Session::set_pin`][crate::session::Session::set_pin] function can
279    /// be called. Calling any other function that required the user to be
280    /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired]
281    /// to be returned until
282    /// [`Session::set_pin`][crate::session::Session::set_pin] is called
283    /// successfully.
284    pub fn user_pin_to_be_changed(&self) -> bool {
285        self.flags.contains(TokenInfoFlags::USER_PIN_TO_BE_CHANGED)
286    }
287
288    /// True if an incorrect Security Officer login PIN has been entered at least once since
289    /// the last successful authentication
290    ///
291    /// **[Conformance](crate#conformance-notes):**
292    /// This value may be set to always be false if the token either does not
293    /// support the functionality or will not reveal the information because of
294    /// its security policy.
295    pub fn so_pin_count_low(&self) -> bool {
296        self.flags.contains(TokenInfoFlags::SO_PIN_COUNT_LOW)
297    }
298
299    /// True if supplying an incorrect Security Officer PIN will cause it to become locked
300    ///
301    /// **[Conformance](crate#conformance-notes):**
302    /// This value may be set to always be false if the token either does not
303    /// support the functionality or will not reveal the information because of
304    /// its security policy.
305    pub fn so_pin_final_try(&self) -> bool {
306        self.flags.contains(TokenInfoFlags::SO_PIN_FINAL_TRY)
307    }
308
309    /// True if the Security Officer PIN has been locked; Security Officer login to the token is not
310    /// possible
311    pub fn so_pin_locked(&self) -> bool {
312        self.flags.contains(TokenInfoFlags::SO_PIN_LOCKED)
313    }
314
315    /// True if the Security Officer PIN value is the default value set by the token
316    /// initialization or manufacturing, or the PIN has been expired by the card
317    ///
318    /// **[Conformance](crate#conformance-notes):**
319    /// This may be always false if the token either does not support the
320    /// functionality or will not reveal the information because of its security
321    /// policy.
322    ///
323    /// If a PIN is set to the default value or has expired, this function
324    /// returns `true`. When true, logging in with a PIN will succeed, but only
325    /// the [`Session::set_pin`][crate::session::Session::set_pin] function can
326    /// be called. Calling any other function that required the user to be
327    /// logged in will cause [`PinExpired`][crate::error::RvError::PinExpired]
328    /// to be returned until
329    /// [`Session::set_pin`][crate::session::Session::set_pin] is called
330    /// successfully.
331    pub fn so_pin_to_be_changed(&self) -> bool {
332        self.flags.contains(TokenInfoFlags::SO_PIN_TO_BE_CHANGED)
333    }
334
335    /// True if the token failed a FIPS 140-2 self-test and entered an error state
336    pub fn error_state(&self) -> bool {
337        self.flags.contains(TokenInfoFlags::ERROR_STATE)
338    }
339
340    /// The maximum number of sessions that can be opened with the token at one
341    /// time by a single application.
342    pub fn max_session_count(&self) -> Limit {
343        self.max_session_count
344    }
345
346    /// The number of sessions this application currently has open with the
347    /// token
348    pub fn session_count(&self) -> Option<u64> {
349        self.session_count
350    }
351
352    /// The maximum number of read/write sessions that can be opened with the
353    /// token at one time by a single application.
354    pub fn max_rw_session_count(&self) -> Limit {
355        self.max_rw_session_count
356    }
357
358    /// The number of read/write sessions this application currently has open
359    /// with the token
360    pub fn rw_session_count(&self) -> Option<u64> {
361        self.rw_session_count
362    }
363
364    /// The maximum length in bytes of the PIN
365    pub fn max_pin_length(&self) -> usize {
366        self.max_pin_len
367    }
368
369    /// The minimum length in bytes of the PIN
370    pub fn min_pin_length(&self) -> usize {
371        self.min_pin_len
372    }
373
374    /// The total amount of memory on the token (in bytes) in which public
375    /// objects may be stored
376    /// Returns `None` if this information is unavailable
377    pub fn total_public_memory(&self) -> Option<usize> {
378        self.total_public_memory
379    }
380
381    /// The amount of free (unused) emmeory on the token (in bytes) for public
382    /// objects
383    /// Returns `None` if this information is unavailable
384    pub fn free_public_memory(&self) -> Option<usize> {
385        self.free_public_memory
386    }
387
388    /// The total amount of memory on the token (in bytes) in which private
389    /// objects may be stored
390    /// Returns `None` if this information is unavailable
391    pub fn total_private_memory(&self) -> Option<usize> {
392        self.total_private_memory
393    }
394
395    /// The amount of free (unused) emmeory on the token (in bytes) for private
396    /// objects
397    /// Returns `None` if this information is unavailable
398    pub fn free_private_memory(&self) -> Option<usize> {
399        self.free_private_memory
400    }
401
402    /// The version number of the hardware
403    pub fn hardware_version(&self) -> Version {
404        self.hardware_version
405    }
406
407    /// The version number of the firmware
408    pub fn firmware_version(&self) -> Version {
409        self.firmware_version
410    }
411
412    /// The current UTC datetime reported by the token
413    ///
414    /// Returns `None` if the token is not equipped with a clock (i.e.,
415    /// `self.clock_on_token() == false`)
416    ///
417    /// **[Conformance](crate#conformance-notes):**
418    /// The string representation of the datetime from the token is only
419    /// required to be parsable as a string of ASCII digits. No additional
420    /// structure (e.g., months numbered from 0 or from 1) is defined.
421    pub fn utc_time(&self) -> Option<UtcTime> {
422        self.utc_time
423    }
424}
425
426#[doc(hidden)]
427impl TryFrom<CK_TOKEN_INFO> for TokenInfo {
428    type Error = Error;
429    fn try_from(val: CK_TOKEN_INFO) -> Result<Self> {
430        let flags = TokenInfoFlags::from_bits_truncate(val.flags);
431        let utc_time = if flags.contains(TokenInfoFlags::CLOCK_ON_TOKEN) {
432            Some(convert_utc_time(val.utcTime)?)
433        } else {
434            None
435        };
436        Ok(Self {
437            label: string_from_blank_padded(&val.label),
438            manufacturer_id: string_from_blank_padded(&val.manufacturerID),
439            model: string_from_blank_padded(&val.model),
440            serial_number: string_from_blank_padded(&val.serialNumber),
441            flags,
442            max_session_count: maybe_unlimited(val.ulMaxSessionCount),
443            session_count: u64::maybe_unavailable(val.ulSessionCount),
444            max_rw_session_count: maybe_unlimited(val.ulMaxRwSessionCount),
445            rw_session_count: u64::maybe_unavailable(val.ulRwSessionCount),
446            max_pin_len: val.ulMaxPinLen as usize,
447            min_pin_len: val.ulMinPinLen as usize,
448            total_public_memory: usize::maybe_unavailable(val.ulTotalPublicMemory),
449            free_public_memory: usize::maybe_unavailable(val.ulFreePublicMemory),
450            total_private_memory: usize::maybe_unavailable(val.ulTotalPrivateMemory),
451            free_private_memory: usize::maybe_unavailable(val.ulFreePrivateMemory),
452            hardware_version: val.hardwareVersion.into(),
453            firmware_version: val.firmwareVersion.into(),
454            utc_time,
455        })
456    }
457}
458
459#[cfg(test)]
460mod test {
461    use super::{Limit, TokenInfo, TokenInfoFlags};
462    use crate::types::{UtcTime, Version};
463
464    #[test]
465    fn debug_flags_all() {
466        let expected = "\
467RNG | WRITE_PROTECTED | LOGIN_REQUIRED | USER_PIN_INITIALIZED | \
468RESTORE_KEY_NOT_NEEDED | CLOCK_ON_TOKEN | PROTECTED_AUTHENTICATION_PATH | \
469DUAL_CRYPTO_OPERATIONS | TOKEN_INITIALIZED | SECONDARY_AUTHENTICATION | \
470USER_PIN_COUNT_LOW | USER_PIN_FINAL_TRY | USER_PIN_LOCKED | \
471USER_PIN_TO_BE_CHANGED | SO_PIN_COUNT_LOW | SO_PIN_FINAL_TRY | SO_PIN_LOCKED | \
472SO_PIN_TO_BE_CHANGED | ERROR_STATE";
473        let all = TokenInfoFlags::all();
474        let observed = format!("{all:#?}");
475        assert_eq!(observed, expected);
476    }
477
478    #[test]
479    fn debug_info() {
480        let info = TokenInfo {
481            label: String::from("Token Label"),
482            manufacturer_id: String::from("Manufacturer ID"),
483            model: String::from("Token Model"),
484            serial_number: String::from("Serial Number"),
485            flags: TokenInfoFlags::empty(),
486            max_session_count: Limit::Max(100),    // max == 100
487            session_count: None,                   // unavailable
488            max_rw_session_count: Limit::Infinite, // max == infinite
489            rw_session_count: Some(1),
490            max_pin_len: 16,
491            min_pin_len: 4,
492            total_public_memory: Some(1073741824), // 1GiB
493            free_public_memory: Some(1234567890),
494            total_private_memory: None, // unavailable
495            free_private_memory: None,  // unavailable
496            hardware_version: Version::new(0, 255),
497            firmware_version: Version::new(255, 0),
498            utc_time: Some(UtcTime {
499                year: 1970,
500                month: 1,
501                day: 1,
502                hour: 0,
503                minute: 0,
504                second: 0,
505            }),
506        };
507        let expected = r#"TokenInfo {
508    label: "Token Label",
509    manufacturer_id: "Manufacturer ID",
510    model: "Token Model",
511    serial_number: "Serial Number",
512    flags: (empty),
513    max_session_count: Max(
514        100,
515    ),
516    session_count: None,
517    max_rw_session_count: Infinite,
518    rw_session_count: Some(
519        1,
520    ),
521    max_pin_len: 16,
522    min_pin_len: 4,
523    total_public_memory: Some(
524        1073741824,
525    ),
526    free_public_memory: Some(
527        1234567890,
528    ),
529    total_private_memory: None,
530    free_private_memory: None,
531    hardware_version: Version {
532        major: 0,
533        minor: 255,
534    },
535    firmware_version: Version {
536        major: 255,
537        minor: 0,
538    },
539    utc_time: Some(
540        UtcTime {
541            year: 1970,
542            month: 1,
543            day: 1,
544            hour: 0,
545            minute: 0,
546            second: 0,
547        },
548    ),
549}"#;
550        let observed = format!("{info:#?}");
551        assert_eq!(observed, expected);
552    }
553}