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

Implement Cryptography API #133

Closed
mikeee opened this issue Mar 4, 2024 · 7 comments · Fixed by #144
Closed

Implement Cryptography API #133

mikeee opened this issue Mar 4, 2024 · 7 comments · Fixed by #144
Assignees
Labels
enhancement New feature or request P1
Milestone

Comments

@mikeee
Copy link
Member

mikeee commented Mar 4, 2024

Describe the feature/proposal

Implement access to the cryptography API which will include the methods:

  • Encrypt
  • Decrypt

Reference:

Release Note

RELEASE NOTE: ADD Cryptography API support to the client

@zedgell
Copy link
Contributor

zedgell commented Mar 18, 2024

@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(())
}

@mikeee mikeee added this to the 1.14 milestone Mar 18, 2024
@mikeee
Copy link
Member Author

mikeee commented Mar 19, 2024

@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.

@zedgell
Copy link
Contributor

zedgell commented Mar 19, 2024

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)
    }
}

@mikeee
Copy link
Member Author

mikeee commented Mar 19, 2024

This block client.rs#L34 is similar to what the methods should resemble

@zedgell
Copy link
Contributor

zedgell commented Mar 20, 2024

@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!

@zedgell
Copy link
Contributor

zedgell commented Mar 20, 2024

@mikeee scratch the above. I see what I got wrong. I will get that a PR made come morning.

@mikeee
Copy link
Member Author

mikeee commented Mar 20, 2024

Great stuff, have a great evening!

@zedgell zedgell mentioned this issue Mar 20, 2024
3 tasks
@mikeee mikeee added the enhancement New feature or request label Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request P1
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants