Skip to content

Commit e677bfd

Browse files
authored
Merge pull request #122 from RobertDrazkowskiGL/psa-cipher-op
Add PsaCipherEncrypt and PsaCipherDecrypt operations
2 parents e5b7f17 + 0087774 commit e677bfd

9 files changed

+714
-1
lines changed

src/operations/mod.rs

+38
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub mod psa_asymmetric_encrypt;
2323
pub mod psa_asymmetric_decrypt;
2424
pub mod psa_aead_encrypt;
2525
pub mod psa_aead_decrypt;
26+
pub mod psa_cipher_encrypt;
27+
pub mod psa_cipher_decrypt;
2628
pub mod psa_sign_message;
2729
pub mod psa_verify_message;
2830
pub mod list_opcodes;
@@ -83,6 +85,10 @@ pub enum NativeOperation {
8385
PsaAeadEncrypt(psa_aead_encrypt::Operation),
8486
/// PsaAeadDecrypt operation
8587
PsaAeadDecrypt(psa_aead_decrypt::Operation),
88+
/// PsaCipherEncrypt operation
89+
PsaCipherEncrypt(psa_cipher_encrypt::Operation),
90+
/// PsaCipherDecrypt operation
91+
PsaCipherDecrypt(psa_cipher_decrypt::Operation),
8692
/// PsaGenerateRandom operation
8793
PsaGenerateRandom(psa_generate_random::Operation),
8894
/// PsaRawKeyAgreement operation
@@ -117,6 +123,8 @@ impl NativeOperation {
117123
NativeOperation::PsaAsymmetricDecrypt(_) => Opcode::PsaAsymmetricDecrypt,
118124
NativeOperation::PsaAeadEncrypt(_) => Opcode::PsaAeadEncrypt,
119125
NativeOperation::PsaAeadDecrypt(_) => Opcode::PsaAeadDecrypt,
126+
NativeOperation::PsaCipherEncrypt(_) => Opcode::PsaCipherEncrypt,
127+
NativeOperation::PsaCipherDecrypt(_) => Opcode::PsaCipherDecrypt,
120128
NativeOperation::PsaGenerateRandom(_) => Opcode::PsaGenerateRandom,
121129
NativeOperation::PsaRawKeyAgreement(_) => Opcode::PsaRawKeyAgreement,
122130
NativeOperation::PsaSignMessage(_) => Opcode::PsaSignMessage,
@@ -169,6 +177,10 @@ pub enum NativeResult {
169177
PsaAeadEncrypt(psa_aead_encrypt::Result),
170178
/// PsaAeadDecrypt result
171179
PsaAeadDecrypt(psa_aead_decrypt::Result),
180+
/// PsaCipherEncrypt result
181+
PsaCipherEncrypt(psa_cipher_encrypt::Result),
182+
/// PsaCipherDecrypt result
183+
PsaCipherDecrypt(psa_cipher_decrypt::Result),
172184
/// PsaGenerateRandom result
173185
PsaGenerateRandom(psa_generate_random::Result),
174186
/// PsaRawKeyAgreement result
@@ -203,6 +215,8 @@ impl NativeResult {
203215
NativeResult::PsaAsymmetricDecrypt(_) => Opcode::PsaAsymmetricDecrypt,
204216
NativeResult::PsaAeadEncrypt(_) => Opcode::PsaAeadEncrypt,
205217
NativeResult::PsaAeadDecrypt(_) => Opcode::PsaAeadDecrypt,
218+
NativeResult::PsaCipherEncrypt(_) => Opcode::PsaCipherEncrypt,
219+
NativeResult::PsaCipherDecrypt(_) => Opcode::PsaCipherDecrypt,
206220
NativeResult::PsaGenerateRandom(_) => Opcode::PsaGenerateRandom,
207221
NativeResult::PsaRawKeyAgreement(_) => Opcode::PsaRawKeyAgreement,
208222
NativeResult::PsaSignMessage(_) => Opcode::PsaSignMessage,
@@ -350,6 +364,18 @@ impl From<psa_aead_decrypt::Operation> for NativeOperation {
350364
}
351365
}
352366

367+
impl From<psa_cipher_encrypt::Operation> for NativeOperation {
368+
fn from(op: psa_cipher_encrypt::Operation) -> Self {
369+
NativeOperation::PsaCipherEncrypt(op)
370+
}
371+
}
372+
373+
impl From<psa_cipher_decrypt::Operation> for NativeOperation {
374+
fn from(op: psa_cipher_decrypt::Operation) -> Self {
375+
NativeOperation::PsaCipherDecrypt(op)
376+
}
377+
}
378+
353379
impl From<psa_generate_random::Operation> for NativeOperation {
354380
fn from(op: psa_generate_random::Operation) -> Self {
355381
NativeOperation::PsaGenerateRandom(op)
@@ -503,6 +529,18 @@ impl From<psa_aead_decrypt::Result> for NativeResult {
503529
}
504530
}
505531

532+
impl From<psa_cipher_encrypt::Result> for NativeResult {
533+
fn from(op: psa_cipher_encrypt::Result) -> Self {
534+
NativeResult::PsaCipherEncrypt(op)
535+
}
536+
}
537+
538+
impl From<psa_cipher_decrypt::Result> for NativeResult {
539+
fn from(op: psa_cipher_decrypt::Result) -> Self {
540+
NativeResult::PsaCipherDecrypt(op)
541+
}
542+
}
543+
506544
impl From<psa_generate_random::Result> for NativeResult {
507545
fn from(op: psa_generate_random::Result) -> Self {
508546
NativeResult::PsaGenerateRandom(op)

src/operations/psa_cipher_decrypt.rs

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

src/operations/psa_cipher_encrypt.rs

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2021 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! # PsaCipherEncrypt operation
4+
//!
5+
//! Encrypt a short message with a symmetric cipher
6+
7+
use super::psa_key_attributes::Attributes;
8+
use crate::operations::psa_algorithm::Cipher;
9+
use crate::requests::ResponseStatus;
10+
use derivative::Derivative;
11+
12+
/// Native object for cipher 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 cipher encryption algorithm that is compatible with the key type
19+
pub alg: Cipher,
20+
/// The short message to be encrypted.
21+
#[derivative(Debug = "ignore")]
22+
pub plaintext: zeroize::Zeroizing<Vec<u8>>,
23+
}
24+
25+
impl Operation {
26+
/// Validate the contents of the operation against the attributes of the key it targets
27+
///
28+
/// This method checks that:
29+
/// * the key policy allows encrypting messages
30+
/// * the key policy allows the encryption algorithm requested in the operation
31+
/// * the key type is compatible with the requested algorithm
32+
/// * the message to encrypt is valid (not length 0)
33+
pub fn validate(&self, key_attributes: Attributes) -> crate::requests::Result<()> {
34+
key_attributes.can_encrypt_message()?;
35+
key_attributes.permits_alg(self.alg.into())?;
36+
key_attributes.compatible_with_alg(self.alg.into())?;
37+
if self.plaintext.is_empty() {
38+
return Err(ResponseStatus::PsaErrorInvalidArgument);
39+
}
40+
Ok(())
41+
}
42+
}
43+
44+
/// Native object for cipher encrypt result.
45+
#[derive(Derivative)]
46+
#[derivative(Debug)]
47+
pub struct Result {
48+
/// The `ciphertext` field contains the encrypted short message.
49+
#[derivative(Debug = "ignore")]
50+
pub ciphertext: zeroize::Zeroizing<Vec<u8>>,
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use super::*;
56+
use crate::operations::psa_algorithm::Cipher;
57+
use crate::operations::psa_key_attributes::{Lifetime, Policy, Type, UsageFlags};
58+
59+
fn get_attrs() -> Attributes {
60+
let mut usage_flags = UsageFlags::default();
61+
let _ = usage_flags.set_encrypt();
62+
Attributes {
63+
lifetime: Lifetime::Persistent,
64+
key_type: Type::Arc4,
65+
bits: 256,
66+
policy: Policy {
67+
usage_flags,
68+
permitted_algorithms: Cipher::StreamCipher.into(),
69+
},
70+
}
71+
}
72+
73+
#[test]
74+
fn validate_success() {
75+
(Operation {
76+
key_name: String::from("some key"),
77+
alg: Cipher::StreamCipher,
78+
plaintext: vec![0xff, 32].into(),
79+
})
80+
.validate(get_attrs())
81+
.unwrap();
82+
}
83+
84+
#[test]
85+
fn cannot_encrypt() {
86+
let mut attrs = get_attrs();
87+
attrs.policy.usage_flags = UsageFlags::default();
88+
assert_eq!(
89+
(Operation {
90+
key_name: String::from("some key"),
91+
alg: Cipher::StreamCipher,
92+
plaintext: vec![0xff, 32].into(),
93+
})
94+
.validate(attrs)
95+
.unwrap_err(),
96+
ResponseStatus::PsaErrorNotPermitted
97+
);
98+
}
99+
100+
#[test]
101+
fn wrong_algorithm() {
102+
assert_eq!(
103+
(Operation {
104+
key_name: String::from("some key"),
105+
alg: Cipher::Cfb,
106+
plaintext: vec![0xff, 32].into(),
107+
})
108+
.validate(get_attrs())
109+
.unwrap_err(),
110+
ResponseStatus::PsaErrorNotPermitted
111+
);
112+
}
113+
114+
#[test]
115+
fn invalid_plaintext() {
116+
assert_eq!(
117+
(Operation {
118+
key_name: String::from("some key"),
119+
alg: Cipher::StreamCipher,
120+
plaintext: vec![].into(),
121+
})
122+
.validate(get_attrs())
123+
.unwrap_err(),
124+
ResponseStatus::PsaErrorInvalidArgument
125+
);
126+
}
127+
}

src/operations_protobuf/convert_psa_algorithm.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,14 @@ impl TryFrom<CipherProto> for Cipher {
241241
}
242242
}
243243

244+
// Cipher algorithms: from protobuf to native
245+
pub fn i32_to_cipher(cipher_val: i32) -> Result<Cipher> {
246+
let cipher_proto_alg: CipherProto = cipher_val.try_into()?;
247+
cipher_proto_alg.try_into()
248+
}
249+
244250
// Cipher algorithms: from native to protobuf
245-
fn cipher_to_i32(cipher: Cipher) -> i32 {
251+
pub fn cipher_to_i32(cipher: Cipher) -> i32 {
246252
match cipher {
247253
Cipher::StreamCipher => CipherProto::StreamCipher.into(),
248254
Cipher::Ctr => CipherProto::Ctr.into(),

0 commit comments

Comments
 (0)