Skip to content

Commit 1068f0d

Browse files
authored
feat(data-plane): add node-to-node header integrity check (agntcy#1609)
contributes to agntcy#1556 # Description Currently SLIM does not verify if the header has been tampered. Node-to-node HMAC validation will ensure the integrity of the packet header in each hop. In case the header validation fails, the packet will be taken as tampered and the message won't be processed. ## Actions - Added HMAC for relevant header fields to the packet header. - Extended link negotiation with ECDH key exchange - Integration tests for node-to-node hop validation check. ## Type of Change - [ ] Bugfix - [x] New Feature - [ ] Breaking Change - [ ] Refactor - [ ] Documentation - [ ] Other (please describe) ## Checklist - [x] I have read the [contributing guidelines](/agntcy/repo-template/blob/main/CONTRIBUTING.md) - [x] Existing issues have been referenced (where applicable) - [x] I have verified this change is not present in other open pull requests - [x] Functionality is documented - [x] All code style checks pass - [x] New code contribution is covered by automated tests - [x] All new and existing tests pass --------- Signed-off-by: Mark Marton <mark.p.marton@gmail.com> Signed-off-by: Mark Marton <30534230+markpmarton@users.noreply.github.com>
1 parent 62d4d47 commit 1068f0d

30 files changed

Lines changed: 1784 additions & 201 deletions

data-plane/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

data-plane/bindings/go/Taskfile.yaml

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,16 @@ tasks:
222222

223223
install-uniffi-bindgen-go:
224224
desc: Install uniffi-bindgen-go
225+
vars:
226+
VERSION: v0.7.1+v0.31.0
225227
status:
226-
- which uniffi-bindgen-go
228+
- "command -v uniffi-bindgen-go >/dev/null && uniffi-bindgen-go --version
229+
2>/dev/null | grep -Fq '{{.VERSION}}'"
227230
cmds:
228231
- echo "📦 Installing uniffi-bindgen-go..."
229-
- cargo install uniffi-bindgen-go --git https://github.com/NordSecurity/uniffi-bindgen-go.git --tag v0.7.1+v0.31.0 --config net.git-fetch-with-cli=true
232+
- "cargo install uniffi-bindgen-go --git
233+
https://github.com/NordSecurity/uniffi-bindgen-go.git --tag {{.VERSION}}
234+
--config net.git-fetch-with-cli=true --force"
230235
silent: true
231236

232237
copy-library:
@@ -266,7 +271,9 @@ tasks:
266271
echo " $TARGET → $DEST_NAME ($((file_size / 1024 / 1024))MB)"
267272
cp "$LIB_PATH" "$DEST_DIR/$DEST_NAME"
268273
- echo "Libraries in slim_bindings/:"
269-
- ls -lah "slim_bindings/"*{{.LIB_NAME}}*.a "slim_bindings/"*{{.LIB_NAME}}*.lib 2>/dev/null || echo " (no library files found)"
274+
- ls -lah "slim_bindings/"*{{.LIB_NAME}}*.a
275+
"slim_bindings/"*{{.LIB_NAME}}*.lib 2>/dev/null || echo " (no library
276+
files found)"
270277
silent: true
271278

272279
generate-proto:
@@ -332,7 +339,8 @@ tasks:
332339
- go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@{{.GOLANGCI_LINT_VERSION}}
333340
status:
334341
- test -f $(go env GOPATH)/bin/golangci-lint
335-
- $(go env GOPATH)/bin/golangci-lint version | grep -q "{{.GOLANGCI_LINT_VERSION}}"
342+
- $(go env GOPATH)/bin/golangci-lint version | grep -q
343+
"{{.GOLANGCI_LINT_VERSION}}"
336344

337345
lint:
338346
desc: Running lint
@@ -375,7 +383,8 @@ tasks:
375383
dir: examples/point_to_point
376384
cmds:
377385
- echo "Starting Alice (receiver)..."
378-
- go run . --local="org/alice/app" --server="http://localhost:46357" {{.CLI_ARGS}}
386+
- go run . --local="org/alice/app" --server="http://localhost:46357"
387+
{{.CLI_ARGS}}
379388
silent: true
380389

381390
examples:p2p:no-mls:bob:
@@ -385,7 +394,8 @@ tasks:
385394
dir: examples/point_to_point
386395
cmds:
387396
- echo "Starting Bob (sender)..."
388-
- go run . --local="org/bob/app" --remote="org/alice/app" --message="Hello SLIM" --iterations=5 --server="http://localhost:46357" {{.CLI_ARGS}}
397+
- go run . --local="org/bob/app" --remote="org/alice/app" --message="Hello
398+
SLIM" --iterations=5 --server="http://localhost:46357" {{.CLI_ARGS}}
389399
silent: true
390400

391401
examples:p2p:mls:bob:
@@ -395,7 +405,9 @@ tasks:
395405
dir: examples/point_to_point
396406
cmds:
397407
- echo "Starting Bob (sender)..."
398-
- go run . --enable-mls --local="org/bob/app" --remote="org/alice/app" --message="Hello SLIM" --iterations=5 --server="http://localhost:46357" {{.CLI_ARGS}}
408+
- go run . --enable-mls --local="org/bob/app" --remote="org/alice/app"
409+
--message="Hello SLIM" --iterations=5 --server="http://localhost:46357"
410+
{{.CLI_ARGS}}
399411
silent: true
400412

401413
examples:group:moderator:
@@ -405,7 +417,9 @@ tasks:
405417
dir: examples/group
406418
cmds:
407419
- echo "Starting group moderator..."
408-
- go run . --local="org/moderator/app" --remote="org/default/chat-room" --invites="org/alice/app,org/bob/app" --server="http://localhost:46357" {{.CLI_ARGS}}
420+
- go run . --local="org/moderator/app" --remote="org/default/chat-room"
421+
--invites="org/alice/app,org/bob/app" --server="http://localhost:46357"
422+
{{.CLI_ARGS}}
409423
silent: true
410424

411425
examples:group:participant:alice:
@@ -415,7 +429,8 @@ tasks:
415429
dir: examples/group
416430
cmds:
417431
- echo "Starting group participant..."
418-
- go run . --local="org/alice/app" --server="http://localhost:46357" {{.CLI_ARGS}}
432+
- go run . --local="org/alice/app" --server="http://localhost:46357"
433+
{{.CLI_ARGS}}
419434
silent: true
420435

421436
examples:group:participant:bob:
@@ -425,7 +440,8 @@ tasks:
425440
dir: examples/group
426441
cmds:
427442
- echo "Starting group participant..."
428-
- go run . --local="org/bob/app" --server="http://localhost:46357" {{.CLI_ARGS}}
443+
- go run . --local="org/bob/app" --server="http://localhost:46357"
444+
{{.CLI_ARGS}}
429445
silent: true
430446

431447
examples:rpc:server:

data-plane/bindings/java/Taskfile.yaml

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Copyright AGNTCY Contributors (https://github.com/agntcy)
22
# SPDX-License-Identifier: Apache-2.0
3-
43
---
54
version: "3"
65

@@ -23,7 +22,8 @@ vars:
2322
# ROOT_DIR: directory of the invoked Taskfile (java/). TASKFILE_DIR can point at an included taskfile during var eval.
2423
MVNW: '{{.ROOT_DIR}}/mvnw'
2524
BINDINGS_VERSION:
26-
sh: '{{.MVNW}} -f "{{.ROOT_DIR}}/pom.xml" help:evaluate -Dexpression=project.version -q -DforceStdout'
25+
sh: '{{.MVNW}} -f "{{.ROOT_DIR}}/pom.xml" help:evaluate
26+
-Dexpression=project.version -q -DforceStdout'
2727
BINDINGS_DIR: "{{.ROOT_DIR}}/../rust"
2828
PROFILE: '{{.PROFILE | default "debug"}}'
2929
CURRENT_TARGET:
@@ -114,10 +114,13 @@ tasks:
114114
desc: Install uniffi-bindgen-java CLI tool
115115
# Do not skip when ~/.cargo/bin caches an unrelated binary (CI restores ~/.cargo/bin).
116116
status:
117-
- 'command -v uniffi-bindgen-java >/dev/null && uniffi-bindgen-java --version 2>/dev/null | grep -Fq "0.4.2"'
117+
- 'command -v uniffi-bindgen-java >/dev/null && uniffi-bindgen-java
118+
--version 2>/dev/null | grep -Fq "0.4.2"'
118119
cmds:
119120
- echo "Installing uniffi-bindgen-java..."
120-
- cargo install uniffi-bindgen-java --git https://github.com/IronCoreLabs/uniffi-bindgen-java --tag 0.4.2 --config net.git-fetch-with-cli=true --force
121+
- cargo install uniffi-bindgen-java --git
122+
https://github.com/IronCoreLabs/uniffi-bindgen-java --tag 0.4.2 --config
123+
net.git-fetch-with-cli=true --force
121124
- echo "uniffi-bindgen-java installed"
122125

123126
generate:
@@ -163,6 +166,8 @@ tasks:
163166
fi
164167
echo "Using cdylib for UniFFI bindgen: $LIB"
165168
ls -lah "$LIB"
169+
rm -rf generated/uniffi
170+
mkdir -p generated/uniffi
166171
# UniFFI crate_name in metadata is the lib crate name (slim_bindings), not the Cargo package name.
167172
uniffi-bindgen-java generate "$LIB" \
168173
--out-dir generated/uniffi \
@@ -285,7 +290,8 @@ tasks:
285290
- echo "Installing bindings to local Maven repository..."
286291
- '{{.MVNW}} install -DskipTests -q'
287292
- echo "Compiling examples..."
288-
- 'cd examples && {{.MVNW}} clean compile -q && {{.MVNW}} install -DskipTests -q'
293+
- 'cd examples && {{.MVNW}} clean compile -q && {{.MVNW}} install
294+
-DskipTests -q'
289295
- 'cd examples/slimrpc/simple && {{.MVNW}} clean compile -q'
290296
- echo "Build complete"
291297

@@ -338,15 +344,20 @@ tasks:
338344
- echo "Installed to local Maven repository"
339345

340346
publish:maven-central:
341-
desc: Publish Java bindings to Maven Central (requires MVN_TOKEN_NAME, MVN_TOKEN_PASSWORD, GPG_SIGNING_KEY, GPG_PASSPHRASE, PUBLISH_VERSION)
347+
desc: Publish Java bindings to Maven Central (requires MVN_TOKEN_NAME,
348+
MVN_TOKEN_PASSWORD, GPG_SIGNING_KEY, GPG_PASSPHRASE, PUBLISH_VERSION)
342349
deps:
343350
- build
344351
vars:
345352
PUBLISH_VERSION: '{{.PUBLISH_VERSION | default "1.0.0"}}'
346353
cmds:
347-
- '[ -n "$MVN_TOKEN_NAME" ] && [ -n "$MVN_TOKEN_PASSWORD" ] && [ -n "$GPG_SIGNING_KEY" ] && [ -n "$GPG_PASSPHRASE" ] || (echo "Error: MVN_TOKEN_NAME, MVN_TOKEN_PASSWORD, GPG_SIGNING_KEY, GPG_PASSPHRASE must be set"; exit 1)'
354+
- '[ -n "$MVN_TOKEN_NAME" ] && [ -n "$MVN_TOKEN_PASSWORD" ] && [ -n
355+
"$GPG_SIGNING_KEY" ] && [ -n "$GPG_PASSPHRASE" ] || (echo "Error:
356+
MVN_TOKEN_NAME, MVN_TOKEN_PASSWORD, GPG_SIGNING_KEY, GPG_PASSPHRASE must
357+
be set"; exit 1)'
348358
- echo "Publishing Java bindings to Maven Central (OSSRH Staging API)..."
349-
- '{{.MVNW}} deploy -Drevision="{{.PUBLISH_VERSION}}" -DskipTests -DaltDeploymentRepository=central::default::https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/'
359+
- '{{.MVNW}} deploy -Drevision="{{.PUBLISH_VERSION}}" -DskipTests
360+
-DaltDeploymentRepository=central::default::https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/'
350361
- echo "Transferring deployment to Central Portal..."
351362
- |
352363
AUTH=$(echo -n "$MVN_TOKEN_NAME:$MVN_TOKEN_PASSWORD" | base64 | tr -d '\n')
@@ -392,7 +403,8 @@ tasks:
392403
-Dversion={{.BINDINGS_VERSION}} \
393404
-Dpackaging=jar \
394405
-DgeneratePom=true
395-
- echo "Java bindings installed to ~/.m2/repository/io/agntcy/slim/slim-bindings-java/{{.BINDINGS_VERSION}}/"
406+
- echo "Java bindings installed to
407+
~/.m2/repository/io/agntcy/slim/slim-bindings-java/{{.BINDINGS_VERSION}}/"
396408

397409
examples:compile:
398410
desc: Compile Java examples
@@ -496,7 +508,8 @@ tasks:
496508
- build
497509
cmds:
498510
- echo "Starting SlimRPC server (instance={{.INSTANCE}})..."
499-
- 'cd examples/slimrpc/simple && {{.MVNW}} exec:java@server -Dexec.args="--instance {{.INSTANCE}}"'
511+
- 'cd examples/slimrpc/simple && {{.MVNW}} exec:java@server
512+
-Dexec.args="--instance {{.INSTANCE}}"'
500513
vars:
501514
INSTANCE: '{{.INSTANCE | default "server"}}'
502515
CLI_ARGS: '{{.CLI_ARGS | default ""}}'

data-plane/bindings/java/mvnw

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

data-plane/bindings/rust/src/client_config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,9 @@ pub struct ClientConfig {
307307

308308
/// Arbitrary user-provided metadata as JSON string
309309
pub metadata: Option<String>,
310+
311+
/// When true, reject inter-node messages without a valid header MAC (strict mode).
312+
pub require_header_mac: Option<bool>,
310313
}
311314

312315
impl From<ClientConfig> for CoreClientConfig {
@@ -340,6 +343,9 @@ impl From<ClientConfig> for CoreClientConfig {
340343
.metadata
341344
.and_then(|json| serde_json::from_str::<MetadataMap>(&json).ok()),
342345
link_id: core_defaults.link_id,
346+
require_header_mac: config
347+
.require_header_mac
348+
.unwrap_or(core_defaults.require_header_mac),
343349
}
344350
}
345351
}
@@ -362,6 +368,7 @@ impl From<CoreClientConfig> for ClientConfig {
362368
auth: Some(config.auth.into()),
363369
backoff: Some(config.backoff.into()),
364370
metadata: config.metadata.and_then(|m| serde_json::to_string(&m).ok()),
371+
require_header_mac: Some(config.require_header_mac),
365372
}
366373
}
367374
}
@@ -385,6 +392,7 @@ impl Default for ClientConfig {
385392
auth: None,
386393
backoff: None,
387394
metadata: None,
395+
require_header_mac: None,
388396
}
389397
}
390398
}
@@ -463,6 +471,7 @@ mod tests {
463471
auth: None,
464472
backoff: None,
465473
metadata: None,
474+
require_header_mac: None,
466475
};
467476

468477
assert_eq!(config.endpoint, "example.com:443");
@@ -584,6 +593,7 @@ mod tests {
584593
},
585594
}),
586595
metadata: Some(r#"{"client":"test"}"#.to_string()),
596+
require_header_mac: None,
587597
};
588598

589599
let core_config: CoreClientConfig = ffi_config.into();
@@ -645,6 +655,7 @@ mod tests {
645655
config: ExponentialBackoff::default(),
646656
}),
647657
metadata: None,
658+
require_header_mac: None,
648659
};
649660

650661
// FFI -> Core -> FFI using the new From implementation

data-plane/bindings/rust/src/server_config.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ pub struct ServerConfig {
103103

104104
/// Arbitrary user-provided metadata as JSON string
105105
pub metadata: Option<String>,
106+
107+
/// When true, reject inter-node messages without a valid header MAC (strict mode).
108+
pub require_header_mac: Option<bool>,
109+
110+
/// Timeout (in seconds) to wait for the link HMAC session to be installed.
111+
pub link_hmac_timeout_secs: Option<u64>,
112+
113+
/// Polling interval (in milliseconds) to wait between HMAC existence checks.
114+
pub link_hmac_poll_interval_ms: Option<u64>,
106115
}
107116

108117
impl Default for ServerConfig {
@@ -120,6 +129,9 @@ impl Default for ServerConfig {
120129
keepalive: None,
121130
auth: None,
122131
metadata: None,
132+
require_header_mac: None,
133+
link_hmac_timeout_secs: None,
134+
link_hmac_poll_interval_ms: None,
123135
}
124136
}
125137
}
@@ -154,6 +166,15 @@ impl From<ServerConfig> for CoreServerConfig {
154166
metadata: config
155167
.metadata
156168
.and_then(|json| serde_json::from_str::<MetadataMap>(&json).ok()),
169+
require_header_mac: config
170+
.require_header_mac
171+
.unwrap_or(core_defaults.require_header_mac),
172+
link_hmac_timeout_secs: config
173+
.link_hmac_timeout_secs
174+
.unwrap_or(core_defaults.link_hmac_timeout_secs),
175+
link_hmac_poll_interval_ms: config
176+
.link_hmac_poll_interval_ms
177+
.unwrap_or(core_defaults.link_hmac_poll_interval_ms),
157178
}
158179
}
159180
}
@@ -172,6 +193,9 @@ impl From<CoreServerConfig> for ServerConfig {
172193
keepalive: Some(config.keepalive.into()),
173194
auth: Some(config.auth.into()),
174195
metadata: config.metadata.and_then(|m| serde_json::to_string(&m).ok()),
196+
require_header_mac: Some(config.require_header_mac),
197+
link_hmac_timeout_secs: Some(config.link_hmac_timeout_secs),
198+
link_hmac_poll_interval_ms: Some(config.link_hmac_poll_interval_ms),
175199
}
176200
}
177201
}
@@ -258,6 +282,8 @@ mod tests {
258282
assert_eq!(config.keepalive, None);
259283
assert_eq!(config.auth, None);
260284
assert_eq!(config.metadata, None);
285+
assert_eq!(config.link_hmac_timeout_secs, None);
286+
assert_eq!(config.link_hmac_poll_interval_ms, None);
261287

262288
// Verify core defaults are applied when converting to CoreServerConfig
263289
let core: CoreServerConfig = config.into();
@@ -266,6 +292,8 @@ mod tests {
266292
assert_eq!(core.max_concurrent_streams, Some(100));
267293
assert_eq!(core.read_buffer_size, Some(1024 * 1024));
268294
assert_eq!(core.write_buffer_size, Some(1024 * 1024));
295+
assert_eq!(core.link_hmac_timeout_secs, 5);
296+
assert_eq!(core.link_hmac_poll_interval_ms, 5);
269297
assert_eq!(
270298
core.auth,
271299
slim_config::grpc::server::AuthenticationConfig::None
@@ -333,6 +361,9 @@ mod tests {
333361
keepalive: Some(KeepaliveServerParameters::default()),
334362
auth: Some(ServerAuthenticationConfig::None),
335363
metadata: Some(r#"{"key":"value"}"#.to_string()),
364+
require_header_mac: None,
365+
link_hmac_timeout_secs: None,
366+
link_hmac_poll_interval_ms: None,
336367
};
337368

338369
let core_config: CoreServerConfig = ffi_config.into();
@@ -386,6 +417,9 @@ mod tests {
386417
keepalive: Some(KeepaliveServerParameters::default()),
387418
auth: Some(ServerAuthenticationConfig::None),
388419
metadata: None,
420+
require_header_mac: None,
421+
link_hmac_timeout_secs: None,
422+
link_hmac_poll_interval_ms: None,
389423
};
390424

391425
// FFI -> Core -> FFI using the new From implementation

data-plane/config/jwt-auth-hmac/client-config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,4 @@ services:
3232
algorithm: "HS256"
3333
format: pem
3434
key:
35-
data: |
36-
"secret2"
35+
data: "secret"

0 commit comments

Comments
 (0)