1use crate::capability::{CapabilityType, OperationCapability};
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum BiometricKind {
12 Face,
13 Fingerprint,
14 Iris,
15 DeviceCredential,
16 Unknown,
17}
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
21pub enum BiometricStrength {
22 #[default]
23 Any,
24 Weak,
25 Strong,
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
30pub struct BiometricAvailability {
31 pub supported: bool,
32 pub enrolled: bool,
33 pub strong: bool,
34 pub weak: bool,
35 pub device_credential: bool,
36 pub kinds: Vec<BiometricKind>,
37 pub reason: Option<String>,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
42pub struct BiometricAuthenticateRequest {
43 pub reason: String,
44 pub title: Option<String>,
45 pub subtitle: Option<String>,
46 pub fallback_title: Option<String>,
47 pub cancel_title: Option<String>,
48 pub allow_device_credential: bool,
49 pub required_strength: BiometricStrength,
50}
51
52impl Default for BiometricAuthenticateRequest {
53 fn default() -> Self {
54 Self {
55 reason: String::new(),
56 title: None,
57 subtitle: None,
58 fallback_title: None,
59 cancel_title: None,
60 allow_device_credential: true,
61 required_strength: BiometricStrength::Any,
62 }
63 }
64}
65
66#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
68pub struct BiometricAuthenticateResult {
69 pub verified: bool,
70 pub kind: Option<BiometricKind>,
71 pub used_device_credential: bool,
72}
73
74#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
76pub struct BiometricError {
77 pub code: String,
78 pub message: String,
79}
80
81impl BiometricError {
82 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
89 Self {
90 code: code.into(),
91 message: message.into(),
92 }
93 }
94
95 pub fn unsupported(operation: impl Into<String>) -> Self {
101 Self::new(
102 "unsupported",
103 format!(
104 "biometric operation `{}` is not supported by this host",
105 operation.into()
106 ),
107 )
108 }
109}
110
111pub struct GetBiometricAvailabilityCapability;
112impl OperationCapability for GetBiometricAvailabilityCapability {
113 type Request = ();
114 type Ok = BiometricAvailability;
115 type Err = BiometricError;
116}
117
118pub struct AuthenticateBiometricCapability;
119impl OperationCapability for AuthenticateBiometricCapability {
120 type Request = BiometricAuthenticateRequest;
121 type Ok = BiometricAuthenticateResult;
122 type Err = BiometricError;
123}
124
125pub struct CancelBiometricAuthenticationCapability;
126impl OperationCapability for CancelBiometricAuthenticationCapability {
127 type Request = ();
128 type Ok = ();
129 type Err = BiometricError;
130}
131
132pub const GET_BIOMETRIC_AVAILABILITY: CapabilityType<GetBiometricAvailabilityCapability> =
133 CapabilityType::new("fission.biometric.get_availability");
134pub const AUTHENTICATE_BIOMETRIC: CapabilityType<AuthenticateBiometricCapability> =
135 CapabilityType::new("fission.biometric.authenticate");
136pub const CANCEL_BIOMETRIC_AUTHENTICATION: CapabilityType<CancelBiometricAuthenticationCapability> =
137 CapabilityType::new("fission.biometric.cancel_authentication");
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn biometric_request_round_trips() {
145 let request = BiometricAuthenticateRequest {
146 reason: "Unlock encrypted notes".into(),
147 title: Some("Unlock".into()),
148 subtitle: Some("Confirm it is you".into()),
149 fallback_title: Some("Use passcode".into()),
150 cancel_title: Some("Cancel".into()),
151 allow_device_credential: true,
152 required_strength: BiometricStrength::Strong,
153 };
154
155 let bytes = serde_json::to_vec(&request).unwrap();
156 let decoded: BiometricAuthenticateRequest = serde_json::from_slice(&bytes).unwrap();
157
158 assert_eq!(decoded, request);
159 }
160
161 #[test]
162 fn availability_round_trips() {
163 let availability = BiometricAvailability {
164 supported: true,
165 enrolled: true,
166 strong: true,
167 weak: true,
168 device_credential: true,
169 kinds: vec![BiometricKind::Face, BiometricKind::Fingerprint],
170 reason: None,
171 };
172
173 let bytes = serde_json::to_vec(&availability).unwrap();
174 let decoded: BiometricAvailability = serde_json::from_slice(&bytes).unwrap();
175
176 assert_eq!(decoded, availability);
177 }
178}