atsamd_hal/peripherals/pukcc/
mod.rs

1#![warn(missing_docs)]
2//! # Public Key Cryptography Controller (PUKCC)
3//!
4//! This module provides both low and high level abstractions for dealing with
5//! a PUKCC peripheral.
6//!
7//! PUKCC consists of a set of functions (called services) hidden within a
8//! reserved region of memory. These functions usually make use of a separate
9//! piece of RAM to operate called CryptoRAM.
10//!
11//! [`c_abi`] module contains raw structs and callable C-like function
12//! definitions. [`Pukcc`] wraps this low-level access API and exposes it in a
13//! safe manner.
14//!
15//! ## WARNING!
16//! This module has not been evaluated for correctness nor suitability for any
17//! use-case. Subtle implementation details may have catastrophic implications
18//! for the security of your cryptosystem, and users are advised to engage a
19//! cryptographer before making use of this module.
20#![allow(clippy::just_underscores_and_digits)]
21pub mod c_abi;
22pub mod curves;
23
24use core::iter::{once, repeat};
25
26use crate::pac::Mclk;
27use c_abi::{u2, u4, CryptoRamSlice, Service};
28use curves::Curve;
29
30use rand_core::{CryptoRng, RngCore};
31
32/// This macro linearly copies provided iterable slices/arrays to CryptoRAM and
33/// assigns slices to provided declared local variables from outer scope
34macro_rules! copy_to_cryptoram {
35        (
36            $crypto_ram:expr,
37            $(
38                ($name:ident, $data:expr)
39            ),+
40        ) =>
41        {
42            {
43            (&[])
44                .iter()
45                .cloned()
46            $(
47                .chain($data)
48            )+
49            .zip($crypto_ram.iter_mut())
50            .for_each(|(data_iter, cr_iter)| *cr_iter = data_iter);
51
52            let mut _offset = 0;
53            $(
54                let len = $data.size_hint().1.unwrap_or_else(|| panic!("provided iterator has no size hint"));
55                $name = &$crypto_ram[_offset.._offset + len];
56                _offset += len;
57            )+
58            }
59        }
60}
61
62/// Struct representing a PUKCC peripheral.
63///
64/// It provides an access to cryptographic algorithms in a safe, high-level
65/// manner
66pub struct Pukcc {
67    __: (),
68}
69
70impl Pukcc {
71    /// Constructor.
72    ///
73    /// Waits for a CryptoRAM readiness, enables a synchronous PUKCC clock and
74    /// performs a self test. In case a self test fails it returns an error
75    pub fn enable(mclk: &mut Mclk) -> Result<Self, SelfTestFailure> {
76        unsafe {
77            c_abi::wait_for_crypto_ram_clear_process();
78        }
79        mclk.ahbmask().modify(|_, w| w.pukcc_().set_bit());
80        let pukcc = Self { __: () };
81        pukcc.self_test().map(|_| pukcc)
82    }
83
84    /// Self test service.
85    ///
86    /// Clears up a CryptoRAM and does the checksum. If a checksum and a version
87    /// matches one defined in a HAL, it means that a self test passed
88    /// successfully.
89    ///
90    /// While using a high-level API, user should not need to use this service
91    /// explicitly.
92    pub fn self_test(&self) -> Result<(), SelfTestFailure> {
93        const PUKCL_VERSION: u4 = 0x04070100;
94        const CHECKNUM_1: u4 = 0x6E70DDD2;
95        const CHECKNUM_2: u4 = 0x25C8D64F;
96        let mut pukcl_params = c_abi::PukclParams::default();
97        unsafe {
98            c_abi::SelfTest::call(&mut pukcl_params);
99        }
100        let header = pukcl_params.header;
101        let service_params = unsafe { pukcl_params.params.SelfTest };
102        match header.u2Status.into() {
103            PukclReturnCode::Ok => {}
104            _ => return Err(SelfTestFailure(service_params)),
105        };
106        if service_params.u4Version != PUKCL_VERSION {
107            return Err(SelfTestFailure(service_params));
108        }
109        if service_params.u4CheckNum1 != CHECKNUM_1 {
110            return Err(SelfTestFailure(service_params));
111        }
112        if service_params.u4CheckNum2 != CHECKNUM_2 {
113            return Err(SelfTestFailure(service_params));
114        }
115
116        Ok(())
117    }
118
119    /// Service generating an ECDSA signature.
120    ///
121    /// GF(p) service. GF(2^n) variant is not implemented -- use low-level API.
122    ///
123    /// Input parameters:
124    /// - `hash`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
125    ///     - Hash of a message that is supposed to be signed.
126    /// - `private_key`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
127    ///     - Private key used for signing. Poorly generated `private_key` might
128    ///       have negative security implications.
129    /// - `k_buffer`: `&mut [u8]` of length [`Curve::SCALAR_LENGTH`]
130    ///     - Mutable buffer that is being populated by an entropy source and
131    ///       then used for signing.
132    /// - `k_entropy_source`: `&mut (impl RngCore + CryptoRng)`
133    ///     - Generic source of cryptographically secure randomness.
134    ///
135    /// Output parameters:
136    /// - `signature`: `&mut [u8]` of length `2 * `[`Curve::MOD_LENGTH`]
137    ///     - Mutable slice that signature will be copied to from CryptoRAM
138    ///       after generation is finished. First [`Curve::MOD_LENGTH`] bytes
139    ///       contain `R` part of a signature. Last [`Curve::MOD_LENGTH`] bytes
140    ///       contain `S` part of a signature.
141    ///
142    /// Return value:
143    /// - `Result::Ok`
144    ///     - Signature was generated successfully
145    /// - `Result::Err`
146    ///     - Possible failure scenarios are encapsulated in a
147    ///       [`EcdsaSignFailure`] enum type
148    ///
149    /// Note: Provided [`Curve`] needs to be sound. Otherwise, point
150    /// multiplication can become reversible (lack of _trapdoor function_
151    /// property) and an attacker might be able to reverse engineer a
152    /// `private_key` from a `signature`.
153    pub fn zp_ecdsa_sign_with_entropy<C: Curve>(
154        &self,
155        signature: &mut [u8],
156        hash: &[u8],
157        private_key: &[u8],
158        k_buffer: &mut [u8],
159        k_entropy_source: &mut (impl RngCore + CryptoRng),
160    ) -> Result<(), EcdsaSignFailure> {
161        k_entropy_source.fill_bytes(k_buffer);
162        self.zp_ecdsa_sign::<C>(signature, hash, private_key, k_buffer)
163    }
164
165    /// Service generating an ECDSA signature.
166    ///
167    /// GF(p) service. GF(2^n) variant is not implemented -- use low-level API.
168    ///
169    /// Input parameters:
170    /// - `hash`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
171    ///     - Hash of a message that is supposed to be signed.
172    /// - `private_key`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
173    ///     - Private key used for signing. Poorly generated `private_key` might
174    ///       have negative security implications.
175    /// - `k`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
176    ///     - A random number used for signature generation. It is heavily
177    ///       encouraged to use cryptographically-secure random number
178    ///       generators. One should never use the same `k` more than once.
179    ///       Private key can be extracted from signatures generated with a
180    ///       poorly randomized / the same `k` value.
181    ///
182    /// Exact same set of input parameters (hash, private_key and k) produces
183    /// exactly the same signature.
184    ///
185    /// Output parameters:
186    /// - `signature`: `&mut [u8]` of length `2 * `[`Curve::MOD_LENGTH`]
187    ///     - Mutable slice that signature will be copied to from CryptoRAM
188    ///       after generation is finished. First [`Curve::MOD_LENGTH`] bytes
189    ///       contain `R` part of a signature. Last [`Curve::MOD_LENGTH`] bytes
190    ///       contain `S` part of a signature.
191    ///
192    /// Return value:
193    /// - `Result::Ok`
194    ///     - Signature was generated successfully
195    /// - `Result::Err`
196    ///     - Possible failure scenarios are encapsulated in a
197    ///       [`EcdsaSignFailure`] enum type
198    ///
199    /// Note: Provided [`Curve`] needs to be sound. Otherwise, point
200    /// multiplication can become reversible (lack of _trapdoor function_
201    /// property) and an attacker might be able to reverse engineer a
202    /// `private_key` from a `signature`.
203    ///
204    /// # Safety
205    ///
206    /// `k` value must be cryptographically secure.
207    pub unsafe fn zp_ecdsa_sign_with_raw_k<C: Curve>(
208        &self,
209        signature: &mut [u8],
210        hash: &[u8],
211        private_key: &[u8],
212        k: &[u8],
213    ) -> Result<(), EcdsaSignFailure> {
214        self.zp_ecdsa_sign::<C>(signature, hash, private_key, k)
215    }
216
217    fn zp_ecdsa_sign<C: Curve>(
218        &self,
219        signature: &mut [u8],
220        hash: &[u8],
221        private_key: &[u8],
222        k: &[u8],
223    ) -> Result<(), EcdsaSignFailure> {
224        C::verify_curve().map_err(EcdsaSignFailure::InvalidCurve)?;
225
226        if signature.len() != (2 * C::MOD_LENGTH).into() {
227            return Err(EcdsaSignFailure::WrongInputParameterLength {
228                faulty_slice: "signature",
229                expected_length: (2 * C::MOD_LENGTH).into(),
230                actual_length: signature.len(),
231            });
232        }
233        if hash.len() != (C::SCALAR_LENGTH).into() {
234            return Err(EcdsaSignFailure::WrongInputParameterLength {
235                faulty_slice: "hash",
236                expected_length: (C::SCALAR_LENGTH).into(),
237                actual_length: hash.len(),
238            });
239        }
240        if private_key.len() != (C::SCALAR_LENGTH).into() {
241            return Err(EcdsaSignFailure::WrongInputParameterLength {
242                faulty_slice: "private_key",
243                expected_length: (C::SCALAR_LENGTH).into(),
244                actual_length: private_key.len(),
245            });
246        }
247        if k.len() != (C::SCALAR_LENGTH).into() {
248            return Err(EcdsaSignFailure::WrongInputParameterLength {
249                faulty_slice: "k",
250                expected_length: (C::SCALAR_LENGTH).into(),
251                actual_length: k.len(),
252            });
253        }
254        let (
255            modulo_p,
256            a_curve,
257            base_point_a_x,
258            base_point_a_y,
259            base_point_a_z,
260            order_point,
261            cns,
262            hash_cr,
263            private_key_cr,
264            k_cr,
265            workspace,
266            mut __,
267        );
268        let mut crypto_ram = unsafe { c_abi::CryptoRam::new() };
269        // 32-byte padding with zeroes on a MSB side of every parameter is required by
270        // PUKCC algorithms. Little endianness requires padding *after* a parameter
271        // as MSB is placed on high addresses.
272
273        // 32-byte zero padding for curve parameters should be included in original
274        // slices.
275        copy_to_cryptoram! {
276            crypto_ram,
277            (modulo_p, C::MODULO_P.iter().cloned().rev()),
278            (a_curve, C::A_CURVE.iter().cloned().rev()),
279            (base_point_a_x, C::BASE_POINT_A_X.iter().cloned().rev()),
280            (base_point_a_y, C::BASE_POINT_A_Y.iter().cloned().rev()),
281            (base_point_a_z, C::BASE_POINT_A_Z.iter().cloned().rev()),
282            (order_point, C::ORDER_POINT.iter().cloned().rev()),
283            (cns, C::CNS.iter().cloned().rev()),
284            (hash_cr, hash.iter().cloned().rev()),
285            (__, repeat(0).take(4)),
286            (private_key_cr, private_key.iter().cloned().rev()),
287            (__, repeat(0).take(4)),
288            (k_cr, k.iter().cloned().rev()),
289            (__, repeat(0).take(4)),
290            // Workspace is just marked with a zero length iterator just to get its address.
291            // As it is placed at the end, idea is that algorithm will use whatever amount
292            // of memory it needs
293            (workspace, 0..0)
294        };
295        let mut pukcl_params = c_abi::PukclParams::default();
296        unsafe {
297            let service_params = &mut pukcl_params.params.ZpEcDsaGenerateFast;
298            service_params.nu1ModBase = modulo_p.pukcc_base();
299            service_params.nu1CnsBase = cns.pukcc_base();
300            service_params.u2ModLength = C::MOD_LENGTH;
301            service_params.nu1ScalarNumber = k_cr.pukcc_base();
302            service_params.nu1OrderPointBase = order_point.pukcc_base();
303            service_params.nu1PrivateKey = private_key_cr.pukcc_base();
304            service_params.nu1HashBase = hash_cr.pukcc_base();
305            service_params.u2ScalarLength = C::SCALAR_LENGTH;
306            service_params.nu1PointABase = base_point_a_x.pukcc_base();
307            service_params.nu1ABase = a_curve.pukcc_base();
308            service_params.nu1Workspace = workspace.pukcc_base();
309        }
310
311        unsafe { c_abi::ZpEcDsaGenerateFast::call(&mut pukcl_params) };
312
313        match pukcl_params.header.u2Status.into() {
314            PukclReturnCode::Ok => {}
315            error_code => return Err(EcdsaSignFailure::ServiceFailure(error_code)),
316        };
317
318        // Generated signature R part is written to base point X coordinate memory.
319        // Generated signature S part is written to base point Y coordinate memory.
320        // Base point Z coordinate should be zero.
321
322        if !base_point_a_z.iter().all(|&el| el == 0) {
323            return Err(EcdsaSignFailure::BasePointZCoordinateIsNotZero);
324        }
325
326        // Copying signature back from the CryptoRAM while ignoring irrelevant padding.
327        signature
328            .iter_mut()
329            .zip(
330                base_point_a_x
331                    .iter()
332                    .rev()
333                    .skip(4)
334                    .chain(base_point_a_y.iter().rev().skip(4)),
335            )
336            .for_each(|(target_iter, source_iter)| *target_iter = *source_iter);
337
338        Ok(())
339    }
340
341    /// Service verifying an ECDSA signature.
342    ///
343    /// GF(p) service. GF(2^n) variant is not implemented -- use low-level API.
344    ///
345    /// Input parameters:
346    /// - `signature`: `&[u8]` of length `2 * `[`Curve::SCALAR_LENGTH`]
347    ///     - Signature that is being verified
348    /// - `hash`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
349    ///     - Hash of a message that is signed.
350    /// - `public_key`: `&[u8]` of length [`Curve::SCALAR_LENGTH`]
351    ///     - Public key used for a signature verification.
352    ///
353    /// Return value:
354    /// - `Result::Ok`
355    ///     - Signature is valid against chosen `hash` and `public_key`
356    /// - `Result::Err`
357    ///     - Possible failure scenarios are encapsulated in a
358    ///       [`EcdsaSignatureVerificationFailure`] enum type
359    ///
360    /// In case of an invalid signature the returned error type will be
361    /// [`EcdsaSignatureVerificationFailure::ServiceFailure`]`(`
362    /// [`Warning`][`PukclReturnCode::Warning`]`(`
363    /// [`WrongSignature`][`PukclReturnCodeWarning::WrongSignature`]`))`
364    pub fn zp_ecdsa_verify_signature<C: Curve>(
365        &self,
366        signature: &[u8],
367        hash: &[u8],
368        public_key: &[u8],
369    ) -> Result<(), EcdsaSignatureVerificationFailure> {
370        C::verify_curve().map_err(EcdsaSignatureVerificationFailure::InvalidCurve)?;
371
372        let (
373            modulo_p,
374            a_curve,
375            base_point_a_x,
376            order_point,
377            cns,
378            signature_cr,
379            hash_cr,
380            public_key_cr,
381            workspace,
382            mut __,
383        );
384        if signature.len() != (2 * C::SCALAR_LENGTH).into() {
385            return Err(
386                EcdsaSignatureVerificationFailure::WrongInputParameterLength {
387                    faulty_slice: "signature",
388                    expected_length: (2 * C::SCALAR_LENGTH).into(),
389                    actual_length: signature.len(),
390                },
391            );
392        }
393        if hash.len() != (C::SCALAR_LENGTH).into() {
394            return Err(
395                EcdsaSignatureVerificationFailure::WrongInputParameterLength {
396                    faulty_slice: "hash",
397                    expected_length: (C::SCALAR_LENGTH).into(),
398                    actual_length: hash.len(),
399                },
400            );
401        }
402        if public_key.len() != (2 * C::MOD_LENGTH).into() {
403            return Err(
404                EcdsaSignatureVerificationFailure::WrongInputParameterLength {
405                    faulty_slice: "public_key",
406                    expected_length: (2 * C::MOD_LENGTH).into(),
407                    actual_length: public_key.len(),
408                },
409            );
410        }
411        let mut crypto_ram = unsafe { c_abi::CryptoRam::new() };
412        // 32-byte padding with zeroes on a MSB side of every parameter is required by
413        // PUKCC algorithms. Little endianness requires padding *after* a parameter
414        // as MSB is placed on high addresses.
415
416        // 32-byte zero padding for curve parameters should be included in original
417        // slices.
418        copy_to_cryptoram! {
419            crypto_ram,
420            (modulo_p, C::MODULO_P.iter().cloned().rev()),
421            (a_curve, C::A_CURVE.iter().cloned().rev()),
422            (base_point_a_x, C::BASE_POINT_A_X.iter().cloned().rev()),
423            (__, C::BASE_POINT_A_Y.iter().cloned().rev()),
424            (__, C::BASE_POINT_A_Z.iter().cloned().rev()),
425            (order_point, C::ORDER_POINT.iter().cloned().rev()),
426            (cns, C::CNS.iter().cloned().rev()),
427            // Signature has to be split into two parts + padding must be added
428            // Signature layout:
429            //   [ R: (little endian) ][ 0_u32 ]..
430            (signature_cr, signature.iter().cloned().take(C::SCALAR_LENGTH.into()).rev()),
431            (__, repeat(0).take(4)),
432            // ..[ S: (little endian) ][ 0_u32 ]
433            (__, signature.iter().cloned().skip(C::SCALAR_LENGTH.into()).take(C::SCALAR_LENGTH.into()).rev()),
434            (__, repeat(0).take(4)),
435            (hash_cr, hash.iter().cloned().rev()),
436            (__, repeat(0).take(4)),
437            // Public key has to be represented as a point + padding must be added
438            // Public key layout:
439            //   [ X coordinate: (little endian) ][ 0_u32 ]..
440            (public_key_cr, public_key.iter().cloned().take(C::MOD_LENGTH.into()).rev()),
441            (__, repeat(0).take(4)),
442            // ..[ Y coordinate: (little endian) ][ 0_u32 ]
443            (__, public_key.iter().cloned().skip(C::MOD_LENGTH.into()).take(C::MOD_LENGTH.into()).rev()),
444            (__, repeat(0).take(4)),
445            // ..[ Z coordinate: (little endian) ][ 0_u32 ] == 1
446            (__, once(1).chain(repeat(0).take((C::MOD_LENGTH - 1).into()))),
447            (__, repeat(0).take(4)),
448            // Workspace is just marked with a zero length iterator just to get its address.
449            // As it is placed at the end, idea is that algorithm will use whatever amount
450            // of memory it needs
451            (workspace, 0..0)
452        };
453        let mut pukcl_params = c_abi::PukclParams::default();
454        unsafe {
455            let service_params = &mut pukcl_params.params.ZpEcDsaVerifyFast;
456            service_params.nu1ModBase = modulo_p.pukcc_base();
457            service_params.nu1CnsBase = cns.pukcc_base();
458            service_params.u2ModLength = C::MOD_LENGTH;
459            service_params.nu1OrderPointBase = order_point.pukcc_base();
460            service_params.nu1PointSignature = signature_cr.pukcc_base();
461            service_params.nu1HashBase = hash_cr.pukcc_base();
462            service_params.u2ScalarLength = C::SCALAR_LENGTH;
463            service_params.nu1PointABase = base_point_a_x.pukcc_base();
464            service_params.nu1PointPublicKeyGen = public_key_cr.pukcc_base();
465            service_params.nu1ABase = a_curve.pukcc_base();
466            service_params.nu1Workspace = workspace.pukcc_base();
467        }
468
469        unsafe { c_abi::ZpEcDsaVerifyFast::call(&mut pukcl_params) };
470
471        match pukcl_params.header.u2Status.into() {
472            PukclReturnCode::Ok => Ok(()),
473            error_code => Err(EcdsaSignatureVerificationFailure::ServiceFailure(
474                error_code,
475            )),
476        }
477    }
478
479    /// Service performing a modular exponentiation.
480    ///
481    /// ```text
482    /// result = pow(input, exponent) % modulus
483    /// ```
484    ///
485    /// Input parameters:
486    /// - `input`: `&[u8]`
487    ///     - Requirements:
488    ///         - `len(input) <= len(modulus)`
489    ///     - Message, hash, any slice of data that will undergo modular
490    ///       exponentiation
491    /// - `exponent`: `&[u8]`
492    ///     - Requirements:
493    ///         - `len(exponent) <= len(modulus)`
494    /// - `modulus`: `&[u8]`
495    ///     - Requirements:
496    ///         - `len(modulus) % 4`
497    ///         - `12 <= len(modulus) < ?`
498    ///     - Note: Maximum size depends on few factors like CryptoRAM and
499    ///       workspace window size. Consult the table with data layout down
500    ///       below.
501    /// - `mode`: [`ExpModMode`]
502    ///     - Mode of operation: use regular or fast variant of the underlying
503    ///       algorithm
504    ///     - This parameter does not influence the end result of a computation
505    /// - `window_size`: [`ExpModWindowSize`]
506    ///     - Enum describing 4 predefined workspace sizes (from smallest to
507    ///       biggest) in CryptoRAM.
508    ///     - Bigger the workspace size - faster the algorithm can operate -
509    ///       greater limitations on input parameters are put (as they occupy
510    ///       CryptoRAM address space as well). Consult the table with data
511    ///       layout down below.
512    ///     - This parameter does not influence the end result of a computation
513    /// - `buffer`: `&'a mut [u8]`
514    ///     - Requirements:
515    ///         - `len(buffer) >= len(modulus) + 5`
516    ///     - Buffer used internally for CNS calculation. Piece of it is used
517    ///       also for a return value.
518    ///
519    /// Return value:
520    /// - `Result::Ok(&'a [u8])`
521    ///     - Length: `len(modulus)`
522    ///     - A result of modular exponentiation
523    /// - `Result::Err`
524    ///     - Possible failure scenarios are encapsulated in a [`ExpModFailure`]
525    ///       enum type
526    ///
527    /// Failing to meet the requirements for any input parameter will end up
528    /// with an error being returned.
529    ///
530    /// CryptoRAM is `4KiB` (`0x1000` bytes) in size. Data layout in CryptoRAM
531    /// looks as follows and its size cannot go over the threshold of `4KiB`.
532    ///
533    /// ```text
534    /// - modulus: len(modulus) + 4
535    /// - cns (reduction constant): len(modulus) + 8
536    /// - output/input (after/before calculation): len(modulus) + 16
537    /// - exponent: len(exponent) + 4 (+ padding to be % 4)
538    /// - workspace: (depending on `window_size`)
539    ///     - ExpModWindowSize::One => 3 * (modulus.len() + 4) + 8
540    ///     - ExpModWindowSize::Two => 4 * (modulus.len() + 4) + 8
541    ///     - ExpModWindowSize::Three => 6 * (modulus.len() + 4) + 8
542    ///     - ExpModWindowSize::Four => 10 * (modulus.len() + 4) + 8
543    /// ```
544    ///
545    /// # RSA
546    ///
547    /// This function can be used to perform RSA related computation like
548    /// encryption, decryption, signature generation and validation.
549    /// - To **encrypt** `input` value, split _public key_ into _public_
550    ///   `exponent` and `modulus` and pass them into the function. Return value
551    ///   is going to be a cipher of an `input`.
552    /// - To **decrypt** `input` value, split _private key_ into _private_
553    ///   `exponent` and `modulus` and pass them into the function. Return value
554    ///   is going to be a decrypted `input`.
555    /// - To **generate a signature**, pass the _hash_ of a message being signed
556    ///   as an `input` into the function and use a _private key_. Return value
557    ///   is going to be a signature (encrypted _hash_).
558    /// - To **validate a signature**, pass it as an `input` into the function
559    ///   and use the _public key_. Decrypted signature is an expected hash of a
560    ///   message. Calculate the hash of your message and compare it with an
561    ///   expected value. If they are the same, validation can be considered
562    ///   successful.
563    ///
564    /// All RSA variants up to **RSA4096** (included) will fit into CryptoRAM
565    /// and therefore are supported.
566    pub fn modular_exponentiation<'a>(
567        &self,
568        input: &[u8],
569        exponent: &[u8],
570        modulus: &[u8],
571        mode: ExpModMode,
572        window_size: ExpModWindowSize,
573        buffer: &'a mut [u8],
574    ) -> Result<&'a [u8], ExpModFailure> {
575        const PUKCL_EXPMOD_EXPINPUKCCRAM: u16 = 0x02;
576
577        // Modulus validation
578        if modulus.len() % 4 != 0 {
579            return Err(ExpModFailure::WrongInputParameterAlignment {
580                faulty_slice: "modulus",
581            });
582        }
583        // Modulus size must be at least 12 bytes (43.3.5.2.3 of DS60001507F datasheet)
584        const MINIMUM_MODULUS_LEN: usize = 12;
585        if modulus.len() < MINIMUM_MODULUS_LEN {
586            return Err(ExpModFailure::WrongInputParameterLength {
587                faulty_slice: "modulus",
588                actual_length: modulus.len(),
589                expected_length: ExpectedLengthError::AtLeast(MINIMUM_MODULUS_LEN),
590            });
591        }
592        // Input validation
593        // Note: Only length is checked but in theory value itself being larger than
594        // modulus is probably wrong as well
595        if input.len() > modulus.len() {
596            return Err(ExpModFailure::WrongInputParameterLength {
597                faulty_slice: "input",
598                actual_length: input.len(),
599                expected_length: ExpectedLengthError::AtMost(modulus.len()),
600            });
601        }
602        // Exponent validation
603        // Note: Only length is checked but in theory value itself being larger than
604        // modulus is probably wrong as well
605        if exponent.len() > modulus.len() {
606            return Err(ExpModFailure::WrongInputParameterLength {
607                faulty_slice: "exponent",
608                actual_length: exponent.len(),
609                expected_length: ExpectedLengthError::AtMost(modulus.len()),
610            });
611        }
612
613        let (modulus_cr, cns_cr, output, workspace, exponent_cr, mut __);
614
615        let cns = self.zp_calculate_cns(buffer, modulus)?;
616        let padding_for_cns = padding_for_len(cns.len());
617        // Sanity check in case someone changes `zp_calculate_cns` implementation
618        assert!(cns.len() + padding_for_cns == modulus.len() + 8);
619        let padding_for_exponent = padding_for_len(exponent.len());
620
621        let mut crypto_ram = unsafe { c_abi::CryptoRam::new() };
622        // 32-byte padding with zeroes on a MSB side of every parameter is required by
623        // PUKCC algorithms (unless said otherwise). Little endianness requires padding
624        // *after* a parameter as MSB is placed on high addresses.
625        copy_to_cryptoram! {
626            crypto_ram,
627            (modulus_cr, modulus.iter().cloned().rev()),
628            (__, repeat(0).take(4)),
629            (cns_cr, cns.iter().cloned().rev()),
630            (__, repeat(0).take(padding_for_cns)),
631            // 1. `input` is replaced with an outcome of the computation
632            // 2. `input` is padded to match `len(modulus)`
633            (output, input.iter().cloned().rev().chain(repeat(0).take(modulus.len() - input.len()))),
634            // `input` area is used for computation and requires additional 4 0_u32 on MSB side
635            (__, repeat(0).take(16)),
636            // Exponent is required to have 0_u32 on LSB side
637            // `exponent_cr` is only used to get a pointer during `pukcl_params` initialization
638            (exponent_cr, repeat(0).take(4)),
639            (__, exponent.iter().cloned().rev()),
640            (__, repeat(0).take(padding_for_exponent)),
641            // Workspace is just marked with a zero length slice just to get its address. As
642            // it is placed at the end, idea is that algorithm will use whatever amount of
643            // memory it needs
644            (workspace, 0..0)
645        };
646        // Table 43-52; 43.3.5.2.5 in DS60001507F
647        let workspace_len: usize = match window_size {
648            ExpModWindowSize::One => 3 * (modulus.len() + 4) + 8,
649            ExpModWindowSize::Two => 4 * (modulus.len() + 4) + 8,
650            ExpModWindowSize::Three => 6 * (modulus.len() + 4) + 8,
651            ExpModWindowSize::Four => 10 * (modulus.len() + 4) + 8,
652        };
653        let workspace_end_ptr = workspace.as_ptr().wrapping_add(workspace_len);
654        let crypto_ram_end_ptr = crypto_ram.as_ptr_range().end;
655        if workspace_end_ptr > crypto_ram_end_ptr {
656            return Err(ExpModFailure::RunOutOfCryptoRam {
657                workspace_end_ptr,
658                crypto_ram_end_ptr,
659            });
660        }
661        let mut pukcl_params = c_abi::PukclParams::default();
662        unsafe {
663            // Note: `exponent` outside of Crypto RAM is not supported
664            pukcl_params.header.u2Option = PUKCL_EXPMOD_EXPINPUKCCRAM
665                | window_size.get_windows_size_mask()
666                | mode.get_mode_mask();
667            let service_params = &mut pukcl_params.params.ExpMod;
668            service_params.nu1XBase = output.pukcc_base();
669            service_params.nu1ModBase = modulus_cr.pukcc_base();
670            service_params.nu1CnsBase = cns_cr.pukcc_base();
671            service_params.nu1PrecompBase = workspace.pukcc_base();
672            service_params.pfu1ExpBase = exponent_cr.as_ptr() as _;
673            service_params.u2ModLength = modulus.len() as _;
674            service_params.u2ExpLength = (exponent.len() + padding_for_exponent) as _;
675            service_params.u1Blinding = 0;
676        }
677
678        unsafe { c_abi::ExpMod::call(&mut pukcl_params) };
679        match pukcl_params.header.u2Status.into() {
680            PukclReturnCode::Ok => {}
681            error_code => return Err(ExpModFailure::ServiceFailure(error_code)),
682        }
683
684        buffer
685            .iter_mut()
686            .zip(output.iter().rev())
687            .for_each(|(target_iter, source_iter)| *target_iter = *source_iter);
688
689        Ok(&buffer[..modulus.len()])
690    }
691
692    /// Service producing a reduction constant value
693    fn zp_calculate_cns<'a>(
694        &self,
695        buffer: &'a mut [u8],
696        modulus: &[u8],
697    ) -> Result<&'a [u8], CalculateCnsFailure> {
698        const PUKCL_REDMOD_SETUP: u16 = 0x0100;
699        if modulus.len() % 4 != 0 {
700            return Err(CalculateCnsFailure::WrongInputParameterAlignment {
701                faulty_slice: "modulus",
702            });
703        }
704
705        // Even though documentation says that CNS occupies len(modulus) + 12 of space,
706        // it is only needed for computation, 7 MSB bytes are zeroes. This distinction
707        // between lengths allows to skip these 7 MSB zero bytes.
708        let cns_length = modulus.len() + 12;
709        let actual_cns_length = modulus.len() + 5;
710
711        if buffer.len() < actual_cns_length {
712            return Err(CalculateCnsFailure::WrongInputParameterLength {
713                faulty_slice: "buffer",
714                actual_length: buffer.len(),
715                expected_length: ExpectedLengthError::AtLeast(actual_cns_length),
716            });
717        }
718        let (modulus_cr, cns_cr, workspace_r, workspace_x, mut __);
719        let mut crypto_ram = unsafe { c_abi::CryptoRam::new() };
720        copy_to_cryptoram! {
721            crypto_ram,
722            (modulus_cr, modulus.iter().cloned().rev()),
723            (__, repeat(0).take(4)),
724            (cns_cr, repeat(0).take(actual_cns_length)),
725            (__, repeat(0).take(cns_length - actual_cns_length)),
726             // GF(p) -> 64 bytes
727            (workspace_r, repeat(0).take(64)),
728            (workspace_x, 0..0)
729        };
730        let mut pukcl_params = c_abi::PukclParams::default();
731        unsafe {
732            // Flag that switches behaviour of `RedMod` service into CNS generator
733            pukcl_params.header.u2Option = PUKCL_REDMOD_SETUP;
734            let service_params = &mut pukcl_params.params.RedMod;
735            service_params.nu1ModBase = modulus_cr.pukcc_base();
736            service_params.nu1CnsBase = cns_cr.pukcc_base();
737            service_params.u2ModLength = modulus.len() as _;
738            service_params.nu1RBase = workspace_r.pukcc_base();
739            service_params.nu1XBase = workspace_x.pukcc_base();
740        }
741        unsafe { c_abi::RedMod::call(&mut pukcl_params) };
742        match pukcl_params.header.u2Status.into() {
743            PukclReturnCode::Ok => {}
744            error_code => return Err(CalculateCnsFailure::ServiceFailure(error_code)),
745        }
746
747        buffer
748            .iter_mut()
749            .zip(cns_cr.iter().rev())
750            .for_each(|(target_iter, source_iter)| *target_iter = *source_iter);
751        Ok(&buffer[..actual_cns_length])
752    }
753}
754
755/// An error type representing failure modes a [`Pukcc::self_test`] service
756#[derive(Debug)]
757#[allow(dead_code)]
758pub struct SelfTestFailure(c_abi::SelfTest);
759
760/// An error type representing failure modes for a
761/// [`Pukcc::zp_ecdsa_sign_with_entropy`] and
762/// [`Pukcc::zp_ecdsa_sign_with_raw_k`] service
763#[allow(missing_docs)]
764#[derive(Debug)]
765pub enum EcdsaSignFailure {
766    WrongInputParameterLength {
767        faulty_slice: &'static str,
768        expected_length: usize,
769        actual_length: usize,
770    },
771    InvalidCurve(curves::CurveVerificationFailure),
772    BasePointZCoordinateIsNotZero,
773    ServiceFailure(PukclReturnCode),
774}
775
776/// An error type representing failure modes for a
777/// [`Pukcc::zp_ecdsa_verify_signature`] service
778#[allow(missing_docs)]
779#[derive(Debug)]
780pub enum EcdsaSignatureVerificationFailure {
781    WrongInputParameterLength {
782        faulty_slice: &'static str,
783        expected_length: usize,
784        actual_length: usize,
785    },
786    InvalidCurve(curves::CurveVerificationFailure),
787    ServiceFailure(PukclReturnCode),
788}
789
790/// An error type specifying an expected length of a slice in question
791#[allow(missing_docs)]
792#[derive(Debug)]
793pub enum ExpectedLengthError {
794    AtMost(usize),
795    AtLeast(usize),
796    Exactly(usize),
797}
798
799/// An enum describing available modes of operation of
800/// `Pukcc::modular_exponentiation` algoritm
801#[allow(missing_docs)]
802#[derive(Debug)]
803pub enum ExpModMode {
804    Regular,
805    Fast,
806}
807
808impl ExpModMode {
809    /// Function mapping the enum variant with a low level mask value needed in
810    /// [`c_abi::PukclHeader::u2Option`] for [`c_abi::ExpMod`] service
811    pub fn get_mode_mask(&self) -> u2 {
812        use ExpModMode::*;
813        match self {
814            Regular => 0x01,
815            Fast => 0x04,
816        }
817    }
818}
819
820/// An enum describing allowed, predefined window sizes for a calculation
821/// workspace in CryptoRAM for [`Pukcc::modular_exponentiation`] algorithm
822#[allow(missing_docs)]
823#[derive(Debug)]
824pub enum ExpModWindowSize {
825    /// 3 * (len(modulus) + 4) + 8 bytes allowed to be used as a workspace
826    One,
827    /// 4 * (len(modulus) + 4) + 8 bytes allowed to be used as a workspace
828    Two,
829    /// 6 * (len(modulus) + 4) + 8 bytes allowed to be used as a workspace
830    Three,
831    /// 10 * (len(modulus) + 4) + 8 bytes allowed to be used as a workspace
832    Four,
833}
834
835impl ExpModWindowSize {
836    /// Function mapping the enum variant with a low level mask value needed in
837    /// [`c_abi::PukclHeader::u2Option`] for [`c_abi::ExpMod`] service
838    pub fn get_windows_size_mask(&self) -> u2 {
839        use ExpModWindowSize::*;
840        match self {
841            One => 0x00,
842            Two => 0x08,
843            Three => 0x10,
844            Four => 0x18,
845        }
846    }
847}
848
849/// An error type representing failure modes for a
850/// [`Pukcc::modular_exponentiation`] service
851#[allow(missing_docs)]
852#[derive(Debug)]
853pub enum ExpModFailure {
854    WrongInputParameterLength {
855        faulty_slice: &'static str,
856        expected_length: ExpectedLengthError,
857        actual_length: usize,
858    },
859    /// Should be 4-aligned
860    WrongInputParameterAlignment {
861        faulty_slice: &'static str,
862    },
863    RunOutOfCryptoRam {
864        workspace_end_ptr: *const u8,
865        crypto_ram_end_ptr: *const u8,
866    },
867    CalculateCnsFailure(CalculateCnsFailure),
868    ServiceFailure(PukclReturnCode),
869}
870
871/// An error type representing failure modes for a
872/// `Pukcc::zp_calculate_cns` service
873#[allow(missing_docs)]
874#[derive(Debug)]
875pub enum CalculateCnsFailure {
876    WrongInputParameterLength {
877        faulty_slice: &'static str,
878        expected_length: ExpectedLengthError,
879        actual_length: usize,
880    },
881    /// Should be 4-aligned
882    WrongInputParameterAlignment {
883        faulty_slice: &'static str,
884    },
885    ServiceFailure(PukclReturnCode),
886}
887
888impl From<CalculateCnsFailure> for ExpModFailure {
889    fn from(f: CalculateCnsFailure) -> Self {
890        ExpModFailure::CalculateCnsFailure(f)
891    }
892}
893
894// PukclReturnCode <-> c_abi::PukclReturnCode
895impl core::convert::From<c_abi::PukclReturnCode> for PukclReturnCode {
896    fn from(v: c_abi::PukclReturnCode) -> Self {
897        use PukclReturnCode::*;
898        match v.0 {
899            0x0000 => Ok,
900            0xC001 => Severe(PukclReturnCodeSevere::ComputationNotStarted),
901            0xC002 => Severe(PukclReturnCodeSevere::UnknownService),
902            0xC003 => Severe(PukclReturnCodeSevere::UnexploitableOptions),
903            0xC004 => Severe(PukclReturnCodeSevere::HardwareIssue),
904            0xC005 => Severe(PukclReturnCodeSevere::WrongHardware),
905            0xC006 => Severe(PukclReturnCodeSevere::LibraryMalformed),
906            0xC007 => Severe(PukclReturnCodeSevere::Error),
907            0xC008 => Severe(PukclReturnCodeSevere::UnknownSubservice),
908            0xC010 => Severe(PukclReturnCodeSevere::OverlapNotAllowed),
909            0xC011 => Severe(PukclReturnCodeSevere::ParamNotInPukccram),
910            0xC012 => Severe(PukclReturnCodeSevere::ParamNotInRam),
911            0xC013 => Severe(PukclReturnCodeSevere::ParamNotInCpuram),
912            0xC014 => Severe(PukclReturnCodeSevere::ParamWrongLength),
913            0xC015 => Severe(PukclReturnCodeSevere::ParamBadAlignment),
914            0xC016 => Severe(PukclReturnCodeSevere::ParamXBiggerThanY),
915            0xC017 => Severe(PukclReturnCodeSevere::ParamLengthTooSmall),
916            0xC101 => Severe(PukclReturnCodeSevere::DivisionByZero),
917            0xC102 => Severe(PukclReturnCodeSevere::MalformedModulus),
918            0xC103 => Severe(PukclReturnCodeSevere::FaultDetected),
919            0xC104 => Severe(PukclReturnCodeSevere::MalformedKey),
920            0xC105 => Severe(PukclReturnCodeSevere::AprioriOk),
921            0xC106 => Severe(PukclReturnCodeSevere::WrongService),
922            0x8001 => Warning(PukclReturnCodeWarning::PointAtInfinity),
923            0x8002 => Warning(PukclReturnCodeWarning::WrongSignature),
924            0x8003 => Warning(PukclReturnCodeWarning::WrongSelectnumber),
925            0x8004 => Warning(PukclReturnCodeWarning::PointIsNotOnCurve),
926            0x4001 => Info(PukclReturnCodeInfo::NumberIsNotPrime),
927            0x4002 => Info(PukclReturnCodeInfo::NumberIsPrime),
928            code => Unknown { code },
929        }
930    }
931}
932
933impl core::convert::From<PukclReturnCode> for c_abi::PukclReturnCode {
934    fn from(v: PukclReturnCode) -> Self {
935        use PukclReturnCode::*;
936        Self(match v {
937            Ok => 0x0000,
938            Severe(PukclReturnCodeSevere::ComputationNotStarted) => 0xC001,
939            Severe(PukclReturnCodeSevere::UnknownService) => 0xC002,
940            Severe(PukclReturnCodeSevere::UnexploitableOptions) => 0xC003,
941            Severe(PukclReturnCodeSevere::HardwareIssue) => 0xC004,
942            Severe(PukclReturnCodeSevere::WrongHardware) => 0xC005,
943            Severe(PukclReturnCodeSevere::LibraryMalformed) => 0xC006,
944            Severe(PukclReturnCodeSevere::Error) => 0xC007,
945            Severe(PukclReturnCodeSevere::UnknownSubservice) => 0xC008,
946            Severe(PukclReturnCodeSevere::OverlapNotAllowed) => 0xC010,
947            Severe(PukclReturnCodeSevere::ParamNotInPukccram) => 0xC011,
948            Severe(PukclReturnCodeSevere::ParamNotInRam) => 0xC012,
949            Severe(PukclReturnCodeSevere::ParamNotInCpuram) => 0xC013,
950            Severe(PukclReturnCodeSevere::ParamWrongLength) => 0xC014,
951            Severe(PukclReturnCodeSevere::ParamBadAlignment) => 0xC015,
952            Severe(PukclReturnCodeSevere::ParamXBiggerThanY) => 0xC016,
953            Severe(PukclReturnCodeSevere::ParamLengthTooSmall) => 0xC017,
954            Severe(PukclReturnCodeSevere::DivisionByZero) => 0xC101,
955            Severe(PukclReturnCodeSevere::MalformedModulus) => 0xC102,
956            Severe(PukclReturnCodeSevere::FaultDetected) => 0xC103,
957            Severe(PukclReturnCodeSevere::MalformedKey) => 0xC104,
958            Severe(PukclReturnCodeSevere::AprioriOk) => 0xC105,
959            Severe(PukclReturnCodeSevere::WrongService) => 0xC106,
960            Warning(PukclReturnCodeWarning::PointAtInfinity) => 0x8001,
961            Warning(PukclReturnCodeWarning::WrongSignature) => 0x8002,
962            Warning(PukclReturnCodeWarning::WrongSelectnumber) => 0x8003,
963            Warning(PukclReturnCodeWarning::PointIsNotOnCurve) => 0x8004,
964            Info(PukclReturnCodeInfo::NumberIsNotPrime) => 0x4001,
965            Info(PukclReturnCodeInfo::NumberIsPrime) => 0x4002,
966            Unknown { code } => code,
967        })
968    }
969}
970
971/// An enum type that is a human readable representation of a low-level
972/// [`c_abi::PukclReturnCode`] type. Useful when used together with a [`Debug`]
973/// traits and formatters.
974#[allow(missing_docs)]
975#[derive(Clone, Copy, Debug)]
976pub enum PukclReturnCode {
977    Ok,
978    Info(PukclReturnCodeInfo),
979    Warning(PukclReturnCodeWarning),
980    Severe(PukclReturnCodeSevere),
981    Unknown { code: u16 },
982}
983
984/// [`PukclReturnCode`] nested enum subtype
985#[allow(missing_docs)]
986#[derive(Clone, Copy, Debug)]
987pub enum PukclReturnCodeInfo {
988    NumberIsNotPrime,
989    NumberIsPrime,
990}
991
992/// [`PukclReturnCode`] nested enum subtype
993#[allow(missing_docs)]
994#[derive(Clone, Copy, Debug)]
995pub enum PukclReturnCodeWarning {
996    PointAtInfinity,
997    WrongSignature,
998    WrongSelectnumber,
999    PointIsNotOnCurve,
1000}
1001
1002/// [`PukclReturnCode`] nested enum subtype
1003#[allow(missing_docs)]
1004#[derive(Clone, Copy, Debug)]
1005pub enum PukclReturnCodeSevere {
1006    ComputationNotStarted,
1007    UnknownService,
1008    UnexploitableOptions,
1009    HardwareIssue,
1010    WrongHardware,
1011    LibraryMalformed,
1012    Error,
1013    UnknownSubservice,
1014    OverlapNotAllowed,
1015    ParamNotInPukccram,
1016    ParamNotInRam,
1017    ParamNotInCpuram,
1018    ParamWrongLength,
1019    ParamBadAlignment,
1020    ParamXBiggerThanY,
1021    ParamLengthTooSmall,
1022    DivisionByZero,
1023    MalformedModulus,
1024    FaultDetected,
1025    MalformedKey,
1026    AprioriOk,
1027    WrongService,
1028}
1029
1030fn padding_for_len(len: usize) -> usize {
1031    const ALIGNMENT: usize = 4;
1032    if len % ALIGNMENT != 0 {
1033        ALIGNMENT - (len % ALIGNMENT)
1034    } else {
1035        0
1036    }
1037}