/// <summary> /// If the msg contains a handshake token, inject it into the given params </summary> private static SortedDictionary <string, string> InjectHandshakeToken(AuthMsg msg, SortedDictionary <string, string> @params) { string tok = msg.Param("handshakeToken", false); if (tok != null) { @params["handshakeToken"] = tok; } return(@params); }
private AuthMsg FinalMsg(IAuthClientContext cx, AuthMsg msg) { // Decode server-first-message string s1_msg = Base64.URI.decodeUTF8(msg.Param("data")); IDictionary data = DecodeMsg(s1_msg); // c2-no-proof string cbind_input = gs2_header; string channel_binding = Base64.URI.EncodeUtf8(cbind_input); string nonce = (string)data["r"]; string c2_no_proof = "c=" + channel_binding + ",r=" + nonce; // proof string hash = msg.Param("hash"); string salt = (string)data["s"]; int iterations = int.Parse((string)data["i"]); string c1_bare = (string)cx.stash["c1_bare"]; string authMsg = c1_bare + "," + s1_msg + "," + c2_no_proof; string c2_msg = null; try { sbyte[] saltedPassword = Pbk(hash, cx.pass, salt, iterations); string clientProof = CreateClientProof(hash, saltedPassword, (sbyte[])(Array)Encoding.UTF8.GetBytes(authMsg)); c2_msg = c2_no_proof + ",p= " + clientProof; } catch (Exception e) { throw new AuthException("Failed to compute scram", e); } // build auth msg SortedDictionary <string, string> @params = new SortedDictionary <string, string>(); @params["data"] = Base64.URI.EncodeUtf8(c2_msg); return(new AuthMsg(name, InjectHandshakeToken(msg, @params))); }
/// <summary> /// Attempt standard authentication /// </summary> /// <param name="resp"> The response to the hello message </param> /// <returns> /// true if haystack authentciation was used, false if the /// server does not appear to implement RFC 7235. /// </returns> private bool OpenStd(HttpWebResponse resp) { // must be 401 challenge with WWW-Authenticate header if (resp.StatusCode != HttpStatusCode.Unauthorized) { return(false); } var wwwAuth = ResHeader(resp, "WWW-Authenticate"); // don't Use this mechanism for Basic which we // handle as a non-standard scheme because the headers // don't fit nicely into our restricted AuthMsg format if (string.IsNullOrEmpty(wwwAuth) || wwwAuth.ToLower().StartsWith("basic", StringComparison.Ordinal)) { return(false); } // process res/req messages until we have 200 or non-401 failure AuthScheme scheme = null; for (var loopCount = 0; ; ++loopCount) { // sanity check that we don't loop too many times if (loopCount > 5) { throw new AuthException("Loop count exceeded"); } // parse the WWW-Auth header and Use the first scheme var header = ResHeader(resp, "WWW-Authenticate"); AuthMsg[] resMsgs = AuthMsg.ListFromStr(header); var resMsg = resMsgs[0]; scheme = AuthScheme.Find(resMsg.scheme); // let scheme handle message var reqMsg = scheme.OnClient(this, resMsg); // send request back to the server resp = GetAuth(reqMsg); try { DumpRes(resp, false); } catch (Exception e) { e.ToString(); } // 200 means we are done, 401 means keep looping, // consider anything else a failure if (resp.StatusCode == HttpStatusCode.OK) { break; } if (resp.StatusCode == HttpStatusCode.Unauthorized) { continue; } throw new AuthException((int)resp.StatusCode + " " + resp.GetResponseStream()); } // init the bearer token var authInfo = ResHeader(resp, "Authentication-Info"); AuthMsg authInfoMsg = AuthMsg.FromStr("bearer " + authInfo); // callback to scheme for client Success scheme.OnClientSuccess(this, authInfoMsg); // only keep authToken parameter for Authorization header authInfoMsg = new AuthMsg("bearer", new[] { "authToken", authInfoMsg.Param("authToken") }); headers["Authorization"] = authInfoMsg.ToString(); // we did it! return(true); }
public override AuthMsg OnClient(IAuthClientContext cx, AuthMsg msg) { return(ReferenceEquals(msg.Param("data", false), null) ? FirstMsg(cx, msg) : FinalMsg(cx, msg)); }