Skip to main content

signstar_yubihsm2/object/
capability.rs

1//! YubiHSM2 object capabilities.
2
3use std::{collections::BTreeSet, fmt::Display, hash::Hash};
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use strum::{AsRefStr, Display, EnumIter, EnumString, IntoEnumIterator, IntoStaticStr};
8
9/// A capability of an object stored on a YubiHSM2.
10#[derive(
11    AsRefStr,
12    Clone,
13    Copy,
14    Debug,
15    Display,
16    EnumIter,
17    EnumString,
18    Eq,
19    Hash,
20    IntoStaticStr,
21    Ord,
22    PartialEq,
23    PartialOrd,
24)]
25#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
26#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
27#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
28#[strum(serialize_all = "kebab-case")]
29pub enum Capability {
30    /// Replace authentication key objects.
31    ///
32    /// Applicable for authentication key objects.
33    ChangeAuthenticationKey,
34
35    /// Create OTP AEAD.
36    ///
37    /// Applicable for authentication and OTP AEAD key objects.
38    CreateOtpAead,
39
40    /// Decrypt data using AES CBC mode.
41    ///
42    /// Applicable for authentication and symmetric key objects.
43    DecryptCbc,
44
45    /// Decrypt data using AES ECB mode.
46    ///
47    /// Applicable for authentication and symmetric key objects.
48    DecryptEcb,
49
50    /// Decrypt data using RSA-OAEP.
51    ///
52    /// Applicable for authentication and asymmetric key objects.
53    DecryptOaep,
54
55    /// Decrypt OTP.
56    ///
57    /// Applicable for authentication and OTP AEAD key objects.
58    DecryptOtp,
59
60    /// Decrypt data using RSA-PKCS1v1.5.
61    ///
62    /// Applicable for authentication and asymmetric key objects.
63    DecryptPkcs,
64
65    /// Delete asymmetric key objects.
66    ///
67    /// Applicable for authentication key objects.
68    DeleteAsymmetricKey,
69
70    /// Delete authentication key objects.
71    ///
72    /// Applicable for authentication key objects.
73    DeleteAuthenticationKey,
74
75    /// Delete HMAC key objects.
76    ///
77    /// Applicable for authentication key objects.
78    DeleteHmacKey,
79
80    /// Delete opaque objects.
81    ///
82    /// Applicable for authentication key objects.
83    DeleteOpaque,
84
85    /// Delete OTP AEAD key objects.
86    ///
87    /// Applicable for authentication key objects.
88    DeleteOtpAeadKey,
89
90    /// Delete RSA public wrap key.
91    ///
92    /// Applicable for authentication and wrap key objects.
93    DeletePublicWrapKey,
94
95    /// Delete AES key.
96    ///
97    /// Applicable for authentication key objects.
98    DeleteSymmetricKey,
99
100    /// Delete template objects.
101    ///
102    /// Applicable for authentication key objects.
103    DeleteTemplate,
104
105    /// Delete wrap key objects.
106    ///
107    /// Applicable for authentication key objects.
108    DeleteWrapKey,
109
110    /// Perform ECDH.
111    ///
112    /// Applicable for authentication and asymmetric key objects.
113    DeriveEcdh,
114
115    /// Encrypt data using AES CBC mode.
116    ///
117    /// Applicable for authentication and symmetric key objects.
118    EncryptCbc,
119
120    /// Encrypt data using AES ECB mode.
121    ///
122    /// Applicable for authentication and symmetric key objects.
123    EncryptEcb,
124
125    /// The object can be exported under wrap (encrypted).
126    ///
127    /// Applicable for all objects.
128    ExportableUnderWrap,
129
130    /// Export other objects under wrap.
131    ///
132    /// Applicable for authentication and wrap key objects.
133    ///
134    /// # Note
135    ///
136    /// Both the authentication key used for export *and* the wrapping key need to be capable of
137    /// export.
138    ExportWrapped,
139
140    /// Generate asymmetric key objects.
141    ///
142    /// Applicable for authentication key objects.
143    GenerateAsymmetricKey,
144
145    /// Generate HMAC key objects.
146    ///
147    /// Applicable for authentication key objects.
148    GenerateHmacKey,
149
150    /// Generate OTP AEAD key objects.
151    ///
152    /// Applicable for authentication key objects.
153    GenerateOtpAeadKey,
154
155    /// Generate AES key.
156    ///
157    /// Applicable for authentication key objects.
158    GenerateSymmetricKey,
159
160    /// Generate wrap key objects.
161    ///
162    /// Applicable for authentication key objects.
163    GenerateWrapKey,
164
165    /// Read the log store.
166    ///
167    /// Applicable for authentication key objects.
168    GetLogEntries,
169
170    /// Read opaque objects.
171    ///
172    /// Applicable for authentication key objects.
173    GetOpaque,
174
175    /// Read device-global options.
176    ///
177    /// Applicable for authentication key objects.
178    GetOption,
179
180    /// Extract random bytes.
181    ///
182    /// Applicable for authentication key objects.
183    GetPseudoRandom,
184
185    /// Read template objects.
186    ///
187    /// Applicable for authentication key objects.
188    GetTemplate,
189
190    /// Import wrapped objects.
191    ///
192    /// Applicable for authentication and wrap key objects.
193    ///
194    /// # Note
195    ///
196    /// Both the authentication key used for import *and* the wrapping key need to be capable of
197    /// import.
198    ImportWrapped,
199
200    /// Write asymmetric key objects.
201    ///
202    /// Applicable for authentication key objects.
203    PutAsymmetricKey,
204
205    /// Write authentications key objects.
206    ///
207    /// Applicable for authentication key objects.
208    PutAuthenticationKey,
209
210    /// Write HMAC key objects.
211    ///
212    /// Applicable for authentication key objects.
213    PutHmacKey,
214
215    /// Write opaque objects.
216    ///
217    /// Applicable for authentication key objects.
218    PutOpaque,
219
220    /// Write OTP AEAD key objects.
221    ///
222    /// Applicable for authentication key objects.
223    PutOtpAeadKey,
224
225    /// Write RSA public wrap key.
226    ///
227    /// Applicable for authentication and wrap key objects.
228    PutPublicWrapKey,
229
230    /// Import AES key.
231    ///
232    /// Applicable for authentication key objects.
233    PutSymmetricKey,
234
235    /// Write template objects.
236    ///
237    /// Applicable for authentication key objects.
238    PutTemplate,
239
240    /// Write wrap key objects.
241    ///
242    /// Applicable for authentication key objects.
243    PutWrapKey,
244
245    /// Create OTP AEAD from random data.
246    ///
247    /// Applicable for authentication and OTP AEAD key objects.
248    RandomizeOtpAead,
249
250    /// Perform a factory reset on the device.
251    ///
252    /// Applicable for authentication key objects.
253    ResetDevice,
254
255    /// Rewrap AEADs from one OTP AEAD key to another.
256    ///
257    /// Applicable for authentication and OTP AEAD key objects.
258    RewrapFromOtpAeadKey,
259
260    /// Rewrap AEADs to one OTP AEAD key from another.
261    ///
262    /// Applicable for authentication and OTP AEAD key objects.
263    RewrapToOtpAeadKey,
264
265    /// Write device-global options.
266    ///
267    /// Applicable for authentication key objects.
268    SetOption,
269
270    /// Attest properties of asymmetric key objects.
271    ///
272    /// Applicable for authentication and asymmetric key objects.
273    SignAttestationCertificate,
274
275    /// Compute digital signatures using ECDSA.
276    ///
277    /// Applicable for authentication and asymmetric key objects.
278    SignEcdsa,
279
280    /// Compute digital signatures using [EdDSA].
281    ///
282    /// Applicable for authentication and asymmetric key objects.
283    ///
284    /// [EdDSA]: https://en.wikipedia.org/wiki/EdDSA
285    SignEddsa,
286
287    /// Compute HMAC of data.
288    ///
289    /// Applicable for authentication and HMAC key objects.
290    SignHmac,
291
292    /// Compute digital signatures using RSA-PKCS1v1.5.
293    ///
294    /// Applicable for authentication and asymmetric key objects.
295    SignPkcs,
296
297    /// Compute digital signatures using RSA-PSS.
298    ///
299    /// Applicable for authentication and asymmetric key objects.
300    SignPss,
301
302    /// Sign SSH certificates.
303    ///
304    /// Applicable for authentication and asymmetric key objects.
305    SignSshCertificate,
306
307    /// Unwrap user-provided data.
308    ///
309    /// Applicable for authentication and wrap key objects.
310    UnwrapData,
311
312    /// Verify HMAC of data.
313    ///
314    /// Applicable for authentication and HMAC key objects.
315    VerifyHmac,
316
317    /// Wrap user-provided data.
318    ///
319    /// Applicable for authentication and wrap key objects.
320    WrapData,
321}
322
323impl From<&Capability> for yubihsm::Capability {
324    fn from(value: &Capability) -> Self {
325        match *value {
326            Capability::ChangeAuthenticationKey => yubihsm::Capability::CHANGE_AUTHENTICATION_KEY,
327            Capability::CreateOtpAead => yubihsm::Capability::CREATE_OTP_AEAD,
328            Capability::DecryptCbc => yubihsm::Capability::UNKNOWN_CAPABILITY_52,
329            Capability::DecryptEcb => yubihsm::Capability::UNKNOWN_CAPABILITY_50,
330            Capability::DecryptOaep => yubihsm::Capability::DECRYPT_OAEP,
331            Capability::DecryptOtp => yubihsm::Capability::DECRYPT_OTP,
332            Capability::DecryptPkcs => yubihsm::Capability::DECRYPT_PKCS,
333            Capability::DeleteAsymmetricKey => yubihsm::Capability::DELETE_ASYMMETRIC_KEY,
334            Capability::DeleteAuthenticationKey => yubihsm::Capability::DELETE_AUTHENTICATION_KEY,
335            Capability::DeleteHmacKey => yubihsm::Capability::DELETE_HMAC_KEY,
336            Capability::DeleteOpaque => yubihsm::Capability::DELETE_OPAQUE,
337            Capability::DeleteOtpAeadKey => yubihsm::Capability::DELETE_OTP_AEAD_KEY,
338            Capability::DeletePublicWrapKey => yubihsm::Capability::UNKNOWN_CAPABILITY_55,
339            Capability::DeleteSymmetricKey => yubihsm::Capability::UNKNOWN_CAPABILITY_49,
340            Capability::DeleteTemplate => yubihsm::Capability::DELETE_TEMPLATE,
341            Capability::DeleteWrapKey => yubihsm::Capability::DELETE_WRAP_KEY,
342            Capability::DeriveEcdh => yubihsm::Capability::DERIVE_ECDH,
343            Capability::EncryptCbc => yubihsm::Capability::UNKNOWN_CAPABILITY_53,
344            Capability::EncryptEcb => yubihsm::Capability::UNKNOWN_CAPABILITY_51,
345            Capability::ExportableUnderWrap => yubihsm::Capability::EXPORTABLE_UNDER_WRAP,
346            Capability::ExportWrapped => yubihsm::Capability::EXPORT_WRAPPED,
347            Capability::GenerateAsymmetricKey => yubihsm::Capability::GENERATE_ASYMMETRIC_KEY,
348            Capability::GenerateHmacKey => yubihsm::Capability::GENERATE_HMAC_KEY,
349            Capability::GenerateOtpAeadKey => yubihsm::Capability::GENERATE_OTP_AEAD_KEY,
350            Capability::GenerateSymmetricKey => yubihsm::Capability::UNKNOWN_CAPABILITY_48,
351            Capability::GenerateWrapKey => yubihsm::Capability::GENERATE_WRAP_KEY,
352            Capability::GetOpaque => yubihsm::Capability::GET_OPAQUE,
353            Capability::GetOption => yubihsm::Capability::GET_OPTION,
354            Capability::GetPseudoRandom => yubihsm::Capability::GET_PSEUDO_RANDOM,
355            Capability::GetLogEntries => yubihsm::Capability::GET_LOG_ENTRIES,
356            Capability::GetTemplate => yubihsm::Capability::GET_TEMPLATE,
357            Capability::ImportWrapped => yubihsm::Capability::IMPORT_WRAPPED,
358            Capability::PutAsymmetricKey => yubihsm::Capability::PUT_ASYMMETRIC_KEY,
359            Capability::PutAuthenticationKey => yubihsm::Capability::PUT_AUTHENTICATION_KEY,
360            Capability::PutHmacKey => yubihsm::Capability::PUT_HMAC_KEY,
361            Capability::PutOpaque => yubihsm::Capability::PUT_OPAQUE,
362            Capability::PutOtpAeadKey => yubihsm::Capability::PUT_OTP_AEAD_KEY,
363            Capability::PutPublicWrapKey => yubihsm::Capability::UNKNOWN_CAPABILITY_51,
364            Capability::PutSymmetricKey => yubihsm::Capability::UNKNOWN_CAPABILITY_47,
365            Capability::PutTemplate => yubihsm::Capability::PUT_TEMPLATE,
366            Capability::PutWrapKey => yubihsm::Capability::PUT_WRAP_KEY,
367            Capability::RandomizeOtpAead => yubihsm::Capability::RANDOMIZE_OTP_AEAD,
368            Capability::RewrapFromOtpAeadKey => yubihsm::Capability::REWRAP_FROM_OTP_AEAD_KEY,
369            Capability::RewrapToOtpAeadKey => yubihsm::Capability::REWRAP_TO_OTP_AEAD_KEY,
370            Capability::ResetDevice => yubihsm::Capability::RESET_DEVICE,
371            Capability::SetOption => yubihsm::Capability::PUT_OPTION,
372            Capability::SignAttestationCertificate => {
373                yubihsm::Capability::SIGN_ATTESTATION_CERTIFICATE
374            }
375            Capability::SignEcdsa => yubihsm::Capability::SIGN_ECDSA,
376            Capability::SignEddsa => yubihsm::Capability::SIGN_EDDSA,
377            Capability::SignHmac => yubihsm::Capability::SIGN_HMAC,
378            Capability::SignPkcs => yubihsm::Capability::SIGN_PKCS,
379            Capability::SignPss => yubihsm::Capability::SIGN_PSS,
380            Capability::SignSshCertificate => yubihsm::Capability::SIGN_SSH_CERTIFICATE,
381            Capability::UnwrapData => yubihsm::Capability::UNWRAP_DATA,
382            Capability::VerifyHmac => yubihsm::Capability::VERIFY_HMAC,
383            Capability::WrapData => yubihsm::Capability::WRAP_DATA,
384        }
385    }
386}
387
388/// A set of capabilities of an object on a YubiHSM2.
389#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
390#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
391pub struct Capabilities(BTreeSet<Capability>);
392
393impl Display for Capabilities {
394    /// Formats a [`Capabilities`] as a string.
395    ///
396    /// Here, the capabilities in `self` are represented as a comma-separated list (e.g.
397    /// `change-authentication-key, create-otp-aead, decrypt-cbc` or
398    /// `change-authentication-key`).
399    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400        write!(
401            f,
402            "{}",
403            self.0
404                .iter()
405                .map(|capability| capability.as_ref())
406                .collect::<Vec<_>>()
407                .join(", ")
408        )
409    }
410}
411
412impl AsRef<BTreeSet<Capability>> for Capabilities {
413    fn as_ref(&self) -> &BTreeSet<Capability> {
414        &self.0
415    }
416}
417
418impl From<[u8; 8]> for Capabilities {
419    fn from(bytes: [u8; 8]) -> Self {
420        let numeric = u64::from_be_bytes(bytes);
421        let value = yubihsm::Capability::from_bits_retain(numeric);
422        Self(
423            Capability::iter()
424                .filter(|capability| value.contains(capability.into()))
425                .collect(),
426        )
427    }
428}
429
430impl From<yubihsm::Capability> for Capabilities {
431    fn from(value: yubihsm::Capability) -> Self {
432        let lookup = [
433            (
434                yubihsm::Capability::CHANGE_AUTHENTICATION_KEY,
435                Capability::ChangeAuthenticationKey,
436            ),
437            (
438                yubihsm::Capability::CREATE_OTP_AEAD,
439                Capability::CreateOtpAead,
440            ),
441            // NOTE: This capability is not yet understood by the underlying library.
442            (
443                yubihsm::Capability::UNKNOWN_CAPABILITY_52,
444                Capability::DecryptCbc,
445            ),
446            // NOTE: This capability is not yet understood by the underlying library.
447            (
448                yubihsm::Capability::UNKNOWN_CAPABILITY_50,
449                Capability::DecryptEcb,
450            ),
451            (yubihsm::Capability::DECRYPT_OAEP, Capability::DecryptOaep),
452            (yubihsm::Capability::DECRYPT_OTP, Capability::DecryptOtp),
453            (yubihsm::Capability::DECRYPT_PKCS, Capability::DecryptPkcs),
454            (
455                yubihsm::Capability::DELETE_ASYMMETRIC_KEY,
456                Capability::DeleteAsymmetricKey,
457            ),
458            (
459                yubihsm::Capability::DELETE_AUTHENTICATION_KEY,
460                Capability::DeleteAuthenticationKey,
461            ),
462            (
463                yubihsm::Capability::DELETE_HMAC_KEY,
464                Capability::DeleteHmacKey,
465            ),
466            (yubihsm::Capability::DELETE_OPAQUE, Capability::DeleteOpaque),
467            (
468                yubihsm::Capability::DELETE_OTP_AEAD_KEY,
469                Capability::DeleteOtpAeadKey,
470            ),
471            // NOTE: This capability is not yet understood by the underlying library.
472            (
473                yubihsm::Capability::UNKNOWN_CAPABILITY_55,
474                Capability::DeletePublicWrapKey,
475            ),
476            // NOTE: This capability is not yet understood by the underlying library.
477            (
478                yubihsm::Capability::UNKNOWN_CAPABILITY_49,
479                Capability::DeleteSymmetricKey,
480            ),
481            (
482                yubihsm::Capability::DELETE_TEMPLATE,
483                Capability::DeleteTemplate,
484            ),
485            (
486                yubihsm::Capability::DELETE_WRAP_KEY,
487                Capability::DeleteWrapKey,
488            ),
489            (yubihsm::Capability::DERIVE_ECDH, Capability::DeriveEcdh),
490            // NOTE: This capability is not yet understood by the underlying library.
491            (
492                yubihsm::Capability::UNKNOWN_CAPABILITY_53,
493                Capability::EncryptCbc,
494            ),
495            // NOTE: This capability is not yet understood by the underlying library.
496            (
497                yubihsm::Capability::UNKNOWN_CAPABILITY_51,
498                Capability::EncryptEcb,
499            ),
500            (
501                yubihsm::Capability::EXPORTABLE_UNDER_WRAP,
502                Capability::ExportableUnderWrap,
503            ),
504            (
505                yubihsm::Capability::EXPORT_WRAPPED,
506                Capability::ExportWrapped,
507            ),
508            (
509                yubihsm::Capability::GENERATE_ASYMMETRIC_KEY,
510                Capability::GenerateAsymmetricKey,
511            ),
512            (
513                yubihsm::Capability::GENERATE_HMAC_KEY,
514                Capability::GenerateHmacKey,
515            ),
516            (
517                yubihsm::Capability::GENERATE_OTP_AEAD_KEY,
518                Capability::GenerateOtpAeadKey,
519            ),
520            // NOTE: This capability is not yet understood by the underlying library.
521            (
522                yubihsm::Capability::UNKNOWN_CAPABILITY_48,
523                Capability::GenerateSymmetricKey,
524            ),
525            (
526                yubihsm::Capability::GENERATE_WRAP_KEY,
527                Capability::GenerateWrapKey,
528            ),
529            (yubihsm::Capability::GET_OPAQUE, Capability::GetOpaque),
530            (yubihsm::Capability::GET_OPTION, Capability::GetOption),
531            (
532                yubihsm::Capability::GET_PSEUDO_RANDOM,
533                Capability::GetPseudoRandom,
534            ),
535            (
536                yubihsm::Capability::GET_LOG_ENTRIES,
537                Capability::GetLogEntries,
538            ),
539            (yubihsm::Capability::GET_TEMPLATE, Capability::GetTemplate),
540            (
541                yubihsm::Capability::IMPORT_WRAPPED,
542                Capability::ImportWrapped,
543            ),
544            (
545                yubihsm::Capability::PUT_ASYMMETRIC_KEY,
546                Capability::PutAsymmetricKey,
547            ),
548            (
549                yubihsm::Capability::PUT_AUTHENTICATION_KEY,
550                Capability::PutAuthenticationKey,
551            ),
552            (yubihsm::Capability::PUT_HMAC_KEY, Capability::PutHmacKey),
553            (yubihsm::Capability::PUT_OPAQUE, Capability::PutOpaque),
554            (
555                yubihsm::Capability::PUT_OTP_AEAD_KEY,
556                Capability::PutOtpAeadKey,
557            ),
558            // NOTE: This capability is not yet understood by the underlying library.
559            (
560                yubihsm::Capability::UNKNOWN_CAPABILITY_54,
561                Capability::PutPublicWrapKey,
562            ),
563            // NOTE: This capability is not yet understood by the underlying library.
564            (
565                yubihsm::Capability::UNKNOWN_CAPABILITY_47,
566                Capability::PutSymmetricKey,
567            ),
568            (yubihsm::Capability::PUT_TEMPLATE, Capability::PutTemplate),
569            (yubihsm::Capability::PUT_WRAP_KEY, Capability::PutWrapKey),
570            (
571                yubihsm::Capability::RANDOMIZE_OTP_AEAD,
572                Capability::RandomizeOtpAead,
573            ),
574            (
575                yubihsm::Capability::REWRAP_FROM_OTP_AEAD_KEY,
576                Capability::RewrapFromOtpAeadKey,
577            ),
578            (
579                yubihsm::Capability::REWRAP_TO_OTP_AEAD_KEY,
580                Capability::RewrapToOtpAeadKey,
581            ),
582            (yubihsm::Capability::RESET_DEVICE, Capability::ResetDevice),
583            (yubihsm::Capability::PUT_OPTION, Capability::SetOption),
584            (
585                yubihsm::Capability::SIGN_ATTESTATION_CERTIFICATE,
586                Capability::SignAttestationCertificate,
587            ),
588            (yubihsm::Capability::SIGN_ECDSA, Capability::SignEcdsa),
589            (yubihsm::Capability::SIGN_EDDSA, Capability::SignEddsa),
590            (yubihsm::Capability::SIGN_HMAC, Capability::SignHmac),
591            (yubihsm::Capability::SIGN_PKCS, Capability::SignPkcs),
592            (yubihsm::Capability::SIGN_PSS, Capability::SignPss),
593            (
594                yubihsm::Capability::SIGN_SSH_CERTIFICATE,
595                Capability::SignSshCertificate,
596            ),
597            (yubihsm::Capability::UNWRAP_DATA, Capability::UnwrapData),
598            (yubihsm::Capability::VERIFY_HMAC, Capability::VerifyHmac),
599            (yubihsm::Capability::WRAP_DATA, Capability::WrapData),
600        ];
601
602        Self(BTreeSet::from_iter(lookup.iter().filter_map(
603            |(yubi_cap, cap)| {
604                if value.contains(*yubi_cap) {
605                    Some(*cap)
606                } else {
607                    None
608                }
609            },
610        )))
611    }
612}
613
614impl From<&Capabilities> for [u8; 8] {
615    fn from(value: &Capabilities) -> Self {
616        yubihsm::Capability::from(value).bits().to_be_bytes()
617    }
618}
619
620impl From<&Capabilities> for yubihsm::Capability {
621    fn from(value: &Capabilities) -> Self {
622        value
623            .0
624            .iter()
625            .map(yubihsm::Capability::from)
626            .fold(yubihsm::Capability::empty(), |acc, c| acc | c)
627    }
628}
629
630impl From<&[Capability]> for Capabilities {
631    fn from(value: &[Capability]) -> Self {
632        Self(value.iter().copied().collect())
633    }
634}
635
636#[cfg(test)]
637mod tests {
638    use rstest::rstest;
639
640    use super::*;
641
642    /// Ensures that [`Capabilities::to_string`] works as expected.
643    #[test]
644    fn capabilities_to_string() {
645        let capability_list = vec![Capability::ChangeAuthenticationKey];
646        let capabilities = Capabilities::from(capability_list.as_slice());
647        assert_eq!("change-authentication-key", capabilities.to_string());
648
649        let capability_list = vec![
650            Capability::ChangeAuthenticationKey,
651            Capability::CreateOtpAead,
652        ];
653        let capabilities = Capabilities::from(capability_list.as_slice());
654        assert_eq!(
655            "change-authentication-key, create-otp-aead",
656            capabilities.to_string()
657        );
658    }
659
660    /// Ensures that [`Capabilities`] are created correctly from [`yubihsm::Capabilities`].
661    #[rstest]
662    #[case::change_authentication_key(
663        yubihsm::Capability::CHANGE_AUTHENTICATION_KEY,
664        Capabilities(BTreeSet::from_iter([Capability::ChangeAuthenticationKey])),
665    )]
666    #[case::create_otp_aead(
667        yubihsm::Capability::CREATE_OTP_AEAD,
668        Capabilities(BTreeSet::from_iter([Capability::CreateOtpAead])),
669    )]
670    // NOTE: This capability is not yet understood by the underlying library.
671    #[case::decrypt_cbc(
672        yubihsm::Capability::UNKNOWN_CAPABILITY_52,
673        Capabilities(BTreeSet::from_iter([Capability::DecryptCbc])),
674    )]
675    // NOTE: This capability is not yet understood by the underlying library.
676    #[case::decrypt_ecb(
677        yubihsm::Capability::UNKNOWN_CAPABILITY_50,
678        Capabilities(BTreeSet::from_iter([Capability::DecryptEcb])),
679    )]
680    #[case::decrypt_oaep(
681        yubihsm::Capability::DECRYPT_OAEP,
682        Capabilities(BTreeSet::from_iter([Capability::DecryptOaep])),
683    )]
684    #[case::decrypt_otp(
685        yubihsm::Capability::DECRYPT_OTP,
686        Capabilities(BTreeSet::from_iter([Capability::DecryptOtp])),
687    )]
688    #[case::decrypt_pkcs(
689        yubihsm::Capability::DECRYPT_PKCS,
690        Capabilities(BTreeSet::from_iter([Capability::DecryptPkcs])),
691    )]
692    #[case::delete_asymmetric_key(
693        yubihsm::Capability::DELETE_ASYMMETRIC_KEY,
694        Capabilities(BTreeSet::from_iter([Capability::DeleteAsymmetricKey])),
695    )]
696    #[case::delete_authentication_key(
697        yubihsm::Capability::DELETE_AUTHENTICATION_KEY,
698        Capabilities(BTreeSet::from_iter([Capability::DeleteAuthenticationKey])),
699    )]
700    #[case::delete_hmac_key(
701        yubihsm::Capability::DELETE_HMAC_KEY,
702        Capabilities(BTreeSet::from_iter([Capability::DeleteHmacKey])),
703    )]
704    #[case::delete_opaque(
705        yubihsm::Capability::DELETE_OPAQUE,
706        Capabilities(BTreeSet::from_iter([Capability::DeleteOpaque])),
707    )]
708    #[case::delete_otp_aead_key(
709        yubihsm::Capability::DELETE_OTP_AEAD_KEY,
710        Capabilities(BTreeSet::from_iter([Capability::DeleteOtpAeadKey])),
711    )]
712    // NOTE: This capability is not yet understood by the underlying library.
713    #[case::delete_public_wrap_key(
714        yubihsm::Capability::UNKNOWN_CAPABILITY_55,
715        Capabilities(BTreeSet::from_iter([Capability::DeletePublicWrapKey])),
716    )]
717    // NOTE: This capability is not yet understood by the underlying library.
718    #[case::delete_symmetric_key(
719        yubihsm::Capability::UNKNOWN_CAPABILITY_49,
720        Capabilities(BTreeSet::from_iter([Capability::DeleteSymmetricKey])),
721    )]
722    #[case::delete_template(
723        yubihsm::Capability::DELETE_TEMPLATE,
724        Capabilities(BTreeSet::from_iter([Capability::DeleteTemplate])),
725    )]
726    #[case::delete_wrap_key(
727        yubihsm::Capability::DELETE_WRAP_KEY,
728        Capabilities(BTreeSet::from_iter([Capability::DeleteWrapKey])),
729    )]
730    #[case::derive_ecdh(
731        yubihsm::Capability::DERIVE_ECDH,
732        Capabilities(BTreeSet::from_iter([Capability::DeriveEcdh])),
733    )]
734    // NOTE: This capability is not yet understood by the underlying library.
735    #[case::encrypt_cbc(
736        yubihsm::Capability::UNKNOWN_CAPABILITY_53,
737        Capabilities(BTreeSet::from_iter([Capability::EncryptCbc])),
738    )]
739    // NOTE: This capability is not yet understood by the underlying library.
740    #[case::encrypt_ecb(
741        yubihsm::Capability::UNKNOWN_CAPABILITY_51,
742        Capabilities(BTreeSet::from_iter([Capability::EncryptEcb])),
743    )]
744    #[case::exportable_under_wrap(
745        yubihsm::Capability::EXPORTABLE_UNDER_WRAP,
746        Capabilities(BTreeSet::from_iter([Capability::ExportableUnderWrap])),
747    )]
748    #[case::export_wrapped(
749        yubihsm::Capability::EXPORT_WRAPPED,
750        Capabilities(BTreeSet::from_iter([Capability::ExportWrapped])),
751    )]
752    #[case::generate_asymmetric_key(
753        yubihsm::Capability::GENERATE_ASYMMETRIC_KEY,
754        Capabilities(BTreeSet::from_iter([Capability::GenerateAsymmetricKey])),
755    )]
756    #[case::generate_hmac_key(
757        yubihsm::Capability::GENERATE_HMAC_KEY,
758        Capabilities(BTreeSet::from_iter([Capability::GenerateHmacKey])),
759    )]
760    #[case::generate_otp_aead_key(
761        yubihsm::Capability::GENERATE_OTP_AEAD_KEY,
762        Capabilities(BTreeSet::from_iter([Capability::GenerateOtpAeadKey])),
763    )]
764    // NOTE: This capability is not yet understood by the underlying library.
765    #[case::generate_symmetric_key(
766        yubihsm::Capability::UNKNOWN_CAPABILITY_48,
767        Capabilities(BTreeSet::from_iter([Capability::GenerateSymmetricKey])),
768    )]
769    #[case::generate_wrap_key(
770        yubihsm::Capability::GENERATE_WRAP_KEY,
771        Capabilities(BTreeSet::from_iter([Capability::GenerateWrapKey])),
772    )]
773    #[case::get_opaque(
774        yubihsm::Capability::GET_OPAQUE,
775        Capabilities(BTreeSet::from_iter([Capability::GetOpaque])),
776    )]
777    #[case::get_option(
778        yubihsm::Capability::GET_OPTION,
779        Capabilities(BTreeSet::from_iter([Capability::GetOption])),
780    )]
781    #[case::get_pseudo_random(
782        yubihsm::Capability::GET_PSEUDO_RANDOM,
783        Capabilities(BTreeSet::from_iter([Capability::GetPseudoRandom])),
784    )]
785    #[case::get_log_entries(
786        yubihsm::Capability::GET_LOG_ENTRIES,
787        Capabilities(BTreeSet::from_iter([Capability::GetLogEntries])),
788    )]
789    #[case::get_template(
790        yubihsm::Capability::GET_TEMPLATE,
791        Capabilities(BTreeSet::from_iter([Capability::GetTemplate])),
792    )]
793    #[case::import_wrapped(
794        yubihsm::Capability::IMPORT_WRAPPED,
795        Capabilities(BTreeSet::from_iter([Capability::ImportWrapped])),
796    )]
797    #[case::put_asymmetric_key(
798        yubihsm::Capability::PUT_ASYMMETRIC_KEY,
799        Capabilities(BTreeSet::from_iter([Capability::PutAsymmetricKey])),
800    )]
801    #[case::put_authentication_key(
802        yubihsm::Capability::PUT_AUTHENTICATION_KEY,
803        Capabilities(BTreeSet::from_iter([Capability::PutAuthenticationKey])),
804    )]
805    #[case::put_hmac_key(
806        yubihsm::Capability::PUT_HMAC_KEY,
807        Capabilities(BTreeSet::from_iter([Capability::PutHmacKey])),
808    )]
809    #[case::put_opaque(
810        yubihsm::Capability::PUT_OPAQUE,
811        Capabilities(BTreeSet::from_iter([Capability::PutOpaque])),
812    )]
813    #[case::put_otp_aead_key(
814        yubihsm::Capability::PUT_OTP_AEAD_KEY,
815        Capabilities(BTreeSet::from_iter([Capability::PutOtpAeadKey])),
816    )]
817    // NOTE: This capability is not yet understood by the underlying library.
818    #[case::put_public_wrap_key(
819        yubihsm::Capability::UNKNOWN_CAPABILITY_54,
820        Capabilities(BTreeSet::from_iter([Capability::PutPublicWrapKey])),
821    )]
822    // NOTE: This capability is not yet understood by the underlying library.
823    #[case::put_symmetric_key(
824        yubihsm::Capability::UNKNOWN_CAPABILITY_47,
825        Capabilities(BTreeSet::from_iter([Capability::PutSymmetricKey])),
826    )]
827    #[case::put_template(
828        yubihsm::Capability::PUT_TEMPLATE,
829        Capabilities(BTreeSet::from_iter([Capability::PutTemplate])),
830    )]
831    #[case::put_wrap_key(
832        yubihsm::Capability::PUT_WRAP_KEY,
833        Capabilities(BTreeSet::from_iter([Capability::PutWrapKey])),
834    )]
835    #[case::randomize_otp_aead(
836        yubihsm::Capability::RANDOMIZE_OTP_AEAD,
837        Capabilities(BTreeSet::from_iter([Capability::RandomizeOtpAead])),
838    )]
839    #[case::rewrap_from_otp_aead_key(
840        yubihsm::Capability::REWRAP_FROM_OTP_AEAD_KEY,
841        Capabilities(BTreeSet::from_iter([Capability::RewrapFromOtpAeadKey])),
842    )]
843    #[case::rewrap_to_otp_aead_key(
844        yubihsm::Capability::REWRAP_TO_OTP_AEAD_KEY,
845        Capabilities(BTreeSet::from_iter([Capability::RewrapToOtpAeadKey])),
846    )]
847    #[case::reset_device(
848        yubihsm::Capability::RESET_DEVICE,
849        Capabilities(BTreeSet::from_iter([Capability::ResetDevice])),
850    )]
851    #[case::put_option(
852        yubihsm::Capability::PUT_OPTION,
853        Capabilities(BTreeSet::from_iter([Capability::SetOption])),
854    )]
855    #[case::sign_attestation_certificate(
856        yubihsm::Capability::SIGN_ATTESTATION_CERTIFICATE,
857        Capabilities(BTreeSet::from_iter([Capability::SignAttestationCertificate])),
858    )]
859    #[case::sign_ecdsa(
860        yubihsm::Capability::SIGN_ECDSA,
861        Capabilities(BTreeSet::from_iter([Capability::SignEcdsa])),
862    )]
863    #[case::sign_eddsa(
864        yubihsm::Capability::SIGN_EDDSA,
865        Capabilities(BTreeSet::from_iter([Capability::SignEddsa])),
866    )]
867    #[case::sign_hmac(
868        yubihsm::Capability::SIGN_HMAC,
869        Capabilities(BTreeSet::from_iter([Capability::SignHmac])),
870    )]
871    #[case::sign_pkcs(
872        yubihsm::Capability::SIGN_PKCS,
873        Capabilities(BTreeSet::from_iter([Capability::SignPkcs])),
874    )]
875    #[case::sign_pss(
876        yubihsm::Capability::SIGN_PSS,
877        Capabilities(BTreeSet::from_iter([Capability::SignPss])),
878    )]
879    #[case::sign_ssh_certificate(
880        yubihsm::Capability::SIGN_SSH_CERTIFICATE,
881        Capabilities(BTreeSet::from_iter([Capability::SignSshCertificate])),
882    )]
883    #[case::unwrap_data(
884        yubihsm::Capability::UNWRAP_DATA,
885        Capabilities(BTreeSet::from_iter([Capability::UnwrapData])),
886    )]
887    #[case::verify_hmac(
888        yubihsm::Capability::VERIFY_HMAC,
889        Capabilities(BTreeSet::from_iter([Capability::VerifyHmac])),
890    )]
891    #[case::wrap_data(
892        yubihsm::Capability::WRAP_DATA,
893        Capabilities(BTreeSet::from_iter([Capability::WrapData])),
894    )]
895    fn capabilities_from_yubihsm_capability(
896        #[case] yubi_cap: yubihsm::Capability,
897        #[case] cap: Capabilities,
898    ) {
899        assert_eq!(Capabilities::from(yubi_cap), cap);
900    }
901}