Skip to content

Commit 54ea5b7

Browse files
markpmartonmicpapalhackeramitkumaramitami2msardara
authored
feat: e2e header integrity validation (agntcy#1677)
closes agntcy#1556 # Description In some cases checking the header integrity between nodes can't guarantee the header was not tempered with (e.g. in single-node deployments. To ensure the the inmutability of the routing header fields, we need to implement an integrity check also on the client side. # Changes - replaced `enable_mls` flag with more complex `MlsSettings` struct (the field is optional: `None` is equal to `enable_mls = false` and `Some()` is equal to `enable_mls = true`. - `MlsSettings` contains the percentage of messages to be validated (`header_integrity_validation_percent`). - bench tests added testing the validation of 0%, 50% and 100% of the messages - AAD generation is added to MLS message encryption `encrypt_output` - header integrity check added to `decrypt_message` (only the set percentage of headers will be checked). - dynamic port allocation added to servers running parallel in tests. ## 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: Michele Papalini <micpapal@cisco.com> Signed-off-by: amitami2 <amitami2@cisco.com> Signed-off-by: Mauro Sardara <msardara@cisco.com> Signed-off-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com> Signed-off-by: Janos Sarusi-Kis <janossk@cisco.com> Signed-off-by: Mark Marton <30534230+markpmarton@users.noreply.github.com> Co-authored-by: Michele Papalini <49271675+micpapal@users.noreply.github.com> Co-authored-by: Amit kumar <amit9116260192@gmail.com> Co-authored-by: amitami2 <amitami2@cisco.com> Co-authored-by: Mauro Sardara <msardara@cisco.com> Co-authored-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com> Co-authored-by: János Sarusi-Kis <janossk@cisco.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 015403f commit 54ea5b7

81 files changed

Lines changed: 1571 additions & 597 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

buf.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,17 @@ deps:
1313
modules:
1414
- path: proto
1515
name: buf.build/agntcy/slim
16+
breaking:
17+
use:
18+
- FILE
19+
ignore_only:
20+
FIELD_NO_DELETE:
21+
- proto/data-plane/v1/data_plane.proto
22+
FIELD_SAME_NAME:
23+
- proto/data-plane/v1/data_plane.proto
24+
FIELD_SAME_TYPE:
25+
- proto/data-plane/v1/data_plane.proto
26+
FIELD_SAME_CARDINALITY:
27+
- proto/data-plane/v1/data_plane.proto
28+
FIELD_SAME_JSON_NAME:
29+
- proto/data-plane/v1/data_plane.proto

data-plane/Cargo.lock

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

data-plane/Cargo.toml

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ agntcy-slim-testing = { path = "testing" }
5656
agntcy-slim-tracing = { path = "core/tracing", version = "0.3.13" }
5757
agntcy-slim-version = { path = "core/version", version = "2.0.0-alpha.0" }
5858
agntcy-slimctl = { path = "slimctl", version = "2.0.0-alpha.0" }
59-
6059
anyhow = "1.0.98"
6160

62-
arc-swap = "1"
61+
arc-swap = "1.9.1"
6362

6463
async-stream = "0.3"
6564
async-trait = "0.1.88"
@@ -71,13 +70,20 @@ chrono = "0.4"
7170
clap = { version = "4.5.23", features = ["derive", "env"] }
7271

7372
criterion = { version = "0.5", features = ["html_reports"] }
74-
diesel = { version = "2", features = ["sqlite"] }
75-
diesel-async = { version = "0.9", features = ["sqlite", "deadpool", "migrations"] }
73+
diesel = { version = "2.3.10", features = ["sqlite"] }
74+
diesel-async = { version = "0.9", features = [
75+
"sqlite",
76+
"deadpool",
77+
"migrations",
78+
] }
7679
diesel_migrations = { version = "2" }
7780
display-error-chain = { version = "0.2" }
7881
drain = { version = "0.2", features = ["retain"] }
7982
duration-string = { version = "0.5.3", features = ["serde"] }
80-
fastwebsockets = { version = "0.10.0", features = ["upgrade", "unstable-split"] }
83+
fastwebsockets = { version = "0.10.0", features = [
84+
"upgrade",
85+
"unstable-split",
86+
] }
8187
futures = "0.3.31"
8288
futures-timer = "3.0.3"
8389
h2 = "0.4.7"
@@ -86,7 +92,10 @@ hex = "0.4.3"
8692
http = "1.2.0"
8793
http-body-util = "0.1.3"
8894
hyper = { version = "1.8.1", features = ["http1", "server", "client"] }
89-
hyper-rustls = { version = "0.27", features = ["http2", "aws-lc-rs"], default-features = false }
95+
hyper-rustls = { version = "0.27", features = [
96+
"http2",
97+
"aws-lc-rs",
98+
], default-features = false }
9099
hyper-util = "0.1.10"
91100
indexmap = "2"
92101
indicatif = "0.17.11"
@@ -103,10 +112,18 @@ num_cpus = "1.16"
103112
oauth2 = { version = "5", default-features = false, features = ["reqwest"] }
104113
once_cell = "1.21.0"
105114
opentelemetry = { version = "0.31.0", features = ["trace", "metrics"] }
106-
opentelemetry-otlp = { version = "0.31.0", features = ["metrics", "grpc-tonic"] }
107-
opentelemetry-semantic-conventions = { version = "0.31.0", features = ["semconv_experimental"] }
115+
opentelemetry-otlp = { version = "0.31.0", features = [
116+
"metrics",
117+
"grpc-tonic",
118+
] }
119+
opentelemetry-semantic-conventions = { version = "0.31.0", features = [
120+
"semconv_experimental",
121+
] }
108122
opentelemetry-stdout = "0.31.0"
109-
opentelemetry_sdk = { version = "0.31.0", default-features = false, features = ["trace", "rt-tokio"] }
123+
opentelemetry_sdk = { version = "0.31.0", default-features = false, features = [
124+
"trace",
125+
"rt-tokio",
126+
] }
110127
parking_lot = "0.12.3"
111128
percent-encoding = "2.3"
112129
pin-project = "1.1.10"
@@ -121,7 +138,14 @@ pyo3-stub-gen = "0.14"
121138
rand = "0.9.0"
122139
regex = "1.11.1"
123140

124-
reqwest = { version = "0.12", features = ["json", "rustls-tls-no-provider", "charset", "http2", "system-proxy", "blocking"], default-features = false }
141+
reqwest = { version = "0.12", features = [
142+
"json",
143+
"rustls-tls-no-provider",
144+
"charset",
145+
"http2",
146+
"system-proxy",
147+
"blocking",
148+
], default-features = false }
125149
rlimit = "0.11.0"
126150
rustls = { version = "0.23.31" }
127151
rustls-native-certs = "0.8"

data-plane/bindings/dotnet/Slim.Examples.Group/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ static async Task RunModerator(SlimApp app, ulong connId, string remote,
112112
var config = new SlimSessionConfig
113113
{
114114
SessionType = SlimSessionType.Group,
115-
EnableMls = enableMls,
115+
MlsSettings = enableMls ? new SlimMlsSettings() : null,
116116
MaxRetries = 5,
117117
RetryInterval = TimeSpan.FromSeconds(5),
118118
Metadata = new Dictionary<string, string>()

data-plane/bindings/dotnet/Slim.Examples.PointToPoint/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ static async Task RunSender(SlimApp app, ulong connId, string remote, string mes
105105
var config = new SlimSessionConfig
106106
{
107107
SessionType = SlimSessionType.PointToPoint,
108-
EnableMls = enableMls
108+
MlsSettings = enableMls ? new SlimMlsSettings() : null
109109
};
110110

111111
Console.WriteLine($"[{instance}] Creating session to {remote}...");

data-plane/bindings/dotnet/Slim.Tests/SlimTests.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,25 +121,23 @@ public void SessionConfig_DefaultValues()
121121
{
122122
var config = new SlimSessionConfig
123123
{
124-
SessionType = SlimSessionType.PointToPoint,
125-
EnableMls = false
124+
SessionType = SlimSessionType.PointToPoint
126125
};
127126

128127
Assert.Equal(SlimSessionType.PointToPoint, config.SessionType);
129-
Assert.False(config.EnableMls);
128+
Assert.Null(config.MlsSettings);
130129
}
131130

132131
[Fact]
133132
public void SessionConfig_GroupType()
134133
{
135134
var config = new SlimSessionConfig
136135
{
137-
SessionType = SlimSessionType.Group,
138-
EnableMls = false
136+
SessionType = SlimSessionType.Group
139137
};
140138

141139
Assert.Equal(SlimSessionType.Group, config.SessionType);
142-
Assert.False(config.EnableMls);
140+
Assert.Null(config.MlsSettings);
143141
}
144142

145143
[Fact]
@@ -148,10 +146,10 @@ public void SessionConfig_WithMls()
148146
var config = new SlimSessionConfig
149147
{
150148
SessionType = SlimSessionType.PointToPoint,
151-
EnableMls = true
149+
MlsSettings = new SlimMlsSettings()
152150
};
153151

154-
Assert.True(config.EnableMls);
152+
Assert.NotNull(config.MlsSettings);
155153
}
156154

157155
[Fact]

data-plane/bindings/dotnet/Slim/Slim.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,18 @@ public enum SlimSessionType
212212
Group
213213
}
214214

215+
/// <summary>
216+
/// MLS-specific settings for a session.
217+
/// </summary>
218+
public sealed class SlimMlsSettings
219+
{
220+
/// <summary>0 = disable header-integrity checks; 1–100 = percent of messages to verify after decrypt.</summary>
221+
public uint HeaderIntegrityValidationPercent { get; init; } = 100;
222+
223+
internal Internal.MlsSettings ToInternal() =>
224+
new(HeaderIntegrityValidationPercent);
225+
}
226+
215227
/// <summary>
216228
/// Configuration for creating a SLIM session.
217229
/// </summary>
@@ -220,9 +232,6 @@ public sealed class SlimSessionConfig
220232
/// <summary>Session type (PointToPoint or Group).</summary>
221233
public SlimSessionType SessionType { get; init; } = SlimSessionType.PointToPoint;
222234

223-
/// <summary>Whether to enable MLS encryption.</summary>
224-
public bool EnableMls { get; init; }
225-
226235
/// <summary>Maximum retry attempts for session establishment.</summary>
227236
public uint MaxRetries { get; init; } = 5;
228237

@@ -232,16 +241,19 @@ public sealed class SlimSessionConfig
232241
/// <summary>Optional metadata key-value pairs.</summary>
233242
public Dictionary<string, string>? Metadata { get; init; }
234243

244+
/// <summary>MLS options (None disables MLS).</summary>
245+
public SlimMlsSettings? MlsSettings { get; init; }
246+
235247
internal Internal.SessionConfig ToInternal()
236248
{
237249
return new Internal.SessionConfig(
238250
SessionType: SessionType == SlimSessionType.PointToPoint
239251
? Internal.SessionType.PointToPoint
240252
: Internal.SessionType.Group,
241-
EnableMls: EnableMls,
242253
MaxRetries: MaxRetries,
243254
Interval: RetryInterval,
244-
Metadata: Metadata ?? new Dictionary<string, string>()
255+
Metadata: Metadata ?? new Dictionary<string, string>(),
256+
MlsSettings: MlsSettings?.ToInternal()
245257
);
246258
}
247259
}

data-plane/bindings/go/examples/group/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,17 @@ func runModerator(app *slim.App, connID uint64, remote string, invites []string,
8282

8383
// Create multicast session
8484
interval := time.Second * 5
85+
var mlsSettings *slim.MlsSettings
86+
if enableMLS {
87+
mlsSettings = &slim.MlsSettings{HeaderIntegrityValidationPercent: 100}
88+
}
89+
8590
config := slim.SessionConfig{
8691
SessionType: slim.SessionTypeGroup,
87-
EnableMls: enableMLS,
8892
MaxRetries: &[]uint32{5}[0], // 5 retries
8993
Interval: &interval,
9094
Metadata: make(map[string]string),
95+
MlsSettings: mlsSettings,
9196
}
9297

9398
session, err := app.CreateSessionAndWaitAsync(config, channelName)

data-plane/bindings/go/examples/point_to_point/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,14 @@ func runSender(app *slim.App, connID uint64, remote, message string, iterations
6464
}
6565
fmt.Printf("[%s] 📍 Route set to %s via connection %d\n", instance, remote, connID)
6666

67+
var mlsSettings *slim.MlsSettings
68+
if enableMLS {
69+
mlsSettings = &slim.MlsSettings{HeaderIntegrityValidationPercent: 100}
70+
}
71+
6772
config := slim.SessionConfig{
6873
SessionType: slim.SessionTypePointToPoint,
69-
EnableMls: enableMLS,
74+
MlsSettings: mlsSettings,
7075
}
7176

7277
fmt.Printf("[%s] 🔍 Creating session to %s...\n", instance, remote)

data-plane/bindings/go/tests/integration_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ func TestGroupSession(t *testing.T) {
399399

400400
sessionConfig := slim.SessionConfig{
401401
SessionType: slim.SessionTypeGroup,
402-
EnableMls: false,
402+
MlsSettings: nil,
403403
}
404404

405405
destination := slim.NewName("org", "group", "v1")
@@ -420,7 +420,7 @@ func TestSessionInviteRemove(t *testing.T) {
420420

421421
sessionConfig := slim.SessionConfig{
422422
SessionType: slim.SessionTypeGroup,
423-
EnableMls: false,
423+
MlsSettings: nil,
424424
}
425425

426426
destination := slim.NewName("org", "group", "v1")
@@ -515,7 +515,7 @@ func TestSessionCreationAutoWait(t *testing.T) {
515515

516516
sessionConfig := slim.SessionConfig{
517517
SessionType: slim.SessionTypePointToPoint,
518-
EnableMls: false,
518+
MlsSettings: nil,
519519
}
520520

521521
// CreateSession should auto-wait for session establishment
@@ -540,7 +540,7 @@ func TestInviteAutoWait(t *testing.T) {
540540

541541
sessionConfig := slim.SessionConfig{
542542
SessionType: slim.SessionTypeGroup,
543-
EnableMls: false,
543+
MlsSettings: nil,
544544
}
545545

546546
destination := slim.NewName("org", "group", "v1")
@@ -574,7 +574,7 @@ func TestRemoveAutoWait(t *testing.T) {
574574

575575
sessionConfig := slim.SessionConfig{
576576
SessionType: slim.SessionTypeGroup,
577-
EnableMls: false,
577+
MlsSettings: nil,
578578
}
579579

580580
destination := slim.NewName("org", "group", "v1")
@@ -726,7 +726,7 @@ func BenchmarkPublishFireAndForget(b *testing.B) {
726726
defer app.Destroy()
727727

728728
session, err := app.CreateSession(
729-
slim.SessionConfig{SessionType: slim.SessionTypePointToPoint, EnableMls: false},
729+
slim.SessionConfig{SessionType: slim.SessionTypePointToPoint, MlsSettings: nil},
730730
slim.NewName("org", "receiver", "v1"),
731731
)
732732
if err != nil {
@@ -754,7 +754,7 @@ func BenchmarkPublishWithCompletion(b *testing.B) {
754754
defer app.Destroy()
755755

756756
session, err := app.CreateSession(
757-
slim.SessionConfig{SessionType: slim.SessionTypePointToPoint, EnableMls: false},
757+
slim.SessionConfig{SessionType: slim.SessionTypePointToPoint, MlsSettings: nil},
758758
slim.NewName("org", "receiver", "v1"),
759759
)
760760
if err != nil {
@@ -785,7 +785,7 @@ func BenchmarkPublishWithCompletionAndWait(b *testing.B) {
785785
defer app.Destroy()
786786

787787
session, err := app.CreateSession(
788-
slim.SessionConfig{SessionType: slim.SessionTypePointToPoint, EnableMls: false},
788+
slim.SessionConfig{SessionType: slim.SessionTypePointToPoint, MlsSettings: nil},
789789
slim.NewName("org", "receiver", "v1"),
790790
)
791791
if err != nil {

0 commit comments

Comments
 (0)