diff --git a/Cargo.lock b/Cargo.lock index 3ee191f668d94..d2f91049238f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,13 +107,29 @@ dependencies = [ "aead 0.4.3", "aes 0.7.5", "cipher 0.3.0", - "cmac", + "cmac 0.6.0", "crypto-mac", "ctr 0.7.0", "dbl", "zeroize", ] +[[package]] +name = "aes-siv" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e08d0cdb774acd1e4dac11478b1a0c0d203134b2aab0ba25eb430de9b18f8b9" +dependencies = [ + "aead 0.5.2", + "aes 0.8.3", + "cipher 0.4.4", + "cmac 0.7.2", + "ctr 0.9.2", + "dbl", + "digest", + "zeroize", +] + [[package]] name = "ahash" version = "0.7.8" @@ -2582,6 +2598,17 @@ dependencies = [ "dbl", ] +[[package]] +name = "cmac" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" +dependencies = [ + "cipher 0.4.4", + "dbl", + "digest", +] + [[package]] name = "cmake" version = "0.1.50" @@ -9470,7 +9497,7 @@ dependencies = [ "indoc", "libc", "memoffset", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "portable-atomic", "pyo3-build-config", "pyo3-ffi", @@ -11186,6 +11213,7 @@ dependencies = [ name = "risingwave_meta" version = "1.9.0-alpha" dependencies = [ + "aes-siv 0.7.0", "anyhow", "arc-swap", "assert_matches", @@ -11193,6 +11221,7 @@ dependencies = [ "aws-config", "axum 0.7.4", "base64-url", + "bincode 1.3.3", "bytes", "chrono", "clap", @@ -13058,7 +13087,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "252d91f300f603abb49b3ecf2ff6d076f885de5f940b562698e16d096e929f20" dependencies = [ - "aes-siv", + "aes-siv 0.6.1", "bincode 1.3.3", "rand 0.7.3", "serde", diff --git a/src/meta/Cargo.toml b/src/meta/Cargo.toml index cb1b5fd193cb9..aeba05ee3e2f9 100644 --- a/src/meta/Cargo.toml +++ b/src/meta/Cargo.toml @@ -14,6 +14,7 @@ ignored = ["workspace-hack"] normal = ["workspace-hack"] [dependencies] +aes-siv = "0.7" anyhow = "1" arc-swap = "1" assert_matches = "1" @@ -21,6 +22,7 @@ async-trait = "0.1" aws-config = { workspace = true } aws-sdk-ec2 = { workspace = true } base64-url = { version = "3.0.0" } +bincode = "1.3" bytes = { version = "1", features = ["serde"] } chrono = "0.4" clap = { workspace = true } diff --git a/src/meta/src/rpc/ddl_controller.rs b/src/meta/src/rpc/ddl_controller.rs index ec76f002ae7ee..7ca539076cdc8 100644 --- a/src/meta/src/rpc/ddl_controller.rs +++ b/src/meta/src/rpc/ddl_controller.rs @@ -18,9 +18,12 @@ use std::num::NonZeroUsize; use std::sync::Arc; use std::time::Duration; +use aes_siv::aead::generic_array::GenericArray; +use aes_siv::aead::Aead; +use aes_siv::{Aes128SivAead, KeyInit}; use anyhow::Context; use itertools::Itertools; -use rand::Rng; +use rand::{Rng, RngCore}; use risingwave_common::config::DefaultParallelism; use risingwave_common::hash::{ParallelUnitMapping, VirtualNode}; use risingwave_common::system_param::reader::SystemParamsRead; @@ -58,6 +61,7 @@ use risingwave_pb::stream_plan::{ Dispatcher, DispatcherType, FragmentTypeFlag, MergeNode, PbStreamFragmentGraph, StreamFragmentGraph as StreamFragmentGraphProto, }; +use serde::{Deserialize, Serialize}; use thiserror_ext::AsReport; use tokio::sync::Semaphore; use tokio::time::sleep; @@ -156,6 +160,12 @@ pub enum DdlCommand { DropSubscription(SubscriptionId, DropMode), } +#[derive(Deserialize, Serialize)] +struct SecretEncryption { + nonce: [u8; 16], + ciphertext: Vec, +} + impl DdlCommand { fn allow_in_recovery(&self) -> bool { match self { @@ -620,16 +630,34 @@ impl DdlController { // The 'secret' part of the request we receive from the frontend is in plaintext; // here, we need to encrypt it before storing it in the catalog. - let encrypted_payload = simplestcrypt::encrypt_and_serialize( - self.env.opts.secret_store_private_key.as_slice(), - secret.get_value().as_slice(), - ) - .map_err(|e| { - MetaError::from(MetaErrorInner::InvalidParameter(format!( - "failed to encrypt secret {}: {:?}", - secret.name, e - ))) - })?; + let encrypted_payload = { + let data = secret.get_value().as_slice(); + let key = self.env.opts.secret_store_private_key.as_slice(); + let encrypt_key = { + let mut k = key[..(std::cmp::min(key.len(), 32))].to_vec(); + k.resize_with(32, || 0); + k + }; + + let mut rng = rand::thread_rng(); + let mut nonce: [u8; 16] = [0; 16]; + rng.fill_bytes(&mut nonce); + let nonce_array = GenericArray::from_slice(&nonce); + let cipher = Aes128SivAead::new(encrypt_key.as_slice().into()); + + let ciphertext = cipher.encrypt(nonce_array, data).map_err(|e| { + MetaError::from(MetaErrorInner::InvalidParameter(format!( + "failed to encrypt secret {}: {:?}", + secret.name, e + ))) + })?; + bincode::serialize(&SecretEncryption { nonce, ciphertext }).map_err(|e| { + MetaError::from(MetaErrorInner::InvalidParameter(format!( + "failed to serialize secret {}: {:?}", + secret.name, e + ))) + })? + }; secret.value = encrypted_payload; match &self.metadata_manager {