diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 481229840e..31203908da 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -22,6 +22,7 @@ Currently supported XEPs of smack-tcp | Name | XEP | Description | |---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------| | [Stream Management](streammanagement.md) | [XEP-0198](http://xmpp.org/extensions/xep-0198.html) | Allows active management of an XML Stream between two XMPP entities (stanza acknowledgement, stream resumption). | +| Instant Stream Resumption | [XEP-xxxx](http://xmpp.org/extensions/inbox/isr.html) | Allows XMPP entities to instantaneously resume an XMPP stream.. | Smack Extensions and currently supported XEPs of smack-extensions diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/HMAC.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/HMAC.java new file mode 100644 index 0000000000..ae7fbc97e4 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/HMAC.java @@ -0,0 +1,75 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/** + * Instant Stream Resumption HMAC class. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +public class HMAC { + + /** + * Get the HMAC digest. + * + * @param msg + * @param keyString + * @param algo + * @return the HMAC digest + */ + public static String hmacDigest(String msg, String keyString, String algo) { + String digest = null; + try { + SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), algo); + + String algorithm; + if (!algo.toLowerCase().startsWith("hmac")) { + algorithm = "hmac" + algo.toLowerCase(); + } else { + algorithm = algo.toLowerCase(); + } + + Mac mac = Mac.getInstance(algorithm); + mac.init(key); + + byte[] bytes = mac.doFinal(msg.getBytes("ASCII")); + + StringBuffer hash = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xFF & bytes[i]); + if (hex.length() == 1) { + hash.append('0'); + } + hash.append(hex); + } + digest = hash.toString(); + } catch (UnsupportedEncodingException | InvalidKeyException | NoSuchAlgorithmException e) { + throw new IllegalStateException("HMAC digest could not be obtained correctly.", e); + } + return digest; + } +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/ISRUtils.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/ISRUtils.java new file mode 100644 index 0000000000..f0ca231333 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/ISRUtils.java @@ -0,0 +1,49 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr; + +import java.io.IOException; + +import org.jivesoftware.smack.isr.element.InstantStreamResumption; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Instant Stream Resumption utils. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +public class ISRUtils { + + /** + * Check if is a nonza from Instant Stream Resumption. + * + * @param parser + * @return true if is a nonza from Instant Stream Resumption + * @throws XmlPullParserException + * @throws IOException + */ + public static boolean isISRNonza(XmlPullParser parser) throws XmlPullParserException, IOException { + String isrNamespace = parser.getNamespace(InstantStreamResumption.NAMESPACE_PREFIX); + return (isrNamespace != null && isrNamespace.equals(InstantStreamResumption.NAMESPACE)) + || parser.getNamespace().equals(InstantStreamResumption.NAMESPACE); + } + +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/InstantStreamResumption.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/InstantStreamResumption.java new file mode 100644 index 0000000000..179505e9b5 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/InstantStreamResumption.java @@ -0,0 +1,480 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr.element; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.Nonza; +import org.jivesoftware.smack.sm.packet.StreamManagement; +import org.jivesoftware.smack.util.XmlStringBuilder; + +/** + * XEP-xxxx: Instant Stream Resumption. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +public class InstantStreamResumption { + + public static final String NAMESPACE = "urn:xmpp:isr:0"; + + public static final String NAMESPACE_PREFIX = "isr"; + + /** + * Instant Stream Resumption feature. + * + * @author Fernando Ramirez + * @see XEP-xxxx: + * Instant Stream Resumption + * + */ + public static final class InstantStreamResumptionFeature implements ExtensionElement { + + public static final String ELEMENT = "isr"; + public static final InstantStreamResumptionFeature INSTANCE = new InstantStreamResumptionFeature(); + + private InstantStreamResumptionFeature() { + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.closeEmptyElement(); + return xml; + } + } + + /** + * Instant Stream Resumption enabled nonza. + * + * @author Fernando Ramirez + * @see XEP-xxxx: + * Instant Stream Resumption + * + */ + public static class Enabled implements Nonza { + + public static final String ELEMENT = "enabled"; + + private final String key; + private final String location; + + /** + * Enabled nonza constructor. + * + * @param key + * @param location + */ + public Enabled(String key, String location) { + this.key = key; + this.location = location; + } + + /** + * Enabled nonza constructor. + * + * @param key + */ + public Enabled(String key) { + this(key, null); + } + + /** + * Get the key. + * + * @return the key + */ + public String getKey() { + return key; + } + + /** + * Get the location. + * + * @return the location + */ + public String getLocation() { + return location; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public String getNamespace() { + return StreamManagement.NAMESPACE; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.halfOpenElement(ELEMENT); + xml.xmlnsAttribute(StreamManagement.NAMESPACE); + xml.attribute("xmlns:isr", NAMESPACE); + xml.attribute("isr:key", key); + xml.optAttribute("isr:location", location); + xml.closeEmptyElement(); + return xml; + } + + } + + /** + * Instant Stream Resumption resume nonza. + * + * @author Fernando Ramirez + * @see XEP-xxxx: + * Instant Stream Resumption + * + */ + public static class InstResume implements Nonza { + + public static final String ELEMENT = "inst-resume"; + + private final String prevId; + private final long handledCount; + private final String hash; + private final String algo; + + /** + * Instant resume nonza constructor. + * + * @param prevId + * @param handledCount + * @param hash + * @param algo + */ + public InstResume(String prevId, long handledCount, String hash, String algo) { + this.prevId = prevId; + this.handledCount = handledCount; + this.hash = hash; + this.algo = algo; + } + + /** + * Get the previous id. + * + * @return the previous id + */ + public String getPrevId() { + return prevId; + } + + /** + * Get the handled count. + * + * @return the handled count + */ + public long getHandledCount() { + return handledCount; + } + + /** + * Get the hash. + * + * @return the hash + */ + public String getHash() { + return hash; + } + + /** + * Get the algorithm. + * + * @return the algorithm + */ + public String getAlgo() { + return algo; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.optAttribute("previd", prevId); + xml.optAttribute("h", Long.toString(handledCount)); + xml.rightAngleBracket(); + + xml.openElement("hmac"); + + xml.element(new HashElement(hash, algo)); + + xml.closeElement("hmac"); + xml.closeElement(this); + return xml; + } + + } + + /** + * Instant Stream Resumption resumed nonza. + * + * @author Fernando Ramirez + * @see XEP-xxxx: + * Instant Stream Resumption + * + */ + public static class InstResumed implements Nonza { + + public static final String ELEMENT = "inst-resumed"; + + private final String key; + private final long handledCount; + private final String hash; + private final String algo; + + /** + * Instant resumed nonza constructor. + * + * @param key + * @param handledCount + * @param hash + * @param algo + */ + public InstResumed(String key, long handledCount, String hash, String algo) { + this.key = key; + this.handledCount = handledCount; + this.hash = hash; + this.algo = algo; + } + + /** + * Instant resumed nonza constructor. + * + * @param key + * @param hash + * @param algo + */ + public InstResumed(String key, String hash, String algo) { + this(key, -1, hash, algo); + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + /** + * Get the key. + * + * @return the key + */ + public String getKey() { + return key; + } + + /** + * Get the handled count. + * + * @return the handled count + */ + public long getHandledCount() { + return handledCount; + } + + /** + * Get the hash. + * + * @return the hash + */ + public String getHash() { + return hash; + } + + /** + * Get the algorithm. + * + * @return the algorithm + */ + public String getAlgo() { + return algo; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.optAttribute("key", key); + if (handledCount > 0) { + xml.optAttribute("h", Long.toString(handledCount)); + } + xml.rightAngleBracket(); + + xml.openElement("hmac"); + + xml.element(new HashElement(hash, algo)); + + xml.closeElement("hmac"); + xml.closeElement(this); + return xml; + } + + } + + /** + * Hash element. + * + * @author Fernando Ramirez + * + */ + public static class HashElement implements ExtensionElement { + + public static final String ELEMENT = "hash"; + public static final String NAMESPACE = "urn:xmpp:hashes:1"; + + private final String hash; + private final String algo; + + /** + * Hash element constructor. + * + * @param hash + * @param algo + */ + public HashElement(String hash, String algo) { + this.hash = hash; + this.algo = algo; + } + + /** + * Get the hash. + * + * @return the hash. + */ + public String getHash() { + return hash; + } + + /** + * Get the algorithm. + * + * @return the algorithm + */ + public String getAlgo() { + return algo; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + xml.attribute("algo", algo); + xml.rightAngleBracket(); + xml.escape(hash); + xml.closeElement(this); + return xml; + } + + } + + /** + * Instant Stream Resumption failed nonza. + * + * @author Fernando Ramirez + * @see XEP-xxxx: + * Instant Stream Resumption + * + */ + public static class Failed implements Nonza { + + public static final String ELEMENT = "failed"; + + private final long handledCount; + + /** + * Failed nonza constructor. + * + * @param handledCount + */ + public Failed(long handledCount) { + this.handledCount = handledCount; + } + + /** + * Failed nonza constructor. + */ + public Failed() { + this(-1); + } + + /** + * Get the handled count. + * + * @return the handled count + */ + public long getHandledCount() { + return handledCount; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder xml = new XmlStringBuilder(this); + if (handledCount > 0) { + xml.optAttribute("h", Long.toString(handledCount)); + } + xml.closeEmptyElement(); + return xml; + } + + } + +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/package-info.java new file mode 100644 index 0000000000..b2cd1771b1 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +/** + * XEP-xxxx: Instant Stream Resumption. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +package org.jivesoftware.smack.isr.element; diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/package-info.java new file mode 100644 index 0000000000..4ec242875d --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +/** + * XEP-xxxx: Instant Stream Resumption. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +package org.jivesoftware.smack.isr; diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/InstantStreamResumptionFeatureProvider.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/InstantStreamResumptionFeatureProvider.java new file mode 100644 index 0000000000..042db3d7f9 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/InstantStreamResumptionFeatureProvider.java @@ -0,0 +1,38 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr.provider; + +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstantStreamResumptionFeature; +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * Instant Stream Resumption feature provider class. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +public class InstantStreamResumptionFeatureProvider extends ExtensionElementProvider { + + @Override + public InstantStreamResumptionFeature parse(XmlPullParser parser, int initialDepth) throws Exception { + return InstantStreamResumptionFeature.INSTANCE; + } + +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/ParseInstantStreamResumption.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/ParseInstantStreamResumption.java new file mode 100644 index 0000000000..54de81efb1 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/ParseInstantStreamResumption.java @@ -0,0 +1,93 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr.provider; + +import java.io.IOException; + +import org.jivesoftware.smack.isr.element.InstantStreamResumption; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.Enabled; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.Failed; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResumed; +import org.jivesoftware.smack.util.ParserUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Parse Instant Stream Resumption class. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +public class ParseInstantStreamResumption { + + public static Enabled enabled(XmlPullParser parser) throws XmlPullParserException, IOException { + ParserUtils.assertAtStartTag(parser); + String key = parser.getAttributeValue(InstantStreamResumption.NAMESPACE, "key"); + String location = parser.getAttributeValue(InstantStreamResumption.NAMESPACE, "location"); + parser.next(); + ParserUtils.assertAtEndTag(parser); + return new Enabled(key, location); + } + + public static Failed failed(XmlPullParser parser) throws XmlPullParserException, IOException { + ParserUtils.assertAtStartTag(parser); + Long handledCount = ParserUtils.getLongAttribute(parser, "h"); + parser.next(); + ParserUtils.assertAtEndTag(parser); + + if (handledCount == null) { + return new Failed(); + } else { + return new Failed(handledCount); + } + } + + public static InstResumed resumed(XmlPullParser parser) throws XmlPullParserException, IOException { + // inst-resumed + ParserUtils.assertAtStartTag(parser); + String key = parser.getAttributeValue("", "key"); + Long handledCount = ParserUtils.getLongAttribute(parser, "h"); + + // hmac + parser.next(); + ParserUtils.assertAtStartTag(parser); + + // hash + parser.next(); + ParserUtils.assertAtStartTag(parser); + String algo = parser.getAttributeValue("", "algo"); + String hash = parser.nextText(); + ParserUtils.assertAtEndTag(parser); + + // close hmac + parser.next(); + ParserUtils.assertAtEndTag(parser); + + // close inst-resumed + parser.next(); + ParserUtils.assertAtEndTag(parser); + + if (handledCount == null) { + return new InstResumed(key, hash, algo); + } else { + return new InstResumed(key, handledCount, hash, algo); + } + } + +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/package-info.java new file mode 100644 index 0000000000..bb58e4e6b6 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/package-info.java @@ -0,0 +1,25 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +/** + * XEP-xxxx: Instant Stream Resumption. + * + * @author Fernando Ramirez + * @see XEP-xxxx: Instant + * Stream Resumption + * + */ +package org.jivesoftware.smack.isr.provider; diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 92a2ba631b..7c196de13d 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -16,36 +16,99 @@ */ package org.jivesoftware.smack.tcp; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.SocketFactory; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; + import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; -import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.AlreadyConnectedException; import org.jivesoftware.smack.SmackException.AlreadyLoggedInException; +import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; +import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.SynchronizationPoint; -import org.jivesoftware.smack.XMPPException.FailedNonzaException; -import org.jivesoftware.smack.XMPPException.StreamErrorException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.XMPPException.FailedNonzaException; +import org.jivesoftware.smack.XMPPException.StreamErrorException; +import org.jivesoftware.smack.compress.packet.Compress; import org.jivesoftware.smack.compress.packet.Compressed; import org.jivesoftware.smack.compression.XMPPInputOutputStream; import org.jivesoftware.smack.filter.StanzaFilter; -import org.jivesoftware.smack.compress.packet.Compress; +import org.jivesoftware.smack.isr.HMAC; +import org.jivesoftware.smack.isr.ISRUtils; +import org.jivesoftware.smack.isr.element.InstantStreamResumption; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResume; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResumed; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstantStreamResumptionFeature; +import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption; import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.StreamOpen; -import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StartTls; +import org.jivesoftware.smack.packet.StreamOpen; +import org.jivesoftware.smack.packet.XMPPError.Condition; +import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.packet.StreamError; import org.jivesoftware.smack.sasl.packet.SaslStreamElements; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge; @@ -67,8 +130,6 @@ import org.jivesoftware.smack.sm.packet.StreamManagement.StreamManagementFeature; import org.jivesoftware.smack.sm.predicates.Predicate; import org.jivesoftware.smack.sm.provider.ParseStreamManagement; -import org.jivesoftware.smack.packet.Nonza; -import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown; import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.DNSUtil; @@ -86,59 +147,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import javax.net.SocketFactory; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.PasswordCallback; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.lang.reflect.Constructor; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Provider; -import java.security.SecureRandom; -import java.security.Security; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * Creates a socket connection to an XMPP server. This is the default connection * to an XMPP server and is specified in the XMPP Core (RFC 6120). @@ -221,6 +229,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { */ private String smSessionId; + private String isrKey; + + private String isrAlgo; + + // TODO check again how to use this + private String isrHmacHash; + private final SynchronizationPoint smResumedSyncPoint = new SynchronizationPoint<>( this, "stream resumed element"); @@ -389,7 +404,16 @@ protected synchronized void loginInternal(String username, String password, Reso maybeEnableCompression(); if (isSmResumptionPossible()) { - smResumedSyncPoint.sendAndWaitForResponse(new Resume(clientHandledStanzasCount, smSessionId)); + + if (isrKey != null && isrAlgo != null) { + // instant stream resumption + smResumedSyncPoint.sendAndWaitForResponse(new InstResume(smSessionId, clientHandledStanzasCount, + HMAC.hmacDigest("Initiator", isrKey, isrAlgo), isrAlgo)); + } else { + // stream management resume + smResumedSyncPoint.sendAndWaitForResponse(new Resume(clientHandledStanzasCount, smSessionId)); + } + if (smResumedSyncPoint.wasSuccessful()) { // We successfully resumed the stream, be done here afterSuccessfulLogin(true); @@ -1107,31 +1131,57 @@ private void parsePackets() { compressSyncPoint.reportSuccess(); break; case Enabled.ELEMENT: - Enabled enabled = ParseStreamManagement.enabled(parser); - if (enabled.isResumeSet()) { - smSessionId = enabled.getId(); - if (StringUtils.isNullOrEmpty(smSessionId)) { - SmackException xmppException = new SmackException("Stream Management 'enabled' element with resume attribute but without session id received"); + if (ISRUtils.isISRNonza(parser)) { + org.jivesoftware.smack.isr.element.InstantStreamResumption.Enabled enabled = ParseInstantStreamResumption + .enabled(parser); + isrKey = enabled.getKey(); + if (isrKey == null) { + SmackException xmppException = new SmackException("Instant Stream Resumption 'enabled' element without key"); smEnabledSyncPoint.reportFailure(xmppException); throw xmppException; } - smServerMaxResumptimTime = enabled.getMaxResumptionTime(); } else { - // Mark this a non-resumable stream by setting smSessionId to null - smSessionId = null; + Enabled enabled = ParseStreamManagement.enabled(parser); + if (enabled.isResumeSet()) { + smSessionId = enabled.getId(); + if (StringUtils.isNullOrEmpty(smSessionId)) { + SmackException xmppException = new SmackException("Stream Management 'enabled' element with resume attribute but without session id received"); + smEnabledSyncPoint.reportFailure(xmppException); + throw xmppException; + } + smServerMaxResumptimTime = enabled.getMaxResumptionTime(); + } else { + // Mark this a non-resumable stream by + // setting smSessionId to null + smSessionId = null; + } } + clientHandledStanzasCount = 0; smWasEnabledAtLeastOnce = true; smEnabledSyncPoint.reportSuccess(); LOGGER.fine("Stream Management (XEP-198): succesfully enabled"); break; case Failed.ELEMENT: - Failed failed = ParseStreamManagement.failed(parser); - FailedNonzaException xmppException = new FailedNonzaException(failed, failed.getXMPPErrorCondition()); - // If only XEP-198 would specify different failure elements for the SM - // enable and SM resume failure case. But this is not the case, so we - // need to determine if this is a 'Failed' response for either 'Enable' - // or 'Resume'. + FailedNonzaException xmppException = null; + + if (ISRUtils.isISRNonza(parser)) { + org.jivesoftware.smack.isr.element.InstantStreamResumption.Failed failed = ParseInstantStreamResumption + .failed(parser); + processHandledCount(failed.getHandledCount()); + + xmppException = new FailedNonzaException(failed, Condition.undefined_condition); + } else { + Failed failed = ParseStreamManagement.failed(parser); + xmppException = new FailedNonzaException(failed, failed.getXMPPErrorCondition()); + // If only XEP-198 would specify different + // failure elements for the SM + // enable and SM resume failure case. But this + // is not the case, so we + // need to determine if this is a 'Failed' + // response for either 'Enable' + // or 'Resume'. + } if (smResumedSyncPoint.requestSent()) { smResumedSyncPoint.reportFailure(xmppException); } @@ -1146,24 +1196,59 @@ private void parsePackets() { lastFeaturesReceived.reportSuccess(); } break; + case InstResumed.ELEMENT: + InstResumed instResumed = ParseInstantStreamResumption.resumed(parser); + isrKey = instResumed.getKey(); + isrHmacHash = instResumed.getHash(); + isrAlgo = instResumed.getAlgo(); + + // Mark SM as enabled and resumption as + // successful. + smResumedSyncPoint.reportSuccess(); + smEnabledSyncPoint.reportSuccess(); + // First, drop the stanzas already handled by + // the server + processHandledCount(instResumed.getHandledCount()); + // Then re-send what is left in the unacknowledged + // queue + List stanzasListToResend = new ArrayList<>(unacknowledgedStanzas.size()); + unacknowledgedStanzas.drainTo(stanzasListToResend); + for (Stanza stanza : stanzasListToResend) { + sendStanzaInternal(stanza); + } + // If there where stanzas resent, then request a SM + // ack for them. + // Writer's sendStreamElement() won't do it + // automatically based on + // predicates. + if (!stanzasListToResend.isEmpty()) { + requestSmAcknowledgementInternal(); + } + LOGGER.fine("Stream Management (XEP-198): Stream resumed"); + break; case Resumed.ELEMENT: Resumed resumed = ParseStreamManagement.resumed(parser); if (!smSessionId.equals(resumed.getPrevId())) { throw new StreamIdDoesNotMatchException(smSessionId, resumed.getPrevId()); } - // Mark SM as enabled and resumption as successful. + // Mark SM as enabled and resumption as + // successful. smResumedSyncPoint.reportSuccess(); smEnabledSyncPoint.reportSuccess(); - // First, drop the stanzas already handled by the server + // First, drop the stanzas already handled by + // the server processHandledCount(resumed.getHandledCount()); - // Then re-send what is left in the unacknowledged queue + // Then re-send what is left in the unacknowledged + // queue List stanzasToResend = new ArrayList<>(unacknowledgedStanzas.size()); unacknowledgedStanzas.drainTo(stanzasToResend); for (Stanza stanza : stanzasToResend) { sendStanzaInternal(stanza); } - // If there where stanzas resent, then request a SM ack for them. - // Writer's sendStreamElement() won't do it automatically based on + // If there where stanzas resent, then request a SM + // ack for them. + // Writer's sendStreamElement() won't do it + // automatically based on // predicates. if (!stanzasToResend.isEmpty()) { requestSmAcknowledgementInternal(); @@ -1752,7 +1837,17 @@ public boolean isSmAvailable() { } /** - * Returns true if Stream Management was successfully negotiated with the server. + * Returns true if Instant Stream Resumption is supported by the server. + * + * @return true if Instant Stream Resumption is supported by the server. + */ + public boolean isISRAvailable() { + return hasFeature(InstantStreamResumptionFeature.ELEMENT, InstantStreamResumption.NAMESPACE); + } + + /** + * Returns true if Stream Management was successfully negotiated with the + * server. * * @return true if Stream Management was negotiated. */ diff --git a/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers b/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers index 92238115e3..02e663fbda 100644 --- a/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers +++ b/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers @@ -8,4 +8,11 @@ org.jivesoftware.smack.sm.provider.StreamManagementStreamFeatureProvider + + + isr + urn:xmpp:isr:0 + org.jivesoftware.smack.isr.provider.InstantStreamResumptionFeatureProvider + + diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISREnabledNonzaTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISREnabledNonzaTest.java new file mode 100644 index 0000000000..daa29e76bb --- /dev/null +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISREnabledNonzaTest.java @@ -0,0 +1,55 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr; + +import org.jivesoftware.smack.isr.element.InstantStreamResumption.Enabled; +import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class ISREnabledNonzaTest { + + private static final String enabledNonza = ""; + + private static final String enabledNonzaWithLocation = ""; + + @Test + public void checkISREnabledNonza() throws Exception { + Enabled enabled = new Enabled("a0b9162d-0981-4c7d-9174-1f55aedd1f52"); + Assert.assertEquals(enabledNonza, enabled.toXML().toString()); + + Enabled enabledWithLocation = new Enabled("a0b9162d-0981-4c7d-9174-1f55aedd1f52", "isr.example.org:5222"); + Assert.assertEquals(enabledNonzaWithLocation, enabledWithLocation.toXML().toString()); + } + + @Test + public void checkParseISREnabledNonza() throws Exception { + XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(enabledNonza); + Enabled enabled = ParseInstantStreamResumption.enabled(xmlPullParser); + Assert.assertEquals(enabledNonza, enabled.toXML().toString()); + + XmlPullParser xmlPullParser2 = PacketParserUtils.getParserFor(enabledNonzaWithLocation); + Enabled enabledWithLocation = ParseInstantStreamResumption.enabled(xmlPullParser2); + Assert.assertEquals(enabledNonzaWithLocation, enabledWithLocation.toXML().toString()); + } + +} diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRFailedNonzaTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRFailedNonzaTest.java new file mode 100644 index 0000000000..b3ce06a8f1 --- /dev/null +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRFailedNonzaTest.java @@ -0,0 +1,51 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr; + +import org.jivesoftware.smack.isr.element.InstantStreamResumption.Failed; +import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class ISRFailedNonzaTest { + + private static final String failedNonza = ""; + private static final String failedNonzaWithHandledCount = ""; + + @Test + public void checkISRFailedNonza() throws Exception { + Failed failed = new Failed(); + Assert.assertEquals(failedNonza, failed.toXML().toString()); + + Failed failedWithHandledCount = new Failed(22); + Assert.assertEquals(failedNonzaWithHandledCount, failedWithHandledCount.toXML().toString()); + } + + @Test + public void checkParseISRFailedNonza() throws Exception { + XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(failedNonza); + Failed failed = ParseInstantStreamResumption.failed(xmlPullParser); + Assert.assertEquals(failedNonza, failed.toXML().toString()); + + XmlPullParser xmlPullParser2 = PacketParserUtils.getParserFor(failedNonzaWithHandledCount); + Failed failedWithHandledCount = ParseInstantStreamResumption.failed(xmlPullParser2); + Assert.assertEquals(failedNonzaWithHandledCount, failedWithHandledCount.toXML().toString()); + } + +} diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRResumeNonzasTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRResumeNonzasTest.java new file mode 100644 index 0000000000..b69de2ea4a --- /dev/null +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRResumeNonzasTest.java @@ -0,0 +1,58 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr; + +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResume; +import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResumed; +import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class ISRResumeNonzasTest { + + private static final String resumeNonza = "" + "" + "" + "initator-hmac" + "" + + "" + ""; + + private static final String resumedNonza = "" + "" + + "" + "responder-hmac" + "" + "" + + ""; + + @Test + public void checkResumeNonza() throws Exception { + InstResume instResume = new InstResume("some-long-sm-id", 42, "initator-hmac", "sha256"); + Assert.assertEquals(resumeNonza, instResume.toXML().toString()); + } + + @Test + public void checkResumedNonza() throws Exception { + InstResumed instResumed = new InstResumed("006b1a29-c549-41c7-a12c-2a931822f8c0", 21, "responder-hmac", + "sha256"); + Assert.assertEquals(resumedNonza, instResumed.toXML().toString()); + } + + @Test + public void checkParseResumedNonza() throws Exception { + XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(resumedNonza); + InstResumed instResumed = ParseInstantStreamResumption.resumed(xmlPullParser); + Assert.assertEquals(resumedNonza, instResumed.toXML().toString()); + } + +} diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRUtilsTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRUtilsTest.java new file mode 100644 index 0000000000..a956bdf7a3 --- /dev/null +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRUtilsTest.java @@ -0,0 +1,117 @@ +/** + * + * Copyright © 2016 Fernando Ramirez + * + * Licensed 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. + */ +package org.jivesoftware.smack.isr; + +import org.jivesoftware.smack.util.PacketParserUtils; +import org.junit.Assert; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class ISRUtilsTest { + + private static final String isrFailedNonza = ""; + + private static final String isrFailedNonzaWithHandledCount = ""; + + private static final String isrEnabledNonza = ""; + + private static final String isrEnabledNonzaWithLocation = ""; + + private static final String isrResumedNonza = "" + "" + + "" + "responder-hmac" + "" + "" + + ""; + + private static final String smFailedNonza = "" + + "" + ""; + + private static final String smEnabledNonza = ""; + + private static final String smEnabledNonzaResume = ""; + + private static final String smEnabledNonzaResumeWithLocation = ""; + + private static final String smResumedNonza = ""; + + @Test + public void checkIsISRNonzaValidation() throws Exception { + XmlPullParser parserISRFailedNonza = PacketParserUtils.getParserFor(isrFailedNonza); + Assert.assertTrue(ISRUtils.isISRNonza(parserISRFailedNonza)); + + XmlPullParser parserISRFailedNonzaWithHandledCount = PacketParserUtils + .getParserFor(isrFailedNonzaWithHandledCount); + Assert.assertTrue(ISRUtils.isISRNonza(parserISRFailedNonzaWithHandledCount)); + + XmlPullParser parserISREnabledNonza = PacketParserUtils.getParserFor(isrEnabledNonza); + Assert.assertTrue(ISRUtils.isISRNonza(parserISREnabledNonza)); + + XmlPullParser parserISREnabledNonzaWithLocation = PacketParserUtils.getParserFor(isrEnabledNonzaWithLocation); + Assert.assertTrue(ISRUtils.isISRNonza(parserISREnabledNonzaWithLocation)); + + XmlPullParser parserISRResumedNonza = PacketParserUtils.getParserFor(isrResumedNonza); + Assert.assertTrue(ISRUtils.isISRNonza(parserISRResumedNonza)); + + XmlPullParser parserSMFailedNonza = PacketParserUtils.getParserFor(smFailedNonza); + Assert.assertFalse(ISRUtils.isISRNonza(parserSMFailedNonza)); + + XmlPullParser parserSMEnabledNonza = PacketParserUtils.getParserFor(smEnabledNonza); + Assert.assertFalse(ISRUtils.isISRNonza(parserSMEnabledNonza)); + + XmlPullParser parserSMEnabledNonzaResume = PacketParserUtils.getParserFor(smEnabledNonzaResume); + Assert.assertFalse(ISRUtils.isISRNonza(parserSMEnabledNonzaResume)); + + XmlPullParser parserSMEnabledNonzaResumeWithLocation = PacketParserUtils + .getParserFor(smEnabledNonzaResumeWithLocation); + Assert.assertFalse(ISRUtils.isISRNonza(parserSMEnabledNonzaResumeWithLocation)); + + XmlPullParser parserSMResumedNonza = PacketParserUtils.getParserFor(smResumedNonza); + Assert.assertFalse(ISRUtils.isISRNonza(parserSMResumedNonza)); + } + + @Test + public void checkHMACDigest() throws Exception { + String msg = "Initiator"; + String token = "006b1a29-c549-41c7-a12c-2a931822f8c0"; + + String sha256DigestResult = "e5e6241898f631342111958a51b61a1d75d492c782c8620b4efd0d9f172b55ca"; + String sha1DigestResult = "583ff09b098c3f0bb3dbbc8cb0ef279755db7cde"; + String md5DigestResult = "14b8db2ba5cdb9088e92a640485207a3"; + + String hmacDigest = HMAC.hmacDigest(msg, token, "SHA256"); + Assert.assertEquals(sha256DigestResult, hmacDigest); + + hmacDigest = HMAC.hmacDigest(msg, token, "sha256"); + Assert.assertEquals(sha256DigestResult, hmacDigest); + + hmacDigest = HMAC.hmacDigest(msg, token, "SHA1"); + Assert.assertEquals(sha1DigestResult, hmacDigest); + + hmacDigest = HMAC.hmacDigest(msg, token, "md5"); + Assert.assertEquals(md5DigestResult, hmacDigest); + + hmacDigest = HMAC.hmacDigest(msg, token, "hMACmd5"); + Assert.assertEquals(md5DigestResult, hmacDigest); + + } + +}