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}