/// <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() }); }
private static SaslChallengeAction GetChallengeAction(SaslChallenge saslChallenge) { var challengeBytes = saslChallenge.Challenge.Array; if (challengeBytes == null || challengeBytes.Length == 0) { throw new AmqpException(AmqpErrorCode.InvalidField, "Sasl challenge message is missing or empty."); } if (challengeBytes[0] == 0x0) { return(SaslChallengeAction.First); } if ((challengeBytes[0] & SaslControlByteMask.InterimSegment) != 0x0) { if ((challengeBytes[0] & SaslControlByteMask.FinalSegment) != 0x0) { return(SaslChallengeAction.Final); } return(SaslChallengeAction.Interim); } throw new AmqpException(AmqpErrorCode.InvalidField, $"Sasl challenge control byte contains invalid data: {challengeBytes[0]:X}."); }
public void OnCommand_ReturnsAmqpException_WhenCommandNotSupported() { var command = new SaslChallenge(); var profile = new AzureSaslProfileProxy(); AmqpException exception = Should.Throw <AmqpException>(() => profile.CallOnCommand(command)); exception.Error.Condition.ShouldBe((Symbol)ErrorCode.NotAllowed); exception.Message.ShouldBe(command.ToString()); }
/// <summary> /// Process the SASL challenge message. /// </summary> /// <param name="challenge">The server challenge.</param> /// <returns> /// The challenge response. /// </returns> public SaslResponse ProcessChallenge(SaslChallenge challenge) { // Response to teh Authentication Information Request this.digestChallenge = DecodeDigestChallenge(challenge); if (this.digestChallenge.ContainsKey("rspauth")) { return(new SaslResponse()); } return(null); }
private void SaveEncodedNonceSegment(SaslChallenge saslChallenge) { var sequenceNumber = GetSequenceNumber(saslChallenge); if (_nextSequenceNumber != sequenceNumber) { throw new AmqpException(AmqpErrorCode.InvalidField, $"Invalid sequence number, expected: {_nextSequenceNumber}, actual: {sequenceNumber}."); } var nonceSegment = Encoding.UTF8.GetString(saslChallenge.Challenge.Array, 1, saslChallenge.Challenge.Array.Length - 1); _encodedNonceStringBuilder.Append(nonceSegment); _nextSequenceNumber++; }
public override void OnChallenge(SaslChallenge challenge) { var challengeAction = GetChallengeAction(challenge); switch (challengeAction) { case SaslChallengeAction.First: SendStorageRootKey(); break; case SaslChallengeAction.Interim: SaveEncodedNonceSegment(challenge); SendInterimResponse(); break; case SaslChallengeAction.Final: SaveEncodedNonceSegment(challenge); SendLastResponse(); break; } }
private void SaveEncodedNonceSegment(SaslChallenge saslChallenge) { var sequenceNumber = GetSequenceNumber(saslChallenge); if (_nextSequenceNumber != sequenceNumber) { throw new AmqpException(AmqpErrorCode.InvalidField, $"Invalid sequence number, expected: {_nextSequenceNumber}, actual: {sequenceNumber}."); } byte[] tempNonce = new byte[_nonceBuffer.Length]; Buffer.BlockCopy(_nonceBuffer, 0, tempNonce, 0, _nonceBuffer.Length); _nonceBuffer = new byte[_nonceBuffer.Length + saslChallenge.Challenge.Array.Length - 1]; Buffer.BlockCopy(tempNonce, 0, _nonceBuffer, 0, tempNonce.Length); Buffer.BlockCopy( saslChallenge.Challenge.Array, 1, _nonceBuffer, tempNonce.Length, saslChallenge.Challenge.Array.Length - 1); _nextSequenceNumber++; }
private static Dictionary <string, string> DecodeDigestChallenge(SaslChallenge challenge) { var table = new Dictionary <string, string>(); var buffer = Convert.FromBase64String(challenge.Value); var decoded = Encoding.UTF8.GetString(buffer, 0, buffer.Length); var keyPairs = Regex.Matches(decoded, @"([\w\s\d]*)\s*=\s*([^,]*)"); foreach (Match match in keyPairs) { if (match.Success && match.Groups.Count == 3) { string key = match.Groups[1].Value.Trim(); string value = match.Groups[2].Value.Trim(); // Strip quotes from the value if (value.StartsWith("\"", StringComparison.OrdinalIgnoreCase) || value.StartsWith("'", StringComparison.OrdinalIgnoreCase)) { value = value.Remove(0, 1); } if (value.EndsWith("\"", StringComparison.OrdinalIgnoreCase) || value.EndsWith("'", StringComparison.OrdinalIgnoreCase)) { value = value.Remove(value.Length - 1, 1); } if (key == "nonce" && table.ContainsKey(key)) { return(null); } table.Add(key, value); } } return(table); }
private static byte GetSequenceNumber(SaslChallenge saslChallenge) { return((byte)(saslChallenge.Challenge.Array[0] & SaslControlByteMask.SequenceNumber)); }
/// <summary> /// Process the SASL challenge message. /// </summary> /// <param name="challenge">The server challenge.</param> /// <returns> /// The challenge response. /// </returns> public SaslResponse ProcessChallenge(SaslChallenge challenge) { return(null); }
private Element CreateNode(XmlNodeResult nd, Element parent) { string ens; if (nd.Prefix != null) { RemObjects.InternetPack.XMPP.Elements.Attribute at = nd.Attribute.FirstOrDefault(a => a.Prefix == "xmlns" && a.Name == nd.Prefix); if (at == null) { Element el = parent; ens = string.Empty; while (el != null) { RemObjects.InternetPack.XMPP.Elements.Attribute els = el.Attributes.Where(a => a.Prefix == "xmlns" && a.Name == nd.Prefix).FirstOrDefault(); if (els != null) { ens = els.Value; break; } el = el.Parent; } } else { ens = at.Value; } } else { RemObjects.InternetPack.XMPP.Elements.Attribute at = nd.Attribute.FirstOrDefault(a => a.Prefix == null && a.Name == "xmlns"); if (at == null) { ens = string.Empty; } else { ens = at.Value; } } Element res = null; switch (ens) { case Namespaces.ClientStreamNamespace: case Namespaces.ServerStreamNamespace: case "": if (ens == null && parent != null && parent.Type == ElementType.IQ && nd.Name == "error") { res = new IQError(); } else { switch (nd.Name) { case "iq": res = new IQ(); break; case "presence": res = new Presence(); break; case "message": res = new Message(); break; } } break; case Namespaces.StreamNamespace: switch (nd.Name) { case "stream": RemObjects.InternetPack.XMPP.Elements.Attribute att = nd.Attribute.FirstOrDefault(a => a.Prefix == null && a.Name == "xmlns"); if (att == null || att.Value == Namespaces.ClientStreamNamespace) { res = new ClientStream(); } else { res = new ServerStream(); } break; case "features": res = new StreamFeatures(); break; case "error": res = new StreamError(); break; } break; case Namespaces.StartTLSNamespace: switch (nd.Name) { case "starttls": res = new StartTLS(); break; case "failure": res = new StartTLSFailure(); break; case "proceed": res = new StartTLSProceed(); break; } break; case Namespaces.SaslNamespace: switch (nd.Name) { case "mechanisms": res = new Mechanisms(); break; case "auth": res = new SaslAuth(); break; case "challenge": res = new SaslChallenge(); break; case "response": res = new SaslResponse(); break; case "abort": res = new SaslAbort(); break; case "success": res = new SaslSuccess(); break; case "failure": res = new SaslFailure(); break; } break; } if (res == null) { res = new UnknownElement(); } else { res.Attributes.Clear(); // default ones shouldn't be here during the reading process } if (parent != null) { res.Parent = parent; if (parent != fServerRoot) { parent.Elements.Add(res); } } res.Prefix = nd.Prefix; res.Name = nd.Name; foreach (var el in nd.Attribute) { res.Attributes.Add(el); } return(res); }