Skip to content

Commit c25167f

Browse files
authored
Merge pull request #207 from benthecarman/auth-body
Sign auth HMAC over request bodies
2 parents 47b8468 + 819bed8 commit c25167f

4 files changed

Lines changed: 220 additions & 167 deletions

File tree

docs/api-guide.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ x-auth: HMAC <unix_timestamp>:<hmac_hex>
2424
Where:
2525

2626
- `unix_timestamp` is the current time in seconds since the Unix epoch
27-
- `hmac_hex` is the hex-encoded result of `HMAC-SHA256(api_key_bytes, timestamp_be_bytes)`
27+
- `hmac_hex` is the hex-encoded result of
28+
`HMAC-SHA256(api_key_bytes, timestamp_be_bytes || grpc_request_body_bytes)`
2829
- `api_key_bytes` is the API key string encoded as UTF-8 bytes
2930
- `timestamp_be_bytes` is the timestamp as a big-endian 8-byte unsigned integer
31+
- `grpc_request_body_bytes` is the raw gRPC request body sent over HTTP/2, including
32+
the 5-byte gRPC message frame
3033

3134
The server rejects requests where the timestamp differs from the server's clock by more than
3235
**60 seconds**.

ldk-server-client/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ println!("Node ID: {}", info.node_id);
3030

3131
The client handles HMAC-SHA256 authentication automatically. Pass the hex-encoded API key
3232
(found at `<storage_dir>/<network>/api_key`) and the server's TLS certificate (found at
33-
`<storage_dir>/tls.crt`).
33+
`<storage_dir>/tls.crt`). Each request signature covers both the timestamp and the raw gRPC
34+
request body bytes.
3435

3536
## Event Streaming
3637

ldk-server-client/src/client.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,16 @@ impl LdkServerClient {
106106

107107
/// Computes the HMAC-SHA256 authentication header value.
108108
/// Format: "HMAC <timestamp>:<hmac_hex>"
109-
/// Uses timestamp-only HMAC (no body) since TLS guarantees integrity.
110-
fn compute_auth_header(&self) -> String {
109+
/// The signature covers the timestamp and raw gRPC request body bytes.
110+
fn compute_auth_header(&self, body: &[u8]) -> String {
111111
let timestamp = SystemTime::now()
112112
.duration_since(UNIX_EPOCH)
113113
.expect("System time should be after Unix epoch")
114114
.as_secs();
115115

116-
// HMAC-SHA256(api_key, timestamp_bytes) — no body
117116
let mut hmac_engine: HmacEngine<sha256::Hash> = HmacEngine::new(self.api_key.as_bytes());
118117
hmac_engine.input(&timestamp.to_be_bytes());
118+
hmac_engine.input(body);
119119
let hmac_result = Hmac::<sha256::Hash>::from_engine(hmac_engine);
120120

121121
format!("HMAC {}:{}", timestamp, hmac_result)
@@ -428,7 +428,7 @@ impl LdkServerClient {
428428
let grpc_body = encode_grpc_frame(&request.encode_to_vec()).to_vec();
429429

430430
let url = format!("https://{}{}{}", self.base_url, GRPC_SERVICE_PREFIX, method);
431-
let auth_header = self.compute_auth_header();
431+
let auth_header = self.compute_auth_header(&grpc_body);
432432

433433
let response = self
434434
.client
@@ -471,7 +471,7 @@ impl LdkServerClient {
471471
let grpc_body = encode_grpc_frame(&request.encode_to_vec()).to_vec();
472472

473473
let url = format!("https://{}{}{}", self.base_url, GRPC_SERVICE_PREFIX, method);
474-
let auth_header = self.compute_auth_header();
474+
let auth_header = self.compute_auth_header(&grpc_body);
475475

476476
let response = self
477477
.streaming_client

0 commit comments

Comments
 (0)