/// <summary> /// Process the SASL challenge message. /// </summary> /// <param name="challenge">The server challenge.</param> /// <returns> /// The challenge response. /// </returns> /// <remarks> /// SaltedPassword := Hi(Normalize(password), salt, i) /// ClientKey := HMAC(SaltedPassword, "Client Key") /// StoredKey := H(ClientKey) /// AuthMessage := client-first-message-bare + "," + /// server-first-message + "," + /// client-final-message-without-proof /// ClientSignature := HMAC(StoredKey, AuthMessage) /// ClientProof := ClientKey XOR ClientSignature /// ServerKey := HMAC(SaltedPassword, "Server Key") /// ServerSignature := HMAC(ServerKey, AuthMessage) /// </remarks> public SaslResponse ProcessChallenge(SaslChallenge challenge) { var password = Normalize(this.connectionString.UserPassword); var decoded = Convert.FromBase64String(challenge.Value); var serverFirstMessage = XmppEncoding.Utf8.GetString(decoded, 0, decoded.Length); var tokens = SaslTokenizer.ToDictionary(serverFirstMessage); var snonce = tokens["r"]; var ssalt = Convert.FromBase64String(tokens["s"]); var ssaltSize = Convert.ToUInt32(tokens["i"]); var clientFinalMessageWP = $"c={XmppEncoding.Utf8.GetBytes("n,,").ToBase64String()},r={snonce}"; var saltedPassword = password.Rfc2898DeriveBytes(ssalt, ssaltSize, 20); var clientKey = saltedPassword.ComputeHmacSha1("Client Key"); var storedKey = clientKey.ComputeSHA1Hash(); var authMessage = $"{this.clientFirstMessageBare},{serverFirstMessage},{clientFinalMessageWP}"; var clientSignature = storedKey.ComputeHmacSha1(authMessage); var clientProof = clientKey.Xor(clientSignature); var clientFinalMessage = $"{clientFinalMessageWP},p={clientProof.ToBase64String()}"; var serverKey = saltedPassword.ComputeHmacSha1("Server Key"); this.serverSignature = serverKey.ComputeHmacSha1(authMessage).ToBase64String(); return(new SaslResponse { Value = clientFinalMessage.ToBase64String() }); }
/// <summary> /// Verifies the SASL success message if needed. /// </summary> /// <param name="success">The server success response</param> /// <returns> /// <b>true</b> if the reponse has been verified; otherwise <b>false</b> /// </returns> public bool ProcessSuccess(SaslSuccess success) { // The server verifies the nonce and the proof, verifies that the // authorization identity (if supplied by the client in the first // message) is authorized to act as the authentication identity, and, // finally, it responds with a "server-final-message", concluding the // authentication exchange. var decoded = Convert.FromBase64String(success.Value); var serverFinalMessage = XmppEncoding.Utf8.GetString(decoded, 0, decoded.Length); var tokens = SaslTokenizer.ToDictionary(serverFinalMessage); var serverSignature = tokens["v"]; return(serverSignature == this.serverSignature); }