Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`Ikev2SaInitCryptoErrorCode` to `Ikev2ChildSaKeyMaterialDiagnostic`.

### Added
- `opc-proto-ikev2::nwu`: bounded TS 24.502 configuration and QoS payloads,
opened Child-SA creation/modification, explicit NWu Child/IKE deletion and
caller-ordered AEAD selection. Conditional MOBIKE capability advertisement
is supported; authenticated mobility remains pending (Refs #786).
- **Bounded acknowledged configuration history — `opc-persist`:** the existing
consensus authority commits exact-head retention decisions with record and
encoded-byte limits, authenticated cursor boundaries, and protected rollback
Expand Down
66 changes: 66 additions & 0 deletions crates/opc-proto-ikev2/CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,69 @@ exchange, correlates both protocol responses, commits the GTP response for
exact replay, and then performs the corresponding Delete Bearer and IKEv2
Child-SA deletion flow. Admission, identifier allocation, key installation,
and dataplane programming remain explicit application responsibilities.
## NWu payload profile

The `nwu` module implements opened payload boundaries from
[TS 24.502 V18.8.0](https://www.etsi.org/deliver/etsi_ts/124500_124599/124502/18.08.00_60/ts_124502v180800p.pdf)
sections 7.3.2.2, 7.4–7.7 and 9.3.1, with generic framing, CP, TS, Notify and
Delete from [RFC 7296](https://www.rfc-editor.org/rfc/rfc7296.html).
These are synthetic constructed/receive claims; no peer capture, live network,
authentication, key custody or XFRM installation is claimed.

- Constructed and received: IPv4/IPv6/dual CFG_REQUEST and correlated
CFG_REPLY with NAS addresses and NAS_TCP_PORT; 5G_QOS_INFO including zero or
several QFIs, default indication, optional DSCP and additional parameters;
UP addresses and sender-inbound UP_SA_INFO; network-initiated CREATE_CHILD_SA
with complete SA/Nonce/optional KE/all-packet TS payloads; complete replacement
modification; both-initiator Child and IKE deletion.
- Additional QoS uses exact wire units. Characteristics use six octets for
non-GBR, eight for GBR, and ten for delay-critical GBR (including its averaging
window and maximum burst). Known fields have exact lengths and range checks;
rate unit codes above 25 are retained with the standard's 256 Pbps meaning.
Non-GBR characteristics with GBR-only rate/loss parameters are unsupported.
Unknown parameter identifiers and Notification Control are framed, counted,
then discarded as required by this release. Parameters remain optional; this
codec does not reserve or admit QoS resources.
- Protocol ID is ignored for zero-SPI notifies. UP_SA_INFO requires ESP and a
four-octet nonzero SPI; optional future extension bytes are ignored. Spare
QFI/flag bits and CP reserved fields are ignored on receive and cleared by
canonical encoding. N3GPP_BACKOFF_TIMER is explicitly unsupported here.
- Local admission policy rejects repeated known CP attributes/notifies/QFIs/QoS
parameters and repeated SPIs. `Limits` defaults to 65,535 opened bytes and
128 payloads/CP attributes/SPIs as applicable. Every ignored entry counts.
CP and Delete counts are checked before generic vector allocation. The QoS
one-octet Length and Delete generic 16-bit length bounds are checked before
construction. CP attributes outside this profile remain caller-owned.
- `CreateRequest` validates original IKE responder role for network creation,
one applicable UP address and complete selector ranges. Accepted responses
are matched to both IKE SPIs, message ID, role, proposal/transforms, KE and
all-packet selectors. `AeadPolicy` uses explicit caller suite order, with no
default list, fallback, separate integrity, crypto calls or backend effects.
Whole-IKE selection continues to use `Ikev2SaInitNegotiationPolicy`.
- Modification outcomes distinguish a matching empty acceptance, a matching
error rejection, and an ambiguous caller-declared timeout. The entire QoS
association replaces the old association. A roster must enforce the one
default SA per PDU session invariant; a payload cannot establish that fact.
- TS 24.502 Child Delete echoes the complete received SPI sequence for ordinary
and crossed requests. `validate_complete` compares it with a caller-supplied
authoritative roster. The generic RFC 7296 and TS 24.302 paired-direction
deletion profiles retain their existing behavior. An unanswered initiated
NWu Child Delete reports whole-IKE/all-children discard intent, not just the
original SPI subset. IKE Delete is Protocol ID 1 without SPIs and uses an
empty acknowledgement; its timeout has the same whole-IKE scope.
- Conditional MOBIKE_SUPPORTED advertisement and receiver-ignored capability
extension data are covered. Authenticated
UPDATE_SA_ADDRESSES, address advertisement processing, return-routability,
replay/source rejection, and NAT-T migration are **not implemented by this
change** and keep #786 open. The published additional-address vector is
consumed through generic Notify framing only. The opened lifecycle helpers
accept SK headers; SKF reassembly and peer authentication are separate.

`tests/nwu.rs` consumes all 15 published `nwu-ike` fixture files, using their
unchanged provenance/digest manifests under `opc-n3iwf-fixtures`. Full CP and
CREATE request literals, complete additional-QoS parameters, IPv6/dual-family
cases, duplicate/length/count mutations, response-correlation failures and
redaction checks are independently assembled synthetic test vectors. The `nwu`
fuzz target checks bounded decode and canonical re-encoding, using exact binary
copies of the published payload seeds. Round trips do not establish external
interoperability. Review and authenticated mobility evidence remain outstanding.
24 changes: 24 additions & 0 deletions crates/opc-proto-ikev2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ state, retransmission policy, cookie policy, Child SA lifecycle, XFRM/IPsec
programming, bearer admission or allocation policy, carrier acceptance
evidence, or a production ePDG control-plane stack.

## NWu payload profiles

`nwu` adds the TS 24.502 V18.8.0 configuration and opened Child-SA payload
profiles tracked by #786. It supports empty per-family CFG_REQUEST attributes,
correlated CFG_REPLY/NAS endpoints, complete QoS associations and additional
QoS parameters, one applicable UP address, network-initiated creation with
all-packet selectors, full replacement modification, and explicit NWu deletion.
`PendingModification` distinguishes acceptance, rejection, and ambiguous timeout;
`PendingChildDelete` requires the ordinary/crossed received-SPI echo.
`PendingIkeDelete` uses Protocol ID 1 with no SPIs and an empty response.

`AeadPolicy` tries the caller's ordered `AeadSuite` list before peer proposal
order and emits no integrity transform or fallback. Existing
`Ikev2SaInitNegotiationPolicy` supplies ordered whole-IKE-suite selection.
No default downstream suite list is included. Construction and receive helpers
return intent only; protection, key custody, replay admission, roster authority,
retransmission scheduling and backend operations belong to their own boundaries.

MOBIKE_SUPPORTED is conditional on the original IPv4 request and UE support.
Authenticated MOBIKE address updates, return-routability, source/replay checks
and NAT-T migration are still pending in #786. This payload change does not
close that issue or establish external interoperability. See
[CONFORMANCE.md](CONFORMANCE.md#nwu-payload-profile) for exact scope and evidence.

## API Shape

- `Message<'a>` and `OwnedMessage` provide borrowed and owned IKEv2 messages.
Expand Down
5 changes: 5 additions & 0 deletions crates/opc-proto-ikev2/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ test = false
name = "dedicated_bearer"
path = "fuzz_targets/dedicated_bearer.rs"
test = false

[[bin]]
name = "nwu"
path = "fuzz_targets/nwu.rs"
test = false
8 changes: 8 additions & 0 deletions crates/opc-proto-ikev2/fuzz/README-nwu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# NWu seeds

The 15 files named for `opc-n3iwf-fixtures/fixtures/nwu-ike/wire/*.hex`
are exact binary decodings of those independently published synthetic vectors.
Their JSON manifests retain source releases, clauses, provenance, outcomes and
SHA-256 digests. They prove payload shape only, not authentication or live mobility.
The fuzz target also exercises complete opened configuration/create/modify/delete
profiles. Mutated corpora belong in a temporary directory, not in this seed set.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
104 changes: 104 additions & 0 deletions crates/opc-proto-ikev2/fuzz/fuzz_targets/nwu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use opc_proto_ikev2::{
nwu::*, Header, HeaderFlags, PayloadChain, PayloadType, EXCHANGE_TYPE_CREATE_CHILD_SA,
EXCHANGE_TYPE_INFORMATIONAL,
};

fuzz_target!(|data: &[u8]| {
if data.len() > 8192 {
return;
}
let limits = Limits {
bytes: 8192,
entries: 128,
};
for body in [Some(data), data.get(4..)].into_iter().flatten() {
if let Ok(Some(notify)) = Notify::decode_body(body) {
let canonical = notify.encode_body().expect("accepted Notify encodes");
let again = Notify::decode_body(&canonical)
.expect("canonical Notify parses")
.expect("known type");
assert_eq!(
again.encode_body().expect("canonical Notify encodes"),
canonical
);
}
if let Ok(qos) = QosInfo::decode(body) {
let canonical = qos.encode().expect("accepted QoS encodes");
assert_eq!(
QosInfo::decode(&canonical)
.expect("canonical QoS parses")
.encode()
.expect("canonical QoS encodes"),
canonical
);
}
let _ = AdditionalQos::new(body);
}
let header = Header::new(
1,
2,
PayloadType::Encrypted,
EXCHANGE_TYPE_INFORMATIONAL,
HeaderFlags::from_bits(false, false, false),
7,
);
for first in [
PayloadType::Notify,
PayloadType::Configuration,
PayloadType::Delete,
PayloadType::SecurityAssociation,
PayloadType::Unknown(250),
] {
for p in PayloadChain::new(first, data).iter() {
if let Ok(p) = p {
if p.payload_type == PayloadType::Notify {
let _ = Notify::decode_body(p.body);
}
}
}
let _ = ConfigurationRequest::decode(first, data, limits);
for families in [
AddressFamilies::Ipv4,
AddressFamilies::Ipv6,
AddressFamilies::Dual,
] {
for mobike_supported in [false, true] {
let request = ConfigurationRequest {
families,
mobike_supported,
};
if let Ok(reply) = ConfigurationReply::decode(request, first, data, limits) {
let (first, bytes) = encode_payloads(&reply.payloads().expect("reply builds"))
.expect("reply encodes");
assert_eq!(
ConfigurationReply::decode(request, first, &bytes, limits)
.expect("canonical reply parses"),
reply
);
}
}
let mut create = header.clone();
create.exchange_type = EXCHANGE_TYPE_CREATE_CHILD_SA;
let _ = CreateRequest::decode(&create, first, data, families, limits);
}
if let Ok((_, value)) = Modification::decode(&header, first, data, limits) {
let (first, canonical) =
encode_payloads(&value.payloads().expect("modification builds"))
.expect("modification encodes");
assert!(Modification::decode(&header, first, &canonical, limits).is_ok());
}
if let Ok((_, delete)) = ChildDelete::decode(&header, Peer::Network, first, data, limits) {
let (first, canonical) = encode_payloads(&delete.payloads().expect("delete builds"))
.expect("delete encodes");
assert_eq!(
ChildDelete::decode(&header, Peer::Network, first, &canonical, limits)
.expect("canonical delete parses")
.1,
delete
);
}
let _ = PendingIkeDelete::decode_request(&header, Peer::Network, first, data, limits);
}
});
2 changes: 1 addition & 1 deletion crates/opc-proto-ikev2/src/dedicated_bearer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ mod exchange;
mod qos;

pub(crate) use exchange::{
traffic_selector_payload_is_narrowed, validate_sa_build, validate_sa_view,
traffic_selector_payload_is_narrowed, validate_ke_view, validate_sa_build, validate_sa_view,
validate_selected_key_exchange, validate_selected_proposal, Ikev2SelectedKeyExchangeError,
};

Expand Down
2 changes: 1 addition & 1 deletion crates/opc-proto-ikev2/src/dedicated_bearer/exchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,7 @@ fn validate_proposal_spi(
})
}

fn validate_ke_view(
pub(crate) fn validate_ke_view(
sa: &Ikev2SaPayload<'_>,
key_exchange: Option<&Ikev2KeyExchangePayload<'_>>,
) -> Result<(), Ikev2DedicatedBearerExchangeError> {
Expand Down
5 changes: 5 additions & 0 deletions crates/opc-proto-ikev2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
//! Child SA installation, XFRM
//! programming, or any product-specific 3GPP ePDG policy.
//!
//! The separate [`nwu`] module adds bounded TS 24.502 configuration, QoS,
//! opened creation/modification/deletion and caller-ordered AEAD selection.
//! Authenticated MOBIKE address updates remain outside that payload profile.
//!
//! Network decoders follow RFC 7296 receiver rules through
//! [`Ikev2ValidationProfile::NetworkReceive`]: sender-zero reserved fields and
//! higher minor versions are ignored without weakening structural, critical
Expand Down Expand Up @@ -85,6 +89,7 @@ pub mod message;
pub mod nat_detection;
pub mod nat_traversal;
pub mod notify;
pub mod nwu;
pub mod payload;
pub mod pcscf_restoration;
pub mod pre_admission;
Expand Down
Loading
Loading