Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added asymmetric encrypt and decrypt to psa-crypto and psa-crypto-sys #37

Merged
merged 1 commit into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions psa-crypto-sys/src/c/shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ shim_PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(psa_algorithm_t alg) {
return PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(alg);
}

int
shim_PSA_ALG_IS_RSA_OAEP(psa_algorithm_t alg) {
return PSA_ALG_IS_RSA_OAEP(alg);
}

int
shim_PSA_ALG_IS_KEY_AGREEMENT(psa_algorithm_t alg) {
return PSA_ALG_IS_KEY_AGREEMENT(alg);
Expand Down Expand Up @@ -157,6 +162,11 @@ shim_PSA_ALG_SIGN_GET_HASH(psa_algorithm_t alg) {
return PSA_ALG_SIGN_GET_HASH(alg);
}

psa_algorithm_t
shim_PSA_ALG_RSA_OAEP_GET_HASH(psa_algorithm_t alg) {
return PSA_ALG_RSA_OAEP_GET_HASH(alg);
}

psa_algorithm_t
shim_PSA_ALG_RSA_PKCS1V15_SIGN(psa_algorithm_t hash_alg) {
return PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg);
Expand Down Expand Up @@ -201,6 +211,12 @@ shim_PSA_KEY_TYPE_IS_DH_KEY_PAIR(psa_key_type_t key_type)
return PSA_KEY_TYPE_IS_DH_KEY_PAIR(key_type);
}

psa_algorithm_t
shim_PSA_ALG_RSA_OAEP(psa_algorithm_t alg_type)
{
return PSA_ALG_RSA_OAEP(alg_type);
}

psa_ecc_curve_t
shim_PSA_KEY_TYPE_GET_CURVE(psa_key_type_t key_type)
{
Expand Down Expand Up @@ -249,6 +265,18 @@ shim_PSA_SIGN_OUTPUT_SIZE(psa_key_type_t key_type, size_t key_bits, psa_algorith
return PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg);
}

size_t
shim_PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(psa_key_type_t key_type, size_t key_bits, psa_algorithm_t alg)
{
return PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(key_type, key_bits, alg);
}

size_t
shim_PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(psa_key_type_t key_type, size_t key_bits, psa_algorithm_t alg)
{
return PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(key_type, key_bits, alg);
}

size_t
shim_PSA_KEY_EXPORT_MAX_SIZE(psa_key_type_t key_type, size_t key_bits)
{
Expand Down
5 changes: 5 additions & 0 deletions psa-crypto-sys/src/c/shim.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,20 @@ int shim_PSA_ALG_IS_CIPHER(psa_algorithm_t alg);
int shim_PSA_ALG_IS_AEAD(psa_algorithm_t alg);
int shim_PSA_ALG_IS_SIGN(psa_algorithm_t alg);
int shim_PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(psa_algorithm_t alg);
int shim_PSA_ALG_IS_RSA_OAEP(psa_algorithm_t alg);
int shim_PSA_ALG_IS_KEY_AGREEMENT(psa_algorithm_t alg);
int shim_PSA_ALG_IS_KEY_DERIVATION(psa_algorithm_t alg);
int shim_PSA_ALG_IS_RSA_PKCS1V15_SIGN(psa_algorithm_t alg);
int shim_PSA_ALG_IS_RSA_PSS(psa_algorithm_t alg);
int shim_PSA_ALG_IS_ECDSA(psa_algorithm_t alg);
int shim_PSA_ALG_IS_DETERMINISTIC_ECDSA(psa_algorithm_t alg);
psa_algorithm_t shim_PSA_ALG_RSA_OAEP(psa_algorithm_t alg);
psa_algorithm_t shim_PSA_ALG_RSA_PKCS1V15_SIGN(psa_algorithm_t hash_alg);
psa_algorithm_t shim_PSA_ALG_RSA_PSS(psa_algorithm_t hash_alg);
psa_algorithm_t shim_PSA_ALG_ECDSA(psa_algorithm_t hash_alg);
psa_algorithm_t shim_PSA_ALG_DETERMINISTIC_ECDSA(psa_algorithm_t hash_alg);
psa_algorithm_t shim_PSA_ALG_SIGN_GET_HASH(psa_algorithm_t alg);
psa_algorithm_t shim_PSA_ALG_RSA_OAEP_GET_HASH(psa_algorithm_t alg);
int shim_PSA_KEY_TYPE_IS_ECC_KEY_PAIR(psa_key_type_t key_type);
int shim_PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(psa_key_type_t key_type);
int shim_PSA_KEY_TYPE_IS_DH_PUBLIC_KEY(psa_key_type_t key_type);
Expand All @@ -117,4 +120,6 @@ psa_key_type_t shim_PSA_KEY_TYPE_DH_KEY_PAIR(psa_dh_group_t group);
psa_key_type_t shim_PSA_KEY_TYPE_DH_PUBLIC_KEY(psa_dh_group_t group);
psa_key_type_t shim_PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(psa_key_type_t key_type);
size_t shim_PSA_SIGN_OUTPUT_SIZE(psa_key_type_t key_type, size_t key_bits, psa_algorithm_t alg);
size_t shim_PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(psa_key_type_t key_type, size_t key_bits, psa_algorithm_t alg);
size_t shim_PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(psa_key_type_t key_type, size_t key_bits, psa_algorithm_t alg);
size_t shim_PSA_KEY_EXPORT_MAX_SIZE(psa_key_type_t key_type, size_t key_bits);
7 changes: 4 additions & 3 deletions psa-crypto-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ pub use types::*;

#[cfg(feature = "implementation-defined")]
pub use psa_crypto_binding::{
psa_close_key, psa_crypto_init, psa_destroy_key, psa_export_public_key, psa_generate_key,
psa_get_key_attributes, psa_import_key, psa_key_attributes_t, psa_open_key,
psa_reset_key_attributes, psa_sign_hash, psa_verify_hash,
psa_asymmetric_decrypt, psa_asymmetric_encrypt, psa_close_key, psa_crypto_init,
psa_destroy_key, psa_export_public_key, psa_generate_key, psa_get_key_attributes,
psa_import_key, psa_key_attributes_t, psa_open_key, psa_reset_key_attributes, psa_sign_hash,
psa_verify_hash,
};

// Secure Element Driver definitions
Expand Down
28 changes: 28 additions & 0 deletions psa-crypto-sys/src/shim_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ pub fn PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(alg: psa_algorithm_t) -> bool {
unsafe { psa_crypto_binding::shim_PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(alg) == 1 }
}

pub unsafe fn PSA_ALG_IS_RSA_OAEP(alg: psa_algorithm_t) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one can be safe because it can only return 1 or 0 with no unspecified behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did wonder about this one. I put unsafe because although it can only return 0 or 1, if you give it an unsupported input, the value it returns doesn't necessarily tell you that (because it can be either 0 or 1), so you couldn't trust it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean that, you could give it a value that is not one of the recognised possible algorithm value for psa_algorithm_t, and the result would not really make sense.
In my opinion, psa_algorithm_t being just a typedef for u32, you are allowed to call this function with any u32 value and for all these values, the function will always return true or false and never exhibit an unsafe behaviour.

I guess the problem is what you define as safe/unsafe :/

This is mostly bikeshedding at this point, so please ignore, it is not really important, and it is perfectly fine with unsafe 👌

psa_crypto_binding::shim_PSA_ALG_IS_RSA_OAEP(alg) == 1
}

pub fn PSA_ALG_IS_KEY_AGREEMENT(alg: psa_algorithm_t) -> bool {
unsafe { psa_crypto_binding::shim_PSA_ALG_IS_KEY_AGREEMENT(alg) == 1 }
}
Expand Down Expand Up @@ -116,6 +120,10 @@ pub fn PSA_ALG_SIGN_GET_HASH(alg: psa_algorithm_t) -> psa_algorithm_t {
unsafe { psa_crypto_binding::shim_PSA_ALG_SIGN_GET_HASH(alg) }
}

pub fn PSA_ALG_RSA_OAEP_GET_HASH(alg: psa_algorithm_t) -> psa_algorithm_t {
unsafe { psa_crypto_binding::shim_PSA_ALG_RSA_OAEP_GET_HASH(alg) }
}

pub fn PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg: psa_algorithm_t) -> psa_algorithm_t {
unsafe { psa_crypto_binding::shim_PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg) }
}
Expand All @@ -132,6 +140,10 @@ pub fn PSA_ALG_DETERMINISTIC_ECDSA(hash_alg: psa_algorithm_t) -> psa_algorithm_t
unsafe { psa_crypto_binding::shim_PSA_ALG_DETERMINISTIC_ECDSA(hash_alg) }
}

pub unsafe fn PSA_ALG_RSA_OAEP(hash_alg: psa_algorithm_t) -> psa_algorithm_t {
psa_crypto_binding::shim_PSA_ALG_RSA_OAEP(hash_alg)
}

pub fn PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type: psa_key_type_t) -> bool {
unsafe { psa_crypto_binding::shim_PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) == 1 }
}
Expand Down Expand Up @@ -184,6 +196,22 @@ pub unsafe fn PSA_SIGN_OUTPUT_SIZE(
psa_crypto_binding::shim_PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg)
}

pub unsafe fn PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(
key_type: psa_key_type_t,
key_bits: usize,
alg: psa_algorithm_t,
) -> usize {
psa_crypto_binding::shim_PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(key_type, key_bits, alg)
}

pub unsafe fn PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(
key_type: psa_key_type_t,
key_bits: usize,
alg: psa_algorithm_t,
) -> usize {
psa_crypto_binding::shim_PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(key_type, key_bits, alg)
}

pub unsafe fn PSA_EXPORT_KEY_OUTPUT_SIZE(key_type: psa_key_type_t, key_bits: usize) -> usize {
psa_crypto_binding::shim_PSA_KEY_EXPORT_MAX_SIZE(key_type, key_bits)
}
4 changes: 4 additions & 0 deletions psa-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ log = "0.4.8"
serde = { version = "1.0.110", features = ["derive"] }
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }

[dev-dependencies]
rsa = "0.3.0"
rand = "0.7.3"

[features]
default = ["with-mbed-crypto", "no-std"]
with-mbed-crypto = ["psa-crypto-sys/implementation-defined"]
Expand Down
170 changes: 170 additions & 0 deletions psa-crypto/src/operations/asym_encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,173 @@
// SPDX-License-Identifier: Apache-2.0

//! # Asymmetric Encryption operations
//!
//! See the PSA Crypto API for the format of the different parameters used in this module.

use crate::initialized;
use crate::types::algorithm::AsymmetricEncryption;
use crate::types::key::Id;
use crate::types::status::{Result, Status};

/// Encrypt a short message with a key pair or public key
///
/// The encrypted message is written in `ciphertext`. The function returns the number of bytes written.
///
/// # Example
///
/// ```
/// # use psa_crypto::operations::key_management::generate;
/// # use psa_crypto::operations::asym_encryption::encrypt;
/// # use psa_crypto::types::key::{Attributes, Type, Lifetime, Policy, UsageFlags};
/// # use psa_crypto::types::algorithm::{AsymmetricEncryption, Hash};
/// #
/// # let mut attributes = Attributes {
/// # key_type: Type::RsaKeyPair,
/// # bits: 1024,
/// # lifetime: Lifetime::Volatile,
/// # policy: Policy {
/// # usage_flags: UsageFlags {
/// # encrypt: true,
/// # ..Default::default()
/// # },
/// # permitted_algorithms: AsymmetricEncryption::RsaPkcs1v15Crypt.into(),
/// # },
/// # };
/// # const MESSAGE: [u8; 32] = [
/// # 0x69, 0x3E, 0xDB, 0x1B, 0x22, 0x79, 0x03, 0xF4, 0xC0, 0xBF, 0xD6, 0x91, 0x76, 0x37, 0x84, 0xA2,
/// # 0x94, 0x8E, 0x92, 0x50, 0x35, 0xC2, 0x8C, 0x5C, 0x3C, 0xCA, 0xFE, 0x18, 0xE8, 0x81, 0x37, 0x78,
/// # ];
/// psa_crypto::init().unwrap();
/// let my_key = generate(attributes, None).unwrap();
/// let alg = AsymmetricEncryption::RsaPkcs1v15Crypt;
/// let buffer_size = attributes.asymmetric_encrypt_output_size(alg).unwrap();
/// let mut encrypted_message = vec![0; buffer_size];
///
/// let size = encrypt(my_key,
/// alg,
/// &MESSAGE,
/// None,
/// &mut encrypted_message).unwrap();
/// encrypted_message.resize(size, 0);
/// ```
pub fn encrypt(
key_id: Id,
alg: AsymmetricEncryption,
plaintext: &[u8],
salt: Option<&[u8]>,
ciphertext: &mut [u8],
) -> Result<usize> {
initialized()?;

let handle = key_id.handle()?;

let mut output_length = 0;
let (salt_ptr, salt_len) = match salt {
Some(salt) => (salt.as_ptr(), salt.len()),
None => (core::ptr::null(), 0),
};

let encrypt_res = Status::from(unsafe {
psa_crypto_sys::psa_asymmetric_encrypt(
handle,
alg.into(),
plaintext.as_ptr(),
plaintext.len(),
salt_ptr,
salt_len,
ciphertext.as_mut_ptr(),
ciphertext.len(),
&mut output_length,
)
})
.to_result();
let close_key_handle_res = key_id.close_handle(handle);
encrypt_res?;
close_key_handle_res?;
Ok(output_length)
}

/// Decrypt a short message with a key pair or private key
///
/// The decrypted message is written in `plaintext`. The function returns the number of bytes written.
///
/// # Example
///
/// ```
/// # use psa_crypto::operations::key_management::{generate, export_public};
/// # use psa_crypto::operations::asym_encryption::decrypt;
/// # use psa_crypto::types::key::{Attributes, Type, Lifetime, Policy, UsageFlags};
/// # use psa_crypto::types::algorithm::{AsymmetricEncryption, Hash};
/// # use rsa::{RSAPublicKey, PaddingScheme, PublicKey};
/// # use rand::rngs::OsRng;
/// # let mut attributes = Attributes {
/// # key_type: Type::RsaKeyPair,
/// # bits: 1024,
/// # lifetime: Lifetime::Volatile,
/// # policy: Policy {
/// # usage_flags: UsageFlags {
/// # decrypt: true,
/// # ..Default::default()
/// # },
/// # permitted_algorithms: AsymmetricEncryption::RsaPkcs1v15Crypt.into()
/// # },
/// # };
/// # const MESSAGE: [u8; 64] = [ 0x4e, 0x31, 0x74, 0x96, 0x8f, 0xe4, 0xba, 0xb3, 0xaf, 0x77, 0x75,
/// # 0x76, 0x61, 0xde, 0xe5, 0xb8, 0x2c, 0x4f, 0x2a, 0x77, 0x6f, 0x2a, 0x86, 0x36, 0x13, 0xc3, 0xd1,
/// # 0x26, 0x77, 0x30, 0x64, 0x9c, 0xb9, 0x95, 0x84, 0x73, 0x54, 0xfd, 0x6d, 0x2f, 0xba, 0x7e, 0x6c,
/// # 0xb5, 0x0a, 0xe1, 0x09, 0x4e, 0x57, 0x3e, 0xeb, 0x7c, 0x64, 0xcc, 0x9d, 0xf2, 0xf2, 0x37, 0x2e,
/// # 0xb1, 0xe9, 0x92, 0xb7, 0x7b];
/// psa_crypto::init().unwrap();
///
/// let key_id = generate(attributes, None).unwrap();
/// let mut pub_key = vec![0; attributes.export_public_key_output_size().unwrap()];
/// let _pub_key_length = export_public(key_id.clone(), &mut pub_key);
/// let rsa_pub_key = RSAPublicKey::from_pkcs1(&pub_key).unwrap();
/// let ciphertext = rsa_pub_key.encrypt(&mut OsRng, PaddingScheme::new_pkcs1v15_encrypt(), &MESSAGE).unwrap();
///
/// let alg = AsymmetricEncryption::RsaPkcs1v15Crypt;
/// let buffer_size = attributes.asymmetric_decrypt_output_size(alg).unwrap();
/// let mut decrypted_message = vec![0; buffer_size];
/// let size = decrypt(key_id,
/// alg,
/// &ciphertext,
/// None,
/// &mut decrypted_message).unwrap();
/// decrypted_message.resize(size, 0);
/// ```
pub fn decrypt(
key_id: Id,
alg: AsymmetricEncryption,
encrypted_message: &[u8],
salt: Option<&[u8]>,
plaintext: &mut [u8],
) -> Result<usize> {
initialized()?;

let handle = key_id.handle()?;

let mut output_length = 0;
let (salt_ptr, salt_len) = match salt {
Some(salt) => (salt.as_ptr(), salt.len()),
None => (core::ptr::null(), 0),
};

let decrypt_res = Status::from(unsafe {
psa_crypto_sys::psa_asymmetric_decrypt(
handle,
alg.into(),
encrypted_message.as_ptr(),
encrypted_message.len(),
salt_ptr,
salt_len,
plaintext.as_mut_ptr(),
plaintext.len(),
&mut output_length,
)
})
.to_result();
let close_handle_res = key_id.close_handle(handle);
decrypt_res?;
close_handle_res?;
Ok(output_length)
}
37 changes: 35 additions & 2 deletions psa-crypto/src/types/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,8 @@ impl TryFrom<psa_crypto_sys::psa_algorithm_t> for Algorithm {
let asym_sign: AsymmetricSignature = alg.try_into()?;
Ok(asym_sign.into())
} else if psa_crypto_sys::PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(alg) {
error!("Asymmetric Encryption algorithms are not supported.");
Err(Error::NotSupported)
let asym_encryption: AsymmetricEncryption = alg.try_into()?;
Ok(asym_encryption.into())
} else if psa_crypto_sys::PSA_ALG_IS_KEY_AGREEMENT(alg) {
error!("Key Agreement algorithms are not supported.");
Err(Error::NotSupported)
Expand All @@ -583,6 +583,7 @@ impl TryFrom<Algorithm> for psa_crypto_sys::psa_algorithm_t {
Algorithm::None => Ok(0),
Algorithm::Hash(hash) => Ok(hash.into()),
Algorithm::AsymmetricSignature(asym_sign) => Ok(asym_sign.into()),
Algorithm::AsymmetricEncryption(asym_encrypt) => Ok(asym_encrypt.into()),
_ => {
error!("Algorithm not supported: {:?}.", alg);
Err(Error::NotSupported)
Expand Down Expand Up @@ -722,6 +723,38 @@ impl From<AsymmetricSignature> for psa_crypto_sys::psa_algorithm_t {
}
}

#[cfg(feature = "with-mbed-crypto")]
impl TryFrom<psa_crypto_sys::psa_algorithm_t> for AsymmetricEncryption {
type Error = Error;
fn try_from(alg: psa_crypto_sys::psa_algorithm_t) -> Result<Self> {
if alg == psa_crypto_sys::PSA_ALG_RSA_PKCS1V15_CRYPT {
Ok(AsymmetricEncryption::RsaPkcs1v15Crypt)
} else if unsafe { psa_crypto_sys::PSA_ALG_IS_RSA_OAEP(alg) } {
Ok(AsymmetricEncryption::RsaOaep {
hash_alg: psa_crypto_sys::PSA_ALG_RSA_OAEP_GET_HASH(alg).try_into()?,
})
} else {
error!(
"Can not find a valid AsymmetricEncryption algorithm for {}.",
alg
);
Err(Error::InvalidArgument)
}
}
}

#[cfg(feature = "with-mbed-crypto")]
impl From<AsymmetricEncryption> for psa_crypto_sys::psa_algorithm_t {
fn from(asym_encrypt: AsymmetricEncryption) -> Self {
match asym_encrypt {
AsymmetricEncryption::RsaPkcs1v15Crypt => psa_crypto_sys::PSA_ALG_RSA_PKCS1V15_CRYPT,
AsymmetricEncryption::RsaOaep { hash_alg } => unsafe {
psa_crypto_sys::PSA_ALG_RSA_OAEP(hash_alg.into())
},
}
}
}

#[cfg(test)]
mod test {
use crate::types::algorithm::{Algorithm, AsymmetricSignature, Hash, SignHash};
Expand Down
Loading