Implement Cryptography API
Closed this issue · 7 comments
Describe the feature/proposal
Implement access to the cryptography API which will include the methods:
- Encrypt
- Decrypt
Reference:
- https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/
- dapr/proto/runtime/v1 - encrypt
Release Note
RELEASE NOTE: ADD Cryptography API support to the client
@mikeee Its a pretty basic example but I am not getting back the decode response.
use tonic::{Request, Status};
use tonic::codegen::tokio_stream;
use crate::Client;
use crate::client::TonicClient;
use crate::dapr::dapr::proto::common::v1::StreamPayload;
use crate::dapr::dapr::proto::runtime::v1::{
DecryptRequest, DecryptRequestOptions, EncryptRequest, EncryptRequestOptions,
};
impl Client<TonicClient> {
pub async fn encrypt(
&mut self,
payload: String,
request_options: EncryptRequestOptions,
) -> Result<StreamPayload, Status> {
let stream_payload = StreamPayload {
data: payload.as_bytes().to_vec(),
seq: 0,
};
let request = EncryptRequest {
options: Some(request_options),
payload: Some(stream_payload),
};
let request = Request::new(tokio_stream::iter([request]));
let stream = self.0.encrypt_alpha1(request).await?;
Ok(stream
.into_inner()
.message()
.await?
.unwrap()
.payload
.unwrap())
}
pub async fn decrypt<T>(
&mut self,
encrypted: T,
options: DecryptRequestOptions,
) -> Result<Vec<u8>, Status>
where
T: Into<Vec<u8>>,
{
let payload = StreamPayload {
data: encrypted.into(),
seq: 0,
};
let request = DecryptRequest {
options: Some(options),
payload: Some(payload),
};
let request = Request::new(tokio_stream::iter([request]));
let stream = self.0.decrypt_alpha1(request).await?;
Ok(stream
.into_inner()
.message()
.await?
.unwrap()
.payload
.unwrap()
.data)
}
}
Sample output:
== APP == Compiling dapr v0.14.0 (/home/zachary/Documents/code/dapr/rust-sdk)
== APP == Finished dev [unoptimized + debuginfo] target(s) in 4.70s
== APP == Running `target/debug/examples/crypto`
== APP == dapr.io/enc/v1
== APP == {"k":"rsa-private-key.pem","kw":5,"wfk":"nmfaImoD16Z6QcCz3etHGvnEb9NoO2VC/mk0gd57ovB39J/I74W+Eq7gcvFMKAWvSGHrfPK2iFWhJZKHDjx1OB8vXQpHp3Dg8IYaCOsCdM2+aPuh5yf9ta4xmHlM9VJ0Fr2Kk89sbIIVU1IBh8NvPFqryjm48RmjuCNWGREFBHAXj+kc3+s3g9ZeOsIubjtXKg4htszMkTRwIC9+j2dvEehJB13qENjD5gn1290HSzXE+Dk3LvFXPhOiDXraOitAZouOVfErQEyeg8nuOMe3C/dFWR8wsphCfVJrFnddiRO+RpOcZ3vucHyuj4Vx6LXIQgCcHoH/ZpSRlrCmqqU7/cTi9SZlUcc/NtlDWAFJ03+PTyDqUdoQxcJa8Ka3C3hoKpC8k4aNbwaVyhNQA2ncAW9zVraKF/PJhOFsMFoETgoGDllgIbbCoC3Mh2J0vEqM8LYZxeRwItPKNfPrupQdJGNn5++FEAvs5tB4Np5DRszfjb5R47JSSMOmvRBTEicfna2yPgOi775jzKLneg6Cju4suuHRr1gbmI0OceoPTszBYmYCzHC8WMlnRVKHdo/7neq1gO68UQwOTfdTE/+U8ooNumpDrggTscSCo4F27PQTV9W63PCZof1+R88Eoor1ux1r9CdWOJstwXGryYuEE3zpfxHqFuF6vBAhHFg9fLw=","cph":1,"np":"WABh99CjPg=="}
== APP == 93ooYd7SK5a4OhkJkkVgFi5qF3V6QsTOtsPY5/rrDCw=
== APP ==
== APP == thread 'main' panicked at /home/zachary/Documents/code/dapr/rust-sdk/src/crypto.rs:58:14:
== APP == called `Option::unwrap()` on a `None` value
== APP == note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Sample code
use tokio::time::sleep;
use dapr::dapr::dapr::proto::runtime::v1::{DecryptRequestOptions, EncryptRequestOptions};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// TODO: Handle this issue in the sdk
// Introduce delay so that dapr grpc port is assigned before app tries to connect
sleep(std::time::Duration::new(2, 0)).await;
// Get the Dapr port and create a connection
let port: u16 = std::env::var("DAPR_GRPC_PORT")?.parse()?;
let addr = format!("https://127.0.0.1:{}", port);
// Create the client
let mut client = dapr::Client::<dapr::client::TonicClient>::connect(addr).await?;
let encrypted = client
.encrypt(
"Test".to_string(),
EncryptRequestOptions {
component_name: "localstorage".to_string(),
key_name: "rsa-private-key.pem".to_string(),
key_wrap_algorithm: "RSA".to_string(),
data_encryption_cipher: "aes-gcm".to_string(),
omit_decryption_key_name: false,
decryption_key_name: "rsa-private-key.pem".to_string(),
},
)
.await
.unwrap();
println!("{}", String::from_utf8(encrypted.clone().data).unwrap());
let decrypted = client
.decrypt(
encrypted.clone().data,
DecryptRequestOptions {
component_name: "localstorage".to_string(),
key_name: "rsa-private-key.pem".to_string(),
},
)
.await
.unwrap();
assert_eq!("Test".to_string(), String::from_utf8(decrypted).unwrap());
Ok(())
}
@zedgell I believe you'll need to implement bidirectional streams in the client methods which you can then use to send/receive and await responses.
I have done that base off the tonic example here
use futures::StreamExt;
use tonic::codegen::tokio_stream;
use tonic::{Request, Status};
use crate::client::TonicClient;
use crate::dapr::dapr::proto::common::v1::StreamPayload;
use crate::dapr::dapr::proto::runtime::v1::{
DecryptRequest, DecryptRequestOptions, EncryptRequest, EncryptRequestOptions,
};
use crate::Client;
impl Client<TonicClient> {
pub async fn encrypt(
&mut self,
payload: String,
request_options: EncryptRequestOptions,
) -> Result<StreamPayload, Status> {
let stream_payload = StreamPayload {
data: payload.as_bytes().to_vec(),
seq: 0,
};
let request = EncryptRequest {
options: Some(request_options),
payload: Some(stream_payload),
};
let request = Request::new(tokio_stream::iter([request]));
let stream = self.0.encrypt_alpha1(request).await?;
Ok(stream
.into_inner()
.take(1)
.next()
.await
.unwrap()
.unwrap()
.payload
.unwrap())
}
pub async fn decrypt<T>(
&mut self,
encrypted: T,
options: DecryptRequestOptions,
) -> Result<Vec<u8>, Status>
where
T: Into<Vec<u8>>,
{
let payload = StreamPayload {
data: encrypted.into(),
seq: 0,
};
let request = DecryptRequest {
options: Some(options),
payload: Some(payload),
};
let request = Request::new(tokio_stream::iter([request]));
let stream = self.0.decrypt_alpha1(request).await?;
Ok(stream
.into_inner()
.take(1)
.next()
.await
.unwrap()
.unwrap()
.payload
.unwrap()
.data)
}
}
This block client.rs#L34 is similar to what the methods should resemble
@mikeee that is pretty much what I am doing in the second example using take but I am just awaiting one response instead of an infinite amount even if I implement while Some loop it will not return anything since next is None. I have to be just overlooking something!
@mikeee scratch the above. I see what I got wrong. I will get that a PR made come morning.
Great stuff, have a great evening!