parsec_interface/operations/
psa_asymmetric_decrypt.rs

1// Copyright 2020 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3//! # PsaAsymmetricDecrypt operation
4//!
5//! Decrypt a short message with a public key.
6
7use super::psa_key_attributes::Attributes;
8use crate::operations::psa_algorithm::AsymmetricEncryption;
9use crate::requests::ResponseStatus;
10use derivative::Derivative;
11
12/// Native object for asymmetric decryption operations.
13#[derive(Derivative)]
14#[derivative(Debug)]
15pub struct Operation {
16    /// Defines which key should be used for the signing operation.
17    pub key_name: String,
18    /// An asymmetric encryption algorithm to be used for decryption, that is compatible with the type of key.
19    pub alg: AsymmetricEncryption,
20    /// The short encrypted message to be decrypted.
21    #[derivative(Debug = "ignore")]
22    pub ciphertext: zeroize::Zeroizing<Vec<u8>>,
23    /// Salt to use during decryption, if supported by the algorithm.
24    #[derivative(Debug = "ignore")]
25    pub salt: Option<zeroize::Zeroizing<Vec<u8>>>,
26}
27
28impl Operation {
29    /// Validate the contents of the operation against the attributes of the key it targets
30    ///
31    /// This method checks that:
32    /// * the key policy allows decrypting messages
33    /// * the key policy allows the decryption algorithm requested in the operation
34    /// * the key type is compatible with the requested algorithm
35    /// * if the algorithm is RsaPkcs1v15Crypt, it has no salt (it is not compatible with salt)
36    /// * the message to decrypt is valid (not length 0)
37    pub fn validate(&self, key_attributes: Attributes) -> crate::requests::Result<()> {
38        key_attributes.can_decrypt_message()?;
39        key_attributes.permits_alg(self.alg.into())?;
40        key_attributes.compatible_with_alg(self.alg.into())?;
41        if (self.alg == AsymmetricEncryption::RsaPkcs1v15Crypt && self.salt.is_some())
42            || self.ciphertext.is_empty()
43        {
44            return Err(ResponseStatus::PsaErrorInvalidArgument);
45        }
46        Ok(())
47    }
48}
49
50/// Native object for asymmetric decrypt result.
51// Debug derived as NativeResult enum requires it, even though nothing inside this Result is debuggable
52// as `plaintext` is sensitive.
53#[derive(Derivative)]
54#[derivative(Debug)]
55pub struct Result {
56    /// Decrypted message
57    #[derivative(Debug = "ignore")]
58    pub plaintext: zeroize::Zeroizing<Vec<u8>>,
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::operations::psa_algorithm::{AsymmetricEncryption, Hash};
65    use crate::operations::psa_key_attributes::{Lifetime, Policy, Type, UsageFlags};
66    use zeroize::Zeroizing;
67
68    fn get_attrs() -> Attributes {
69        let mut usage_flags = UsageFlags::default();
70        let _ = usage_flags.set_decrypt();
71        Attributes {
72            lifetime: Lifetime::Persistent,
73            key_type: Type::RsaKeyPair,
74            bits: 256,
75            policy: Policy {
76                usage_flags,
77                permitted_algorithms: AsymmetricEncryption::RsaPkcs1v15Crypt.into(),
78            },
79        }
80    }
81
82    #[test]
83    fn validate_success() {
84        (Operation {
85            key_name: String::from("some key"),
86            alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
87            ciphertext: Zeroizing::new(vec![0xff, 32]),
88            salt: None,
89        })
90        .validate(get_attrs())
91        .unwrap();
92    }
93
94    #[test]
95    fn cannot_decrypt() {
96        let mut attrs = get_attrs();
97        attrs.policy.usage_flags = UsageFlags::default();
98        assert_eq!(
99            (Operation {
100                key_name: String::from("some key"),
101                alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
102                ciphertext: Zeroizing::new(vec![0xff, 32]),
103                salt: None,
104            })
105            .validate(attrs)
106            .unwrap_err(),
107            ResponseStatus::PsaErrorNotPermitted
108        );
109    }
110
111    #[test]
112    fn wrong_algorithm() {
113        assert_eq!(
114            (Operation {
115                key_name: String::from("some key"),
116                alg: AsymmetricEncryption::RsaOaep {
117                    hash_alg: Hash::Sha256,
118                },
119                ciphertext: Zeroizing::new(vec![0xff, 32]),
120                salt: None,
121            })
122            .validate(get_attrs())
123            .unwrap_err(),
124            ResponseStatus::PsaErrorNotPermitted
125        );
126    }
127
128    #[test]
129    fn invalid_ciphertext() {
130        assert_eq!(
131            (Operation {
132                key_name: String::from("some key"),
133                alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
134                ciphertext: Zeroizing::new(vec![]),
135                salt: None,
136            })
137            .validate(get_attrs())
138            .unwrap_err(),
139            ResponseStatus::PsaErrorInvalidArgument
140        );
141    }
142
143    #[test]
144    fn salt_with_rsapkcs1v15crypt() {
145        assert_eq!(
146            (Operation {
147                key_name: String::from("some key"),
148                alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
149                ciphertext: Zeroizing::new(vec![0xff, 32]),
150                salt: Some(zeroize::Zeroizing::new(vec![0xff, 32])),
151            })
152            .validate(get_attrs())
153            .unwrap_err(),
154            ResponseStatus::PsaErrorInvalidArgument
155        );
156    }
157}