Using the tink-rust library with Tonic = type system battle

I'm trying to port a Java service to Rust. We currently use Google's Tink encryption library and I need to be able to perform decryption and encryption operations in the service using it. There's a Rust port called tink-rust.

However, I'm quite a Rust newbie and this turned out into a fight with the type system. The project contains the following files:

proto/hello.proto:

syntax = "proto3";

package hello;

service HelloService {
  rpc Hello (HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
}
message HelloResponse {
}

build.rs:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::compile_protos("proto/hello.proto")?;
    Ok(())
}

src/main.rs:

mod hello {
    tonic::include_proto!("hello");
}

use hello::{HelloRequest,HelloResponse};
use hello::hello_service_server::{HelloService,HelloServiceServer};
use tonic::transport::Server;
use tonic::{Request, Response, Status};

static KEY: &str = "my encryption key";

fn parse_encryption_key(key: &str) -> Option<Box<dyn tink_core::Aead>> {
    if key != "" {
        let mut key_reader = tink_core::keyset::BinaryReader::new(key.as_bytes());
        match tink_core::keyset::insecure::read(&mut key_reader) {
            Ok(h) => Some(tink_aead::new(&h).unwrap()),
            Err(e) => None        }
    } else {
        None
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let encrypter = parse_encryption_key(KEY);

    let addr = "[::1]:50051";
    let service = MyServer {/* encrypter */};

    Server::builder()
        .add_service(HelloServiceServer::new(service))
        .serve(addr.parse().unwrap())
        .await?;

    Ok(())
}

struct MyServer {
    //encrypter: Option<Box<dyn tink_core::Aead>>
}

#[tonic::async_trait]
impl HelloService for MyServer {
    async fn hello(
        &self,
        req: Request<HelloRequest>,
    ) -> Result<Response<HelloResponse>, Status> {
        Ok(Response::new(HelloResponse::default()))
    }
}

Cargo.toml:

[package]
name = "hello-tink"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "hello-server"
path = "src/main.rs"

[dependencies]
prost = "0.12.3"
tink-aead = "0.2.5"
tink-core = { version = "0.2.5", features = [ "json", "insecure" ] }
tink-proto = { version = "0.2.5", features = [ "base64" ] }
tink-signature = "0.2.5"
tokio = { version = "1.36.0", features = ["full"] }
tonic = { version = "0.11", features = ["tls", "tls-roots"] }

[build-dependencies]
tonic-build = "0.11"

What I would like to do is to be able to use a tink_core::Aead encrypter inside my gRPC service. However, the tink library gives me a Box<dyn tink_core::Aead> back. I'm simply lost with how to pass this into the service as MyServer needs to be both Send and Sync.

Any ideas how to accomplish this? I'm also trying to avoid any locks possible when accessing the encrypter, since the main sources of CPU usage in the current Java service is garbage collection and the encryption library ops.

The following code compiles, but I'm wondering if there's a better way of doing this?

mod hello {
    tonic::include_proto!("hello");
}

use std::cell::Cell;

use hello::{HelloRequest,HelloResponse};
use hello::hello_service_server::{HelloService,HelloServiceServer};
use tonic::transport::Server;
use tonic::{Request, Response, Status};

static KEY: &str = "my encryption key";

thread_local! {
    static DECRYPTER: Cell<Option<Box<dyn tink_core::Aead>>> = Cell::new(None)
}

fn parse_encryption_key(key: &str) -> Option<Box<dyn tink_core::Aead>> {
    if key != "" {
        let mut key_reader = tink_core::keyset::BinaryReader::new(key.as_bytes());
        match tink_core::keyset::insecure::read(&mut key_reader) {
            Ok(h) => Some(tink_aead::new(&h).unwrap()),
            Err(e) => None        }
    } else {
        None
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051";
    let service = MyServer { };

    Server::builder()
        .add_service(HelloServiceServer::new(service))
        .serve(addr.parse().unwrap())
        .await?;

    Ok(())
}

struct MyServer {
}

#[tonic::async_trait]
impl HelloService for MyServer {
    async fn hello(
        &self,
        req: Request<HelloRequest>,
    ) -> Result<Response<HelloResponse>, Status> {
        DECRYPTER.with(|c| {
            let decrypter = match c.take() {
                Some(d) => d,
                None => parse_encryption_key(KEY).unwrap()
            };
            c.set(Some(decrypter));
        });

        Ok(Response::new(HelloResponse::default()))
    }
}