/* | |
* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
* | |
*/ | |
using System; | |
using System.Collections; | |
using System.Collections.Specialized; | |
using System.Text; | |
using NUnit.Framework; | |
using Apache.Qpid.Sasl; | |
using Apache.Qpid.Sasl.Mechanisms; | |
namespace Apache.Qpid.Sasl.Tests.Mechanisms | |
{ | |
[TestFixture] | |
public class DigestSaslClientTests : ISaslCallbackHandler | |
{ | |
private const string USERNAME = "chris"; | |
private const string PASSWORD = "secret"; | |
private const string AUTHID = null; | |
private const string PROTOCOL = "IMAP"; | |
private const string SERVERNAME = "elwood.innosoft.com"; | |
#region Digest Challenge Parsing Tests | |
// | |
// Digest Challenge Parsing Tests | |
// | |
[Test] | |
public void CanParseSimpleString() | |
{ | |
string challenge = "realm=\"elwood.innosoft.com\", algorithm=md5-sess"; | |
StringDictionary values = DigestChallenge.ParseParameters(challenge); | |
Assert.AreEqual(2, values.Count); | |
Assert.AreEqual("elwood.innosoft.com", values["realm"]); | |
Assert.AreEqual("md5-sess", values["algorithm"]); | |
} | |
[Test] | |
public void CanParseEscapedQuotes() | |
{ | |
string challenge = "realm=\"elwood\\\".innosoft.com\", algorithm=md5-sess"; | |
StringDictionary values = DigestChallenge.ParseParameters(challenge); | |
Assert.AreEqual(2, values.Count); | |
Assert.AreEqual("elwood\\\".innosoft.com", values["realm"]); | |
Assert.AreEqual("md5-sess", values["algorithm"]); | |
} | |
[Test] | |
public void CanParseEmbeddedDelimiter() | |
{ | |
string challenge = "realm=\"elwood,innosoft.com\", algorithm=md5-sess"; | |
StringDictionary values = DigestChallenge.ParseParameters(challenge); | |
Assert.AreEqual(2, values.Count); | |
Assert.AreEqual("elwood,innosoft.com", values["realm"]); | |
Assert.AreEqual("md5-sess", values["algorithm"]); | |
} | |
[Test] | |
public void CanParse1() | |
{ | |
string challenge = "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8"; | |
DigestChallenge parsed = DigestChallenge.Parse(challenge); | |
Assert.AreEqual("elwood.innosoft.com", parsed.Realm); | |
Assert.AreEqual("OA6MG9tEQGm2hh", parsed.Nonce); | |
Assert.Contains("auth", parsed.QopOptions); | |
Assert.AreEqual("md5-sess", parsed.Algorithm); | |
Assert.AreEqual("utf-8", parsed.Charset); | |
} | |
#endregion // Digest Challenge Parsing Tests | |
#region Digest Response Tests | |
// | |
// Digest Response Tests | |
// | |
[Test] | |
public void CanWriteResponse() | |
{ | |
DigestResponse resp = new DigestResponse(); | |
resp.Username = "user"; | |
resp.Realm = "nowhere.com"; | |
resp.Nonce = "OA9BSXrbuRhWay"; | |
resp.Cnonce = "OA9BSuZWMSpW8m"; | |
resp.NonceCount = 16; | |
resp.DigestUri = "acap/elwood.innosoft.com"; | |
resp.Response = "6084c6db3fede7352c551284490fd0fc"; | |
resp.Qop = "auth"; | |
resp.MaxBuffer = 65536; | |
resp.Cipher = "3des"; | |
resp.Authzid = "user2"; | |
resp.AuthParam = "ap"; | |
resp.Charset = "utf-8"; | |
string expected = "username=\"user\",realm=\"nowhere.com\",nonce=\"OA9BSXrbuRhWay\",cnonce=\"OA9BSuZWMSpW8m\",nc=00000010,qop=auth,digest-uri=\"acap/elwood.innosoft.com\",response=\"6084c6db3fede7352c551284490fd0fc\",maxbuf=65536,charset=utf-8,cipher=3des,authzid=\"user2\",auth-param=\"ap\""; | |
Assert.AreEqual(expected, resp.ToString()); | |
} | |
[Test] | |
public void CanWriteEscapedSecuence() | |
{ | |
DigestResponse resp = new DigestResponse(); | |
resp.Username = "us\"er"; | |
string expected = "username=\"us\\\"er\",nc=00000000,maxbuf=0"; | |
Assert.AreEqual(expected, resp.ToString()); | |
} | |
#endregion // Digest Response Tests | |
#region Authentication Tests | |
// | |
// Authentication Tests | |
// | |
[Test] | |
public void ReturnsRightMechanismName() | |
{ | |
ISaslClient client = CreateClient(); | |
Assert.AreEqual("DIGEST-MD5", client.MechanismName); | |
} | |
[Test] | |
public void HasInitialResponseReturnsFalse() | |
{ | |
ISaslClient client = CreateClient(); | |
Assert.IsFalse(client.HasInitialResponse); | |
} | |
[Test] | |
public void CanAuthenticate() | |
{ | |
string challenge = "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8"; | |
DigestSaslClient client = CreateClient(); | |
client.Cnonce = "OA6MHXh6VqTrRk"; | |
byte[] bresp = client.EvaluateChallenge(Encoding.UTF8.GetBytes(challenge)); | |
string response = Encoding.UTF8.GetString(bresp); | |
string expectedResp = "username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",cnonce=\"" + | |
client.Cnonce + "\",nc=00000001,qop=auth,digest-uri=\"imap/elwood.innosoft.com\",response=\"d388dad90d4bbd760a152321f2143af7\",maxbuf=65536,charset=utf-8"; | |
Assert.AreEqual(expectedResp, response); | |
Assert.IsFalse(client.IsComplete); | |
string challenge2 = "rspauth=ea40f60335c427b5527b84dbabcdfffd"; | |
bresp = client.EvaluateChallenge(Encoding.UTF8.GetBytes(challenge2)); | |
// client responds with zero-length array | |
Assert.AreEqual(0, bresp.Length); | |
Assert.IsTrue(client.IsComplete); | |
} | |
[Test] | |
[ExpectedException(typeof(ArgumentNullException))] | |
public void ThrowsExceptionWhenChallengeIsMissing() | |
{ | |
DigestSaslClient client = CreateClient(); | |
client.EvaluateChallenge(null); | |
} | |
[Test] | |
[ExpectedException(typeof(SaslException))] | |
public void ThrowsExceptionWhenNonceMissing() | |
{ | |
string challenge = "realm=\"elwood.innosoft.com\""; | |
DigestSaslClient client = CreateClient(); | |
client.EvaluateChallenge(Encoding.UTF8.GetBytes(challenge)); | |
} | |
[Test] | |
[ExpectedException(typeof(SaslException))] | |
public void ThrowsExceptionWhenAlgorithmMissing() | |
{ | |
string challenge = "realm=\"elwood.innosoft.com\",nonce=\"asdasadsad\""; | |
DigestSaslClient client = CreateClient(); | |
client.EvaluateChallenge(Encoding.UTF8.GetBytes(challenge)); | |
} | |
[Test] | |
[ExpectedException(typeof(SaslException))] | |
public void ThrowsExceptionWhenSecondChallengeInvalid() | |
{ | |
string challenge = "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8"; | |
DigestSaslClient client = CreateClient(); | |
byte[] bresp = client.EvaluateChallenge(Encoding.UTF8.GetBytes(challenge)); | |
Encoding.UTF8.GetString(bresp); | |
// repeat challenge 1, which is incorrect | |
client.EvaluateChallenge(Encoding.UTF8.GetBytes(challenge)); | |
} | |
private DigestSaslClient CreateClient() | |
{ | |
return new DigestSaslClient( | |
AUTHID, SERVERNAME, PROTOCOL, | |
new Hashtable(), this | |
); | |
} | |
void ISaslCallbackHandler.Handle(ISaslCallback[] callbacks) | |
{ | |
foreach ( ISaslCallback cb in callbacks ) | |
{ | |
if ( cb is NameCallback ) | |
{ | |
((NameCallback)cb).Text = USERNAME; | |
} else if ( cb is PasswordCallback ) | |
{ | |
((PasswordCallback)cb).Text = PASSWORD; | |
} else if ( cb is RealmCallback ) | |
{ | |
((RealmCallback)cb).Text = SERVERNAME; | |
} | |
} | |
} | |
#endregion // Authentication Tests | |
} // class DigestSaslClientTests | |
} // namespace Apache.Qpid.Sasl.Tests.Mechanisms |