Skip to content

Commit 3b65a09

Browse files
committed
fix: validate static keys at compile time
1 parent 1dfe003 commit 3b65a09

3 files changed

Lines changed: 21 additions & 17 deletions

File tree

conformance/src/bin/server.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ fn custom_header_tool() -> Tool {
5252
/// Signing key for SEP-2322 `requestState` sealing. A fixed key is fine for a
5353
/// conformance harness; real servers must load a secret out of clients' reach.
5454
const REQUEST_STATE_KEY: &[u8] = b"rust-sdk-conformance-request-state-key!!";
55+
const _: () = assert!(
56+
REQUEST_STATE_KEY.len() >= RequestStateCodec::MIN_KEY_LENGTH,
57+
"REQUEST_STATE_KEY is shorter than RequestStateCodec::MIN_KEY_LENGTH",
58+
);
5559

5660
#[derive(Clone)]
5761
struct ConformanceServer {
@@ -70,8 +74,7 @@ impl ConformanceServer {
7074
subscriptions: Arc::new(Mutex::new(HashMap::new())),
7175
next_subscription: Arc::new(AtomicU64::new(0)),
7276
log_level: Arc::new(Mutex::new(LoggingLevel::Debug)),
73-
request_state_codec: RequestStateCodec::try_new(REQUEST_STATE_KEY)
74-
.expect("conformance request-state key meets the minimum length"),
77+
request_state_codec: RequestStateCodec::new(REQUEST_STATE_KEY),
7578
tasks: TaskManager::new(),
7679
}
7780
}

crates/rmcp/tests/test_mrtr_behavior.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -594,14 +594,15 @@ async fn request_state_codec_seals_and_verifies_through_the_loop() -> anyhow::Re
594594
use rmcp::model::RequestStateCodec;
595595

596596
// A shared per-process signing key, mirroring how a real server would derive one.
597-
static KEY: &[u8] = b"integration-signing-key-32-bytes!";
597+
const KEY: &[u8] = b"integration-signing-key-32-bytes!";
598+
const _: () = assert!(
599+
KEY.len() >= RequestStateCodec::MIN_KEY_LENGTH,
600+
"KEY is shorter than RequestStateCodec::MIN_KEY_LENGTH",
601+
);
598602

599603
fn codec() -> &'static RequestStateCodec {
600604
static CODEC: OnceLock<RequestStateCodec> = OnceLock::new();
601-
CODEC.get_or_init(|| {
602-
RequestStateCodec::try_new(KEY)
603-
.expect("test request-state key meets the minimum length")
604-
})
605+
CODEC.get_or_init(|| RequestStateCodec::new(KEY))
605606
}
606607

607608
#[derive(Clone, Default)]

examples/servers/src/mrtr.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ use rmcp::{
4040
};
4141
use serde_json::json;
4242

43-
/// A stable, high-entropy secret. In a real deployment, load this from your
44-
/// secret manager and keep it out of clients' reach. It must stay constant for
43+
/// A fixed key for this example. Real deployments should load at least 32 bytes
44+
/// of random secret key material from a secret manager and keep it stable for
4545
/// the lifetime of any in-flight MRTR exchange.
4646
const REQUEST_STATE_KEY: &[u8] = b"example-request-state-signing-key-32b!";
4747

@@ -51,12 +51,11 @@ struct WeatherServer {
5151
codec: RequestStateCodec,
5252
}
5353

54-
impl Default for WeatherServer {
55-
fn default() -> Self {
56-
Self {
57-
codec: RequestStateCodec::try_new(REQUEST_STATE_KEY)
58-
.expect("example request-state key meets the minimum length"),
59-
}
54+
impl WeatherServer {
55+
fn new(key: impl Into<Vec<u8>>) -> Result<Self, RequestStateError> {
56+
Ok(Self {
57+
codec: RequestStateCodec::try_new(key)?,
58+
})
6059
}
6160
}
6261

@@ -153,18 +152,19 @@ impl ClientHandler for InteractiveClient {
153152
#[tokio::main]
154153
async fn main() -> anyhow::Result<()> {
155154
let (server_transport, client_transport) = tokio::io::duplex(8192);
155+
let server = WeatherServer::new(REQUEST_STATE_KEY)?;
156156

157157
// Spin up the server side.
158158
tokio::spawn(async move {
159-
let server = WeatherServer::default()
159+
let server = server
160160
.serve(server_transport)
161161
.await
162162
.expect("server should start");
163163
let _ = server.waiting().await;
164164
});
165165

166166
// Connect the client (this performs the initialize handshake).
167-
let client = InteractiveClient::default().serve(client_transport).await?;
167+
let client = InteractiveClient.serve(client_transport).await?;
168168

169169
// 1. High-level auto mode: the SDK fulfils the elicitation and retries for us.
170170
println!("== auto mode (call_tool) ==");

0 commit comments

Comments
 (0)