@@ -157,6 +157,72 @@ LT_END_AUTO_TEST(base_auth_fail)
157157// Also skip if libmicrohttpd was built without digest auth support
158158#if !defined(_WINDOWS) && defined(HAVE_DAUTH)
159159
160+ // Pre-computed MD5 hash of "myuser:examplerealm:mypass"
161+ // printf "myuser:examplerealm:mypass" | md5sum
162+ // 6ceef750e0130d6528b938c3abd94110
163+ static const unsigned char PRECOMPUTED_HA1_MD5[16 ] = {
164+ 0x6c , 0xee , 0xf7 , 0x50 , 0xe0 , 0x13 , 0x0d , 0x65 ,
165+ 0x28 , 0xb9 , 0x38 , 0xc3 , 0xab , 0xd9 , 0x41 , 0x10
166+ };
167+
168+ // Pre-computed SHA-256 hash of "myuser:examplerealm:mypass"
169+ // printf "myuser:examplerealm:mypass" | sha256sum
170+ // d4ff5b1795b23b4c625975959f3276526f3f4f4ef7d22083207e02d7c4bd8a05
171+ static const unsigned char PRECOMPUTED_HA1_SHA256[32 ] = {
172+ 0xd4 , 0xff , 0x5b , 0x17 , 0x95 , 0xb2 , 0x3b , 0x4c ,
173+ 0x62 , 0x59 , 0x75 , 0x95 , 0x9f , 0x32 , 0x76 , 0x52 ,
174+ 0x6f , 0x3f , 0x4f , 0x4e , 0xf7 , 0xd2 , 0x20 , 0x83 ,
175+ 0x20 , 0x7e , 0x02 , 0xd7 , 0xc4 , 0xbd , 0x8a , 0x05
176+ };
177+
178+ class digest_ha1_md5_resource : public http_resource {
179+ public:
180+ shared_ptr<http_response> render_GET (const http_request& req) {
181+ if (req.get_digested_user () == " " ) {
182+ return std::make_shared<digest_auth_fail_response>(
183+ " FAIL" , " examplerealm" , MY_OPAQUE, true ,
184+ httpserver::http::http_utils::http_ok,
185+ httpserver::http::http_utils::text_plain,
186+ httpserver::http::http_utils::digest_algorithm::MD5);
187+ }
188+ bool reload_nonce = false ;
189+ if (!req.check_digest_auth_ha1 (" examplerealm" , PRECOMPUTED_HA1_MD5,
190+ httpserver::http::http_utils::md5_digest_size, 300 , &reload_nonce,
191+ httpserver::http::http_utils::digest_algorithm::MD5)) {
192+ return std::make_shared<digest_auth_fail_response>(
193+ " FAIL" , " examplerealm" , MY_OPAQUE, reload_nonce,
194+ httpserver::http::http_utils::http_ok,
195+ httpserver::http::http_utils::text_plain,
196+ httpserver::http::http_utils::digest_algorithm::MD5);
197+ }
198+ return std::make_shared<string_response>(" SUCCESS" , 200 , " text/plain" );
199+ }
200+ };
201+
202+ class digest_ha1_sha256_resource : public http_resource {
203+ public:
204+ shared_ptr<http_response> render_GET (const http_request& req) {
205+ if (req.get_digested_user () == " " ) {
206+ return std::make_shared<digest_auth_fail_response>(
207+ " FAIL" , " examplerealm" , MY_OPAQUE, true ,
208+ httpserver::http::http_utils::http_ok,
209+ httpserver::http::http_utils::text_plain,
210+ httpserver::http::http_utils::digest_algorithm::SHA256);
211+ }
212+ bool reload_nonce = false ;
213+ if (!req.check_digest_auth_ha1 (" examplerealm" , PRECOMPUTED_HA1_SHA256,
214+ httpserver::http::http_utils::sha256_digest_size, 300 , &reload_nonce,
215+ httpserver::http::http_utils::digest_algorithm::SHA256)) {
216+ return std::make_shared<digest_auth_fail_response>(
217+ " FAIL" , " examplerealm" , MY_OPAQUE, reload_nonce,
218+ httpserver::http::http_utils::http_ok,
219+ httpserver::http::http_utils::text_plain,
220+ httpserver::http::http_utils::digest_algorithm::SHA256);
221+ }
222+ return std::make_shared<string_response>(" SUCCESS" , 200 , " text/plain" );
223+ }
224+ };
225+
160226LT_BEGIN_AUTO_TEST (authentication_suite, digest_auth)
161227 webserver ws = create_webserver(PORT)
162228 .digest_auth_random(" myrandom" )
@@ -237,6 +303,166 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass)
237303 ws.stop();
238304LT_END_AUTO_TEST (digest_auth_wrong_pass)
239305
306+ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_with_ha1_md5)
307+ webserver ws = create_webserver(PORT)
308+ .digest_auth_random(" myrandom" )
309+ .nonce_nc_size(300 );
310+
311+ digest_ha1_md5_resource digest_ha1;
312+ LT_ASSERT_EQ (true , ws.register_resource(" base" , &digest_ha1));
313+ ws.start(false );
314+
315+ #if defined(_WINDOWS)
316+ curl_global_init (CURL_GLOBAL_WIN32);
317+ #else
318+ curl_global_init (CURL_GLOBAL_ALL);
319+ #endif
320+
321+ std::string s;
322+ CURL *curl = curl_easy_init();
323+ CURLcode res;
324+ curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
325+ #if defined(_WINDOWS)
326+ curl_easy_setopt (curl, CURLOPT_USERPWD, " examplerealm/myuser:mypass" );
327+ #else
328+ curl_easy_setopt (curl, CURLOPT_USERPWD, " myuser:mypass" );
329+ #endif
330+ curl_easy_setopt (curl, CURLOPT_URL, " localhost:" PORT_STRING " /base" );
331+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L );
332+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc);
333+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s);
334+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L );
335+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L );
336+ curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
337+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1 );
338+ res = curl_easy_perform(curl);
339+ LT_ASSERT_EQ (res, 0 );
340+ LT_CHECK_EQ (s, " SUCCESS" );
341+ curl_easy_cleanup (curl);
342+
343+ ws.stop();
344+ LT_END_AUTO_TEST (digest_auth_with_ha1_md5)
345+
346+ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_with_ha1_md5_wrong_pass)
347+ webserver ws = create_webserver(PORT)
348+ .digest_auth_random(" myrandom" )
349+ .nonce_nc_size(300 );
350+
351+ digest_ha1_md5_resource digest_ha1;
352+ LT_ASSERT_EQ (true , ws.register_resource(" base" , &digest_ha1));
353+ ws.start(false );
354+
355+ #if defined(_WINDOWS)
356+ curl_global_init (CURL_GLOBAL_WIN32);
357+ #else
358+ curl_global_init (CURL_GLOBAL_ALL);
359+ #endif
360+
361+ std::string s;
362+ CURL *curl = curl_easy_init();
363+ CURLcode res;
364+ curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
365+ #if defined(_WINDOWS)
366+ curl_easy_setopt (curl, CURLOPT_USERPWD, " examplerealm/myuser:wrongpass" );
367+ #else
368+ curl_easy_setopt (curl, CURLOPT_USERPWD, " myuser:wrongpass" );
369+ #endif
370+ curl_easy_setopt (curl, CURLOPT_URL, " localhost:" PORT_STRING " /base" );
371+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L );
372+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc);
373+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s);
374+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L );
375+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L );
376+ curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
377+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1 );
378+ res = curl_easy_perform(curl);
379+ LT_ASSERT_EQ (res, 0 );
380+ LT_CHECK_EQ (s, " FAIL" );
381+ curl_easy_cleanup (curl);
382+
383+ ws.stop();
384+ LT_END_AUTO_TEST (digest_auth_with_ha1_md5_wrong_pass)
385+
386+ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_with_ha1_sha256)
387+ webserver ws = create_webserver(PORT)
388+ .digest_auth_random(" myrandom" )
389+ .nonce_nc_size(300 );
390+
391+ digest_ha1_sha256_resource digest_ha1;
392+ LT_ASSERT_EQ (true , ws.register_resource(" base" , &digest_ha1));
393+ ws.start(false );
394+
395+ #if defined(_WINDOWS)
396+ curl_global_init (CURL_GLOBAL_WIN32);
397+ #else
398+ curl_global_init (CURL_GLOBAL_ALL);
399+ #endif
400+
401+ std::string s;
402+ CURL *curl = curl_easy_init();
403+ CURLcode res;
404+ curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
405+ #if defined(_WINDOWS)
406+ curl_easy_setopt (curl, CURLOPT_USERPWD, " examplerealm/myuser:mypass" );
407+ #else
408+ curl_easy_setopt (curl, CURLOPT_USERPWD, " myuser:mypass" );
409+ #endif
410+ curl_easy_setopt (curl, CURLOPT_URL, " localhost:" PORT_STRING " /base" );
411+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L );
412+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc);
413+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s);
414+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L );
415+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L );
416+ curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
417+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1 );
418+ res = curl_easy_perform(curl);
419+ LT_ASSERT_EQ (res, 0 );
420+ LT_CHECK_EQ (s, " SUCCESS" );
421+ curl_easy_cleanup (curl);
422+
423+ ws.stop();
424+ LT_END_AUTO_TEST (digest_auth_with_ha1_sha256)
425+
426+ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_with_ha1_sha256_wrong_pass)
427+ webserver ws = create_webserver(PORT)
428+ .digest_auth_random(" myrandom" )
429+ .nonce_nc_size(300 );
430+
431+ digest_ha1_sha256_resource digest_ha1;
432+ LT_ASSERT_EQ (true , ws.register_resource(" base" , &digest_ha1));
433+ ws.start(false );
434+
435+ #if defined(_WINDOWS)
436+ curl_global_init (CURL_GLOBAL_WIN32);
437+ #else
438+ curl_global_init (CURL_GLOBAL_ALL);
439+ #endif
440+
441+ std::string s;
442+ CURL *curl = curl_easy_init();
443+ CURLcode res;
444+ curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
445+ #if defined(_WINDOWS)
446+ curl_easy_setopt (curl, CURLOPT_USERPWD, " examplerealm/myuser:wrongpass" );
447+ #else
448+ curl_easy_setopt (curl, CURLOPT_USERPWD, " myuser:wrongpass" );
449+ #endif
450+ curl_easy_setopt (curl, CURLOPT_URL, " localhost:" PORT_STRING " /base" );
451+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L );
452+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc);
453+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s);
454+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L );
455+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L );
456+ curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
457+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1 );
458+ res = curl_easy_perform(curl);
459+ LT_ASSERT_EQ (res, 0 );
460+ LT_CHECK_EQ (s, " FAIL" );
461+ curl_easy_cleanup (curl);
462+
463+ ws.stop();
464+ LT_END_AUTO_TEST (digest_auth_with_ha1_sha256_wrong_pass)
465+
240466#endif
241467
242468// Simple resource for centralized auth tests
0 commit comments