Skip to content

Commit 2b6e1d2

Browse files
committed
Added asymmetric encrypt and decrypt
Signed-off-by: Samuel Bailey <[email protected]>
1 parent 6ca5f87 commit 2b6e1d2

File tree

3 files changed

+181
-3
lines changed

3 files changed

+181
-3
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ edition = "2018"
1313
documentation = "https://docs.rs/crate/parsec-client"
1414

1515
[dependencies]
16-
parsec-interface = "0.17.1"
16+
parsec-interface = "0.18.0"
1717
num = "0.2.1"
1818
rand = "0.7.3"
1919
log = "0.4.8"

src/core/basic_client.rs

+114-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use crate::error::{ClientErrorKind, Error, Result};
77
use parsec_interface::operations::list_opcodes::Operation as ListOpcodes;
88
use parsec_interface::operations::list_providers::{Operation as ListProviders, ProviderInfo};
99
use parsec_interface::operations::ping::Operation as Ping;
10-
use parsec_interface::operations::psa_algorithm::AsymmetricSignature;
10+
use parsec_interface::operations::psa_algorithm::{AsymmetricEncryption, AsymmetricSignature};
11+
use parsec_interface::operations::psa_asymmetric_decrypt::Operation as PsaAsymDecrypt;
12+
use parsec_interface::operations::psa_asymmetric_encrypt::Operation as PsaAsymEncrypt;
1113
use parsec_interface::operations::psa_destroy_key::Operation as PsaDestroyKey;
1214
use parsec_interface::operations::psa_export_public_key::Operation as PsaExportPublicKey;
1315
use parsec_interface::operations::psa_generate_key::Operation as PsaGenerateKey;
@@ -229,7 +231,6 @@ impl BasicClient {
229231
ProviderID::Core,
230232
&self.auth_data,
231233
)?;
232-
233234
if let NativeResult::ListProviders(res) = res {
234235
Ok(res.providers)
235236
} else {
@@ -522,6 +523,117 @@ impl BasicClient {
522523
Ok(())
523524
}
524525

526+
/// **[Cryptographic Operation]** Encrypt a short message.
527+
///
528+
/// The key intended for encrypting **must** have its `encrypt` flag set
529+
/// to `true` in its [key policy](https://docs.rs/parsec-interface/*/parsec_interface/operations/psa_key_attributes/struct.Policy.html).
530+
///
531+
/// The encryption will be performed with the algorithm defined in `alg`,
532+
/// but only after checking that the key policy and type conform with it.
533+
///
534+
/// `salt` can be provided if supported by the algorithm. If the algorithm does not support salt, pass
535+
// an empty vector. If the algorithm supports optional salt, pass an empty vector to indicate no
536+
// salt. For RSA PKCS#1 v1.5 encryption, no salt is supported.
537+
///
538+
/// # Errors
539+
///
540+
/// If the implicit client provider is `ProviderID::Core`, a client error
541+
/// of `InvalidProvider` type is returned.
542+
///
543+
/// If the implicit client provider has not been set, a client error of
544+
/// `NoProvider` type is returned.
545+
///
546+
/// See the operation-specific response codes returned by the service
547+
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_asymmetric_encrypt.html#specific-response-status-codes).
548+
pub fn psa_asymmetric_encrypt(
549+
&self,
550+
key_name: String,
551+
encrypt_alg: AsymmetricEncryption,
552+
plaintext: &[u8],
553+
salt: Option<&[u8]>,
554+
) -> Result<Vec<u8>> {
555+
let salt = salt.map(|salt_ref| salt_ref.to_vec().into());
556+
let crypto_provider = self.can_provide_crypto()?;
557+
558+
let op = PsaAsymEncrypt {
559+
key_name,
560+
alg: encrypt_alg,
561+
plaintext: plaintext.to_vec().into(),
562+
salt,
563+
};
564+
565+
let encrypt_res = self.op_client.process_operation(
566+
NativeOperation::PsaAsymmetricEncrypt(op),
567+
crypto_provider,
568+
&self.auth_data,
569+
)?;
570+
571+
if let NativeResult::PsaAsymmetricEncrypt(res) = encrypt_res {
572+
Ok(res.ciphertext.to_vec())
573+
} else {
574+
// Should really not be reached given the checks we do, but it's not impossible if some
575+
// changes happen in the interface
576+
Err(Error::Client(ClientErrorKind::InvalidServiceResponseType))
577+
}
578+
}
579+
580+
/// **[Cryptographic Operation]** Decrypt a short message.
581+
///
582+
/// The key intended for decrypting **must** have its `decrypt` flag set
583+
/// to `true` in its [key policy](https://docs.rs/parsec-interface/*/parsec_interface/operations/psa_key_attributes/struct.Policy.html).
584+
///
585+
/// The decryption will be performed with the algorithm defined in `alg`,
586+
/// but only after checking that the key policy and type conform with it.
587+
///
588+
/// `salt` can be provided if supported by the algorithm. If the algorithm does not support salt, pass
589+
// an empty vector. If the algorithm supports optional salt, pass an empty vector to indicate no
590+
// salt. For RSA PKCS#1 v1.5 encryption, no salt is supported.
591+
///
592+
/// # Errors
593+
///
594+
/// If the implicit client provider is `ProviderID::Core`, a client error
595+
/// of `InvalidProvider` type is returned.
596+
///
597+
/// If the implicit client provider has not been set, a client error of
598+
/// `NoProvider` type is returned.
599+
///
600+
/// See the operation-specific response codes returned by the service
601+
/// [here](https://parallaxsecond.github.io/parsec-book/parsec_client/operations/psa_asymmetric_decrypt.html#specific-response-status-codes).
602+
pub fn psa_asymmetric_decrypt(
603+
&self,
604+
key_name: String,
605+
encrypt_alg: AsymmetricEncryption,
606+
ciphertext: &[u8],
607+
salt: Option<&[u8]>,
608+
) -> Result<Vec<u8>> {
609+
let salt = match salt {
610+
Some(salt) => Some(Zeroizing::new(salt.to_vec())),
611+
None => None,
612+
};
613+
let crypto_provider = self.can_provide_crypto()?;
614+
615+
let op = PsaAsymDecrypt {
616+
key_name,
617+
alg: encrypt_alg,
618+
ciphertext: Zeroizing::new(ciphertext.to_vec()),
619+
salt,
620+
};
621+
622+
let decrypt_res = self.op_client.process_operation(
623+
NativeOperation::PsaAsymmetricDecrypt(op),
624+
crypto_provider,
625+
&self.auth_data,
626+
)?;
627+
628+
if let NativeResult::PsaAsymmetricDecrypt(res) = decrypt_res {
629+
Ok(res.plaintext.to_vec())
630+
} else {
631+
// Should really not be reached given the checks we do, but it's not impossible if some
632+
// changes happen in the interface
633+
Err(Error::Client(ClientErrorKind::InvalidServiceResponseType))
634+
}
635+
}
636+
525637
fn can_provide_crypto(&self) -> Result<ProviderID> {
526638
match self.implicit_provider {
527639
None => Err(Error::Client(ClientErrorKind::NoProvider)),

src/core/testing/core_tests.rs

+66
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,72 @@ fn verify_hash_test() {
352352
// VerifyHash response is empty so no checking to be done
353353
}
354354

355+
#[test]
356+
fn asymmetric_encrypt_test() {
357+
let mut client: TestBasicClient = Default::default();
358+
let plaintext = vec![0x77_u8; 32];
359+
let key_name = String::from("key_name");
360+
let encrypt_algorithm = AsymmetricEncryption::RsaPkcs1v15Crypt;
361+
let ciphertext = vec![0x33_u8; 128];
362+
client.set_mock_read(&get_response_bytes_from_result(
363+
NativeResult::PsaAsymmetricEncrypt(operations::psa_asymmetric_encrypt::Result {
364+
ciphertext: ciphertext.clone().into(),
365+
}),
366+
));
367+
368+
// Check response:
369+
assert_eq!(
370+
client
371+
.psa_asymmetric_encrypt(key_name.clone(), encrypt_algorithm, &plaintext, None)
372+
.expect("Failed to encrypt message"),
373+
ciphertext
374+
);
375+
376+
// Check request:
377+
let op = get_operation_from_req_bytes(client.get_mock_write());
378+
if let NativeOperation::PsaAsymmetricEncrypt(op) = op {
379+
assert_eq!(op.key_name, key_name);
380+
assert_eq!(op.alg, encrypt_algorithm);
381+
assert_eq!(*op.plaintext, plaintext);
382+
assert_eq!(op.salt, None);
383+
} else {
384+
panic!("Got wrong operation type: {:?}", op);
385+
}
386+
}
387+
388+
#[test]
389+
fn asymmetric_decrypt_test() {
390+
let mut client: TestBasicClient = Default::default();
391+
let plaintext = vec![0x77_u8; 32];
392+
let key_name = String::from("key_name");
393+
let encrypt_algorithm = AsymmetricEncryption::RsaPkcs1v15Crypt;
394+
let ciphertext = vec![0x33_u8; 128];
395+
client.set_mock_read(&get_response_bytes_from_result(
396+
NativeResult::PsaAsymmetricDecrypt(operations::psa_asymmetric_decrypt::Result {
397+
plaintext: plaintext.clone().into(),
398+
}),
399+
));
400+
401+
// Check response
402+
assert_eq!(
403+
client
404+
.psa_asymmetric_decrypt(key_name.clone(), encrypt_algorithm, &ciphertext, None)
405+
.expect("Failed to encrypt message"),
406+
plaintext
407+
);
408+
409+
// Check request:
410+
let op = get_operation_from_req_bytes(client.get_mock_write());
411+
if let NativeOperation::PsaAsymmetricDecrypt(op) = op {
412+
assert_eq!(op.key_name, key_name);
413+
assert_eq!(op.alg, encrypt_algorithm);
414+
assert_eq!(*op.ciphertext, ciphertext);
415+
assert_eq!(op.salt, None);
416+
} else {
417+
panic!("Got wrong operation type: {:?}", op);
418+
}
419+
}
420+
355421
#[test]
356422
fn different_response_type_test() {
357423
let mut client: TestBasicClient = Default::default();

0 commit comments

Comments
 (0)