Skip to main content

noxtls_psa/
provider.rs

1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18#[cfg(feature = "alloc")]
19use alloc::{collections::BTreeMap, vec::Vec};
20use noxtls_core::{Error, Result};
21use noxtls_crypto::{
22    noxtls_aes_gcm_encrypt, noxtls_p256_ecdh_shared_secret, noxtls_p256_ecdsa_sign_sha256,
23    noxtls_rsaes_oaep_sha256_decrypt, noxtls_rsaes_pkcs1_v15_decrypt,
24    noxtls_rsassa_pss_sha256_sign, noxtls_rsassa_sha256_sign, noxtls_sha256, noxtls_x25519,
25    AesCipher, P256PrivateKey, P256PublicKey, RsaPrivateKey,
26};
27
28/// Identifies a backend-managed external key object.
29#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
30pub struct PsaExternalKeyHandle {
31    id: Vec<u8>,
32}
33
34impl PsaExternalKeyHandle {
35    /// Constructs an external key handle from opaque identifier bytes.
36    ///
37    /// # Arguments
38    ///
39    /// * `id` - Opaque provider-owned key identifier bytes.
40    ///
41    /// # Returns
42    ///
43    /// A noxtls_new [`PsaExternalKeyHandle`] owning the identifier bytes.
44    pub fn noxtls_new(id: Vec<u8>) -> Self {
45        Self { id }
46    }
47
48    /// Borrows the opaque key identifier bytes.
49    ///
50    /// # Arguments
51    ///
52    /// * `self` - Handle whose stable identifier bytes are required.
53    ///
54    /// # Returns
55    ///
56    /// Borrowed opaque bytes identifying the provider key object.
57    pub fn as_bytes(&self) -> &[u8] {
58        &self.id
59    }
60}
61
62/// Enumerates signing algorithms supported by PSA provider mappings.
63#[derive(Copy, Clone, Debug, Eq, PartialEq)]
64pub enum PsaSignAlgorithm {
65    /// RSA PKCS#1 v1.5 signing with SHA-256 digest.
66    RsaPkcs1Sha256,
67    /// RSA PSS signing with SHA-256 digest.
68    RsaPssSha256,
69    /// ECDSA signing over P-256 with SHA-256 digest.
70    EcdsaP256Sha256,
71}
72
73/// Enumerates decrypt algorithms supported by PSA provider mappings.
74#[derive(Copy, Clone, Debug, Eq, PartialEq)]
75pub enum PsaDecryptAlgorithm {
76    /// RSA PKCS#1 v1.5 decryption.
77    RsaPkcs1v15,
78    /// RSA OAEP decryption using SHA-256.
79    RsaOaepSha256,
80}
81
82/// Enumerates derive algorithms supported by PSA provider mappings.
83#[derive(Copy, Clone, Debug, Eq, PartialEq)]
84pub enum PsaDeriveAlgorithm {
85    /// X25519 shared secret derivation.
86    X25519,
87    /// P-256 ECDH shared secret derivation.
88    EcdhP256,
89}
90
91/// Carries data required for backend sign operations.
92#[derive(Clone, Debug)]
93pub struct KeySignRequest<'a> {
94    /// Opaque provider handle to signing key object.
95    pub handle: &'a PsaExternalKeyHandle,
96    /// Algorithm selector for sign operation.
97    pub noxtls_algorithm: PsaSignAlgorithm,
98    /// Input message bytes to be signed.
99    pub message: &'a [u8],
100    /// Optional PSS salt bytes for algorithms requiring explicit salt.
101    pub salt: Option<&'a [u8]>,
102}
103
104/// Carries data required for backend decrypt operations.
105#[derive(Clone, Debug)]
106pub struct KeyDecryptRequest<'a> {
107    /// Opaque provider handle to decrypt key object.
108    pub handle: &'a PsaExternalKeyHandle,
109    /// Algorithm selector for decrypt operation.
110    pub noxtls_algorithm: PsaDecryptAlgorithm,
111    /// Ciphertext bytes to decrypt.
112    pub ciphertext: &'a [u8],
113    /// Optional OAEP label bytes for algorithms requiring explicit label.
114    pub label: Option<&'a [u8]>,
115}
116
117/// Carries data required for backend derive operations.
118#[derive(Clone, Debug)]
119pub struct KeyDeriveRequest<'a> {
120    /// Opaque provider handle to derive key object.
121    pub handle: &'a PsaExternalKeyHandle,
122    /// Algorithm selector for derive operation.
123    pub noxtls_algorithm: PsaDeriveAlgorithm,
124    /// Peer public-key bytes in noxtls_algorithm-expected encoding.
125    pub peer_public_key: &'a [u8],
126}
127
128/// Carries AES-GCM encrypt operation inputs.
129#[derive(Clone, Debug)]
130pub struct AeadEncryptRequest<'a> {
131    /// Symmetric encryption key bytes.
132    pub key: &'a [u8],
133    /// Nonce bytes for AES-GCM operation.
134    pub nonce: &'a [u8],
135    /// Associated authenticated data bytes.
136    pub aad: &'a [u8],
137    /// Plaintext bytes to encrypt.
138    pub plaintext: &'a [u8],
139}
140
141/// Carries AES-GCM encryption outputs.
142#[derive(Clone, Debug, Eq, PartialEq)]
143pub struct AeadEncryptResponse {
144    /// Produced ciphertext bytes.
145    pub ciphertext: Vec<u8>,
146    /// Produced 16-byte authentication tag.
147    pub tag: [u8; 16],
148}
149
150/// Defines backend operations required by PSA provider surface.
151pub trait PsaCryptoBackend {
152    /// Executes an asymmetric signing operation.
153    ///
154    /// # Arguments
155    ///
156    /// * `self` - Backend implementation receiving sign request.
157    /// * `request` - Sign request containing handle, noxtls_algorithm, and digest.
158    ///
159    /// # Returns
160    ///
161    /// Signature bytes generated by the backend.
162    ///
163    /// # Errors
164    ///
165    /// Returns [`Error`] when handle resolution, policy checks, or crypto operation fails.
166    fn sign(&self, request: &KeySignRequest<'_>) -> Result<Vec<u8>>;
167
168    /// Executes an asymmetric decrypt operation.
169    ///
170    /// # Arguments
171    ///
172    /// * `self` - Backend implementation receiving decrypt request.
173    /// * `request` - Decrypt request containing handle, noxtls_algorithm, and ciphertext.
174    ///
175    /// # Returns
176    ///
177    /// Plaintext bytes on successful decrypt.
178    ///
179    /// # Errors
180    ///
181    /// Returns [`Error`] when handle resolution, policy checks, or crypto operation fails.
182    fn decrypt(&self, request: &KeyDecryptRequest<'_>) -> Result<Vec<u8>>;
183
184    /// Executes a key-agreement derive operation.
185    ///
186    /// # Arguments
187    ///
188    /// * `self` - Backend implementation receiving derive request.
189    /// * `request` - Derive request containing handle, noxtls_algorithm, and peer key.
190    ///
191    /// # Returns
192    ///
193    /// Shared secret bytes for the selected derive noxtls_algorithm.
194    ///
195    /// # Errors
196    ///
197    /// Returns [`Error`] when handle resolution, policy checks, or derive operation fails.
198    fn noxtls_derive(&self, request: &KeyDeriveRequest<'_>) -> Result<Vec<u8>>;
199
200    /// Fills output bytes with random data.
201    ///
202    /// # Arguments
203    ///
204    /// * `self` - Backend implementation receiving random request.
205    /// * `out` - Mutable output buffer for random bytes.
206    ///
207    /// # Returns
208    ///
209    /// `Ok(())` when the output buffer is fully written.
210    ///
211    /// # Errors
212    ///
213    /// Returns [`Error`] when backend cannot provide entropy.
214    fn random(&self, out: &mut [u8]) -> Result<()>;
215
216    /// Computes a SHA-256 digest.
217    ///
218    /// # Arguments
219    ///
220    /// * `self` - Backend implementation receiving hash request.
221    /// * `input` - Bytes to hash.
222    ///
223    /// # Returns
224    ///
225    /// A 32-byte SHA-256 digest.
226    ///
227    /// # Errors
228    ///
229    /// Returns [`Error`] when hashing fails for the backend.
230    fn noxtls_sha256(&self, input: &[u8]) -> Result<[u8; 32]>;
231
232    /// Encrypts plaintext with AES-GCM.
233    ///
234    /// # Arguments
235    ///
236    /// * `self` - Backend implementation receiving AEAD request.
237    /// * `request` - AES-GCM request with key, nonce, AAD, and plaintext.
238    ///
239    /// # Returns
240    ///
241    /// Ciphertext bytes plus a 16-byte authentication tag.
242    ///
243    /// # Errors
244    ///
245    /// Returns [`Error`] when backend lacks AES-GCM support or encryption fails.
246    fn noxtls_aes_gcm_encrypt(
247        &self,
248        request: &AeadEncryptRequest<'_>,
249    ) -> Result<AeadEncryptResponse>;
250}
251
252/// Adapts a concrete backend into a stable PSA provider API.
253#[derive(Clone, Debug)]
254pub struct PsaProvider<B> {
255    backend: B,
256}
257
258impl<B> PsaProvider<B> {
259    /// Constructs a provider from a concrete backend implementation.
260    ///
261    /// # Arguments
262    ///
263    /// * `backend` - Backend implementation used by provider operations.
264    ///
265    /// # Returns
266    ///
267    /// A noxtls_new [`PsaProvider`] owning the backend.
268    pub fn noxtls_new(backend: B) -> Self {
269        Self { backend }
270    }
271}
272
273impl<B: PsaCryptoBackend> PsaProvider<B> {
274    /// Dispatches signing requests to the configured backend.
275    ///
276    /// # Arguments
277    ///
278    /// * `self` - Provider dispatching to backend.
279    /// * `request` - Signing request to execute.
280    ///
281    /// # Returns
282    ///
283    /// Signature bytes produced by backend.
284    ///
285    /// # Errors
286    ///
287    /// Returns backend-provided [`Error`] on failures.
288    pub fn sign(&self, request: &KeySignRequest<'_>) -> Result<Vec<u8>> {
289        self.backend.sign(request)
290    }
291
292    /// Dispatches decrypt requests with uniform decrypt failure posture.
293    ///
294    /// # Arguments
295    ///
296    /// * `self` - Provider dispatching to backend.
297    /// * `request` - Decrypt request to execute.
298    ///
299    /// # Returns
300    ///
301    /// Plaintext bytes returned by backend.
302    ///
303    /// # Errors
304    ///
305    /// Returns [`Error::CryptoFailure`] for decrypt failures to avoid oracle leakage.
306    pub fn decrypt(&self, request: &KeyDecryptRequest<'_>) -> Result<Vec<u8>> {
307        self.backend
308            .decrypt(request)
309            .map_err(|_| Error::CryptoFailure("psa cryptographic operation failed"))
310    }
311
312    /// Dispatches derive requests to the configured backend.
313    ///
314    /// # Arguments
315    ///
316    /// * `self` - Provider dispatching to backend.
317    /// * `request` - Derive request to execute.
318    ///
319    /// # Returns
320    ///
321    /// Shared secret bytes produced by backend.
322    ///
323    /// # Errors
324    ///
325    /// Returns backend-provided [`Error`] on failures.
326    pub fn noxtls_derive(&self, request: &KeyDeriveRequest<'_>) -> Result<Vec<u8>> {
327        self.backend.noxtls_derive(request)
328    }
329
330    /// Fills output bytes with backend-provided random data.
331    ///
332    /// # Arguments
333    ///
334    /// * `self` - Provider dispatching to backend.
335    /// * `out` - Mutable output buffer to fill.
336    ///
337    /// # Returns
338    ///
339    /// `Ok(())` when random output was written to the caller buffer.
340    ///
341    /// # Errors
342    ///
343    /// Returns backend-provided [`Error`] when random generation fails.
344    pub fn random(&self, out: &mut [u8]) -> Result<()> {
345        self.backend.random(out)
346    }
347
348    /// Computes SHA-256 using backend digest implementation.
349    ///
350    /// # Arguments
351    ///
352    /// * `self` - Provider dispatching to backend.
353    /// * `input` - Bytes to hash.
354    ///
355    /// # Returns
356    ///
357    /// 32-byte SHA-256 digest bytes.
358    ///
359    /// # Errors
360    ///
361    /// Returns backend-provided [`Error`] on hashing failures.
362    pub fn noxtls_sha256(&self, input: &[u8]) -> Result<[u8; 32]> {
363        self.backend.noxtls_sha256(input)
364    }
365
366    /// Encrypts plaintext with backend AES-GCM implementation.
367    ///
368    /// # Arguments
369    ///
370    /// * `self` - Provider dispatching to backend.
371    /// * `request` - AES-GCM request with key/nonce/aad/plaintext.
372    ///
373    /// # Returns
374    ///
375    /// Ciphertext bytes plus a 16-byte authentication tag.
376    ///
377    /// # Errors
378    ///
379    /// Returns backend-provided [`Error`] when encryption fails.
380    pub fn noxtls_aes_gcm_encrypt(
381        &self,
382        request: &AeadEncryptRequest<'_>,
383    ) -> Result<AeadEncryptResponse> {
384        self.backend.noxtls_aes_gcm_encrypt(request)
385    }
386}
387
388/// Stores policy flags for registered key handles.
389#[derive(Copy, Clone, Debug, Eq, PartialEq)]
390struct HandlePolicy {
391    allow_sign: bool,
392    allow_decrypt: bool,
393    allow_derive: bool,
394}
395
396/// Represents the private key material variants used for software-backed tests.
397#[derive(Clone, Debug)]
398enum SoftwarePrivateMaterial {
399    Rsa(RsaPrivateKey),
400    X25519([u8; 32]),
401    P256(P256PrivateKey),
402}
403
404/// Implements an in-process backend that mirrors PSA handle semantics for tests.
405#[derive(Clone, Debug, Default)]
406pub struct PsaSoftwareBackend {
407    keys: BTreeMap<Vec<u8>, (SoftwarePrivateMaterial, HandlePolicy)>,
408}
409
410impl PsaSoftwareBackend {
411    /// Constructs an empty software backend.
412    ///
413    /// # Arguments
414    ///
415    /// * `()` - This constructor has no parameters.
416    ///
417    /// # Returns
418    ///
419    /// A noxtls_new empty [`PsaSoftwareBackend`] value.
420    pub fn noxtls_new() -> Self {
421        Self {
422            keys: BTreeMap::new(),
423        }
424    }
425
426    /// Registers an RSA private key with handle-level usage policy.
427    ///
428    /// # Arguments
429    ///
430    /// * `handle` - Opaque key handle used for future operations.
431    /// * `key` - RSA private key material owned by backend.
432    /// * `allow_sign` - Whether sign operations are authorized for this handle.
433    /// * `allow_decrypt` - Whether decrypt operations are authorized for this handle.
434    ///
435    /// # Returns
436    ///
437    /// `Ok(())` after key registration succeeds.
438    ///
439    /// # Errors
440    ///
441    /// Returns [`Error::PolicyViolation`] if the handle is already registered.
442    pub fn register_rsa_key(
443        &mut self,
444        handle: PsaExternalKeyHandle,
445        key: RsaPrivateKey,
446        allow_sign: bool,
447        allow_decrypt: bool,
448    ) -> Result<()> {
449        self.insert_key(
450            handle,
451            SoftwarePrivateMaterial::Rsa(key),
452            HandlePolicy {
453                allow_sign,
454                allow_decrypt,
455                allow_derive: false,
456            },
457        )
458    }
459
460    /// Registers an X25519 private key with derive-policy controls.
461    ///
462    /// # Arguments
463    ///
464    /// * `handle` - Opaque key handle used for future derive operations.
465    /// * `key` - X25519 private scalar bytes.
466    /// * `allow_derive` - Whether derive operations are authorized for this handle.
467    ///
468    /// # Returns
469    ///
470    /// `Ok(())` after key registration succeeds.
471    ///
472    /// # Errors
473    ///
474    /// Returns [`Error::PolicyViolation`] if the handle is already registered.
475    pub fn register_x25519_key(
476        &mut self,
477        handle: PsaExternalKeyHandle,
478        key: [u8; 32],
479        allow_derive: bool,
480    ) -> Result<()> {
481        self.insert_key(
482            handle,
483            SoftwarePrivateMaterial::X25519(key),
484            HandlePolicy {
485                allow_sign: false,
486                allow_decrypt: false,
487                allow_derive,
488            },
489        )
490    }
491
492    /// Registers a P-256 private key with derive/sign policy controls.
493    ///
494    /// # Arguments
495    ///
496    /// * `handle` - Opaque key handle used for future operations.
497    /// * `key` - P-256 private key material.
498    /// * `allow_sign` - Whether sign operations are authorized for this handle.
499    /// * `allow_derive` - Whether derive operations are authorized for this handle.
500    ///
501    /// # Returns
502    ///
503    /// `Ok(())` after key registration succeeds.
504    ///
505    /// # Errors
506    ///
507    /// Returns [`Error::StateError`] if the handle is already registered.
508    pub fn register_p256_key(
509        &mut self,
510        handle: PsaExternalKeyHandle,
511        key: P256PrivateKey,
512        allow_sign: bool,
513        allow_derive: bool,
514    ) -> Result<()> {
515        self.insert_key(
516            handle,
517            SoftwarePrivateMaterial::P256(key),
518            HandlePolicy {
519                allow_sign,
520                allow_decrypt: false,
521                allow_derive,
522            },
523        )
524    }
525
526    /// Inserts a key record while enforcing unique handle ownership.
527    ///
528    /// # Arguments
529    ///
530    /// * `handle` - Opaque key handle used as map key.
531    /// * `material` - Private material variant to store.
532    /// * `policy` - Allowed operation policy for this handle.
533    ///
534    /// # Returns
535    ///
536    /// `Ok(())` when the record is inserted.
537    ///
538    /// # Errors
539    ///
540    /// Returns [`Error::PolicyViolation`] when handle already exists.
541    fn insert_key(
542        &mut self,
543        handle: PsaExternalKeyHandle,
544        material: SoftwarePrivateMaterial,
545        policy: HandlePolicy,
546    ) -> Result<()> {
547        if self.keys.contains_key(handle.as_bytes()) {
548            return Err(Error::StateError("psa key handle already registered"));
549        }
550        self.keys.insert(handle.id, (material, policy));
551        Ok(())
552    }
553
554    /// Resolves a key record and policy by handle identifier.
555    ///
556    /// # Arguments
557    ///
558    /// * `handle` - Opaque handle whose key material is required.
559    ///
560    /// # Returns
561    ///
562    /// Borrowed private material and policy tuple for the handle.
563    ///
564    /// # Errors
565    ///
566    /// Returns [`Error::PolicyViolation`] when the handle is unknown.
567    fn resolve_key(
568        &self,
569        handle: &PsaExternalKeyHandle,
570    ) -> Result<&(SoftwarePrivateMaterial, HandlePolicy)> {
571        self.keys
572            .get(handle.as_bytes())
573            .ok_or(Error::StateError("psa key handle invalid"))
574    }
575}
576
577impl PsaCryptoBackend for PsaSoftwareBackend {
578    /// Executes signing operations using software cryptographic primitives.
579    ///
580    /// # Arguments
581    ///
582    /// * `self` - Software backend state containing registered keys.
583    /// * `request` - Sign request with handle, noxtls_algorithm, and digest.
584    ///
585    /// # Returns
586    ///
587    /// Signature bytes from RSA sign operations.
588    ///
589    /// # Errors
590    ///
591    /// Returns policy or crypto errors for unknown handles, denied usage, or bad key type.
592    fn sign(&self, request: &KeySignRequest<'_>) -> Result<Vec<u8>> {
593        let (material, policy) = self.resolve_key(request.handle)?;
594        if !policy.allow_sign {
595            return Err(Error::StateError("psa sign not permitted by key policy"));
596        }
597        match (request.noxtls_algorithm, material) {
598            (PsaSignAlgorithm::RsaPkcs1Sha256, SoftwarePrivateMaterial::Rsa(key)) => {
599                noxtls_rsassa_sha256_sign(key, request.message)
600            }
601            (PsaSignAlgorithm::RsaPssSha256, SoftwarePrivateMaterial::Rsa(key)) => {
602                let salt = request.salt.ok_or(Error::InvalidLength(
603                    "rsa-pss-sha256 signing requires a salt",
604                ))?;
605                noxtls_rsassa_pss_sha256_sign(key, request.message, salt)
606            }
607            (PsaSignAlgorithm::EcdsaP256Sha256, SoftwarePrivateMaterial::P256(key)) => {
608                let (r, s) = noxtls_p256_ecdsa_sign_sha256(key, request.message)?;
609                let mut signature = Vec::with_capacity(64);
610                signature.extend_from_slice(&r);
611                signature.extend_from_slice(&s);
612                Ok(signature)
613            }
614            _ => Err(Error::UnsupportedFeature(
615                "psa sign noxtls_algorithm/key mismatch",
616            )),
617        }
618    }
619
620    /// Executes decrypt operations using software cryptographic primitives.
621    ///
622    /// # Arguments
623    ///
624    /// * `self` - Software backend state containing registered keys.
625    /// * `request` - Decrypt request with handle, noxtls_algorithm, and ciphertext.
626    ///
627    /// # Returns
628    ///
629    /// Plaintext bytes decrypted from input ciphertext.
630    ///
631    /// # Errors
632    ///
633    /// Returns policy or crypto errors for unknown handles, denied usage, or bad key type.
634    fn decrypt(&self, request: &KeyDecryptRequest<'_>) -> Result<Vec<u8>> {
635        let (material, policy) = self.resolve_key(request.handle)?;
636        if !policy.allow_decrypt {
637            return Err(Error::StateError("psa decrypt not permitted by key policy"));
638        }
639        match (request.noxtls_algorithm, material) {
640            (PsaDecryptAlgorithm::RsaPkcs1v15, SoftwarePrivateMaterial::Rsa(key)) => {
641                noxtls_rsaes_pkcs1_v15_decrypt(key, request.ciphertext)
642            }
643            (PsaDecryptAlgorithm::RsaOaepSha256, SoftwarePrivateMaterial::Rsa(key)) => {
644                noxtls_rsaes_oaep_sha256_decrypt(
645                    key,
646                    request.ciphertext,
647                    request.label.unwrap_or(&[]),
648                )
649            }
650            _ => Err(Error::UnsupportedFeature(
651                "psa decrypt noxtls_algorithm/key mismatch",
652            )),
653        }
654    }
655
656    /// Executes derive operations using software X25519 primitive.
657    ///
658    /// # Arguments
659    ///
660    /// * `self` - Software backend state containing registered keys.
661    /// * `request` - Derive request with handle, noxtls_algorithm, and peer key.
662    ///
663    /// # Returns
664    ///
665    /// Shared secret bytes from X25519 derive operation.
666    ///
667    /// # Errors
668    ///
669    /// Returns policy or parse errors for unknown handles, denied usage, or invalid peer key.
670    fn noxtls_derive(&self, request: &KeyDeriveRequest<'_>) -> Result<Vec<u8>> {
671        let (material, policy) = self.resolve_key(request.handle)?;
672        if !policy.allow_derive {
673            return Err(Error::StateError("psa derive not permitted by key policy"));
674        }
675        match (request.noxtls_algorithm, material) {
676            (PsaDeriveAlgorithm::X25519, SoftwarePrivateMaterial::X25519(private)) => {
677                if request.peer_public_key.len() != 32 {
678                    return Err(Error::ParseFailure("x25519 peer public key length invalid"));
679                }
680                let mut peer = [0u8; 32];
681                peer.copy_from_slice(request.peer_public_key);
682                Ok(noxtls_x25519(private, &peer).to_vec())
683            }
684            (PsaDeriveAlgorithm::EcdhP256, SoftwarePrivateMaterial::P256(private)) => {
685                let peer = P256PublicKey::from_uncompressed(request.peer_public_key)?;
686                Ok(noxtls_p256_ecdh_shared_secret(private, &peer)?.to_vec())
687            }
688            _ => Err(Error::UnsupportedFeature(
689                "psa derive noxtls_algorithm/key mismatch",
690            )),
691        }
692    }
693
694    /// Produces deterministic random bytes for validation-only posture.
695    ///
696    /// # Arguments
697    ///
698    /// * `self` - Software backend state (not used by this implementation).
699    /// * `out` - Mutable output buffer to fill with deterministic bytes.
700    ///
701    /// # Returns
702    ///
703    /// `Ok(())` once all output bytes are filled.
704    ///
705    /// # Errors
706    ///
707    /// This function does not return errors in the software backend.
708    fn random(&self, out: &mut [u8]) -> Result<()> {
709        for (idx, byte) in out.iter_mut().enumerate() {
710            *byte = (idx as u8).wrapping_mul(17).wrapping_add(0x5A);
711        }
712        Ok(())
713    }
714
715    /// Computes SHA-256 digest with software primitive implementation.
716    ///
717    /// # Arguments
718    ///
719    /// * `self` - Software backend state (not used by this implementation).
720    /// * `input` - Input bytes to hash.
721    ///
722    /// # Returns
723    ///
724    /// 32-byte SHA-256 digest.
725    ///
726    /// # Errors
727    ///
728    /// Returns crypto error if digest primitive reports a failure.
729    fn noxtls_sha256(&self, input: &[u8]) -> Result<[u8; 32]> {
730        Ok(noxtls_sha256(input))
731    }
732
733    /// Encrypts using AES-GCM software primitive.
734    ///
735    /// # Arguments
736    ///
737    /// * `self` - Software backend state (not used by this implementation).
738    /// * `request` - Encryption request with key, nonce, AAD, and plaintext.
739    ///
740    /// # Returns
741    ///
742    /// Ciphertext bytes plus 16-byte authentication tag.
743    ///
744    /// # Errors
745    ///
746    /// Returns [`Error::UnsupportedFeature`] because software AES-GCM path is not wired here.
747    fn noxtls_aes_gcm_encrypt(
748        &self,
749        request: &AeadEncryptRequest<'_>,
750    ) -> Result<AeadEncryptResponse> {
751        let cipher = AesCipher::noxtls_new(request.key)?;
752        let (ciphertext, tag) =
753            noxtls_aes_gcm_encrypt(&cipher, request.nonce, request.aad, request.plaintext)?;
754        Ok(AeadEncryptResponse { ciphertext, tag })
755    }
756}
757
758/// Type alias for default software-backed PSA provider.
759pub type PsaSoftwareProvider = PsaProvider<PsaSoftwareBackend>;