heddle_api/v2/
passkey_label.rs1use unicode_general_category::{GeneralCategory, get_general_category};
4use unicode_normalization::UnicodeNormalization;
5
6pub const DEFAULT_PASSKEY_LABEL: &str = "Passkey";
7pub const MAX_PASSKEY_LABEL_BYTES: usize = 256;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
10pub enum PasskeyLabelError {
11 #[error("passkey label contains a forbidden Unicode character")]
12 InvalidCharacter,
13 #[error("passkey label exceeds 256 UTF-8 bytes after NFC")]
14 TooLong,
15}
16
17pub fn normalize_passkey_label(label: &str) -> Result<String, PasskeyLabelError> {
20 let normalized: String = label.trim().nfc().collect();
21 if normalized.chars().any(|character| {
22 matches!(
23 get_general_category(character),
24 GeneralCategory::Control
25 | GeneralCategory::Format
26 | GeneralCategory::Surrogate
27 | GeneralCategory::PrivateUse
28 | GeneralCategory::Unassigned
29 | GeneralCategory::LineSeparator
30 | GeneralCategory::ParagraphSeparator
31 )
32 }) {
33 return Err(PasskeyLabelError::InvalidCharacter);
34 }
35 if normalized.len() > MAX_PASSKEY_LABEL_BYTES {
36 return Err(PasskeyLabelError::TooLong);
37 }
38 Ok(normalized)
39}
40
41pub fn passkey_display_label(stored_label: &str) -> &str {
44 if stored_label.is_empty() {
45 DEFAULT_PASSKEY_LABEL
46 } else {
47 stored_label
48 }
49}