@@ -157,6 +157,46 @@ 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_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+ }
185+ bool reload_nonce = false ;
186+ // Try MD5 first (default), then SHA-256 if that fails
187+ if (!req.check_digest_auth_ha1 (" examplerealm" , PRECOMPUTED_HA1_MD5, 16 , 300 , &reload_nonce,
188+ httpserver::http::http_utils::digest_algorithm::MD5)) {
189+ // Try SHA-256
190+ if (!req.check_digest_auth_ha1 (" examplerealm" , PRECOMPUTED_HA1_SHA256, 32 , 300 , &reload_nonce,
191+ httpserver::http::http_utils::digest_algorithm::SHA256)) {
192+ return std::make_shared<digest_auth_fail_response>(
193+ " FAIL" , " examplerealm" , MY_OPAQUE, reload_nonce);
194+ }
195+ }
196+ return std::make_shared<string_response>(" SUCCESS" , 200 , " text/plain" );
197+ }
198+ };
199+
160200LT_BEGIN_AUTO_TEST (authentication_suite, digest_auth)
161201 webserver ws = create_webserver(PORT)
162202 .digest_auth_random(" myrandom" )
@@ -237,6 +277,86 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass)
237277 ws.stop();
238278LT_END_AUTO_TEST (digest_auth_wrong_pass)
239279
280+ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_with_ha1)
281+ webserver ws = create_webserver(PORT)
282+ .digest_auth_random(" myrandom" )
283+ .nonce_nc_size(300 );
284+
285+ digest_ha1_resource digest_ha1;
286+ LT_ASSERT_EQ (true , ws.register_resource(" base" , &digest_ha1));
287+ ws.start(false );
288+
289+ #if defined(_WINDOWS)
290+ curl_global_init (CURL_GLOBAL_WIN32);
291+ #else
292+ curl_global_init (CURL_GLOBAL_ALL);
293+ #endif
294+
295+ std::string s;
296+ CURL *curl = curl_easy_init();
297+ CURLcode res;
298+ curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
299+ #if defined(_WINDOWS)
300+ curl_easy_setopt (curl, CURLOPT_USERPWD, " examplerealm/myuser:mypass" );
301+ #else
302+ curl_easy_setopt (curl, CURLOPT_USERPWD, " myuser:mypass" );
303+ #endif
304+ curl_easy_setopt (curl, CURLOPT_URL, " localhost:" PORT_STRING " /base" );
305+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L );
306+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc);
307+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s);
308+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L );
309+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L );
310+ curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
311+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1 );
312+ res = curl_easy_perform(curl);
313+ LT_ASSERT_EQ (res, 0 );
314+ LT_CHECK_EQ (s, " SUCCESS" );
315+ curl_easy_cleanup (curl);
316+
317+ ws.stop();
318+ LT_END_AUTO_TEST (digest_auth_with_ha1)
319+
320+ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_with_ha1_wrong_pass)
321+ webserver ws = create_webserver(PORT)
322+ .digest_auth_random(" myrandom" )
323+ .nonce_nc_size(300 );
324+
325+ digest_ha1_resource digest_ha1;
326+ LT_ASSERT_EQ (true , ws.register_resource(" base" , &digest_ha1));
327+ ws.start(false );
328+
329+ #if defined(_WINDOWS)
330+ curl_global_init (CURL_GLOBAL_WIN32);
331+ #else
332+ curl_global_init (CURL_GLOBAL_ALL);
333+ #endif
334+
335+ std::string s;
336+ CURL *curl = curl_easy_init();
337+ CURLcode res;
338+ curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
339+ #if defined(_WINDOWS)
340+ curl_easy_setopt (curl, CURLOPT_USERPWD, " examplerealm/myuser:wrongpass" );
341+ #else
342+ curl_easy_setopt (curl, CURLOPT_USERPWD, " myuser:wrongpass" );
343+ #endif
344+ curl_easy_setopt (curl, CURLOPT_URL, " localhost:" PORT_STRING " /base" );
345+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L );
346+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc);
347+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s);
348+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L );
349+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L );
350+ curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
351+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1 );
352+ res = curl_easy_perform(curl);
353+ LT_ASSERT_EQ (res, 0 );
354+ LT_CHECK_EQ (s, " FAIL" );
355+ curl_easy_cleanup (curl);
356+
357+ ws.stop();
358+ LT_END_AUTO_TEST (digest_auth_with_ha1_wrong_pass)
359+
240360#endif
241361
242362// Simple resource for centralized auth tests
0 commit comments