#include "http_digest.h" #include #include #include /************************************************************/ /************************************************************/ static const char* WWW_PREFIX = "Authorization: Digest "; /************************************************************/ httpDigestHandler::httpDigestHandler() : User_(nullptr) , Password_(nullptr) , Nonce_(nullptr) , NonceCount_(0) , HeaderInstruction_(nullptr) { } /************************************************************/ httpDigestHandler::~httpDigestHandler() { clear(); } /************************************************************/ void httpDigestHandler::clear() { free(Nonce_); free(HeaderInstruction_); User_ = Password_ = nullptr; Nonce_ = HeaderInstruction_ = nullptr; NonceCount_ = 0; } /************************************************************/ void httpDigestHandler::setAuthorization(const char* user, const char* password) { clear(); if (user && password) { User_ = user; Password_ = password; } } /************************************************************/ const char* httpDigestHandler::getHeaderInstruction() const { return HeaderInstruction_; } /************************************************************/ Y_FORCE_INLINE void addMD5(MD5& ctx, const char* value) { ctx.Update((const unsigned char*)(value), strlen(value)); } Y_FORCE_INLINE void addMD5(MD5& ctx, const char* value, int len) { ctx.Update((const unsigned char*)(value), len); } Y_FORCE_INLINE void addMD5(MD5& ctx, std::string_view str) { ctx.Update(str); } Y_FORCE_INLINE void addMD5Sep(MD5& ctx) { addMD5(ctx, ":", 1); } /************************************************************/ /* calculate H(A1) as per spec */ void httpDigestHandler::digestCalcHA1(const THttpAuthHeader& hd, char* outSessionKey, const std::string& outCNonce) { MD5 ctx; ctx.Init(); addMD5(ctx, User_); addMD5Sep(ctx); addMD5(ctx, hd.realm); addMD5Sep(ctx); addMD5(ctx, Password_); if (hd.algorithm == 1) { //MD5-sess unsigned char digest[16]; ctx.Final(digest); ctx.Init(); ctx.Update(digest, 16); addMD5Sep(ctx); addMD5(ctx, hd.nonce); addMD5Sep(ctx); addMD5(ctx, outCNonce); ctx.End(outSessionKey); } ctx.End(outSessionKey); } /************************************************************/ /* calculate request-digest/response-digest as per HTTP Digest spec */ void httpDigestHandler::digestCalcResponse(const THttpAuthHeader& hd, const char* path, const char* method, const char* nonceCount, char* outResponse, const std::string& outCNonce) { char HA1[33]; digestCalcHA1(hd, HA1, outCNonce); char HA2[33]; MD5 ctx; ctx.Init(); addMD5(ctx, method); addMD5Sep(ctx); addMD5(ctx, path); //ignore auth-int ctx.End(HA2); ctx.Init(); addMD5(ctx, HA1, 32); addMD5Sep(ctx); addMD5(ctx, Nonce_); addMD5Sep(ctx); if (hd.qop_auth) { addMD5(ctx, nonceCount, 8); addMD5Sep(ctx); addMD5(ctx, outCNonce); addMD5Sep(ctx); addMD5(ctx, "auth", 4); addMD5Sep(ctx); } addMD5(ctx, HA2, 32); ctx.End(outResponse); } /************************************************************/ bool httpDigestHandler::processHeader(const THttpAuthHeader* header, const char* path, const char* method, const char* cnonceIn) { if (!User_ || !header || !header->use_auth || !header->realm || !header->nonce) return false; if (Nonce_) { if (strcmp(Nonce_, header->nonce)) { free(Nonce_); Nonce_ = nullptr; NonceCount_ = 0; } } if (!Nonce_) { Nonce_ = strdup(header->nonce); NonceCount_ = 0; } free(HeaderInstruction_); HeaderInstruction_ = nullptr; NonceCount_++; char nonceCount[20]; snprintf(nonceCount, sizeof(nonceCount), "%08d", NonceCount_); std::string cNonce = cnonceIn ? std::string(cnonceIn) : std::to_string(time(nullptr)); char response[33]; digestCalcResponse(*header, path, method, nonceCount, response, cNonce); //digest-response = 1#( username | realm | nonce | digest-uri // | response | [ algorithm ] | [cnonce] | // [opaque] | [message-qop] | // [nonce-count] | [auth-param] ) TStringStream out; out << WWW_PREFIX << "username=\"" << User_ << "\""; out << ", realm=\"" << header->realm << "\""; out << ", nonce=\"" << header->nonce << "\""; out << ", uri=\"" << path << "\""; if (header->algorithm == 1) out << ", algorithm=MD5-sess"; else out << ", algorithm=MD5"; if (header->qop_auth) out << ", qop=auth"; out << ", nc=" << nonceCount; if (!cNonce.empty()) out << ", cnonce=\"" << cNonce << "\""; out << ", response=\"" << response << "\""; if (header->opaque) out << ", opaque=\"" << header->opaque << "\""; out << "\r\n"; TString s_out = out.Str(); HeaderInstruction_ = strdup(s_out.c_str()); return true; } /************************************************************/ /************************************************************/