spectre/
types.rs

1use serde::{Deserialize, Serialize};
2use std::str::FromStr;
3use crate::error::{Result, SpectreError};
4
5pub const SPECTRE_ALGORITHM_FIRST: u32 = 0;
6pub const SPECTRE_ALGORITHM_CURRENT: u32 = 3;
7pub const SPECTRE_ALGORITHM_LAST: u32 = 3;
8
9pub const SPECTRE_COUNTER_DEFAULT: u32 = 1;
10pub const SPECTRE_COUNTER_INITIAL: u32 = 0;
11pub const SPECTRE_COUNTER_FIRST: u32 = 0;
12pub const SPECTRE_COUNTER_LAST: u32 = u32::MAX;
13
14pub type SpectreAlgorithm = u32;
15pub type SpectreCounter = u32;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18pub enum SpectreKeyPurpose {
19    Authentication,
20    Identification,
21    Recovery,
22}
23
24impl SpectreKeyPurpose {
25    pub fn name(&self) -> &'static str {
26        match self {
27            Self::Authentication => "authentication",
28            Self::Identification => "identification",
29            Self::Recovery => "recovery",
30        }
31    }
32}
33
34impl FromStr for SpectreKeyPurpose {
35    type Err = SpectreError;
36
37    fn from_str(s: &str) -> Result<Self> {
38        match s {
39            "a" | "auth" | "authentication" => Ok(Self::Authentication),
40            "i" | "ident" | "identification" => Ok(Self::Identification),
41            "r" | "rec" | "recovery" => Ok(Self::Recovery),
42            _ => Err(SpectreError::InvalidKeyPurpose(s.to_string())),
43        }
44    }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
48#[repr(u32)]
49pub enum SpectreResultType {
50    // Template-based results
51    MaximumSecurityPassword = 0x00010000,
52    LongPassword = 0x00010001,
53    MediumPassword = 0x00010002,
54    BasicPassword = 0x00010004,
55    ShortPassword = 0x00010003,
56    PIN = 0x00010005,
57    Name = 0x00010006,
58    Phrase = 0x00010007,
59    
60    // Stateful results
61    PersonalPassword = 0x00020000,
62    DeriveKey = 0x00020001,
63    
64    #[default]
65    None = 0x0,
66}
67
68impl SpectreResultType {
69    pub fn short_name(&self) -> &'static str {
70        match self {
71            Self::MaximumSecurityPassword => "maximum",
72            Self::LongPassword => "long",
73            Self::MediumPassword => "medium",
74            Self::BasicPassword => "basic",
75            Self::ShortPassword => "short",
76            Self::PIN => "pin",
77            Self::Name => "name",
78            Self::Phrase => "phrase",
79            Self::DeriveKey => "key",
80            Self::PersonalPassword => "personal",
81            Self::None => "none",
82        }
83    }
84
85    pub fn is_stateful(&self) -> bool {
86        matches!(self, Self::PersonalPassword | Self::DeriveKey)
87    }
88
89    pub fn template(&self) -> &'static [&'static str] {
90        match self {
91            Self::MaximumSecurityPassword => &[
92                "anoxxxxxxxxxxxxxxxxx",
93                "axxxxxxxxxxxxxxxxxno",
94            ],
95            Self::LongPassword => &[
96                "CvcvnoCvcvCvcv",
97                "CvcvCvcvnoCvcv",
98                "CvcvCvcvCvcvno",
99                "CvccnoCvcvCvcv",
100                "CvccCvcvnoCvcv",
101                "CvccCvcvCvcvno",
102                "CvcvnoCvccCvcv",
103                "CvcvCvccnoCvcv",
104                "CvcvCvccCvcvno",
105                "CvcvnoCvcvCvcc",
106                "CvcvCvcvnoCvcc",
107                "CvcvCvcvCvccno",
108                "CvccnoCvccCvcv",
109                "CvccCvccnoCvcv",
110                "CvccCvccCvcvno",
111                "CvcvnoCvccCvcc",
112                "CvcvCvccnoCvcc",
113                "CvcvCvccCvccno",
114                "CvccnoCvcvCvcc",
115                "CvccCvcvnoCvcc",
116                "CvccCvcvCvccno",
117            ],
118            Self::MediumPassword => &[
119                "CvcnoCvc",
120                "CvcCvcno",
121            ],
122            Self::BasicPassword => &[
123                "aaanaaan",
124                "aannaaan",
125                "aaannaaa",
126            ],
127            Self::ShortPassword => &[
128                "Cvcn",
129            ],
130            Self::PIN => &[
131                "nnnn",
132            ],
133            Self::Name => &[
134                "cvccvcvcv",
135            ],
136            Self::Phrase => &[
137                "cvcc cvc cvccvcv cvc",
138                "cvc cvccvcvcv cvcv",
139                "cv cvccv cvc cvcvccv",
140            ],
141            _ => &[],
142        }
143    }
144}
145
146impl FromStr for SpectreResultType {
147    type Err = SpectreError;
148
149    fn from_str(s: &str) -> Result<Self> {
150        match s {
151            "x" | "max" | "maximum" => Ok(Self::MaximumSecurityPassword),
152            "l" | "long" => Ok(Self::LongPassword),
153            "m" | "medium" => Ok(Self::MediumPassword),
154            "b" | "basic" => Ok(Self::BasicPassword),
155            "s" | "short" => Ok(Self::ShortPassword),
156            "i" | "pin" => Ok(Self::PIN),
157            "n" | "name" => Ok(Self::Name),
158            "p" | "phrase" => Ok(Self::Phrase),
159            "K" | "key" => Ok(Self::DeriveKey),
160            "P" | "personal" => Ok(Self::PersonalPassword),
161            _ => Err(SpectreError::InvalidResultType(s.to_string())),
162        }
163    }
164}
165
166pub const SPECTRE_RESULT_DEFAULT_RESULT: SpectreResultType = SpectreResultType::LongPassword;
167
168// Character classes for templates - based on official Spectre algorithm specification
169pub fn char_class_for_template(c: char) -> &'static [char] {
170    match c {
171        'V' => &['A', 'E', 'I', 'O', 'U'],
172        'C' => &['B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'],
173        'v' => &['a', 'e', 'i', 'o', 'u'],
174        'c' => &['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'],
175        'A' => &['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
176        'a' => &['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'],
177        'n' => &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
178        // Official Spectre symbol set - order matters!
179        // From the reference implementation: "@&%?,=[]_:-+*$#!'^~;()/."
180        'o' => &['@', '&', '%', '?', ',', '=', '[', ']', '_', ':', '-', '+', '*', '$', '#', '!', '\'', '^', '~', ';', '(', ')', '/', '.'],
181        'x' => &['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'],
182        ' ' => &[' '],
183        _ => &[],
184    }
185}
186