Skip to content

Commit 89e1c3e

Browse files
committed
Added asymmetric encrypt and decrypt
Signed-off-by: Samuel Bailey <[email protected]>
1 parent 3ec787e commit 89e1c3e

File tree

10 files changed

+805
-2
lines changed

10 files changed

+805
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ prost = "0.6.1"
2525
arbitrary = { version = "0.4.4", features = ["derive"] }
2626
uuid = "0.7.4"
2727
log = "0.4.8"
28-
psa-crypto = { version = "0.2.0", default-features = false }
28+
psa-crypto = { version = "0.2.2", default-features = false }
2929
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
3030
secrecy = { version = "0.6.0", features = ["serde"] }
3131
derivative = "2.1.1"

src/operations/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ pub mod psa_export_public_key;
1616
pub mod psa_destroy_key;
1717
pub mod psa_sign_hash;
1818
pub mod psa_verify_hash;
19+
pub mod psa_asymmetric_encrypt;
20+
pub mod psa_asymmetric_decrypt;
1921
pub mod list_opcodes;
2022
pub mod list_providers;
2123

@@ -46,6 +48,10 @@ pub enum NativeOperation {
4648
PsaSignHash(psa_sign_hash::Operation),
4749
/// PsaVerifyHash operation
4850
PsaVerifyHash(psa_verify_hash::Operation),
51+
/// PsaAsymmetricEncrypt operation
52+
PsaAsymmetricEncrypt(psa_asymmetric_encrypt::Operation),
53+
/// PsaAsymmetricDecrypt operation
54+
PsaAsymmetricDecrypt(psa_asymmetric_decrypt::Operation),
4955
}
5056

5157
impl NativeOperation {
@@ -61,6 +67,8 @@ impl NativeOperation {
6167
NativeOperation::PsaExportPublicKey(_) => Opcode::PsaExportPublicKey,
6268
NativeOperation::ListOpcodes(_) => Opcode::ListOpcodes,
6369
NativeOperation::ListProviders(_) => Opcode::ListProviders,
70+
NativeOperation::PsaAsymmetricEncrypt(_) => Opcode::PsaAsymmetricEncrypt,
71+
NativeOperation::PsaAsymmetricDecrypt(_) => Opcode::PsaAsymmetricDecrypt,
6472
}
6573
}
6674
}
@@ -87,6 +95,10 @@ pub enum NativeResult {
8795
PsaSignHash(psa_sign_hash::Result),
8896
/// PsaVerifyHash result
8997
PsaVerifyHash(psa_verify_hash::Result),
98+
/// PsaAsymmetricEncrypt result
99+
PsaAsymmetricEncrypt(psa_asymmetric_encrypt::Result),
100+
/// PsaAsymmetricDecrypt result
101+
PsaAsymmetricDecrypt(psa_asymmetric_decrypt::Result),
90102
}
91103

92104
impl NativeResult {
@@ -102,6 +114,8 @@ impl NativeResult {
102114
NativeResult::PsaExportPublicKey(_) => Opcode::PsaExportPublicKey,
103115
NativeResult::ListOpcodes(_) => Opcode::ListOpcodes,
104116
NativeResult::ListProviders(_) => Opcode::ListProviders,
117+
NativeResult::PsaAsymmetricEncrypt(_) => Opcode::PsaAsymmetricEncrypt,
118+
NativeResult::PsaAsymmetricDecrypt(_) => Opcode::PsaAsymmetricDecrypt,
105119
}
106120
}
107121
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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+
7+
use super::psa_key_attributes::Attributes;
8+
use crate::operations::psa_algorithm::AsymmetricEncryption;
9+
use crate::requests::ResponseStatus;
10+
#[cfg(feature = "fuzz")]
11+
use arbitrary::Arbitrary;
12+
use derivative::Derivative;
13+
use secrecy::Secret;
14+
15+
/// Native object for asymmetric decryption operations.
16+
#[derive(Debug)]
17+
pub struct Operation {
18+
/// Defines which key should be used for the signing operation.
19+
pub key_name: String,
20+
/// An asymmetric encryption algorithm to be used for decryption, that is compatible with the type of key.
21+
pub alg: AsymmetricEncryption,
22+
/// The short encrypted message to be decrypted.
23+
pub ciphertext: Vec<u8>,
24+
/// Salt to use during decryption, if supported by the algorithm.
25+
pub salt: Option<zeroize::Zeroizing<Vec<u8>>>,
26+
}
27+
28+
impl 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 encrypting messages
33+
/// * the key policy allows the encryption 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 != None)
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)]
55+
pub struct Result {
56+
/// The `plaintext` field contains the decrypted short message.
57+
#[derivative(Debug = "ignore")]
58+
pub plaintext: Secret<Vec<u8>>,
59+
}
60+
61+
#[cfg(test)]
62+
mod tests {
63+
use super::*;
64+
use crate::operations::psa_algorithm::{AsymmetricEncryption, Hash};
65+
use crate::operations::psa_key_attributes::{Lifetime, Policy, Type, UsageFlags};
66+
67+
fn get_attrs() -> Attributes {
68+
Attributes {
69+
lifetime: Lifetime::Persistent,
70+
key_type: Type::RsaKeyPair,
71+
bits: 256,
72+
policy: Policy {
73+
usage_flags: UsageFlags {
74+
export: false,
75+
copy: false,
76+
cache: false,
77+
encrypt: false,
78+
decrypt: true,
79+
sign_message: false,
80+
verify_message: false,
81+
sign_hash: false,
82+
verify_hash: false,
83+
derive: false,
84+
},
85+
permitted_algorithms: AsymmetricEncryption::RsaPkcs1v15Crypt.into(),
86+
},
87+
}
88+
}
89+
90+
#[test]
91+
fn validate_success() {
92+
(Operation {
93+
key_name: String::from("some key"),
94+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
95+
ciphertext: vec![0xff, 32],
96+
salt: None,
97+
})
98+
.validate(get_attrs())
99+
.unwrap();
100+
}
101+
102+
#[test]
103+
fn cannot_decrypt() {
104+
let mut attrs = get_attrs();
105+
attrs.policy.usage_flags.decrypt = false;
106+
assert_eq!(
107+
(Operation {
108+
key_name: String::from("some key"),
109+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
110+
ciphertext: vec![0xff, 32],
111+
salt: None,
112+
})
113+
.validate(attrs)
114+
.unwrap_err(),
115+
ResponseStatus::PsaErrorNotPermitted
116+
);
117+
}
118+
119+
#[test]
120+
fn wrong_algorithm() {
121+
assert_eq!(
122+
(Operation {
123+
key_name: String::from("some key"),
124+
alg: AsymmetricEncryption::RsaOaep {
125+
hash_alg: Hash::Sha256,
126+
},
127+
ciphertext: vec![0xff, 32],
128+
salt: None,
129+
})
130+
.validate(get_attrs())
131+
.unwrap_err(),
132+
ResponseStatus::PsaErrorNotPermitted
133+
);
134+
}
135+
136+
#[test]
137+
fn invalid_ciphertext() {
138+
assert_eq!(
139+
(Operation {
140+
key_name: String::from("some key"),
141+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
142+
ciphertext: vec![],
143+
salt: None,
144+
})
145+
.validate(get_attrs())
146+
.unwrap_err(),
147+
ResponseStatus::PsaErrorInvalidArgument
148+
);
149+
}
150+
151+
#[test]
152+
fn salt_with_rsapkcs1v15crypt() {
153+
assert_eq!(
154+
(Operation {
155+
key_name: String::from("some key"),
156+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
157+
ciphertext: vec![0xff, 32],
158+
salt: Some(zeroize::Zeroizing::new(vec![0xff, 32])),
159+
})
160+
.validate(get_attrs())
161+
.unwrap_err(),
162+
ResponseStatus::PsaErrorInvalidArgument
163+
);
164+
}
165+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright 2020 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! # PsaAsymmetricEncrypt operation
4+
//!
5+
//! Encrypt a short message with a public key.
6+
7+
use super::psa_key_attributes::Attributes;
8+
use crate::operations::psa_algorithm::AsymmetricEncryption;
9+
use crate::requests::ResponseStatus;
10+
use derivative::Derivative;
11+
use secrecy::{ExposeSecret, Secret};
12+
13+
/// Native object for asymmetric encryption operations.
14+
#[derive(Derivative)]
15+
#[derivative(Debug)]
16+
pub struct Operation {
17+
/// Defines which key should be used for the encryption operation.
18+
pub key_name: String,
19+
/// An asymmetric encryption algorithm that is compatible with the key type
20+
pub alg: AsymmetricEncryption,
21+
/// The short message to be encrypted.
22+
#[derivative(Debug = "ignore")]
23+
pub plaintext: Secret<Vec<u8>>,
24+
/// Salt to use during encryption, if supported by the algorithm.
25+
pub salt: Option<zeroize::Zeroizing<Vec<u8>>>,
26+
}
27+
28+
impl 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 encrypting messages
33+
/// * the key policy allows the encryption 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 encrypt is valid (not length 0)
37+
pub fn validate(&self, key_attributes: Attributes) -> crate::requests::Result<()> {
38+
key_attributes.can_encrypt_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 != None)
42+
|| self.plaintext.expose_secret().is_empty()
43+
{
44+
return Err(ResponseStatus::PsaErrorInvalidArgument);
45+
}
46+
Ok(())
47+
}
48+
}
49+
50+
/// Native object for asymmetric encrypt result.
51+
#[derive(Debug)]
52+
pub struct Result {
53+
/// The `ciphertext` field contains the encrypted short message.
54+
pub ciphertext: Vec<u8>,
55+
}
56+
57+
#[cfg(test)]
58+
mod tests {
59+
use super::*;
60+
use crate::operations::psa_algorithm::{AsymmetricEncryption, Hash};
61+
use crate::operations::psa_key_attributes::{Lifetime, Policy, Type, UsageFlags};
62+
63+
fn get_attrs() -> Attributes {
64+
Attributes {
65+
lifetime: Lifetime::Persistent,
66+
key_type: Type::RsaKeyPair,
67+
bits: 256,
68+
policy: Policy {
69+
usage_flags: UsageFlags {
70+
export: false,
71+
copy: false,
72+
cache: false,
73+
encrypt: true,
74+
decrypt: false,
75+
sign_message: false,
76+
verify_message: false,
77+
sign_hash: false,
78+
verify_hash: false,
79+
derive: false,
80+
},
81+
permitted_algorithms: AsymmetricEncryption::RsaPkcs1v15Crypt.into(),
82+
},
83+
}
84+
}
85+
86+
#[test]
87+
fn validate_success() {
88+
(Operation {
89+
key_name: String::from("some key"),
90+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
91+
plaintext: Secret::new(vec![0xff, 32]),
92+
salt: None,
93+
})
94+
.validate(get_attrs())
95+
.unwrap();
96+
}
97+
98+
#[test]
99+
fn cannot_encrypt() {
100+
let mut attrs = get_attrs();
101+
attrs.policy.usage_flags.encrypt = false;
102+
assert_eq!(
103+
(Operation {
104+
key_name: String::from("some key"),
105+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
106+
plaintext: Secret::new(vec![0xff, 32]),
107+
salt: None,
108+
})
109+
.validate(attrs)
110+
.unwrap_err(),
111+
ResponseStatus::PsaErrorNotPermitted
112+
);
113+
}
114+
115+
#[test]
116+
fn wrong_algorithm() {
117+
assert_eq!(
118+
(Operation {
119+
key_name: String::from("some key"),
120+
alg: AsymmetricEncryption::RsaOaep {
121+
hash_alg: Hash::Sha256,
122+
},
123+
plaintext: Secret::new(vec![0xff, 32]),
124+
salt: None,
125+
})
126+
.validate(get_attrs())
127+
.unwrap_err(),
128+
ResponseStatus::PsaErrorNotPermitted
129+
);
130+
}
131+
132+
#[test]
133+
fn invalid_plaintext() {
134+
assert_eq!(
135+
(Operation {
136+
key_name: String::from("some key"),
137+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
138+
plaintext: Secret::new(vec![]),
139+
salt: None,
140+
})
141+
.validate(get_attrs())
142+
.unwrap_err(),
143+
ResponseStatus::PsaErrorInvalidArgument
144+
);
145+
}
146+
147+
#[test]
148+
fn salt_with_rsapkcs1v15crypt() {
149+
assert_eq!(
150+
(Operation {
151+
key_name: String::from("some key"),
152+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
153+
plaintext: Secret::new(vec![0xff, 32]),
154+
salt: Some(zeroize::Zeroizing::new(vec![0xff, 32])),
155+
})
156+
.validate(get_attrs())
157+
.unwrap_err(),
158+
ResponseStatus::PsaErrorInvalidArgument
159+
);
160+
}
161+
}

0 commit comments

Comments
 (0)