1use crypto_core::Algorithm;
8
9use crate::AlgorithmError;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum ProviderOperation {
15 GenerateKeyPair,
17 DeriveKeyPair,
19 Sign,
21 Verify,
23 DeriveSharedSecret,
25 KemEncapsulate,
27 KemDecapsulate,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum ProviderKind {
35 PackageOwnedRust,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[non_exhaustive]
42pub enum ProviderLane {
43 Native,
45 Wasm,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[non_exhaustive]
52pub enum KeyResidency {
53 ProcessMemory,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59#[non_exhaustive]
60pub enum KeyCopyBoundary {
61 NoSecretInput,
63 BorrowedCallerSecret,
65 ProviderCreatesSecret,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[non_exhaustive]
72pub enum ProviderOutputPolicy {
73 PublicOnly,
75 ZeroizingSecret,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81#[non_exhaustive]
82pub enum ProviderPolicyReason {
83 SelectedCompiledImplementation,
85 RejectedOperationMismatch,
87 RejectedFeatureDisabled,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93#[non_exhaustive]
94pub enum ProviderDisposition {
95 Selected,
97 Rejected,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103#[non_exhaustive]
104pub enum FallbackPolicy {
105 Prohibited,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111#[non_exhaustive]
112pub struct ProviderDecision {
113 pub operation: ProviderOperation,
115 pub algorithm: Algorithm,
117 pub provider_kind: ProviderKind,
119 pub lane: ProviderLane,
121 pub disposition: ProviderDisposition,
123 pub reason: ProviderPolicyReason,
125 pub key_residency: KeyResidency,
127 pub key_copy_boundary: KeyCopyBoundary,
129 pub output_policy: ProviderOutputPolicy,
131 pub fallback: FallbackPolicy,
133}
134
135impl ProviderDecision {
136 #[must_use]
138 pub fn is_selected(self) -> bool {
139 self.disposition == ProviderDisposition::Selected
140 }
141}
142
143#[must_use]
145pub fn provider_decision(operation: ProviderOperation, algorithm: Algorithm) -> ProviderDecision {
146 let operation_supported = operation_supports_algorithm(operation, algorithm);
147 let implementation_compiled = implementation_is_compiled(algorithm);
148 let (disposition, reason) = if !operation_supported {
149 (
150 ProviderDisposition::Rejected,
151 ProviderPolicyReason::RejectedOperationMismatch,
152 )
153 } else if !implementation_compiled {
154 (
155 ProviderDisposition::Rejected,
156 ProviderPolicyReason::RejectedFeatureDisabled,
157 )
158 } else {
159 (
160 ProviderDisposition::Selected,
161 ProviderPolicyReason::SelectedCompiledImplementation,
162 )
163 };
164
165 ProviderDecision {
166 operation,
167 algorithm,
168 provider_kind: ProviderKind::PackageOwnedRust,
169 lane: compiled_lane(),
170 disposition,
171 reason,
172 key_residency: KeyResidency::ProcessMemory,
173 key_copy_boundary: key_copy_boundary(operation),
174 output_policy: output_policy(operation),
175 fallback: FallbackPolicy::Prohibited,
176 }
177}
178
179pub(crate) fn require_provider(
180 operation: ProviderOperation,
181 algorithm: Algorithm,
182) -> Result<ProviderDecision, AlgorithmError> {
183 let decision = provider_decision(operation, algorithm);
184 if decision.is_selected() {
185 Ok(decision)
186 } else {
187 Err(AlgorithmError::UnsupportedAlgorithm(algorithm))
188 }
189}
190
191const fn compiled_lane() -> ProviderLane {
192 #[cfg(target_arch = "wasm32")]
193 {
194 ProviderLane::Wasm
195 }
196 #[cfg(not(target_arch = "wasm32"))]
197 {
198 ProviderLane::Native
199 }
200}
201
202const fn key_copy_boundary(operation: ProviderOperation) -> KeyCopyBoundary {
203 match operation {
204 ProviderOperation::GenerateKeyPair => KeyCopyBoundary::ProviderCreatesSecret,
205 ProviderOperation::DeriveKeyPair
206 | ProviderOperation::Sign
207 | ProviderOperation::DeriveSharedSecret
208 | ProviderOperation::KemDecapsulate => KeyCopyBoundary::BorrowedCallerSecret,
209 ProviderOperation::Verify | ProviderOperation::KemEncapsulate => {
210 KeyCopyBoundary::NoSecretInput
211 }
212 }
213}
214
215const fn output_policy(operation: ProviderOperation) -> ProviderOutputPolicy {
216 match operation {
217 ProviderOperation::GenerateKeyPair
218 | ProviderOperation::DeriveKeyPair
219 | ProviderOperation::DeriveSharedSecret
220 | ProviderOperation::KemEncapsulate
221 | ProviderOperation::KemDecapsulate => ProviderOutputPolicy::ZeroizingSecret,
222 ProviderOperation::Sign | ProviderOperation::Verify => ProviderOutputPolicy::PublicOnly,
223 }
224}
225
226const fn operation_supports_algorithm(operation: ProviderOperation, algorithm: Algorithm) -> bool {
227 match operation {
228 ProviderOperation::GenerateKeyPair | ProviderOperation::DeriveKeyPair => {
229 !matches!(algorithm, Algorithm::SlhDsaSha2_128s)
230 }
231 ProviderOperation::Sign | ProviderOperation::Verify => matches!(
232 algorithm,
233 Algorithm::Ed25519
234 | Algorithm::P256
235 | Algorithm::P384
236 | Algorithm::P521
237 | Algorithm::Secp256k1
238 | Algorithm::MlDsa44
239 | Algorithm::MlDsa65
240 | Algorithm::MlDsa87
241 ),
242 ProviderOperation::DeriveSharedSecret => matches!(
243 algorithm,
244 Algorithm::P256 | Algorithm::P384 | Algorithm::P521 | Algorithm::X25519
245 ),
246 ProviderOperation::KemEncapsulate | ProviderOperation::KemDecapsulate => matches!(
247 algorithm,
248 Algorithm::MlKem512 | Algorithm::MlKem768 | Algorithm::MlKem1024 | Algorithm::XWing768
249 ),
250 }
251}
252
253const fn implementation_is_compiled(algorithm: Algorithm) -> bool {
254 match algorithm {
255 Algorithm::Ed25519 => cfg!(feature = "ed25519"),
256 Algorithm::X25519 => cfg!(feature = "x25519"),
257 Algorithm::P256 => cfg!(feature = "p256"),
258 Algorithm::P384 => cfg!(feature = "p384"),
259 Algorithm::P521 => cfg!(feature = "p521"),
260 Algorithm::Secp256k1 => cfg!(feature = "secp256k1"),
261 Algorithm::MlDsa44 => cfg!(feature = "ml-dsa-44"),
262 Algorithm::MlDsa65 => cfg!(feature = "ml-dsa-65"),
263 Algorithm::MlDsa87 => cfg!(feature = "ml-dsa-87"),
264 Algorithm::SlhDsaSha2_128s => false,
265 Algorithm::MlKem512 => cfg!(feature = "ml-kem-512"),
266 Algorithm::MlKem768 => cfg!(feature = "ml-kem-768"),
267 Algorithm::MlKem1024 => cfg!(feature = "ml-kem-1024"),
268 Algorithm::XWing768 => cfg!(feature = "x-wing"),
269 }
270}