@@ -139,6 +139,10 @@ def __init__(self, fields):
139139 # For compatibility with existing logic that expects a single string:
140140 self .encryption = self .primary_encryption # Overwrite with primary for now
141141 self .authentication = self .primary_authentication # Overwrite with primary for now
142+
143+ # WPA3 information (will be populated by scanner)
144+ self .wpa3_info = None
145+
142146 self .validate ()
143147
144148 def __eq__ (self , other ):
@@ -169,6 +173,36 @@ def transfer_info(self, other):
169173 if hasattr (self , 'primary_authentication' ):
170174 other .primary_authentication = self .primary_authentication
171175 other .full_authentication_string = self .full_authentication_string
176+ if hasattr (self , 'wpa3_info' ):
177+ other .wpa3_info = self .wpa3_info
178+
179+ @property
180+ def is_wpa3 (self ):
181+ """Check if target supports WPA3."""
182+ if self .wpa3_info is None :
183+ return False
184+ return self .wpa3_info .has_wpa3
185+
186+ @property
187+ def is_transition (self ):
188+ """Check if target is in WPA3 transition mode (supports both WPA2 and WPA3)."""
189+ if self .wpa3_info is None :
190+ return False
191+ return self .wpa3_info .is_transition
192+
193+ @property
194+ def pmf_status (self ):
195+ """Get PMF (Protected Management Frames) status."""
196+ if self .wpa3_info is None :
197+ return 'disabled'
198+ return self .wpa3_info .pmf_status
199+
200+ @property
201+ def is_dragonblood_vulnerable (self ):
202+ """Check if target is vulnerable to Dragonblood attacks."""
203+ if self .wpa3_info is None :
204+ return False
205+ return self .wpa3_info .dragonblood_vulnerable
172206
173207 def validate (self ):
174208 """ Checks that the target is valid. """
@@ -232,37 +266,53 @@ def to_str(self, show_bssid=False, show_manufacturer=False):
232266 channel = Color .s (f'{ channel_color } { str (self .channel ).rjust (3 )} ' )
233267
234268 # Use primary_encryption and primary_authentication for display
235- display_encryption = self .primary_encryption .rjust (4 ) # Adjusted rjust for WPA3
236- auth_suffix = ''
237- if self .primary_encryption == 'WPA3' :
238- display_encryption = Color .s ('{P}%s' % display_encryption ) # Purple for WPA3
239- if self .primary_authentication == 'SAE' :
240- auth_suffix = Color .s ('{P}-S' ) # Purple for SAE
241- elif self .primary_authentication == 'MGT' :
242- auth_suffix = Color .s ('{R}-E' ) # Red for Enterprise
243- elif self .primary_encryption == 'WPA2' :
244- display_encryption = Color .s ('{O}%s' % display_encryption ) # Orange for WPA2
245- if self .primary_authentication == 'PSK' :
246- auth_suffix = Color .s ('{O}-P' )
247- elif self .primary_authentication == 'MGT' :
248- auth_suffix = Color .s ('{R}-E' )
249- elif self .primary_encryption == 'WPA' :
250- display_encryption = Color .s ('{O}%s' % display_encryption ) # Orange for WPA
251- if self .primary_authentication == 'PSK' :
252- auth_suffix = Color .s ('{O}-P' )
253- elif self .primary_authentication == 'MGT' :
254- auth_suffix = Color .s ('{R}-E' )
255- elif self .primary_encryption == 'WEP' :
256- display_encryption = Color .s ('{G}%s' % display_encryption ) # Green for WEP
257- elif self .primary_encryption == 'OWE' :
258- display_encryption = Color .s ('{B}%s' % display_encryption ) # Blue for OWE
269+ # Check for WPA3 transition mode
270+ if self .is_transition :
271+ # Show "W23" for transition mode (WPA2/WPA3)
272+ display_encryption = Color .s ('{P}W23' ) # Purple for WPA3 transition
273+ auth_suffix = ''
274+ # Add PMF indicator for transition mode
275+ if self .pmf_status == 'required' :
276+ auth_suffix = Color .s ('{P}+' ) # PMF required
277+ elif self .pmf_status == 'optional' :
278+ auth_suffix = Color .s ('{O}~' ) # PMF optional
259279 else :
260- display_encryption = Color .s ('{W}%s' % display_encryption ) # White for others
280+ display_encryption = self .primary_encryption .rjust (4 ) # Adjusted rjust for WPA3
281+ auth_suffix = ''
282+ if self .primary_encryption == 'WPA3' :
283+ display_encryption = Color .s ('{P}%s' % display_encryption ) # Purple for WPA3
284+ if self .primary_authentication == 'SAE' :
285+ auth_suffix = Color .s ('{P}-S' ) # Purple for SAE
286+ elif self .primary_authentication == 'MGT' :
287+ auth_suffix = Color .s ('{R}-E' ) # Red for Enterprise
288+ # Add PMF indicator for WPA3-only
289+ if self .pmf_status == 'required' :
290+ auth_suffix += Color .s ('{P}+' ) # PMF required
291+ elif self .primary_encryption == 'WPA2' :
292+ display_encryption = Color .s ('{O}%s' % display_encryption ) # Orange for WPA2
293+ if self .primary_authentication == 'PSK' :
294+ auth_suffix = Color .s ('{O}-P' )
295+ elif self .primary_authentication == 'MGT' :
296+ auth_suffix = Color .s ('{R}-E' )
297+ elif self .primary_encryption == 'WPA' :
298+ display_encryption = Color .s ('{O}%s' % display_encryption ) # Orange for WPA
299+ if self .primary_authentication == 'PSK' :
300+ auth_suffix = Color .s ('{O}-P' )
301+ elif self .primary_authentication == 'MGT' :
302+ auth_suffix = Color .s ('{R}-E' )
303+ elif self .primary_encryption == 'WEP' :
304+ display_encryption = Color .s ('{G}%s' % display_encryption ) # Green for WEP
305+ elif self .primary_encryption == 'OWE' :
306+ display_encryption = Color .s ('{B}%s' % display_encryption ) # Blue for OWE
307+ else :
308+ display_encryption = Color .s ('{W}%s' % display_encryption ) # White for others
261309
262310 # Calculate padding for ENCR column based on its content length
263- # Max length of ENCR (e.g. WPA2-P) is 6. OPN is 3.
311+ # Max length of ENCR (e.g. WPA2-P or W23+) is now variable
264312 # Pad with spaces to ensure alignment
265- encryption_padding = " " * (5 - len (self .primary_encryption + self .primary_authentication )) # Max length of WPA2-P is 6 (WPA2 + -P)
313+ base_len = 3 if self .is_transition else len (self .primary_encryption )
314+ suffix_len = len (auth_suffix .replace (Color .s ('{P}' ), '' ).replace (Color .s ('{O}' ), '' ).replace (Color .s ('{R}' ), '' ).replace (Color .s ('{W}' ), '' ))
315+ encryption_padding = " " * max (0 , 7 - base_len - suffix_len )
266316 encryption_display_string = f"{ display_encryption } { auth_suffix } { encryption_padding } "
267317
268318 power = f'{ str (self .power ).rjust (3 )} db'
0 commit comments