Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds digest authentication example. #4112

Merged
merged 15 commits into from
Jan 10, 2018
Merged

Adds digest authentication example. #4112

merged 15 commits into from
Jan 10, 2018

Conversation

1995parham
Copy link
Contributor

No description provided.


#define USE_SERIAL Serial

ESP8266WiFiMulti WiFiMulti;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to use ESP8266WiFiMulti in this file? I know that some of the existing examples do that, but it would be great if the examples could be made orthogonal.


#include <ESP8266HTTPClient.h>

#define USE_SERIAL Serial
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest using Serial directly

ESP8266WiFiMulti WiFiMulti;

String _exractParam(String& authReq, const String& param, const char delimit){
int _begin = authReq.indexOf(param);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please load this sketch into Arduino IDE and use Tools > Auto Format feature to fix the indentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it is not a common practice to prefix function and field names with an underscore, when not implementing class members, at least in Arduino.

}

WiFi.mode(WIFI_STA);
WiFiMulti.addAP("Niligo-Prism-yyr9uma", "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to replace this with something generic, e.g. "wifi-ssid", "wifi-password", and move these settings to the top of the sketch, after includes.



// http.begin("http://admin:[email protected]/api/state?power=1");
http.begin("http://192.168.100.1/api/state?power=0");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a URL of an existing service can be used?

For example, http://httpbin.org/ has a test endpoint which accept Digest Auth (/digest-auth/:qop/:user/:passwd/:algorithm).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's awesome thanks, I will use it. 🙌


http.begin("http://192.168.100.1/api/state?power=0");
USE_SERIAL.println(_nonce);
String Author = "Digest username=\"admin\", realm=\"Protected\", nonce=\"" + _nonce + "\", uri=\"/api/state?power=0\", algorithm=\"MD5\", qop=auth, nc=00000001, cnonce=\"gDBuFY4s\", response=\"" + _response + "\"\r\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest wrapping this part of the code (starting from extractParam calls) into a function which users can copy into their own sketch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

username and URI are duplicated here again, along with nonces...

String _H2 = md5.toString();

md5.begin();
md5.add(_H1 + ":" + _nonce + ":" + "00000001" + ":" + "gDBuFY4s" + ":" + "auth" + ":" + _H2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the 00000001 and gDBuFY4s magic values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In digest authentication client must generate nonce count and client nonce we set them here manually but they can set by creating a random string.

String _H1 = md5.toString();

md5.begin();
md5.add(String("GET:") + String("/api/state?power=0"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth storing the URI part in a separate variable and using it both when making HTTP call and here, instead of duplicating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I created separate variables for them

md5.calculate();
String _response = md5.toString();

http.begin("http://192.168.100.1/api/state?power=0");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, use a variable for URI to avoid duplication.

USE_SERIAL.println(_nonce);
String Author = "Digest username=\"admin\", realm=\"Protected\", nonce=\"" + _nonce + "\", uri=\"/api/state?power=0\", algorithm=\"MD5\", qop=auth, nc=00000001, cnonce=\"gDBuFY4s\", response=\"" + _response + "\"\r\n";
USE_SERIAL.println(Author);
http.addHeader("Authorization", Author);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use lowercase names for local variables. Also, the name author is a bit confusing, should it be authorization?

http.end();
http.begin(String(server) + String(uri));

String authorization = "Digest username=\"admin\", realm=\"" + _realm + "\", nonce=\"" + _nonce + "\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=00000001, cnonce=\"gDBuFY4s\", response=\"" + _response + "\"";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move the part starting from // extracting required parameters for RFC 2069 simpler Diges to calculation of authorization into a separate function, e.g. getDigestAuth.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please break this line, Arduino IDE window is just ~60 characters wide, most of the line will not be readable.

// extracting required parameters for RFC 2069 simpler Digest
String _realm = exractParam(authReq, "realm=\"", '"');
String _nonce = exractParam(authReq, "nonce=\"", '"');
String _opaque = exractParam(authReq, "opaque=\"", '"');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned previously, it is not a common practice in Arduino sketches to start local variable names with underscores.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am sorry about them.

String _H2 = md5.toString();

md5.begin();
md5.add(_H1 + ":" + _nonce + ":" + "00000001" + ":" + "gDBuFY4s" + ":" + "auth" + ":" + _H2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still some hard-coded strings here.
Nonce gDBuFY4s should be randomly generated.
And as far as i understand the RFC, 00000001 should be incremented on each request, which is not the case here.

const char *uri = "/digest-auth/auth/admin/admin/MD5";

String exractParam(String& authReq, const String& param, const char delimit){
int _begin = authReq.indexOf(param);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment about reformatting the sketch using Arduino IDE is still valid, by the way. Arduino sketches use 2 spaces for indentation, this file uses tabs.

@1995parham
Copy link
Contributor Author

1995parham commented Jan 8, 2018

@igrr What do you think about adding digest authentication into ESP8266HTTPClient?

@igrr
Copy link
Member

igrr commented Jan 9, 2018

Looks pretty good @1995parham! I think moving this feature into ESP8266HTTPClient class could be useful. It's up to you whether to do it or not, adding it in the form of an example is also okay with me.

I have only one remaining comment about the request counter field, "00000001". I think you should pass the counter as an argument into getDigestAuth function, and increment it each time the loop runs. (Perhaps the web servers also allow sending the same counter, that's just my reading of the RFC).

@1995parham
Copy link
Contributor Author

@igrr Yes, I add this feature to the getDigestAuth function.

String cNonce = getCNonce(8);

char nc[8];
sprintf(nc, "%08d", counter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's "%08d", the size of nc should be 9 bytes to accommodate the zero terminator.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Counter is an int, so nc should have size 12. Then, the format specifier should be %11d.
Counter should actually be an unsigned int imo, it doesn't make sense for negative values. In that case, nc should have size 11.

Copy link
Member

@igrr igrr Jan 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's read the RFC:

 The nc-value is the hexadecimal
 count of the number of requests (including the current request)
 that the client has sent with the nonce value in this request.  For
 example, in the first request sent in response to a given nonce
 value, the client sends "nc=00000001".

and

 nc-value         = 8LHEX

and

 LHEX               =  "0" | "1" | "2" | "3" |
                       "4" | "5" | "6" | "7" |
                       "8" | "9" | "a" | "b" |
                       "c" | "d" | "e" | "f"

So it must be "%08x".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right, it must be in hex format so I convert it into %08x.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. But what stresses that counter should be unsigned :P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devyte I corrects that too :)

String nonce = exractParam(authReq, "nonce=\"", '"');
String cNonce = getCNonce(8);

char nc[8];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, nc is still an 8 byte buffer, so the zero terminator will be written out of array bounds. Could you please change the array size to 9, and use snprintf(nc, sizeof(nc), "%08x", counter);?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sorry for these mistakes.

@1995parham
Copy link
Contributor Author

@igrr Thanks a lot

@1995parham 1995parham closed this Jan 10, 2018
@1995parham 1995parham reopened this Jan 10, 2018
@igrr igrr merged commit 332e059 into esp8266:master Jan 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants