passkey_authenticator/authenticator/
extensions.rs1use passkey_types::ctap2::{StatusCode, get_assertion, get_info, make_credential};
13
14mod hmac_secret;
15pub use hmac_secret::{HmacSecretConfig, HmacSecretCredentialSupport};
16
17#[cfg(test)]
18pub(crate) use hmac_secret::tests::prf_eval_request;
19
20#[cfg(doc)]
21use passkey_types::webauthn;
22
23use crate::{Authenticator, passkey::PasskeyAccessor};
24
25#[derive(Debug, Default)]
26#[non_exhaustive]
27pub(super) struct Extensions {
28 pub hmac_secret: Option<HmacSecretConfig>,
30}
31
32impl Extensions {
33 pub fn list_extensions(&self) -> Option<Vec<get_info::Extension>> {
35 let prf = self
37 .hmac_secret
38 .is_some()
39 .then_some(get_info::Extension::Prf);
40
41 prf.map(|ext| vec![ext])
42 }
43}
44
45pub(super) struct MakeExtensionOutputs {
46 pub signed: Option<make_credential::SignedExtensionOutputs>,
47 pub unsigned: Option<make_credential::UnsignedExtensionOutputs>,
48 pub credential: passkey_types::CredentialExtensions,
49}
50
51#[derive(Default)]
52pub(super) struct GetExtensionOutputs {
53 pub signed: Option<get_assertion::SignedExtensionOutputs>,
54 pub unsigned: Option<get_assertion::UnsignedExtensionOutputs>,
55}
56
57impl<S, U> Authenticator<S, U> {
58 pub(super) fn make_extensions(
59 &self,
60 request: Option<make_credential::ExtensionInputs>,
61 uv: bool,
62 ) -> Result<MakeExtensionOutputs, StatusCode> {
63 let request = request.and_then(|r| r.zip_contents());
64 let pk_extensions = self.make_passkey_extensions(request.as_ref());
65
66 let prf = request
67 .and_then(|ext| {
68 ext.prf.and_then(|input| {
69 self.make_prf(pk_extensions.hmac_secret.as_ref(), input, uv)
70 .transpose()
71 })
72 })
73 .transpose()?;
74
75 Ok(MakeExtensionOutputs {
76 signed: None,
77 unsigned: make_credential::UnsignedExtensionOutputs { prf }.zip_contents(),
78 credential: pk_extensions,
79 })
80 }
81
82 fn make_passkey_extensions(
83 &self,
84 request: Option<&make_credential::ExtensionInputs>,
85 ) -> passkey_types::CredentialExtensions {
86 let should_build_hmac_secret =
87 request.and_then(|r| r.hmac_secret.or(Some(r.prf.is_some())));
88 let hmac_secret = self.make_hmac_secret(should_build_hmac_secret);
89
90 passkey_types::CredentialExtensions { hmac_secret }
91 }
92
93 pub(super) fn get_extensions<P>(
94 &self,
95 passkey: &P,
96 request: Option<get_assertion::ExtensionInputs>,
97 uv: bool,
98 ) -> Result<GetExtensionOutputs, StatusCode>
99 where
100 P: PasskeyAccessor,
101 {
102 let Some(ext) = request.and_then(get_assertion::ExtensionInputs::zip_contents) else {
103 return Ok(Default::default());
104 };
105
106 let prf = ext
107 .prf
108 .and_then(|salts| {
109 self.get_prf(
110 passkey.credential_id(),
111 passkey.extensions().hmac_secret.as_ref(),
112 salts,
113 uv,
114 )
115 .transpose()
116 })
117 .transpose()?;
118
119 Ok(GetExtensionOutputs {
120 signed: None,
121 unsigned: get_assertion::UnsignedExtensionOutputs { prf }.zip_contents(),
122 })
123 }
124}