in src/Microsoft.Azure.Relay/WebSockets/NetStandard20/AuthenticationHelper.Digest.cs [46:181]
public static string GetDigestTokenForCredential(NetworkCredential credential, string httpMethod, string pathAndQuery, string content, DigestResponse digestResponse)
{
StringBuilder sb = StringBuilderCache.Acquire();
// It is mandatory for servers to implement sha-256 per RFC 7616
// Keep MD5 for backward compatibility.
string algorithm;
if (digestResponse.Parameters.TryGetValue(Algorithm, out algorithm))
{
if (algorithm != Sha256 && algorithm != Md5 && algorithm != Sha256Sess && algorithm != MD5Sess)
{
return null;
}
}
else
{
algorithm = Md5;
}
// Check if nonce is there in challenge
string nonce;
if (!digestResponse.Parameters.TryGetValue(Nonce, out nonce))
{
return null;
}
// opaque token may or may not exist
string opaque;
digestResponse.Parameters.TryGetValue(Opaque, out opaque);
string realm;
if (!digestResponse.Parameters.TryGetValue(Realm, out realm))
{
return null;
}
// Add username
string userhash;
if (digestResponse.Parameters.TryGetValue(UserHash, out userhash) && userhash == "true")
{
sb.AppendKeyValue(Username, ComputeHash(credential.UserName + ":" + realm, algorithm));
sb.AppendKeyValue(UserHash, userhash, includeQuotes: false);
}
else
{
string usernameStar;
if (IsInputEncoded5987(credential.UserName, out usernameStar))
{
sb.AppendKeyValue(UsernameStar, usernameStar, includeQuotes: false);
}
else
{
sb.AppendKeyValue(Username, credential.UserName);
}
}
// Add realm
if (realm != string.Empty)
sb.AppendKeyValue(Realm, realm);
// Add nonce
sb.AppendKeyValue(Nonce, nonce);
// Add uri
sb.AppendKeyValue(Uri, pathAndQuery);
// Set qop, default is auth
string qop = Auth;
if (digestResponse.Parameters.ContainsKey(Qop))
{
// Check if auth-int present in qop string
int index1 = digestResponse.Parameters[Qop].IndexOf(AuthInt);
if (index1 != -1)
{
// Get index of auth if present in qop string
int index2 = digestResponse.Parameters[Qop].IndexOf(Auth);
// If index2 < index1, auth option is available
// If index2 == index1, check if auth option available later in string after auth-int.
if (index2 == index1)
{
index2 = digestResponse.Parameters[Qop].IndexOf(Auth, index1 + AuthInt.Length);
if (index2 == -1)
{
qop = AuthInt;
}
}
}
}
// Set cnonce
string cnonce = GetRandomAlphaNumericString();
// Calculate response
string a1 = credential.UserName + ":" + realm + ":" + credential.Password;
if (algorithm.IndexOf("sess") != -1)
{
a1 = ComputeHash(a1, algorithm) + ":" + nonce + ":" + cnonce;
}
string a2 = httpMethod + ":" + pathAndQuery;
if (qop == AuthInt)
{
a2 = a2 + ":" + ComputeHash(content ?? string.Empty, algorithm);
}
string response = ComputeHash(ComputeHash(a1, algorithm) + ":" +
nonce + ":" +
DigestResponse.NonceCount + ":" +
cnonce + ":" +
qop + ":" +
ComputeHash(a2, algorithm), algorithm);
// Add response
sb.AppendKeyValue(Response, response);
// Add algorithm
sb.AppendKeyValue(Algorithm, algorithm, includeQuotes: false);
// Add opaque
if (opaque != null)
{
sb.AppendKeyValue(Opaque, opaque);
}
// Add qop
sb.AppendKeyValue(Qop, qop, includeQuotes: false);
// Add nc
sb.AppendKeyValue(NC, DigestResponse.NonceCount, includeQuotes: false);
// Add cnonce
sb.AppendKeyValue(CNonce, cnonce, includeComma: false);
return StringBuilderCache.GetStringAndRelease(sb);
}