Skip to main content

psa_crypto/types/
key_derivation.rs

1// Copyright 2020 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3
4//! # PSA Key Derivation Operation types
5
6use super::algorithm::{Hash, KeyDerivation, RawKeyAgreement};
7use super::key::Id;
8#[cfg(feature = "operations")]
9use super::status::{Error, Result, Status};
10#[cfg(feature = "operations")]
11use core::convert::TryFrom;
12
13/// Key derivation operation for deriving keys from existing sources
14#[derive(Debug, Clone, Copy)]
15pub struct Operation<'a> {
16    /// Key derivation algorithm and inputs
17    pub inputs: Inputs<'a>,
18    /// Maximum capacity of a key derivation operation
19    pub capacity: Option<usize>,
20}
21
22/// Wrapper around KeyDerivation to enforce correct `Input`s
23#[derive(Debug, Clone, Copy)]
24pub enum Inputs<'a> {
25    /// HKDF algorithm.
26    Hkdf {
27        /// A hash algorithm to use
28        hash_alg: Hash,
29        /// Salt, used in the "extract" step. It is optional; if omitted, the derivation uses an empty salt.
30        /// Typically a direct input, can also be a key of type `RawData`.
31        salt: Option<Input<'a>>,
32        /// Secret, used in the "extract" step. This is typically a key of type `Derive` , or the shared secret
33        /// resulting from a key agreement, using `Input::KeyAgreement`.
34        /// Must be a key or key agreement input if used with `psa_key_derivation_output_key`.
35        secret: InputSecret<'a>,
36        /// Info, used in the "expand" step. Typically a direct input, can also be a key of type `RawData`.
37        info: Input<'a>,
38    },
39    /// TLS-1.2 PRF algorithm.
40    Tls12Prf {
41        /// A hash algorithm to use.
42        hash_alg: Hash,
43        /// Seed, typically a direct input, can also be a key of type `RawData`.
44        seed: Input<'a>,
45        /// Secret, used in the "extract" step. This is typically a key of type `Derive` , or the shared secret
46        /// resulting from a key agreement, using `Input::KeyAgreement`.
47        /// Must be a key or key agreement input if used with `psa_key_derivation_output_key`.
48        secret: InputSecret<'a>,
49        /// Label. Typically a direct input, can also be a key of type `RawData`.
50        label: Input<'a>,
51    },
52    /// TLS-1.2 PSK-to-MasterSecret algorithm.
53    Tls12PskToMs {
54        /// A hash algorithm to use.
55        hash_alg: Hash,
56        /// Seed, typically a direct input, can also be a key of type `RawData`.
57        seed: Input<'a>,
58        /// Secret, used in the "extract" step. This is typically a key of type `Derive` , or the shared secret
59        /// resulting from a key agreement, using `Input::KeyAgreement`.
60        /// Must be a key or key agreement input if used with `psa_key_derivation_output_key`.
61        /// Must not be larger than `PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE`.
62        secret: InputSecret<'a>,
63        /// Label. Typically a direct input, can also be a key of type `RawData`.
64        label: Input<'a>,
65    },
66}
67
68/// Enumeration of the step of a key derivation.
69#[cfg(feature = "operations")]
70#[derive(Debug, Clone, Copy)]
71enum DerivationStep {
72    /// A secret input for key derivation.
73    Secret,
74    /// A label for key derivation.
75    Label,
76    /// A context for key derivation.
77    //Context, In PSA spec but not in Mbed Crypto
78    /// A salt for key derivation.
79    Salt,
80    /// An information string for key derivation.
81    Info,
82    /// A seed for key derivation.
83    Seed,
84}
85
86#[cfg(feature = "operations")]
87impl From<DerivationStep> for psa_crypto_sys::psa_key_derivation_step_t {
88    fn from(derivation_step: DerivationStep) -> Self {
89        match derivation_step {
90            DerivationStep::Secret => psa_crypto_sys::PSA_KEY_DERIVATION_INPUT_SECRET,
91            DerivationStep::Label => psa_crypto_sys::PSA_KEY_DERIVATION_INPUT_LABEL,
92            DerivationStep::Salt => psa_crypto_sys::PSA_KEY_DERIVATION_INPUT_SALT,
93            //DerivationStep::Context => psa_crypto_sys::PSA_KEY_DERIVATION_INPUT_CONTEXT, In PSA spec but not in Mbed Crypto
94            DerivationStep::Info => psa_crypto_sys::PSA_KEY_DERIVATION_INPUT_INFO,
95            DerivationStep::Seed => psa_crypto_sys::PSA_KEY_DERIVATION_INPUT_SEED,
96        }
97    }
98}
99
100#[cfg(feature = "interface")]
101impl From<Inputs<'_>> for psa_crypto_sys::psa_algorithm_t {
102    fn from(key_derivation_with_inputs: Inputs) -> Self {
103        key_derivation_with_inputs.key_derivation().into()
104    }
105}
106
107impl Inputs<'_> {
108    /// Retrieve key derivation algorithm without inputs
109    pub fn key_derivation(&self) -> KeyDerivation {
110        match self {
111            Inputs::Hkdf { hash_alg, .. } => KeyDerivation::Hkdf {
112                hash_alg: *hash_alg,
113            },
114            Inputs::Tls12Prf { hash_alg, .. } => KeyDerivation::Tls12Prf {
115                hash_alg: *hash_alg,
116            },
117            Inputs::Tls12PskToMs { hash_alg, .. } => KeyDerivation::Tls12PskToMs {
118                hash_alg: *hash_alg,
119            },
120        }
121    }
122
123    #[cfg(feature = "operations")]
124    pub(crate) fn apply_inputs_to_op(
125        &self,
126        op: &mut psa_crypto_sys::psa_key_derivation_operation_t,
127    ) -> Result<()> {
128        match self {
129            Inputs::Hkdf {
130                salt, secret, info, ..
131            } => {
132                if let Some(salt) = salt {
133                    Inputs::apply_input_step_to_op(op, DerivationStep::Salt, salt)?;
134                }
135                Inputs::apply_input_secret_step_to_op(op, secret)?;
136                Inputs::apply_input_step_to_op(op, DerivationStep::Info, info)
137            }
138            Inputs::Tls12Prf {
139                seed,
140                secret,
141                label,
142                ..
143            }
144            | Inputs::Tls12PskToMs {
145                seed,
146                secret,
147                label,
148                ..
149            } => {
150                Inputs::apply_input_step_to_op(op, DerivationStep::Seed, seed)?;
151                Inputs::apply_input_secret_step_to_op(op, secret)?;
152                Inputs::apply_input_step_to_op(op, DerivationStep::Label, label)
153            }
154        }
155    }
156
157    #[cfg(feature = "operations")]
158    fn apply_input_step_to_op(
159        op: &mut psa_crypto_sys::psa_key_derivation_operation_t,
160        step: DerivationStep,
161        input: &Input,
162    ) -> Result<()> {
163        match input {
164            Input::Bytes(bytes) => Status::from(unsafe {
165                psa_crypto_sys::psa_key_derivation_input_bytes(
166                    op,
167                    step.into(),
168                    bytes.as_ptr(),
169                    bytes.len(),
170                )
171            })
172            .to_result(),
173            Input::Key(key_id) => Status::from(unsafe {
174                psa_crypto_sys::psa_key_derivation_input_key(op, step.into(), key_id.0)
175            })
176            .to_result(),
177        }
178    }
179
180    #[cfg(feature = "operations")]
181    fn apply_input_secret_step_to_op(
182        op: &mut psa_crypto_sys::psa_key_derivation_operation_t,
183        secret: &InputSecret,
184    ) -> Result<()> {
185        match secret {
186            InputSecret::Input(input) => {
187                Inputs::apply_input_step_to_op(op, DerivationStep::Secret, input)
188            }
189            InputSecret::KeyAgreement {
190                private_key,
191                peer_key,
192                ..
193            } => Status::from(unsafe {
194                psa_crypto_sys::psa_key_derivation_key_agreement(
195                    op,
196                    DerivationStep::Secret.into(),
197                    private_key.0,
198                    (**peer_key).as_ptr(),
199                    peer_key.len(),
200                )
201            })
202            .to_result(),
203        }
204    }
205}
206
207/// Enumeration of supported input data for different input steps
208#[derive(Debug, Clone, Copy)]
209pub enum Input<'a> {
210    /// Byte input for key derivation
211    Bytes(&'a [u8]),
212    /// Key input for key derivation
213    Key(Id),
214}
215
216/// Enumeration of supported input data for different input steps
217#[derive(Debug, Clone, Copy)]
218pub enum InputSecret<'a> {
219    /// Regular input of bytes or a key ID
220    Input(Input<'a>),
221    /// Output of a key agreement
222    KeyAgreement {
223        /// Key agreement algorithm to use
224        alg: RawKeyAgreement,
225        /// Private key to use in key agreement
226        private_key: Id,
227        /// Public key data of peer key to use. Must be in the same format that `key_management::import` accepts for the public key
228        /// corresponding to the type of private key.
229        peer_key: &'a [u8],
230    },
231}
232
233impl<'a> From<Input<'a>> for InputSecret<'a> {
234    fn from(input: Input<'a>) -> Self {
235        InputSecret::<'a>::Input(input)
236    }
237}
238
239#[cfg(feature = "operations")]
240impl TryFrom<Operation<'_>> for psa_crypto_sys::psa_key_derivation_operation_t {
241    type Error = Error;
242
243    fn try_from(operation: Operation) -> Result<Self> {
244        let mut op = psa_crypto_sys::psa_key_derivation_operation_init();
245        let mut setup_deriv_op = || -> Result<()> {
246            let mut key_derivation_alg: psa_crypto_sys::psa_algorithm_t =
247                operation.inputs.key_derivation().into();
248
249            // If key agreement is used as the input for secret step, extract key agreement alg and combine it with key derivation alg
250            let secret = match operation.inputs {
251                Inputs::Hkdf { secret, .. }
252                | Inputs::Tls12Prf { secret, .. }
253                | Inputs::Tls12PskToMs { secret, .. } => secret,
254            };
255            if let InputSecret::KeyAgreement { alg, .. } = secret {
256                key_derivation_alg = unsafe {
257                    psa_crypto_sys::PSA_ALG_KEY_AGREEMENT(alg.into(), key_derivation_alg)
258                };
259            }
260
261            Status::from(unsafe {
262                psa_crypto_sys::psa_key_derivation_setup(&mut op, key_derivation_alg)
263            })
264            .to_result()?;
265            operation.inputs.apply_inputs_to_op(&mut op)
266        };
267        if let Err(error) = setup_deriv_op() {
268            Operation::abort(op)?;
269            return Err(error);
270        }
271
272        if let Some(capacity) = operation.capacity {
273            // Maybe best to add capacity to the algorithms for static check as some don't support it
274            Status::from(unsafe {
275                psa_crypto_sys::psa_key_derivation_set_capacity(&mut op, capacity)
276            })
277            .to_result()?;
278        }
279        Ok(op)
280    }
281}
282
283impl Operation<'_> {
284    /// Clears operation C struct and consumes KeyDerivationOperation struct
285    #[cfg(feature = "operations")]
286    pub(crate) fn abort(mut op: psa_crypto_sys::psa_key_derivation_operation_t) -> Result<()> {
287        Status::from(unsafe { psa_crypto_sys::psa_key_derivation_abort(&mut op) }).to_result()
288    }
289}