Skip to main content

reallyme_crypto/secret_material/
policy.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use super::{
6    BufferOwner, DestructionPolicy, ExportPolicy, OutputMaterial, RetentionPolicy,
7    SecretSensitivity, ZeroizationPolicy,
8};
9use crypto_core::RngOutputKind;
10
11/// Semantic operation whose entry point binds a secret-material policy.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum SecretMaterialOperation {
15    /// Authenticated encryption.
16    AeadSeal,
17    /// Authenticated decryption.
18    AeadOpen,
19    /// Constant-time comparison.
20    ConstantTimeCompare,
21    /// Cryptographic hashing.
22    Hash,
23    /// Message authentication tag generation.
24    MacAuthenticate,
25    /// Message authentication tag verification.
26    MacVerify,
27    /// Password or key derivation.
28    KeyDerivation,
29    /// Symmetric key wrapping.
30    KeyWrap,
31    /// Symmetric key unwrapping.
32    KeyUnwrap,
33    /// Signature keypair generation.
34    SignatureKeyGeneration,
35    /// Signature keypair derivation.
36    SignatureKeyDerivation,
37    /// Signature creation.
38    SignatureSign,
39    /// Signature verification.
40    SignatureVerify,
41    /// Key-agreement keypair generation.
42    KeyAgreementKeyGeneration,
43    /// Key-agreement keypair derivation.
44    KeyAgreementKeyDerivation,
45    /// Shared-secret derivation.
46    KeyAgreementSharedSecret,
47    /// KEM keypair generation.
48    KemKeyGeneration,
49    /// KEM keypair derivation.
50    KemKeyDerivation,
51    /// KEM encapsulation.
52    KemEncapsulate,
53    /// KEM decapsulation.
54    KemDecapsulate,
55    /// HPKE keypair generation.
56    HpkeKeyGeneration,
57    /// HPKE keypair derivation.
58    HpkeKeyDerivation,
59    /// Establishment of a live, non-exportable HPKE sender context.
60    HpkeSenderSetup,
61    /// Establishment of a live, non-exportable HPKE receiver context.
62    HpkeReceiverSetup,
63    /// HPKE sealing.
64    HpkeSeal,
65    /// HPKE opening.
66    HpkeOpen,
67    /// HPKE exporter-secret derivation.
68    HpkeExport,
69    /// Fill caller-owned storage with cryptographically secure random bytes.
70    RandomFill,
71    /// Generate an AEAD nonce.
72    RandomNonce,
73    /// Generate an Argon2 salt.
74    RandomSalt,
75    /// Public-key encoding or decoding.
76    PublicKeyEncoding,
77}
78
79/// Ownership, retention, export, and destruction rules for one buffer.
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81#[non_exhaustive]
82pub struct MaterialPolicy {
83    /// Layer responsible for the buffer.
84    pub owner: BufferOwner,
85    /// Sensitivity of its contents.
86    pub sensitivity: SecretSensitivity,
87    /// Maximum intended lifetime.
88    pub retention: RetentionPolicy,
89    /// Required zeroization behavior.
90    pub zeroization: ZeroizationPolicy,
91    /// Whether and how it may cross the boundary.
92    pub export: ExportPolicy,
93    /// Required destruction behavior.
94    pub destruction: DestructionPolicy,
95}
96
97/// Complete policy bound to a semantic operation entry point.
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99#[non_exhaustive]
100pub struct OperationSecretMaterialPolicy {
101    /// Caller-supplied or decoded operation input.
102    pub input: MaterialPolicy,
103    /// Semantic operation result before adapter encoding.
104    pub output: MaterialPolicy,
105    /// Encoded operation request bytes.
106    pub request_wire: MaterialPolicy,
107    /// Encoded operation response bytes.
108    pub response_wire: MaterialPolicy,
109    /// Security-relevant class of the semantic result.
110    pub output_material: OutputMaterial,
111}
112
113/// Return the reviewed material policy for one semantic operation.
114#[must_use]
115pub const fn operation_secret_material_policy(
116    operation: SecretMaterialOperation,
117) -> OperationSecretMaterialPolicy {
118    match operation {
119        SecretMaterialOperation::AeadSeal | SecretMaterialOperation::KeyWrap => {
120            secret_input_sensitive_output(OutputMaterial::SensitivePublic)
121        }
122        SecretMaterialOperation::AeadOpen
123        | SecretMaterialOperation::KeyUnwrap
124        | SecretMaterialOperation::KeyDerivation
125        | SecretMaterialOperation::KeyAgreementSharedSecret
126        | SecretMaterialOperation::KemDecapsulate
127        | SecretMaterialOperation::HpkeOpen
128        | SecretMaterialOperation::HpkeExport => secret_output(OutputMaterial::Secret),
129        SecretMaterialOperation::RandomFill => caller_owned_secret_output(),
130        SecretMaterialOperation::RandomNonce => public_operation(),
131        SecretMaterialOperation::RandomSalt => {
132            public_input_sensitive_output(OutputMaterial::SensitivePublic)
133        }
134        SecretMaterialOperation::SignatureKeyGeneration
135        | SecretMaterialOperation::KeyAgreementKeyGeneration
136        | SecretMaterialOperation::KemKeyGeneration
137        | SecretMaterialOperation::KemEncapsulate
138        | SecretMaterialOperation::HpkeKeyGeneration => {
139            public_input_secret_output(OutputMaterial::Mixed)
140        }
141        SecretMaterialOperation::SignatureKeyDerivation
142        | SecretMaterialOperation::KeyAgreementKeyDerivation
143        | SecretMaterialOperation::KemKeyDerivation
144        | SecretMaterialOperation::HpkeKeyDerivation => secret_output(OutputMaterial::Mixed),
145        SecretMaterialOperation::HpkeSenderSetup | SecretMaterialOperation::HpkeReceiverSetup => {
146            secret_input_non_exportable_output(OutputMaterial::Mixed)
147        }
148        SecretMaterialOperation::HpkeSeal => {
149            secret_input_sensitive_output(OutputMaterial::SensitivePublic)
150        }
151        SecretMaterialOperation::Hash
152        | SecretMaterialOperation::ConstantTimeCompare
153        | SecretMaterialOperation::MacAuthenticate
154        | SecretMaterialOperation::MacVerify
155        | SecretMaterialOperation::SignatureSign => {
156            secret_input_public_output(OutputMaterial::Public)
157        }
158        SecretMaterialOperation::SignatureVerify | SecretMaterialOperation::PublicKeyEncoding => {
159            public_operation()
160        }
161    }
162}
163
164/// Return the reviewed caller-owned output policy for one random purpose.
165#[must_use]
166pub const fn random_fill_secret_material_policy(
167    kind: RngOutputKind,
168) -> OperationSecretMaterialPolicy {
169    match kind {
170        RngOutputKind::AeadNonce12 => caller_owned_public_output(),
171        RngOutputKind::Argon2Salt16 | RngOutputKind::Argon2Salt32 => {
172            caller_owned_sensitive_output()
173        }
174        RngOutputKind::Generic
175        | RngOutputKind::Aes256GcmKey
176        | RngOutputKind::MlKem1024Seed
177        | RngOutputKind::MlDsa87Seed
178        | RngOutputKind::Ed25519Seed
179        | RngOutputKind::SlhDsaSha2_128sSeed => caller_owned_secret_output(),
180        _ => caller_owned_secret_output(),
181    }
182}
183
184/// Bind a reviewed policy in production code without retaining runtime data.
185///
186/// Keeping this call in every semantic entry point makes policy omissions
187/// mechanically searchable and prevents tests from being the only evidence.
188#[inline]
189pub(crate) const fn bind_operation_policy(
190    operation: SecretMaterialOperation,
191) -> OperationSecretMaterialPolicy {
192    operation_secret_material_policy(operation)
193}
194
195#[inline]
196#[cfg(feature = "csprng")]
197pub(crate) const fn bind_random_fill_policy(kind: RngOutputKind) -> OperationSecretMaterialPolicy {
198    random_fill_secret_material_policy(kind)
199}
200
201const fn secret_input_public_output(
202    output_material: OutputMaterial,
203) -> OperationSecretMaterialPolicy {
204    operation_policy(
205        borrowed_secret(),
206        public_result(),
207        secret_wire(),
208        public_wire(),
209        output_material,
210    )
211}
212
213const fn secret_input_sensitive_output(
214    output_material: OutputMaterial,
215) -> OperationSecretMaterialPolicy {
216    operation_policy(
217        borrowed_secret(),
218        sensitive_result(),
219        secret_wire(),
220        sensitive_wire(),
221        output_material,
222    )
223}
224
225const fn secret_output(output_material: OutputMaterial) -> OperationSecretMaterialPolicy {
226    operation_policy(
227        borrowed_secret(),
228        owned_secret(),
229        secret_wire(),
230        secret_wire(),
231        output_material,
232    )
233}
234
235const fn secret_input_non_exportable_output(
236    output_material: OutputMaterial,
237) -> OperationSecretMaterialPolicy {
238    operation_policy(
239        borrowed_secret(),
240        MaterialPolicy {
241            owner: BufferOwner::OperationLayer,
242            sensitivity: SecretSensitivity::Secret,
243            retention: RetentionPolicy::ResultLifetime,
244            zeroization: ZeroizationPolicy::OwnerZeroizesOnDrop,
245            export: ExportPolicy::NonExportable,
246            destruction: DestructionPolicy::ZeroizeOnDrop,
247        },
248        public_wire(),
249        public_wire(),
250        output_material,
251    )
252}
253
254const fn public_input_secret_output(
255    output_material: OutputMaterial,
256) -> OperationSecretMaterialPolicy {
257    operation_policy(
258        borrowed_public(),
259        owned_secret(),
260        public_wire(),
261        secret_wire(),
262        output_material,
263    )
264}
265
266const fn public_input_sensitive_output(
267    output_material: OutputMaterial,
268) -> OperationSecretMaterialPolicy {
269    operation_policy(
270        borrowed_public(),
271        sensitive_result(),
272        public_wire(),
273        sensitive_wire(),
274        output_material,
275    )
276}
277
278const fn caller_owned_secret_output() -> OperationSecretMaterialPolicy {
279    operation_policy(
280        borrowed_public(),
281        MaterialPolicy {
282            owner: BufferOwner::Caller,
283            sensitivity: SecretSensitivity::Secret,
284            retention: RetentionPolicy::ResultLifetime,
285            zeroization: ZeroizationPolicy::CallerRetainsResponsibility,
286            export: ExportPolicy::SecretOwnerRequired,
287            destruction: DestructionPolicy::CallerControlled,
288        },
289        public_wire(),
290        secret_wire(),
291        OutputMaterial::Secret,
292    )
293}
294
295const fn caller_owned_sensitive_output() -> OperationSecretMaterialPolicy {
296    operation_policy(
297        borrowed_public(),
298        MaterialPolicy {
299            owner: BufferOwner::Caller,
300            sensitivity: SecretSensitivity::Sensitive,
301            retention: RetentionPolicy::ResultLifetime,
302            zeroization: ZeroizationPolicy::OwnerZeroizesOnDrop,
303            export: ExportPolicy::Public,
304            destruction: DestructionPolicy::ZeroizeOnDrop,
305        },
306        public_wire(),
307        sensitive_wire(),
308        OutputMaterial::SensitivePublic,
309    )
310}
311
312const fn caller_owned_public_output() -> OperationSecretMaterialPolicy {
313    operation_policy(
314        borrowed_public(),
315        MaterialPolicy {
316            owner: BufferOwner::Caller,
317            sensitivity: SecretSensitivity::Public,
318            retention: RetentionPolicy::ResultLifetime,
319            zeroization: ZeroizationPolicy::NotRequired,
320            export: ExportPolicy::Public,
321            destruction: DestructionPolicy::NoneRequired,
322        },
323        public_wire(),
324        public_wire(),
325        OutputMaterial::Public,
326    )
327}
328
329const fn public_operation() -> OperationSecretMaterialPolicy {
330    operation_policy(
331        borrowed_public(),
332        public_result(),
333        public_wire(),
334        public_wire(),
335        OutputMaterial::Public,
336    )
337}
338
339const fn operation_policy(
340    input: MaterialPolicy,
341    output: MaterialPolicy,
342    request_wire: MaterialPolicy,
343    response_wire: MaterialPolicy,
344    output_material: OutputMaterial,
345) -> OperationSecretMaterialPolicy {
346    OperationSecretMaterialPolicy {
347        input,
348        output,
349        request_wire,
350        response_wire,
351        output_material,
352    }
353}
354
355const fn borrowed_secret() -> MaterialPolicy {
356    MaterialPolicy {
357        owner: BufferOwner::Caller,
358        sensitivity: SecretSensitivity::Secret,
359        retention: RetentionPolicy::BorrowedForCall,
360        zeroization: ZeroizationPolicy::CallerRetainsResponsibility,
361        export: ExportPolicy::SecretOwnerRequired,
362        destruction: DestructionPolicy::CallerControlled,
363    }
364}
365
366const fn borrowed_public() -> MaterialPolicy {
367    MaterialPolicy {
368        owner: BufferOwner::Caller,
369        sensitivity: SecretSensitivity::Public,
370        retention: RetentionPolicy::BorrowedForCall,
371        zeroization: ZeroizationPolicy::NotRequired,
372        export: ExportPolicy::Public,
373        destruction: DestructionPolicy::NoneRequired,
374    }
375}
376
377const fn owned_secret() -> MaterialPolicy {
378    MaterialPolicy {
379        owner: BufferOwner::OperationLayer,
380        sensitivity: SecretSensitivity::Secret,
381        retention: RetentionPolicy::ResultLifetime,
382        zeroization: ZeroizationPolicy::OwnerZeroizesOnDrop,
383        export: ExportPolicy::SecretOwnerRequired,
384        destruction: DestructionPolicy::ZeroizeOnDrop,
385    }
386}
387
388const fn public_result() -> MaterialPolicy {
389    MaterialPolicy {
390        owner: BufferOwner::OperationLayer,
391        sensitivity: SecretSensitivity::Public,
392        retention: RetentionPolicy::ResultLifetime,
393        zeroization: ZeroizationPolicy::NotRequired,
394        export: ExportPolicy::Public,
395        destruction: DestructionPolicy::NoneRequired,
396    }
397}
398
399const fn sensitive_result() -> MaterialPolicy {
400    MaterialPolicy {
401        owner: BufferOwner::OperationLayer,
402        sensitivity: SecretSensitivity::Sensitive,
403        retention: RetentionPolicy::ResultLifetime,
404        zeroization: ZeroizationPolicy::NotRequired,
405        export: ExportPolicy::Public,
406        destruction: DestructionPolicy::NoneRequired,
407    }
408}
409
410const fn secret_wire() -> MaterialPolicy {
411    MaterialPolicy {
412        owner: BufferOwner::OperationContract,
413        sensitivity: SecretSensitivity::Secret,
414        retention: RetentionPolicy::OperationTemporary,
415        zeroization: ZeroizationPolicy::OwnerZeroizesOnDrop,
416        export: ExportPolicy::SecretOwnerRequired,
417        destruction: DestructionPolicy::ZeroizeOnDrop,
418    }
419}
420
421const fn public_wire() -> MaterialPolicy {
422    MaterialPolicy {
423        owner: BufferOwner::OperationContract,
424        sensitivity: SecretSensitivity::Public,
425        retention: RetentionPolicy::OperationTemporary,
426        zeroization: ZeroizationPolicy::NotRequired,
427        export: ExportPolicy::Public,
428        destruction: DestructionPolicy::NoneRequired,
429    }
430}
431
432const fn sensitive_wire() -> MaterialPolicy {
433    MaterialPolicy {
434        owner: BufferOwner::OperationContract,
435        sensitivity: SecretSensitivity::Sensitive,
436        retention: RetentionPolicy::OperationTemporary,
437        zeroization: ZeroizationPolicy::NotRequired,
438        export: ExportPolicy::Public,
439        destruction: DestructionPolicy::NoneRequired,
440    }
441}