user4215456
user4215456

Reputation: 79

arduino wifi passphrase with \0

I'm trying to connect Arduino to a WiFi network.

const char* ssid = "ssid";
char* password = "some_hex_chars";
.
.
.
void setup(void){
  WiFi.begin(ssid, password);
.
.
.

The problem is, a have code 0x00 somewhere in passphrase. Since begin() method takes argument char, which is null-terminated string, password is truncated. Is there a way to work around this? Where can I find source of begin() method to modify it?

Edit: WRONG. It's not passphrase, it's PSK with 64 hexadecimal characters, and it doesn't want to connect.

Update: I solved the problem. I wasn't the PSK problem, but WiFi router advanced settings. When

54g™ Mode is set to 54g Performance, it doesn't want to connect. After I changed it to 54g Auto, it works fine.

Upvotes: 0

Views: 934

Answers (1)

jbm
jbm

Reputation: 3163

I know nothing about Arduino - but more about 802.11 aka Wi-Fi. An so:

Don't do that. If you have a 0x00 in the middle your passphrase, it is technically invalid, as per the IEEE 802.11 standards.

And so will, I presume, be interpreted as the next to last character of your passphrase ( = the passphrase is everything before this 0x00) by your 802.11 stack if correctly implemented, and you're looking for undefined behavior - at best, interoperability problems, at worst, you're taking a bet.

How is that?

(warning: this is going to be boring, lots of "network lawyer" stuff)

The IEEE 802.11 standard relevant to this is IEEE Std 802.11i-2004 "Amendment 6: Medium Access Control (MAC) Security Enhancements"[0], aka "WPA2".

(I won't dig deeper down to WEP, which is clearly deprecated to no use, nor "basic" WPA, which was a transition waiting for this WPA2 standard to be complete).

The relevant part can be found in 802.11i, the ASN MIB[1] (annex D, normative), on page 136, define the "dot11RSNAConfigPSKPassPhrase" as a "DisplayString". So what type of data exactly is a "DisplayString"?

RFC 1213, "Management Information Base for Network Management of TCP/IP-based internets: MIB-II", from 1991, on page 3, states that:

"A DisplayString is restricted to the NVT ASCII character set, as defined in pages 10-11 of [6]."

OK...

This "[6]" is RFC 854, from 1983 (Wow! These IETF and IEEE design their standards seriously and really, really build upon). Are you still following me? :-) So having a look at it we learn that NVT stands for "Network Virtual Terminal", and in pointed to page 10 and 11, we found:

The NVT printer [sic! remember that's 1983] [...] can produce representations of all 95 USASCII graphics (codes 32 through 126).

OK, ASCII codes 32 to 126. Now let's come back to IEEE 802.11i:

In Annex H (informative), "RSNA reference implementations and test vectors", section "H.4 Suggested pass-phrase-to-PSK mapping" (remember that the purpose of the passphrase, mathematicaly massaged with the SSID, is to derive a PSK (Pre-Shared Key), more useful for 802.11 operation but much less user-friendly than "a damned simple passphrase that I can type with a damned keyboard"). Which, phrased the IEEE way, gives this (page 165):

The RSNA PSK consists of 256 bits, or 64 octets when represented in hex. It is difficult for a user to correctly enter 64 hex characters. Most users, however, are familiar with passwords and pass-phrases and feel more comfortable entering them than entering keys. A user is more likely to be able to enter an ASCII password or pass-phrase, even though doing so limits the set of possible keys. This suggests that the best that can be done is to introduce a pass-phrase to PSK mapping.

This clause defines a pass-phrase–to–PSK mapping that is the recommended practice for use with RSNAs.

This pass-phrase mapping was introduced to encourage users unfamiliar with cryptographic concepts to enable the security features of their WLAN.

...so for what's the purpose of a passphrase. And then on following page 166:

Here, the following assumptions apply:

  • A pass-phrase is a sequence of between 8 and 63 ASCII-encoded characters. The limit of 63 comes from the desire to distinguish
    between a pass-phrase and a PSK displayed as 64 hexadecimal
    characters.
  • Each character in the pass-phrase must have an encoding in the range of 32 to 126 (decimal), inclusive. [emphasis mine]

And Voila! Indeed, "32 to 126 (decimal), inclusive".

So here we have again our passphrase as ASCII "in the range of 32 to 126 (decimal)", confirmed from IEEE to IETF back to IEEE. We also learn that it's supposed to be between 8 and 63 bytes long, which, I would infer, imply that if longer than 63 bytes it will be trimmed down (and not NULL terminated, which is not a problem), and if shorter, will be cut at the first character outside of the 32-126 ASCII code. Of course the C string NULL terminator 0x00 is the more practical, sensible to use for this BTW.

So, passphrase = a string consisting only of 32 to 126 (decimal) ASCII code.

Have a look at an ASCII table, and you'll see this start with space, and end with the tilde '~'.

And there's definitely not the 0x00 in that.

Hence, long story short: your passphrase is standard-wise technically invalid, and you're looking for undefined behavior.

Congratulation if you've read me this far!

Addendum:

When it comes to networking protocol, do never, ever assume that what looks like "a string" is just "a string" from whatever you may presuppose, and always check the exact encoding/limitations.

Other example regarding Wi-Fi:

Another "string" is the SSID. Is this really a string? No. It is a raw array of 32 bytes, no ASCII, no UTF-8, Unicode, whatever, no termination, just 32 raw bytes, even if you "set it" as "foobar + NULL terminator" a whole 32 bytes will be used by the stack and go on the air (look at a wireshark trace, and doucle-click the SSID field in the dissection: 32 bytes long). So an SSID could consist of only ASCII spaces, tabs, CR, LF and a few 0x00 here and there, or only 0x00 BTW, it will be perfectly valid and managed as a full 32 bytes sequence anyway.

EDIT:

I wondered about your motivation for setting such a passphrase, and the only idea I could come up with - correct me if I'm wrong - is that your purpose was to play a neat trick to ensure that a regular user, using a regular keyboard, could never enter the passphrase. Sadly - or actually hopefully - as I explained, this cannot work because the IEEE precisely designed the passphrase data type to be 100% sure that anybody, using the most basic keyboard, could always type it. That was their motivation.

And so, what can you do?

As an alternative, you could directly use a PSK. That's plain raw 32 bytes (representation 64 hex digit ASCII), with no type-able/printable consideration. For example, from the hostapd.conf file (of course the example PSK is represented here as "text", but that's actually raw bytes):

# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
# (8..63 characters) that will be converted to PSK. This conversion uses SSID
# so the PSK changes when ASCII passphrase is used and the SSID is changed.
# wpa_psk (dot11RSNAConfigPSKValue)
# wpa_passphrase (dot11RSNAConfigPSKPassPhrase)
#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
#wpa_passphrase=secret passphrase

But then of course, 1/ this may not fit your use case (deployment wise), and 2/ the Arduino Wi-Fi API may have no such capabilities.

Hope this help.

[0]: You can download it for free here: http://standards.ieee.org/getieee802/download/802.11i-2004.pdf

[1]: That's IEEE jargon for "Management Information Base" in "Abstract Syntax Notation", which is a formal, hierarchical notation of every data with their names and types for a given standard. You can think of it as "XML", only its not XML, and is used by IETF and IEEE (RFC 2578, RFC 1213).

Upvotes: 4

Related Questions