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

feat(kms): reduce redundant kms calls #1264

Merged
merged 2 commits into from
May 25, 2023
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
2 changes: 2 additions & 0 deletions crates/router/src/configs.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod defaults;
#[cfg(feature = "kms")]
pub(super) mod kms;
pub mod settings;
mod validations;
60 changes: 60 additions & 0 deletions crates/router/src/configs/kms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use common_utils::errors::CustomResult;
use external_services::kms;
use masking::ExposeInterface;

use crate::configs::settings;

#[async_trait::async_trait]
// This trait performs inplace decryption of the structure on which this is implemented
pub(crate) trait KmsDecrypt {
async fn decrypt_inner(self, kms_config: &kms::KmsConfig) -> CustomResult<Self, kms::KmsError>
where
Self: Sized;
}

#[async_trait::async_trait]
impl KmsDecrypt for settings::Jwekey {
async fn decrypt_inner(self, kms_config: &kms::KmsConfig) -> CustomResult<Self, kms::KmsError> {
let client = kms::get_kms_client(kms_config).await;

// If this pattern required repetition, a macro approach needs to be deviced
let (
locker_encryption_key1,
locker_encryption_key2,
locker_decryption_key1,
locker_decryption_key2,
vault_encryption_key,
vault_private_key,
tunnel_private_key,
) = tokio::try_join!(
client.decrypt(self.locker_encryption_key1),
client.decrypt(self.locker_encryption_key2),
client.decrypt(self.locker_decryption_key1),
client.decrypt(self.locker_decryption_key2),
client.decrypt(self.vault_encryption_key),
client.decrypt(self.vault_private_key),
client.decrypt(self.tunnel_private_key),
)?;

Ok(Self {
locker_key_identifier1: self.locker_key_identifier1,
locker_key_identifier2: self.locker_key_identifier2,
locker_encryption_key1,
locker_encryption_key2,
locker_decryption_key1,
locker_decryption_key2,
vault_encryption_key,
vault_private_key,
tunnel_private_key,
})
}
}

#[async_trait::async_trait]
impl KmsDecrypt for settings::ActiveKmsSecrets {
async fn decrypt_inner(self, kms_config: &kms::KmsConfig) -> CustomResult<Self, kms::KmsError> {
Ok(Self {
jwekey: self.jwekey.expose().decrypt_inner(kms_config).await?.into(),
})
}
}
10 changes: 10 additions & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ pub enum Subcommand {
GenerateOpenapiSpec,
}

#[cfg(feature = "kms")]
/// Store the decrypted kms secret values for active use in the application
/// Currently using `StrongSecret` won't have any effect as this struct have smart pointers to heap
/// allocations.
/// note: we can consider adding such behaviour in the future with custom implementation
#[derive(Clone)]
pub struct ActiveKmsSecrets {
pub jwekey: masking::Secret<Jwekey>,
NishantJoshi00 marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Debug, Deserialize, Clone, Default)]
#[serde(default)]
pub struct Settings {
Expand Down
68 changes: 21 additions & 47 deletions crates/router/src/core/payment_methods/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,10 @@ pub async fn add_card_hs(
merchant_account: &storage::MerchantAccount,
) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> {
let locker = &state.conf.locker;
#[cfg(not(feature = "kms"))]
let jwekey = &state.conf.jwekey;

#[cfg(feature = "kms")]
let kms_config = &state.conf.kms;
let jwekey = &state.kms_secrets;

let db = &*state.store;
let merchant_id = &merchant_account.merchant_id;
Expand All @@ -264,16 +264,9 @@ pub async fn add_card_hs(
.get_required_value("locker_id")
.change_context(errors::VaultError::SaveCardFailed)?;

let request = payment_methods::mk_add_card_request_hs(
jwekey,
locker,
&card,
&customer_id,
merchant_id,
#[cfg(feature = "kms")]
kms_config,
)
.await?;
let request =
payment_methods::mk_add_card_request_hs(jwekey, locker, &card, &customer_id, merchant_id)
.await?;

let stored_card_response = if !locker.mock_locker {
let response = services::call_connector_api(state, request)
Expand All @@ -284,15 +277,10 @@ pub async fn add_card_hs(
.get_response_inner("JweBody")
.change_context(errors::VaultError::FetchCardFailed)?;

let decrypted_payload = payment_methods::get_decrypted_response_payload(
jwekey,
jwe_body,
#[cfg(feature = "kms")]
kms_config,
)
.await
.change_context(errors::VaultError::SaveCardFailed)
.attach_printable("Error getting decrypted response payload")?;
let decrypted_payload = payment_methods::get_decrypted_response_payload(jwekey, jwe_body)
.await
.change_context(errors::VaultError::SaveCardFailed)
.attach_printable("Error getting decrypted response payload")?;
let stored_card_resp: payment_methods::StoreCardResp = decrypted_payload
.parse_struct("StoreCardResp")
.change_context(errors::VaultError::ResponseDeserializationFailed)?;
Expand Down Expand Up @@ -394,19 +382,17 @@ pub async fn get_card_from_hs_locker<'a>(
card_reference: &'a str,
) -> errors::CustomResult<payment_methods::Card, errors::VaultError> {
let locker = &state.conf.locker;
#[cfg(not(feature = "kms"))]
let jwekey = &state.conf.jwekey;

#[cfg(feature = "kms")]
let kms_config = &state.conf.kms;
let jwekey = &state.kms_secrets;

let request = payment_methods::mk_get_card_request_hs(
jwekey,
locker,
customer_id,
merchant_id,
card_reference,
#[cfg(feature = "kms")]
kms_config,
)
.await
.change_context(errors::VaultError::FetchCardFailed)
Expand All @@ -419,15 +405,10 @@ pub async fn get_card_from_hs_locker<'a>(
let jwe_body: services::JweBody = response
.get_response_inner("JweBody")
.change_context(errors::VaultError::FetchCardFailed)?;
let decrypted_payload = payment_methods::get_decrypted_response_payload(
jwekey,
jwe_body,
#[cfg(feature = "kms")]
kms_config,
)
.await
.change_context(errors::VaultError::FetchCardFailed)
.attach_printable("Error getting decrypted response payload for get card")?;
let decrypted_payload = payment_methods::get_decrypted_response_payload(jwekey, jwe_body)
.await
.change_context(errors::VaultError::FetchCardFailed)
.attach_printable("Error getting decrypted response payload for get card")?;
let get_card_resp: payment_methods::RetrieveCardResp = decrypted_payload
.parse_struct("RetrieveCardResp")
.change_context(errors::VaultError::FetchCardFailed)?;
Expand Down Expand Up @@ -483,19 +464,17 @@ pub async fn delete_card_from_hs_locker<'a>(
card_reference: &'a str,
) -> errors::RouterResult<payment_methods::DeleteCardResp> {
let locker = &state.conf.locker;
#[cfg(not(feature = "kms"))]
let jwekey = &state.conf.jwekey;

#[cfg(feature = "kms")]
let kms_config = &state.conf.kms;
let jwekey = &state.kms_secrets;

let request = payment_methods::mk_delete_card_request_hs(
jwekey,
locker,
customer_id,
merchant_id,
card_reference,
#[cfg(feature = "kms")]
kms_config,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
Expand All @@ -507,15 +486,10 @@ pub async fn delete_card_from_hs_locker<'a>(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while executing call_connector_api for delete card");
let jwe_body: services::JweBody = response.get_response_inner("JweBody")?;
let decrypted_payload = payment_methods::get_decrypted_response_payload(
jwekey,
jwe_body,
#[cfg(feature = "kms")]
kms_config,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting decrypted response payload for delete card")?;
let decrypted_payload = payment_methods::get_decrypted_response_payload(jwekey, jwe_body)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error getting decrypted response payload for delete card")?;
let delete_card_resp: payment_methods::DeleteCardResp = decrypted_payload
.parse_struct("DeleteCardResp")
.change_context(errors::ApiErrorResponse::InternalServerError)?;
Expand Down
Loading