-
Notifications
You must be signed in to change notification settings - Fork 134
feat: add input encryption for validators #2342
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
Changes from 2 commits
781d918
68c0c3c
cfe6987
fcb3e8b
a861396
db96583
24aa5f9
c6fd347
5bed689
437d0ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,20 +10,26 @@ use miden_node_utils::clap::GrpcOptionsInternal; | |
| use miden_node_utils::logging::OpenTelemetry; | ||
| use miden_node_utils::shutdown::CancellationToken; | ||
| use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey; | ||
| use miden_protocol::crypto::dsa::eddsa_25519_sha512::KeyExchangeKey; | ||
| use miden_protocol::utils::serde::Deserializable; | ||
| use miden_validator::{DataDirectory, ValidatorSigner}; | ||
| use miden_validator::{DataDirectory, LOG_TARGET, ValidatorEncryptor, ValidatorSigner}; | ||
|
|
||
| const ENV_DATA_DIRECTORY: &str = "MIDEN_VALIDATOR_DATA_DIRECTORY"; | ||
| const ENV_LISTEN: &str = "MIDEN_VALIDATOR_LISTEN"; | ||
| const ENV_KEY: &str = "MIDEN_VALIDATOR_KEY"; | ||
| const ENV_KMS_KEY_ID: &str = "MIDEN_VALIDATOR_KMS_KEY_ID"; | ||
| const ENV_ENCRYPTION_KEY: &str = "MIDEN_VALIDATOR_ENCRYPTION_KEY"; | ||
| const ENV_GENESIS_CONFIG_FILE: &str = "MIDEN_VALIDATOR_GENESIS_CONFIG_FILE"; | ||
| const ENV_SQLITE_CONNECTION_POOL_SIZE: &str = "MIDEN_VALIDATOR_SQLITE_CONNECTION_POOL_SIZE"; | ||
|
|
||
| /// A predefined, insecure validator key for development purposes. | ||
| pub(crate) const INSECURE_KEY_HEX: &str = | ||
| "0101010101010101010101010101010101010101010101010101010101010101"; | ||
|
|
||
| /// A predefined, insecure shared transaction encryption key for development purposes. | ||
| pub(crate) const INSECURE_ENCRYPTION_KEY_HEX: &str = | ||
| "0202020202020202020202020202020202020202020202020202020202020202"; | ||
|
|
||
| // VALIDATOR COMMAND | ||
| // ================================================================================================ | ||
|
|
||
|
|
@@ -116,6 +122,20 @@ pub enum ValidatorCommand { | |
| group = "key" | ||
| )] | ||
| kms_key_id: Option<String>, | ||
|
bobbinth marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// Hex-encoded shared secret of the transaction encryption key. | ||
| /// | ||
| /// Unlike the per-validator signing key, this value must be identical across every | ||
| /// validator in the set. | ||
| /// | ||
| /// If not provided, a predefined insecure key is used. | ||
|
SantiagoPittella marked this conversation as resolved.
|
||
| #[arg( | ||
| long = "encryption-key.hex", | ||
| env = ENV_ENCRYPTION_KEY, | ||
| value_name = "VALIDATOR_ENCRYPTION_KEY", | ||
| default_value = INSECURE_ENCRYPTION_KEY_HEX | ||
| )] | ||
| encryption_key: String, | ||
|
Comment on lines
+143
to
+150
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not for this PR, but should we also provide an option to get the key from KMS?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We just need to ensure KMS supports this key type, otherwise we cannot decrypt. |
||
| }, | ||
| } | ||
|
|
||
|
|
@@ -154,34 +174,45 @@ impl ValidatorCommand { | |
| data_directory, | ||
| kms_key_id, | ||
| sqlite_connection_pool_size, | ||
| encryption_key, | ||
| .. | ||
| } => { | ||
| let address = listen; | ||
|
|
||
| if let Some(kms_key_id) = kms_key_id { | ||
| let signer = ValidatorSigner::new_kms(kms_key_id).await?; | ||
| start::start( | ||
| address, | ||
| grpc_options, | ||
| signer, | ||
| data_directory, | ||
| sqlite_connection_pool_size, | ||
| shutdown, | ||
| ) | ||
| .await | ||
| // Unlike the signing key, whose insecure default is caught at startup against the | ||
| // chain's committed validator key, nothing cross-checks the encryption key. Warn | ||
| // loudly so the default never runs in production unnoticed. | ||
| if encryption_key == INSECURE_ENCRYPTION_KEY_HEX { | ||
| tracing::warn!( | ||
| target: LOG_TARGET, | ||
| "Using the predefined, insecure transaction encryption key, configure \ | ||
| --encryption-key.hex for production deployments" | ||
| ); | ||
| } | ||
|
|
||
| let encryption_key_bytes = hex::decode(encryption_key) | ||
| .context("failed to decode the encryption key hex")?; | ||
| let encryption_key = KeyExchangeKey::read_from_bytes(&encryption_key_bytes) | ||
| .context("failed to construct the encryption key")?; | ||
| let encryptor = ValidatorEncryptor::new_local(encryption_key); | ||
|
Mirko-von-Leipzig marked this conversation as resolved.
Outdated
|
||
|
|
||
| let signer = if let Some(kms_key_id) = kms_key_id { | ||
| ValidatorSigner::new_kms(kms_key_id).await? | ||
| } else { | ||
| let signer = SigningKey::read_from_bytes(hex::decode(validator_key)?.as_ref())?; | ||
| let signer = ValidatorSigner::new_local(signer); | ||
| start::start( | ||
| address, | ||
| grpc_options, | ||
| signer, | ||
| data_directory, | ||
| sqlite_connection_pool_size, | ||
| shutdown, | ||
| ) | ||
| .await | ||
| } | ||
| ValidatorSigner::new_local(signer) | ||
| }; | ||
|
|
||
| start::start( | ||
| address, | ||
| grpc_options, | ||
| signer, | ||
| encryptor, | ||
| data_directory, | ||
| sqlite_connection_pool_size, | ||
| shutdown, | ||
| ) | ||
| .await | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| use miden_node_proto::generated as grpc; | ||
| use miden_node_utils::tracing::miden_instrument; | ||
| use miden_tx::utils::serde::Serializable; | ||
|
|
||
| use super::ValidatorService; | ||
| use crate::{COMPONENT, ValidatorEncryptor}; | ||
|
|
||
| #[tonic::async_trait] | ||
| impl grpc::server::validator_api::GetTransactionEncryptionKey for ValidatorService { | ||
| type Input = (); | ||
| type Output = grpc::transaction::TransactionEncryptionKey; | ||
|
|
||
| fn decode(request: ()) -> tonic::Result<Self::Input> { | ||
| Ok(request) | ||
| } | ||
|
|
||
| fn encode(output: Self::Output) -> tonic::Result<grpc::transaction::TransactionEncryptionKey> { | ||
| Ok(output) | ||
| } | ||
|
|
||
| #[miden_instrument( | ||
| target = COMPONENT, | ||
| name = "get_transaction_encryption_key", | ||
| skip_all, | ||
| err, | ||
| )] | ||
| async fn handle( | ||
| &self, | ||
| _input: Self::Input, | ||
| _metadata: &tonic::metadata::MetadataMap, | ||
| _extensions: &tonic::codegen::http::Extensions, | ||
| ) -> tonic::Result<Self::Output> { | ||
| // Built entirely from state fixed at construction, so the endpoint stays available while a | ||
| // backup subscription holds the serve lock. | ||
| Ok(grpc::transaction::TransactionEncryptionKey { | ||
| scheme: ValidatorEncryptor::scheme_id(), | ||
| key_id: self.encryptor.key_id(), | ||
| public_key: self.encryptor.public_key().to_bytes(), | ||
| signature: self.encryption_key_attestation.to_bytes(), | ||
|
Mirko-von-Leipzig marked this conversation as resolved.
Outdated
|
||
| }) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.