Skip to content

Commit c361f5d

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

File tree

10 files changed

+801
-2
lines changed

10 files changed

+801
-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: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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+
13+
/// Native object for asymmetric decryption operations.
14+
#[derive(Debug)]
15+
pub 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+
pub ciphertext: zeroize::Zeroizing<Vec<u8>>,
22+
/// Salt to use during decryption, if supported by the algorithm.
23+
pub salt: Option<zeroize::Zeroizing<Vec<u8>>>,
24+
}
25+
26+
impl Operation {
27+
/// Validate the contents of the operation against the attributes of the key it targets
28+
///
29+
/// This method checks that:
30+
/// * the key policy allows decrypting messages
31+
/// * the key policy allows the decryption algorithm requested in the operation
32+
/// * the key type is compatible with the requested algorithm
33+
/// * if the algorithm is RsaPkcs1v15Crypt, it has no salt (it is not compatible with salt)
34+
/// * the message to decrypt is valid (not length 0)
35+
pub fn validate(&self, key_attributes: Attributes) -> crate::requests::Result<()> {
36+
key_attributes.can_decrypt_message()?;
37+
key_attributes.permits_alg(self.alg.into())?;
38+
key_attributes.compatible_with_alg(self.alg.into())?;
39+
if (self.alg == AsymmetricEncryption::RsaPkcs1v15Crypt && self.salt != None)
40+
|| self.ciphertext.is_empty()
41+
{
42+
return Err(ResponseStatus::PsaErrorInvalidArgument);
43+
}
44+
Ok(())
45+
}
46+
}
47+
48+
/// Native object for asymmetric decrypt result.
49+
#[derive(Debug)]
50+
pub struct Result {
51+
/// Decrypted message
52+
pub plaintext: Vec<u8>,
53+
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use super::*;
58+
use crate::operations::psa_algorithm::{AsymmetricEncryption, Hash};
59+
use crate::operations::psa_key_attributes::{Lifetime, Policy, Type, UsageFlags};
60+
use zeroize::Zeroizing;
61+
62+
fn get_attrs() -> Attributes {
63+
Attributes {
64+
lifetime: Lifetime::Persistent,
65+
key_type: Type::RsaKeyPair,
66+
bits: 256,
67+
policy: Policy {
68+
usage_flags: UsageFlags {
69+
export: false,
70+
copy: false,
71+
cache: false,
72+
encrypt: false,
73+
decrypt: true,
74+
sign_message: false,
75+
verify_message: false,
76+
sign_hash: false,
77+
verify_hash: false,
78+
derive: false,
79+
},
80+
permitted_algorithms: AsymmetricEncryption::RsaPkcs1v15Crypt.into(),
81+
},
82+
}
83+
}
84+
85+
#[test]
86+
fn validate_success() {
87+
(Operation {
88+
key_name: String::from("some key"),
89+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
90+
ciphertext: Zeroizing::new(vec![0xff, 32]),
91+
salt: None,
92+
})
93+
.validate(get_attrs())
94+
.unwrap();
95+
}
96+
97+
#[test]
98+
fn cannot_decrypt() {
99+
let mut attrs = get_attrs();
100+
attrs.policy.usage_flags.decrypt = false;
101+
assert_eq!(
102+
(Operation {
103+
key_name: String::from("some key"),
104+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
105+
ciphertext: Zeroizing::new(vec![0xff, 32]),
106+
salt: None,
107+
})
108+
.validate(attrs)
109+
.unwrap_err(),
110+
ResponseStatus::PsaErrorNotPermitted
111+
);
112+
}
113+
114+
#[test]
115+
fn wrong_algorithm() {
116+
assert_eq!(
117+
(Operation {
118+
key_name: String::from("some key"),
119+
alg: AsymmetricEncryption::RsaOaep {
120+
hash_alg: Hash::Sha256,
121+
},
122+
ciphertext: Zeroizing::new(vec![0xff, 32]),
123+
salt: None,
124+
})
125+
.validate(get_attrs())
126+
.unwrap_err(),
127+
ResponseStatus::PsaErrorNotPermitted
128+
);
129+
}
130+
131+
#[test]
132+
fn invalid_ciphertext() {
133+
assert_eq!(
134+
(Operation {
135+
key_name: String::from("some key"),
136+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
137+
ciphertext: Zeroizing::new(vec![]),
138+
salt: None,
139+
})
140+
.validate(get_attrs())
141+
.unwrap_err(),
142+
ResponseStatus::PsaErrorInvalidArgument
143+
);
144+
}
145+
146+
#[test]
147+
fn salt_with_rsapkcs1v15crypt() {
148+
assert_eq!(
149+
(Operation {
150+
key_name: String::from("some key"),
151+
alg: AsymmetricEncryption::RsaPkcs1v15Crypt,
152+
ciphertext: Zeroizing::new(vec![0xff, 32]),
153+
salt: Some(zeroize::Zeroizing::new(vec![0xff, 32])),
154+
})
155+
.validate(get_attrs())
156+
.unwrap_err(),
157+
ResponseStatus::PsaErrorInvalidArgument
158+
);
159+
}
160+
}
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)