Skip to content

Commit bc47296

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

File tree

10 files changed

+808
-2
lines changed

10 files changed

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

0 commit comments

Comments
 (0)