@@ -26,10 +26,12 @@ class NoiseExtensions:
2626
2727 This class provides support for:
2828 - WebTransport certificate hashes for WebTransport support
29- - Early data payload for 0-RTT support
29+ - Stream multiplexers supported by this peer (spec compliant)
30+ - Early data payload for 0-RTT support (Python extension)
3031 """
3132
3233 webtransport_certhashes : list [bytes ] = field (default_factory = list )
34+ stream_muxers : list [str ] = field (default_factory = list )
3335 early_data : bytes | None = None
3436
3537 def to_protobuf (self ) -> noise_pb .NoiseExtensions :
@@ -42,6 +44,7 @@ def to_protobuf(self) -> noise_pb.NoiseExtensions:
4244 """
4345 ext = noise_pb .NoiseExtensions ()
4446 ext .webtransport_certhashes .extend (self .webtransport_certhashes )
47+ ext .stream_muxers .extend (self .stream_muxers ) # type: ignore[attr-defined]
4548 if self .early_data is not None :
4649 ext .early_data = self .early_data
4750 return ext
@@ -63,6 +66,7 @@ def from_protobuf(cls, pb_ext: noise_pb.NoiseExtensions) -> "NoiseExtensions":
6366 early_data = pb_ext .early_data
6467 return cls (
6568 webtransport_certhashes = list (pb_ext .webtransport_certhashes ),
69+ stream_muxers = list (pb_ext .stream_muxers ), # type: ignore[attr-defined]
6670 early_data = early_data ,
6771 )
6872
@@ -74,7 +78,11 @@ def is_empty(self) -> bool:
7478 bool: True if no extensions data is present
7579
7680 """
77- return not self .webtransport_certhashes and self .early_data is None
81+ return (
82+ not self .webtransport_certhashes
83+ and not self .stream_muxers
84+ and self .early_data is None
85+ )
7886
7987 def has_webtransport_certhashes (self ) -> bool :
8088 """
@@ -86,6 +94,16 @@ def has_webtransport_certhashes(self) -> bool:
8694 """
8795 return bool (self .webtransport_certhashes )
8896
97+ def has_stream_muxers (self ) -> bool :
98+ """
99+ Check if stream multiplexers are present.
100+
101+ Returns:
102+ bool: True if stream multiplexers are present
103+
104+ """
105+ return bool (self .stream_muxers )
106+
89107 def has_early_data (self ) -> bool :
90108 """
91109 Check if early data is present.
@@ -104,13 +122,11 @@ class NoiseHandshakePayload:
104122
105123 This class represents the payload sent during Noise handshake and provides:
106124 - Peer identity verification through public key and signature
107- - Optional early data for 0-RTT support
108- - Optional extensions for advanced features like WebTransport
125+ - Optional extensions for advanced features like WebTransport and stream muxers
109126 """
110127
111128 id_pubkey : PublicKey
112129 id_sig : bytes
113- early_data : bytes | None = None
114130 extensions : NoiseExtensions | None = None
115131
116132 def serialize (self ) -> bytes :
@@ -131,18 +147,8 @@ def serialize(self) -> bytes:
131147 identity_key = self .id_pubkey .serialize (), identity_sig = self .id_sig
132148 )
133149
134- # Handle early data: prefer extensions over legacy data field
135- if self .extensions is not None and self .extensions .early_data is not None :
136- # Early data is in extensions
137- msg .extensions .CopyFrom (self .extensions .to_protobuf ())
138- elif self .early_data is not None :
139- # Legacy early data in data field (for backward compatibility)
140- msg .data = self .early_data
141- if self .extensions is not None :
142- # Still include extensions even if early data is in legacy field
143- msg .extensions .CopyFrom (self .extensions .to_protobuf ())
144- elif self .extensions is not None :
145- # Extensions without early data
150+ # Include extensions if present
151+ if self .extensions is not None :
146152 msg .extensions .CopyFrom (self .extensions .to_protobuf ())
147153
148154 return msg .SerializeToString ()
@@ -174,17 +180,8 @@ def deserialize(cls, protobuf_bytes: bytes) -> "NoiseHandshakePayload":
174180 raise ValueError ("Invalid handshake payload: missing required fields" )
175181
176182 extensions = None
177- early_data = None
178-
179183 if msg .HasField ("extensions" ):
180184 extensions = NoiseExtensions .from_protobuf (msg .extensions )
181- # Early data from extensions takes precedence
182- if extensions .early_data is not None :
183- early_data = extensions .early_data
184-
185- # Fall back to legacy data field if no early data in extensions
186- if early_data is None :
187- early_data = msg .data if msg .data != b"" else None
188185
189186 try :
190187 id_pubkey = deserialize_public_key (msg .identity_key )
@@ -194,7 +191,6 @@ def deserialize(cls, protobuf_bytes: bytes) -> "NoiseHandshakePayload":
194191 return cls (
195192 id_pubkey = id_pubkey ,
196193 id_sig = msg .identity_sig ,
197- early_data = early_data ,
198194 extensions = extensions ,
199195 )
200196
@@ -210,27 +206,25 @@ def has_extensions(self) -> bool:
210206
211207 def has_early_data (self ) -> bool :
212208 """
213- Check if early data is present (either in extensions or legacy field) .
209+ Check if early data is present in extensions.
214210
215211 Returns:
216212 bool: True if early data is present
217213
218214 """
219- if self .extensions is not None and self .extensions .has_early_data ():
220- return True
221- return self .early_data is not None
215+ return self .extensions is not None and self .extensions .has_early_data ()
222216
223217 def get_early_data (self ) -> bytes | None :
224218 """
225- Get early data, preferring extensions over legacy field .
219+ Get early data from extensions.
226220
227221 Returns:
228222 bytes | None: The early data if present
229223
230224 """
231225 if self .extensions is not None and self .extensions .has_early_data ():
232226 return self .extensions .early_data
233- return self . early_data
227+ return None
234228
235229
236230def make_data_to_be_signed (noise_static_pubkey : PublicKey ) -> bytes :
0 commit comments