diff options
25 files changed, 831 insertions, 419 deletions
@@ -80,7 +80,7 @@ <classpathentry kind="lib" path="lib/os-specific/mac/growl4j.jar"/> <classpathentry kind="lib" path="lib/os-specific/mac/OrangeExtensions.jar"/> <classpathentry kind="lib" path="lib/installer-exclude/jmork-1.0.5-SNAPSHOT.jar" sourcepath="/jmork"/> - <classpathentry kind="lib" path="lib/installer-exclude/bcprov-jdk15on-148.jar"/> + <classpathentry kind="lib" path="lib/installer-exclude/bcprov-jdk15on-149.jar"/> <classpathentry kind="lib" path="lib/installer-exclude/bccontrib-1.0-SNAPSHOT.jar"/> <classpathentry kind="lib" path="lib/installer-exclude/zrtp4j-light.jar"/> <classpathentry kind="output" path="classes"/> @@ -1286,6 +1286,7 @@ <zipfileset dir="${dest}/net/java/sip/communicator/impl/libjitsi" prefix="net/java/sip/communicator/impl/libjitsi"/> + <zipfileset src="${lib.noinst}/bcpkix-jdk15on-149.jar" prefix=""/> <zipfileset src="${lib.noinst}/fmj.jar" prefix=""/> <zipfileset src="${lib.noinst}/libjitsi.jar" prefix=""/> </jar> @@ -2517,7 +2518,7 @@ javax.swing.event, javax.swing.border"/> <!--BUNDLE-BOUNCYCASTLE --> <target name="bundle-bouncycastle"> - <copy file="${lib.noinst}/bcprov-jdk15on-148.jar" tofile="${bundles.dest}/bouncycastle.jar"/> + <copy file="${lib.noinst}/bcprov-jdk15on-149.jar" tofile="${bundles.dest}/bouncycastle.jar"/> <copy file="${lib.noinst}/bccontrib-1.0-SNAPSHOT.jar" tofile="${bundles.dest}/bccontrib.jar"/> </target> diff --git a/lib/installer-exclude/bcpkix-jdk15on-149.jar b/lib/installer-exclude/bcpkix-jdk15on-149.jar Binary files differnew file mode 100644 index 0000000..96d1985 --- /dev/null +++ b/lib/installer-exclude/bcpkix-jdk15on-149.jar diff --git a/lib/installer-exclude/bcprov-jdk15on-148.jar b/lib/installer-exclude/bcprov-jdk15on-149.jar Binary files differindex 3fcb136..e1d4bb3 100644 --- a/lib/installer-exclude/bcprov-jdk15on-148.jar +++ b/lib/installer-exclude/bcprov-jdk15on-149.jar diff --git a/lib/installer-exclude/bcprov.manifest.mf b/lib/installer-exclude/bcprov.manifest.mf index 95fd77e..402723b 100644 --- a/lib/installer-exclude/bcprov.manifest.mf +++ b/lib/installer-exclude/bcprov.manifest.mf @@ -361,7 +361,7 @@ Export-Package: org.bouncycastle,org.bouncycastle.asn1;uses:="org.boun urity.auth.x500,org.bouncycastle.x509,org.bouncycastle.jce.provider,o
rg.bouncycastle.util,org.bouncycastle.asn1,javax.naming,org.bouncycas
tle.asn1.x509,org.bouncycastle.jce"
-Bundle-Version: 1.48
+Bundle-Version: 1.49
Bundle-Name: bcprov
Trusted-Library: true
Ant-Version: Apache Ant 1.6.5
@@ -371,6 +371,6 @@ Bundle-SymbolicName: bcprov Tool: Bnd-1.30.0
Specification-Vendor: BouncyCastle.org
Extension-Name: org.bouncycastle.bcprovider
-Implementation-Version: 1.48.0
+Implementation-Version: 1.49.0
Implementation-Vendor: BouncyCastle.org
diff --git a/lib/installer-exclude/jain-sdp.jar b/lib/installer-exclude/jain-sdp.jar Binary files differindex 3cda590..8f4b7a6 100644 --- a/lib/installer-exclude/jain-sdp.jar +++ b/lib/installer-exclude/jain-sdp.jar diff --git a/lib/installer-exclude/libjitsi.jar b/lib/installer-exclude/libjitsi.jar Binary files differindex c7124b2..3b3eeb1 100644 --- a/lib/installer-exclude/libjitsi.jar +++ b/lib/installer-exclude/libjitsi.jar diff --git a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java index 8ba3f61..49bbe47 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java @@ -843,11 +843,8 @@ public class OneToOneCallPeerPanel SrtpControl srtpControl = evt.getSecurityController(); - if ((srtpControl.requiresSecureSignalingTransport() - && callPeer - .getProtocolProvider() - .isSignalingTransportSecure()) - || !srtpControl.requiresSecureSignalingTransport()) + if (!srtpControl.requiresSecureSignalingTransport() + || callPeer.getProtocolProvider().isSignalingTransportSecure()) { if (srtpControl instanceof ZrtpControl) { diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceParticipantPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceParticipantPanel.java index f1876f7..744de28 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceParticipantPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceParticipantPanel.java @@ -484,10 +484,9 @@ public abstract class BasicConferenceParticipantPanel<T> SrtpControl srtpControl = evt.getSecurityController(); - if ((srtpControl.requiresSecureSignalingTransport() - && getCallRenderer().getCall().getProtocolProvider() - .isSignalingTransportSecure()) - || !srtpControl.requiresSecureSignalingTransport()) + if (!srtpControl.requiresSecureSignalingTransport() + || getCallRenderer().getCall().getProtocolProvider() + .isSignalingTransportSecure()) { if (srtpControl instanceof ZrtpControl) { diff --git a/src/net/java/sip/communicator/impl/libjitsi/libjitsi.manifest.mf b/src/net/java/sip/communicator/impl/libjitsi/libjitsi.manifest.mf index da60db3..2cf9ab5 100644 --- a/src/net/java/sip/communicator/impl/libjitsi/libjitsi.manifest.mf +++ b/src/net/java/sip/communicator/impl/libjitsi/libjitsi.manifest.mf @@ -48,12 +48,27 @@ Import-Package: apple.awt, javax.xml.transform, javax.xml.transform.dom, javax.xml.transform.stream, + org.bouncycastle.asn1, + org.bouncycastle.asn1.cryptopro, + org.bouncycastle.asn1.nist, + org.bouncycastle.asn1.oiw, + org.bouncycastle.asn1.pkcs, + org.bouncycastle.asn1.teletrust, + org.bouncycastle.asn1.x500, + org.bouncycastle.asn1.x500.style, + org.bouncycastle.asn1.x509, + org.bouncycastle.asn1.x9, org.bouncycastle.crypto, org.bouncycastle.crypto.digests, org.bouncycastle.crypto.engines, + org.bouncycastle.crypto.generators, org.bouncycastle.crypto.macs, org.bouncycastle.crypto.params, org.bouncycastle.crypto.prng, + org.bouncycastle.crypto.signers, + org.bouncycastle.crypto.tls, + org.bouncycastle.crypto.util, + org.bouncycastle.util, org.jitsi.bccontrib.digests, org.jitsi.bccontrib.engines, org.jitsi.bccontrib.macs, diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java index 6d2d242..7c5c690 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/AbstractCallPeerMediaHandlerJabberGTalkImpl.java @@ -16,7 +16,6 @@ import net.java.sip.communicator.service.protocol.media.*; import net.java.sip.communicator.util.*; import org.jitsi.service.neomedia.*; - import org.jivesoftware.smack.packet.*; /** @@ -104,7 +103,8 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl if (accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("ZRTP") + && accountID.isEncryptionProtocolEnabled( + ZrtpControl.PROTO_NAME) && getPeer().getCall().isSipZrtpAttribute()) { // ZRTP @@ -151,55 +151,33 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl if(accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("SDES")) + && accountID.isEncryptionProtocolEnabled( + SDesControl.PROTO_NAME)) { - Map<MediaTypeSrtpControl, SrtpControl> srtpControls - = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES); - SrtpControl control = srtpControls.get(key); - - if(control == null) - { - control - = JabberActivator.getMediaService().createSDesControl(); - srtpControls.put(key, control); - } - - SDesControl tmpSDesControl = (SDesControl) control; + SrtpControls srtpControls = getSrtpControls(); + SDesControl sdesControl + = (SDesControl) + srtpControls.getOrCreate( + mediaType, + SrtpControlType.SDES); SrtpCryptoAttribute selectedSdes = selectSdesCryptoSuite( isInitiator, - tmpSDesControl, + sdesControl, encryptionPacketExtension); if(selectedSdes != null) { //found an SDES answer, remove all other controls - Iterator<Map.Entry<MediaTypeSrtpControl, SrtpControl>> iter - = srtpControls.entrySet().iterator(); - - while (iter.hasNext()) - { - Map.Entry<MediaTypeSrtpControl, SrtpControl> entry - = iter.next(); - MediaTypeSrtpControl mtsc = entry.getKey(); - - if ((mtsc.mediaType == mediaType) - && (mtsc.srtpControlType - != SrtpControlType.SDES)) - { - entry.getValue().cleanup(); - iter.remove(); - } - } - + removeAndCleanupOtherSrtpControls( + mediaType, + SrtpControlType.SDES); addAdvertisedEncryptionMethod(SrtpControlType.SDES); } else { - control.cleanup(); - srtpControls.remove(key); + sdesControl.cleanup(); + srtpControls.remove(mediaType, SrtpControlType.SDES); } } } @@ -207,27 +185,12 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl // manage it, then we must remove the unusable SDES srtpControl. else if(isInitiator) { - AccountID accountID - = getPeer().getProtocolProvider().getAccountID(); - // SDES - if(accountID.getAccountPropertyBoolean( - ProtocolProviderFactory.DEFAULT_ENCRYPTION, - true) - && accountID.isEncryptionProtocolEnabled("SDES")) - { - Map<MediaTypeSrtpControl, SrtpControl> srtpControls - = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES); - SrtpControl control = srtpControls.get(key); + SrtpControl scontrol + = getSrtpControls().remove(mediaType, SrtpControlType.SDES); - if(control != null) - { - control.cleanup(); - srtpControls.remove(key); - } - } + if (scontrol != null) + scontrol.cleanup(); } } @@ -283,11 +246,8 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl for(int i = 0; i < packetExtensions.size(); ++i) { if(packetExtensions.get(i) instanceof ZrtpHashPacketExtension) - { return true; - } } - return false; } @@ -338,29 +298,21 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl if(accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("ZRTP") + && accountID.isEncryptionProtocolEnabled( + ZrtpControl.PROTO_NAME) && peer.getCall().isSipZrtpAttribute()) { - Map<MediaTypeSrtpControl, SrtpControl> srtpControls - = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.ZRTP); - SrtpControl control = srtpControls.get(key); - - if(control == null) - { - control - = JabberActivator.getMediaService().createZrtpControl(); - srtpControls.put(key, control); - } - - ZrtpControl zcontrol = (ZrtpControl) control; - int versionIndex = zcontrol.getNumberSupportedVersions(); - - for (int i = 0; i < versionIndex; i++) + ZrtpControl zrtpControl + = (ZrtpControl) + getSrtpControls().getOrCreate( + mediaType, + SrtpControlType.ZRTP); + int numberSupportedVersions + = zrtpControl.getNumberSupportedVersions(); + + for (int i = 0; i < numberSupportedVersions; i++) { - String helloHash[] - = ((ZrtpControl) control).getHelloHashSep(i); + String helloHash[] = zrtpControl.getHelloHashSep(i); if ((helloHash != null) && (helloHash[1].length() > 0)) { @@ -415,23 +367,15 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl if (accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("SDES")) + && accountID.isEncryptionProtocolEnabled( + SDesControl.PROTO_NAME)) { // get or create the control - Map<MediaTypeSrtpControl, SrtpControl> srtpControls - = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES); - SrtpControl control = srtpControls.get(key); - - if (control == null) - { - control = JabberActivator.getMediaService().createSDesControl(); - srtpControls.put(key, control); - } - + SrtpControls srtpControls = getSrtpControls(); + SDesControl sdesControl + = (SDesControl) + srtpControls.getOrCreate(mediaType, SrtpControlType.SDES); // set the enabled ciphers suites - SDesControl sdcontrol = (SDesControl) control; String ciphers = accountID.getAccountPropertyString( ProtocolProviderFactory.SDES_CIPHER_SUITES); @@ -442,7 +386,7 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl JabberActivator.getResources().getSettingsString( SDesControl.SDES_CIPHER_SUITES); } - sdcontrol.setEnabledCiphers(Arrays.asList(ciphers.split(","))); + sdesControl.setEnabledCiphers(Arrays.asList(ciphers.split(","))); // act as initiator if (remoteDescription == null) @@ -457,7 +401,7 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl localDescription.addChildExtension(localEncryption); } for(SrtpCryptoAttribute ca: - sdcontrol.getInitiatorCryptoAttributes()) + sdesControl.getInitiatorCryptoAttributes()) { CryptoPacketExtension crypto = new CryptoPacketExtension(ca); @@ -479,7 +423,7 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl { SrtpCryptoAttribute selectedSdes = selectSdesCryptoSuite( false, - sdcontrol, + sdesControl, remoteEncryption); if(selectedSdes != null) @@ -505,8 +449,8 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl { // none of the offered suites match, destroy the sdes // control - sdcontrol.cleanup(); - srtpControls.remove(key); + sdesControl.cleanup(); + srtpControls.remove(mediaType, SrtpControlType.SDES); logger.warn( "Received unsupported sdes crypto attribute"); } @@ -515,8 +459,8 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl { // peer doesn't offer any SDES attribute, destroy the sdes // control - sdcontrol.cleanup(); - srtpControls.remove(key); + sdesControl.cleanup(); + srtpControls.remove(mediaType, SrtpControlType.SDES); } } } @@ -548,9 +492,29 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl for(String preferredEncryptionProtocol : preferredEncryptionProtocols) { + String protoName + = preferredEncryptionProtocol.substring( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + + 1); + + // SDES + if(SDesControl.PROTO_NAME.equals(protoName)) + { + addSDESAdvertisedEncryptions( + false, + remoteDescription, + mediaType); + if(setSDesEncryptionToDescription( + mediaType, + localDescription, + remoteDescription)) + { + // Stop once an encryption advertisement has been chosen. + return; + } + } // ZRTP - if(preferredEncryptionProtocol.equals( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".ZRTP")) + else if(ZrtpControl.PROTO_NAME.equals(protoName)) { boolean isZRTPAddedToDescription = setZrtpEncryptionToDescription( @@ -564,24 +528,7 @@ public abstract class AbstractCallPeerMediaHandlerJabberGTalkImpl false, remoteDescription, mediaType); - // Stops once an encryption advertisement has been chosen. - return; - } - } - // SDES - else if(preferredEncryptionProtocol.equals( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".SDES")) - { - addSDESAdvertisedEncryptions( - false, - remoteDescription, - mediaType); - if(setSDesEncryptionToDescription( - mediaType, - localDescription, - remoteDescription)) - { - // Stops once an encryption advertisement has been chosen. + // Stop once an encryption advertisement has been chosen. return; } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java index 4185a6d..6c4e190 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java @@ -222,11 +222,11 @@ public class CallPeerMediaHandlerGTalkImpl * @throws OperationFailedException if we fail to configure the media stream */ public RtpDescriptionPacketExtension generateSessionAccept( - boolean initStream) + boolean initStream) throws OperationFailedException { - RtpDescriptionPacketExtension description = - new RtpDescriptionPacketExtension(); + RtpDescriptionPacketExtension description + = new RtpDescriptionPacketExtension(); List<PayloadTypePacketExtension> lst = localContentMap.get("audio"); description.setNamespace(SessionIQProvider.GTALK_AUDIO_NAMESPACE); @@ -282,11 +282,10 @@ public class CallPeerMediaHandlerGTalkImpl continue; // stream target - MediaStreamTarget target = transportManager.getStreamTarget( - mediaType); + MediaStreamTarget target + = transportManager.getStreamTarget(mediaType); - List<RTPExtension> rtpExtensions = - new ArrayList<RTPExtension>(); + List<RTPExtension> rtpExtensions = new ArrayList<RTPExtension>(); MediaDirection direction = MediaDirection.SENDRECV; boolean masterStream = false; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java index 052e645..727b735 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -918,8 +918,6 @@ public class CallPeerMediaHandlerJabberImpl * Jitsi VideoBridge working on the server, prefer a * transport which will route the conference through there. */ - CallJabberImpl call = peer.getCall(); - if (isJitsiVideoBridge) { /* diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java index b3f877d..2e70e48 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java @@ -661,7 +661,7 @@ public class IceUdpTransportManager * * @return the {@link IceUdpTransportPacketExtension} that we */ - public PacketExtension createTransport(IceMediaStream stream) + protected PacketExtension createTransport(IceMediaStream stream) { IceUdpTransportPacketExtension trans = new IceUdpTransportPacketExtension(); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountIDImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountIDImpl.java index 9364d38..af33659 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountIDImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountIDImpl.java @@ -32,19 +32,6 @@ public class JabberAccountIDImpl } /** - * Returns the service name - the server we are logging to - * if it is null which is not supposed to be - we return for compatibility - * the string we used in the first release for creating AccountID - * (Using this string is wrong, but used for compatibility for now) - * @param accountProperties Map - * @return String - */ - private static String getServiceName(Map<String, String> accountProperties) - { - return accountProperties.get(ProtocolProviderFactory.SERVER_ADDRESS); - } - - /** * Returns the list of STUN servers that this account is currently * configured to use. * diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java index 50f0a68..546ce99 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/P2PTransportManager.java @@ -120,7 +120,7 @@ public class P2PTransportManager * @return the {@link GTalkTransportPacketExtension} */ @Override - public PacketExtension createTransport(IceMediaStream stream) + protected PacketExtension createTransport(IceMediaStream stream) { GTalkTransportPacketExtension trans = new GTalkTransportPacketExtension(); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java index 928487e..17bd9c8 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java @@ -227,8 +227,8 @@ public class RawUdpTransportManager MediaType mediaType, StreamConnector connector) { - ColibriConferenceIQ.Channel channel = getColibriChannel(mediaType, - false); + ColibriConferenceIQ.Channel channel + = getColibriChannel(mediaType, false); RawUdpTransportPacketExtension ourTransport = new RawUdpTransportPacketExtension(); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/DtlsFingerprintPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/DtlsFingerprintPacketExtension.java new file mode 100644 index 0000000..440cca4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/DtlsFingerprintPacketExtension.java @@ -0,0 +1,62 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * + * @author Lyubomir Marinov + */ +public class DtlsFingerprintPacketExtension + extends AbstractPacketExtension +{ + public static final String ELEMENT_NAME = "fingerprint"; + + private static final String HASH_ATTR_NAME = "hash"; + + public static final String NAMESPACE = "urn:xmpp:jingle:apps:dtls:0"; + + private static final String REQUIRED_ATTR_NAME = "required"; + + public DtlsFingerprintPacketExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } + + public String getFingerprint() + { + return getText(); + } + + public String getHash() + { + return getAttributeAsString(HASH_ATTR_NAME); + } + + public boolean getRequired() + { + String attr = getAttributeAsString(REQUIRED_ATTR_NAME); + + return (attr == null) ? false : Boolean.parseBoolean(attr); + } + + public void setFingerprint(String fingerprint) + { + setText(fingerprint); + } + + public void setHash(String hash) + { + setAttribute(HASH_ATTR_NAME, hash); + } + + public void setRequired(boolean required) + { + setAttribute(REQUIRED_ATTR_NAME, Boolean.valueOf(required)); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java index 4b36b56..583906d 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java @@ -142,6 +142,14 @@ public class JingleIQProvider implements IQProvider new DefaultPacketExtensionProvider<CoinPacketExtension>( CoinPacketExtension.class)); + // DTLS-SRTP + providerManager.addExtensionProvider( + DtlsFingerprintPacketExtension.ELEMENT_NAME, + DtlsFingerprintPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + <DtlsFingerprintPacketExtension>( + DtlsFingerprintPacketExtension.class)); + /* * XEP-0251: Jingle Session Transfer <transfer/> and <transferred> * providers diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java index 7cbd5f7..246a26b 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java @@ -32,12 +32,27 @@ import ch.imvs.sdes4j.srtp.*; * both classes are only separated for reasons of readability. * * @author Emil Ivov - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class CallPeerMediaHandlerSipImpl extends CallPeerMediaHandler<CallPeerSipImpl> { /** + * The name of the SDP attribute which specifies the fingerprint and hash + * function which has computed it of the certificate to validate a DTLS + * flow. + */ + private static final String DTLS_SRTP_FINGERPRINT_ATTR = "fingerprint"; + + private static final String DTLS_SRTP_SETUP_ACTIVE = "active"; + + private static final String DTLS_SRTP_SETUP_ACTPASS = "actpass"; + + private static final String DTLS_SRTP_SETUP_ATTR = "setup"; + + private static final String DTLS_SRTP_SETUP_PASSIVE = "passive"; + + /** * Our class logger. */ private static final Logger logger @@ -197,9 +212,7 @@ public class CallPeerMediaHandlerSipImpl if(direction != MediaDirection.INACTIVE) { - boolean hadSavp = false; - - for (String profileName : getRtpTransports()) + for (String proto : getRtpTransports()) { /* * If we start an audio-only call and re-INVITE the remote @@ -213,7 +226,7 @@ public class CallPeerMediaHandlerSipImpl = direction.allowsSending() ? sendQualityPreset : null; MediaDescription md = createMediaDescription( - profileName, + proto, getLocallySupportedFormats( dev, effectiveSendQualityPreset, @@ -247,16 +260,36 @@ public class CallPeerMediaHandlerSipImpl // do nothing in case of error. } - if(!hadSavp) + if (DtlsControl.UDP_TLS_RTP_SAVP.equals(proto) + || DtlsControl.UDP_TLS_RTP_SAVPF.equals(proto)) + { + updateMediaDescriptionForDtls(mediaType, md, null); + } + else { - updateMediaDescriptionForZrtp(mediaType, md); - updateMediaDescriptionForSDes(mediaType, md, null); + /* + * According to RFC 6189 "ZRTP: Media Path Key Agreement + * for Unicast Secure RTP", "ZRTP utilizes normal + * RTP/AVP (Audio-Visual Profile) profiles", "[t]he + * Secure RTP/AVP (SAVP) profile MAY be used in + * subsequent offer/answer exchanges after a successful + * ZRTP exchange has resulted in an SRTP session, or if + * it is known that the other endpoint supports this + * profile" and "[o]ther profiles MAY also be used." + */ + updateMediaDescriptionForZrtp(mediaType, md, null); + /* + * According to Ingo Bauersachs, SDES "[b]asically + * requires SAVP per RFC." + */ + if (SrtpControl.RTP_SAVP.equals(proto) + || SrtpControl.RTP_SAVPF.equals(proto)) + { + updateMediaDescriptionForSDes(mediaType, md, null); + } } mediaDescs.add(md); - - if(!hadSavp && profileName.contains("SAVP")) - hadSavp = true; } } } @@ -426,16 +459,15 @@ public class CallPeerMediaHandlerSipImpl * or semantics of <tt>newOffer</tt>. */ private Vector<MediaDescription> createMediaDescriptionsForAnswer( - SessionDescription offer) + SessionDescription offer) throws OperationFailedException, IllegalArgumentException { - List<MediaDescription> remoteDescriptions = SdpUtils - .extractMediaDescriptions(offer); - + List<MediaDescription> remoteDescriptions + = SdpUtils.extractMediaDescriptions(offer); // prepare to generate answers to all the incoming descriptions Vector<MediaDescription> answerDescriptions - = new Vector<MediaDescription>( remoteDescriptions.size() ); + = new Vector<MediaDescription>(remoteDescriptions.size()); this.setCallInfoURL(SdpUtils.getCallInfoURL(offer)); @@ -457,22 +489,26 @@ public class CallPeerMediaHandlerSipImpl for (MediaDescription mediaDescription : remoteDescriptions) { - String transportProtocol; + String proto; try { - transportProtocol = mediaDescription.getMedia().getProtocol(); + proto = mediaDescription.getMedia().getProtocol(); } catch (SdpParseException e) { throw new OperationFailedException( - "unable to create the media description", - OperationFailedException.ILLEGAL_ARGUMENT, e); + "Unable to create the media description", + OperationFailedException.ILLEGAL_ARGUMENT, + e); } - //ignore RTP/AVP(F) stream when RTP/SAVP(F) is mandatory + /* + * Ignore a RTP/AVP(F) stream when RTP/SAVP(F) is mandatory. At the + * time of this writing we support ZRTP, SDES and DTLS-SRTP. + */ if ((savpOption == ProtocolProviderFactory.SAVP_MANDATORY) - && !(transportProtocol.equals("RTP/SAVP") - || transportProtocol.equals("RTP/SAVPF"))) + && !(proto.endsWith(SrtpControl.RTP_SAVP) + || proto.endsWith(SrtpControl.RTP_SAVPF))) { rejectedAvpOfferDueToSavpMandatory = true; continue; @@ -630,7 +666,7 @@ public class CallPeerMediaHandlerSipImpl MediaDescription md = createMediaDescription( - transportProtocol, + proto, mutuallySupportedFormats, connector, direction, @@ -676,7 +712,6 @@ public class CallPeerMediaHandlerSipImpl atLeastOneValidDescription = true; } - if (!atLeastOneValidDescription) { if (rejectedAvpOfferDueToSavpMandatory) @@ -699,72 +734,270 @@ public class CallPeerMediaHandlerSipImpl } /** - * Updates the supplied description with zrtp hello hash if necessary. - * - * @param mediaType the media type. - * @param md the description to be updated. + * Updates a specific local <tt>MediaDescription</tt> and the state of this + * instance for the purposes of DTLS-SRTP. * - * @return True if ZRTP is added tp the media description. False, otherwise. + * @param mediaType the <tt>MediaType</tt> of the media described by + * <tt>localMd</tt> and <tt>remoteMd</tt> + * @param localMd the local <tt>MediaDescription</tt> to be updated + * @param remoteMd the remote <tt>MediaDescription</tt>, if any, associated + * with <tt>localMd</tt> + * @return <tt>true</tt> if the specified <tt>localMd</tt> and/or the state + * of this instance was updated for the purposes of DTLS-SRTP or + * <tt>false</tt> if the specified <tt>localMd</tt> (and <tt>remoteMd</tt>) + * did not concern DTLS-SRTP */ - private boolean updateMediaDescriptionForZrtp( - MediaType mediaType, MediaDescription md) + private boolean updateMediaDescriptionForDtls( + MediaType mediaType, + MediaDescription localMd, + MediaDescription remoteMd) { - MediaAwareCallPeer<?, ?, ?> peer = getPeer(); - AccountID accountID = peer.getProtocolProvider().getAccountID(); + AccountID accountID = getPeer().getProtocolProvider().getAccountID(); + boolean b = false; - if(accountID.getAccountPropertyBoolean( + if (accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("ZRTP") - && peer.getCall().isSipZrtpAttribute()) + && accountID.isEncryptionProtocolEnabled( + DtlsControl.PROTO_NAME)) + { + /* + * The transport protocol of the media described by localMd should + * be DTLS-SRTP in order to be of any concern here. + */ + Media localMedia = localMd.getMedia(); + + if (localMedia != null) + { + String proto; + + try + { + proto = localMedia.getProtocol(); + } + catch (SdpParseException e) + { + /* + * Well, if the protocol of the Media cannot be parsed, then + * surely we do not want to have anything to do with it. + */ + proto = null; + } + + boolean dtls + = DtlsControl.UDP_TLS_RTP_SAVP.equals(proto) + || DtlsControl.UDP_TLS_RTP_SAVPF.equals(proto); + SrtpControls srtpControls = getSrtpControls(); + + if (dtls) + { + DtlsControl dtlsControl + = (DtlsControl) + srtpControls.getOrCreate( + mediaType, + SrtpControlType.DTLS_SRTP); + + // SDP attributes + @SuppressWarnings("unchecked") + Vector<Attribute> attrs = localMd.getAttributes(true); + + // setup + String setup + = (remoteMd == null) + ? DTLS_SRTP_SETUP_ACTPASS + : DTLS_SRTP_SETUP_ACTIVE; + Attribute setupAttr + = SdpUtils.createAttribute(DTLS_SRTP_SETUP_ATTR, setup); + + attrs.add(setupAttr); + + // fingerprint + String hashFunction + = dtlsControl.getLocalFingerprintHashFunction(); + String fingerprint = dtlsControl.getLocalFingerprint(); + Attribute fingerprintAttr + = SdpUtils.createAttribute( + DTLS_SRTP_FINGERPRINT_ATTR, + hashFunction + " " + fingerprint); + + attrs.add(fingerprintAttr); + + int dtlsProtocol + = DTLS_SRTP_SETUP_ACTIVE.equals(setup) + ? DtlsControl.DTLS_CLIENT_PROTOCOL + : DtlsControl.DTLS_SERVER_PROTOCOL; + + dtlsControl.setDtlsProtocol(dtlsProtocol); + + if (remoteMd != null) // answer + updateSrtpControlsForDtls(mediaType, localMd, remoteMd); + + b = true; + } + else if (remoteMd != null) // answer + { + /* + * If DTLS-SRTP has been rejected as the transport protocol, + * then halt the operation of DTLS-SRTP. + */ + SrtpControl dtlsControl + = srtpControls.remove( + mediaType, + SrtpControlType.DTLS_SRTP); + + if (dtlsControl != null) + dtlsControl.cleanup(); + } + } + } + return b; + } + + /** + * Updates the <tt>SrtpControls</tt> of this instance in accord with a + * specific <tt>MediaDescription</tt> presented by a remote peer. + * + * @param mediaType the <tt>MediaType</tt> of the specified + * <tt>MediaDescription</tt> to be analyzed + * @param localMd the <tt>MediaDescription</tt> of the local peer that is + * the answer to the offer presented by a remote peer represented by + * <tt>remoteMd</tt> or <tt>null</tt> if the specified <tt>remoteMd</tt> is + * an answer to an offer of the local peer + * @param remoteMd the <tt>MediaDescription</tt> presented by a remote peer + * to be analyzed + */ + private void updateSrtpControlsForDtls( + MediaType mediaType, + MediaDescription localMd, + MediaDescription remoteMd) + { + SrtpControls srtpControls = getSrtpControls(); + DtlsControl dtlsControl + = (DtlsControl) + srtpControls.get(mediaType, SrtpControlType.DTLS_SRTP); + + if (dtlsControl == null) + return; + + Media remoteMedia = remoteMd.getMedia(); + boolean dtls = false; + + if (remoteMedia != null) { + String proto; + try { - Map<MediaTypeSrtpControl, SrtpControl> srtpControls - = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.ZRTP); - SrtpControl scontrol = srtpControls.get(key); + proto = remoteMedia.getProtocol(); + } + catch (SdpParseException e) + { + /* + * Well, if the protocol of the Media cannot be parsed, then + * surely we do not want to have anything to do with it. + */ + proto = null; + } + dtls + = DtlsControl.UDP_TLS_RTP_SAVP.equals(proto) + || DtlsControl.UDP_TLS_RTP_SAVPF.equals(proto); + } + if (dtls) + { + if (localMd == null) // answer + { + // setup + /* + * RFC 5763 requires setup:actpass from the offerer i.e. the + * offerer is the DTLS server and recommends setup:active to the + * answerer i.e the answerer is the DTLS client. If the answerer + * chooses setup:passive i.e. the answerer is the DTLS server, + * the offerer has to become the DTLS client. + */ + String setup; - if(scontrol == null) + try + { + setup = remoteMd.getAttribute(DTLS_SRTP_SETUP_ATTR); + } + catch (SdpParseException spe) + { + setup = null; + } + if (DTLS_SRTP_SETUP_PASSIVE.equals(setup)) { - scontrol - = SipActivator.getMediaService().createZrtpControl(); - srtpControls.put(key, scontrol); + dtlsControl.setDtlsProtocol( + DtlsControl.DTLS_CLIENT_PROTOCOL); } + } - ZrtpControl zcontrol = (ZrtpControl) scontrol; - int versionIndex = zcontrol.getNumberSupportedVersions(); - boolean zrtpHashSet = false; // will become true if at least one is set + // fingerprint + @SuppressWarnings("unchecked") + Vector<Attribute> attrs = remoteMd.getAttributes(false); + Map<String, String> remoteFingerprints + = new LinkedHashMap<String, String>(); - for (int i = 0; i < versionIndex; i++) + if (attrs != null) + { + for (Attribute attr : attrs) { - String helloHash = zcontrol.getHelloHash(i); + String fingerprint; - if ((helloHash != null) && helloHash.length() > 0) + try { - md.setAttribute(SdpUtils.ZRTP_HASH_ATTR, helloHash); - zrtpHashSet = true; + if (DTLS_SRTP_FINGERPRINT_ATTR.equals(attr.getName())) + { + fingerprint = attr.getValue(); + if (fingerprint == null) + continue; + else + fingerprint = fingerprint.trim(); + } + else + { + continue; + } + } + catch (SdpParseException spe) + { + /* + * Whatever part of the SDP failed to parse, we would + * better not try to recover from it. + */ + continue; + } + + int spIndex = fingerprint.indexOf(' '); + + if ((spIndex > 0) && (spIndex < fingerprint.length() - 1)) + { + String hashFunction = fingerprint.substring(0, spIndex); + + fingerprint = fingerprint.substring(spIndex + 1); + remoteFingerprints.put(hashFunction, fingerprint); } } - return zrtpHashSet; - } - catch (SdpException ex) - { - logger.error("Cannot add zrtp-hash to sdp", ex); } + dtlsControl.setRemoteFingerprints(remoteFingerprints); + + removeAndCleanupOtherSrtpControls( + mediaType, + SrtpControlType.DTLS_SRTP); + } + else + { + srtpControls.remove(mediaType, SrtpControlType.DTLS_SRTP); + dtlsControl.cleanup(); } - return false; } /** - * Updates the supplied description with SDES attributes if necessary. + * Updates the supplied media description with SDES attributes if necessary. * * @param mediaType the media type. * @param localMd the description of the local peer. * @param remoteMd the description of the remote peer. - * - * @return <tt>true</tt> if SDES is added to the media description; + * @return <tt>true</tt> if SDES has been added to the media description; * <tt>false</tt>, otherwise. */ private boolean updateMediaDescriptionForSDes( @@ -774,29 +1007,22 @@ public class CallPeerMediaHandlerSipImpl { AccountID accountID = getPeer().getProtocolProvider().getAccountID(); - // check if SDES and encryption is enabled at all + // Check if encryption and SDES are enabled at all. if(!accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - || !accountID.isEncryptionProtocolEnabled("SDES")) + || !accountID.isEncryptionProtocolEnabled( + SDesControl.PROTO_NAME)) { return false; } // get or create the control - Map<MediaTypeSrtpControl, SrtpControl> srtpControls = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES); - SrtpControl scontrol = srtpControls.get(key); - - if (scontrol == null) - { - scontrol = SipActivator.getMediaService().createSDesControl(); - srtpControls.put(key, scontrol); - } - + SrtpControls srtpControls = getSrtpControls(); + SDesControl sdesControl + = (SDesControl) + srtpControls.getOrCreate(mediaType, SrtpControlType.SDES); // set the enabled ciphers suites - SDesControl sdcontrol = (SDesControl) scontrol; String ciphers = accountID.getAccountPropertyString( ProtocolProviderFactory.SDES_CIPHER_SUITES); @@ -807,26 +1033,24 @@ public class CallPeerMediaHandlerSipImpl = SipActivator.getResources().getSettingsString( SDesControl.SDES_CIPHER_SUITES); } - sdcontrol.setEnabledCiphers(Arrays.asList(ciphers.split(","))); + sdesControl.setEnabledCiphers(Arrays.asList(ciphers.split(","))); - // act as initiator - if (remoteMd == null) + if (remoteMd == null) // act as initiator { @SuppressWarnings("unchecked") Vector<Attribute> atts = localMd.getAttributes(true); for (SrtpCryptoAttribute ca - : sdcontrol.getInitiatorCryptoAttributes()) + : sdesControl.getInitiatorCryptoAttributes()) { atts.add(SdpUtils.createAttribute("crypto", ca.encode())); } return true; } - // act as responder - else + else // act as responder { SrtpCryptoAttribute localAttr - = selectSdesCryptoSuite(false, sdcontrol, remoteMd); + = selectSdesCryptoSuite(false, sdesControl, remoteMd); if (localAttr != null) { @@ -842,16 +1066,84 @@ public class CallPeerMediaHandlerSipImpl } else { - // none of the offered suites match, destroy the sdes control - sdcontrol.cleanup(); - srtpControls.remove(key); + // None of the offered suites match, destroy the SDES control. + sdesControl.cleanup(); + srtpControls.remove(mediaType, SrtpControlType.SDES); logger.warn("Received unsupported sdes crypto attribute."); } return false; } } - private List<String> getRtpTransports() throws OperationFailedException + /** + * Updates the supplied media description with ZRTP hello hash if necessary. + * + * @param mediaType the media type. + * @param localMd the media description to update. + * @return <tt>true</tt> if ZRTP is added to the media description; + * <tt>false</tt>, otherwise. + */ + private boolean updateMediaDescriptionForZrtp( + MediaType mediaType, + MediaDescription localMd, + MediaDescription remoteMd) + { + MediaAwareCallPeer<?, ?, ?> peer = getPeer(); + AccountID accountID = peer.getProtocolProvider().getAccountID(); + boolean b = false; + + if(accountID.getAccountPropertyBoolean( + ProtocolProviderFactory.DEFAULT_ENCRYPTION, + true) + && accountID.isEncryptionProtocolEnabled(ZrtpControl.PROTO_NAME) + && peer.getCall().isSipZrtpAttribute()) + { + ZrtpControl zrtpControl + = (ZrtpControl) + getSrtpControls().getOrCreate( + mediaType, + SrtpControlType.ZRTP); + int numberSupportedVersions + = zrtpControl.getNumberSupportedVersions(); + + try + { + for (int i = 0; i < numberSupportedVersions; i++) + { + String helloHash = zrtpControl.getHelloHash(i); + + if ((helloHash != null) && helloHash.length() > 0) + { + localMd.setAttribute( + SdpUtils.ZRTP_HASH_ATTR, + helloHash); + /* + * Will return true if at least one zrtp-hash has been + * set. + */ + b = true; + } + } + } + catch (SdpException ex) + { + logger.error("Cannot add zrtp-hash to sdp", ex); + } + } + return b; + } + + /** + * Gets a list of (RTP) transport protocols (i.e. <tt><proto></tt>) to + * be announced in a SDP media description (i.e. <tt>m=</tt> line). + * + * @return a <tt>List</tt> of (RTP) transport protocols to be announced in a + * SDP media description + * @throws OperationFailedException if the value of the <tt>AccountID</tt> + * property {@link ProtocolProviderFactory#SAVP_OPTION} is invalid + */ + private List<String> getRtpTransports() + throws OperationFailedException { AccountID accountID = getPeer().getProtocolProvider().getAccountID(); int savpOption @@ -862,25 +1154,65 @@ public class CallPeerMediaHandlerSipImpl ProtocolProviderFactory.SAVP_OPTION, ProtocolProviderFactory.SAVP_OFF) : ProtocolProviderFactory.SAVP_OFF; - List<String> result = new ArrayList<String>(2); + List<String> result = new ArrayList<String>(3); - switch (savpOption) + if (savpOption == ProtocolProviderFactory.SAVP_OFF) { - case ProtocolProviderFactory.SAVP_MANDATORY: - result.add("RTP/SAVP"); - break; - case ProtocolProviderFactory.SAVP_OFF: - result.add(SdpConstants.RTP_AVP); - break; - case ProtocolProviderFactory.SAVP_OPTIONAL: - result.add("RTP/SAVP"); result.add(SdpConstants.RTP_AVP); - break; - default: - throw new OperationFailedException( - "invalid value for SAVP_OPTION", - OperationFailedException.GENERAL_ERROR); } + else + { + /* + * List the secure transports in the result according to the order + * of preference of their respective encryption protocols. + */ + List<String> encryptionProtocols + = accountID.getSortedEnabledEncryptionProtocolList(); + int encryptionProtocolCount = encryptionProtocols.size(); + + for (int i = encryptionProtocolCount - 1; i >= 0; i--) + { + String encryptionProtocol = encryptionProtocols.get(i); + String protoName + = encryptionProtocol.substring( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + + 1); + String proto; + + if (DtlsControl.PROTO_NAME.equals(protoName)) + { + proto = DtlsControl.UDP_TLS_RTP_SAVP; + } + else + { + /* + * According to Ingo Bauersachs, SDES "[b]asically requires + * SAVP per RFC." + */ + /* + * According to RFC 6189 "ZRTP: Media Path Key Agreement for + * Unicast Secure RTP", "ZRTP utilizes normal RTP/AVP + * (Audio-Visual Profile) profiles", "[t]he Secure RTP/AVP + * (SAVP) profile MAY be used in subsequent offer/answer + * exchanges after a successful ZRTP exchange has resulted + * in an SRTP session, or if it is known that the other + * endpoint supports this profile" and "[o]ther profiles MAY + * also be used." + */ + proto = SrtpControl.RTP_SAVP; + } + + int protoIndex = result.indexOf(proto); + + if (protoIndex > 0) + result.remove(protoIndex); + result.add(0, proto); + } + + if (savpOption == ProtocolProviderFactory.SAVP_OPTIONAL) + result.add(SdpConstants.RTP_AVP); + } + return result; } @@ -1051,45 +1383,34 @@ public class CallPeerMediaHandlerSipImpl = SdpUtils.containsAttribute(mediaDescription, "imageattr"); } + + // DTLS-SRTP + updateSrtpControlsForDtls(mediaType, null, mediaDescription); + + // SDES // select the crypto key the peer has chosen from our proposal - Map<MediaTypeSrtpControl, SrtpControl> srtpControls - = getSrtpControls(); - MediaTypeSrtpControl key - = new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES); - SrtpControl scontrol = srtpControls.get(key); + SrtpControls srtpControls = getSrtpControls(); + SDesControl sdesControl + = (SDesControl) + srtpControls.get(mediaType, SrtpControlType.SDES); - if(scontrol != null) + if(sdesControl != null) { if(selectSdesCryptoSuite( true, - (SDesControl) scontrol, + sdesControl, mediaDescription) == null) { - scontrol.cleanup(); - srtpControls.remove(key); + sdesControl.cleanup(); + srtpControls.remove(mediaType, SrtpControlType.SDES); logger.warn("Received unsupported sdes crypto attribute."); } else { //found an SDES answer, remove all other controls - Iterator<Map.Entry<MediaTypeSrtpControl, SrtpControl>> iter - = srtpControls.entrySet().iterator(); - - while (iter.hasNext()) - { - Map.Entry<MediaTypeSrtpControl, SrtpControl> entry - = iter.next(); - MediaTypeSrtpControl mtsc = entry.getKey(); - - if ((mtsc.mediaType == mediaType) - && (mtsc.srtpControlType - != SrtpControlType.SDES)) - { - entry.getValue().cleanup(); - iter.remove(); - } - } - + removeAndCleanupOtherSrtpControls( + mediaType, + SrtpControlType.SDES); addAdvertisedEncryptionMethod(SrtpControlType.SDES); } } @@ -1386,28 +1707,37 @@ public class CallPeerMediaHandlerSipImpl .getAccountID() .getSortedEnabledEncryptionProtocolList(); - for(int i = 0; i < preferredEncryptionProtocols.size(); ++i) + for(String preferredEncryptionProtocol : preferredEncryptionProtocols) { - // ZRTP - if(preferredEncryptionProtocols.get(i).equals( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".ZRTP")) + String protoName + = preferredEncryptionProtocol.substring( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + + 1); + + // DTLS-SRTP + if (DtlsControl.PROTO_NAME.equals(protoName)) { - if(updateMediaDescriptionForZrtp(mediaType, localMd)) + if(updateMediaDescriptionForDtls(mediaType, localMd, remoteMd)) { - // Stops once an encryption advertisement has been chosen. + // Stop once an encryption advertisement has been chosen. return; } } // SDES - else if(preferredEncryptionProtocols.get(i).equals( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".SDES")) + else if(SDesControl.PROTO_NAME.equals(protoName)) { - if(updateMediaDescriptionForSDes( - mediaType, - localMd, - remoteMd)) + if(updateMediaDescriptionForSDes(mediaType, localMd, remoteMd)) + { + // Stop once an encryption advertisement has been chosen. + return; + } + } + // ZRTP + else if(ZrtpControl.PROTO_NAME.equals(protoName)) + { + if(updateMediaDescriptionForZrtp(mediaType, localMd, remoteMd)) { - // Stops once an encryption advertisement has been chosen. + // Stop once an encryption advertisement has been chosen. return; } } diff --git a/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java b/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java index 9b39493..b838da4 100644 --- a/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/wizard/SecurityPanel.java @@ -310,7 +310,7 @@ public class SecurityPanel pnlAdvancedSettings.add(lblEncryptionProtocolPreferences, c); int nbEncryptionProtocols - = SecurityAccountRegistration.ENCRYPTION_PROTOCOLS.length; + = SecurityAccountRegistration.ENCRYPTION_PROTOCOLS.size(); String[] encryptions = new String[nbEncryptionProtocols]; boolean[] selectedEncryptions = new boolean[nbEncryptionProtocols]; diff --git a/src/net/java/sip/communicator/service/protocol/SecurityAccountRegistration.java b/src/net/java/sip/communicator/service/protocol/SecurityAccountRegistration.java index a66f155..e4a62ac 100644 --- a/src/net/java/sip/communicator/service/protocol/SecurityAccountRegistration.java +++ b/src/net/java/sip/communicator/service/protocol/SecurityAccountRegistration.java @@ -13,11 +13,12 @@ import java.util.*; /** * The <tt>SecurityAccountRegistration</tt> is used to determine security - * options for different registration protocol (Jabber, SIP). Useful fot the + * options for different registration protocol (Jabber, SIP). Useful to the * SecurityPanel. * * @author Vincent Lucas * @author Pawel Domas + * @author Lyubomir Marinov */ public abstract class SecurityAccountRegistration implements Serializable @@ -25,7 +26,12 @@ public abstract class SecurityAccountRegistration /** * The encryption protocols managed by this SecurityPanel. */ - public static final String[] ENCRYPTION_PROTOCOLS = {"ZRTP", "SDES"}; + public static final List<String> ENCRYPTION_PROTOCOLS + = Collections.unmodifiableList( + Arrays.asList( + ZrtpControl.PROTO_NAME, + SDesControl.PROTO_NAME, + DtlsControl.PROTO_NAME)); /** * Enables support to encrypt calls. @@ -33,7 +39,7 @@ public abstract class SecurityAccountRegistration private boolean defaultEncryption = true; /** - * Enqbles ZRTP encryption. + * Enables ZRTP encryption. */ private boolean sipZrtpAttribute = true; @@ -65,13 +71,13 @@ public abstract class SecurityAccountRegistration public SecurityAccountRegistration() { // Sets the default values. - this.encryptionProtocols = new HashMap<String, Integer>(1); - this.encryptionProtocols.put("ZRTP", 0); - this.encryptionProtocolStatus = new HashMap<String, Boolean>(1); - this.encryptionProtocolStatus.put("ZRTP", true); + encryptionProtocols = new HashMap<String, Integer>(1); + encryptionProtocols.put("ZRTP", 0); + encryptionProtocolStatus = new HashMap<String, Boolean>(1); + encryptionProtocolStatus.put("ZRTP", true); sdesCipherSuites - = UtilActivator.getResources() - .getSettingsString(SDesControl.SDES_CIPHER_SUITES); + = UtilActivator.getResources().getSettingsString( + SDesControl.SDES_CIPHER_SUITES); } /** @@ -223,19 +229,13 @@ public abstract class SecurityAccountRegistration private void addEncryptionProtocolsToProperties( Map<String, String> properties) { - Map<String, Integer> encryptionProtocols - = this.getEncryptionProtocols(); - Iterator<String> encryptionProtocolIterator - = encryptionProtocols.keySet().iterator(); - String encryptionProtocol; - while(encryptionProtocolIterator.hasNext()) + for (Map.Entry<String, Integer> e : getEncryptionProtocols().entrySet()) { - encryptionProtocol = encryptionProtocolIterator.next(); properties.put( ProtocolProviderFactory.ENCRYPTION_PROTOCOL + "." - + encryptionProtocol, - encryptionProtocols.get(encryptionProtocol).toString()); + + e.getKey(), + e.getValue().toString()); } } @@ -248,20 +248,14 @@ public abstract class SecurityAccountRegistration private void addEncryptionProtocolStatusToProperties( Map<String, String> properties) { - Map<String, Boolean> encryptionProtocolStatus - = this.getEncryptionProtocolStatus(); - Iterator<String> encryptionProtocolStatusIterator - = encryptionProtocolStatus.keySet().iterator(); - String encryptionProtocol; - while(encryptionProtocolStatusIterator.hasNext()) + for (Map.Entry<String,Boolean> e + : getEncryptionProtocolStatus().entrySet()) { - encryptionProtocol = encryptionProtocolStatusIterator.next(); properties.put( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + "." - + encryptionProtocol, - encryptionProtocolStatus.get(encryptionProtocol) - .toString()); + + e.getKey(), + e.getValue().toString()); } } @@ -278,16 +272,13 @@ public abstract class SecurityAccountRegistration // Sets the ordered list of encryption protocols. addEncryptionProtocolsToProperties(propertiesMap); - // Sets the list of encryption protocol status. addEncryptionProtocolStatusToProperties(propertiesMap); propertiesMap.put(ProtocolProviderFactory.DEFAULT_SIPZRTP_ATTRIBUTE, Boolean.toString(isSipZrtpAttribute())); - propertiesMap.put(ProtocolProviderFactory.SAVP_OPTION, Integer.toString(getSavpOption())); - propertiesMap.put(ProtocolProviderFactory.SDES_CIPHER_SUITES, getSDesCipherSuites()); } @@ -306,31 +297,30 @@ public abstract class SecurityAccountRegistration encryptionProtocols = new HashMap<String, Integer>(); encryptionProtocolStatus = new HashMap<String, Boolean>(); - Map<String, Integer> srcEncryptionProtocols - = accountID.getIntegerPropertiesByPrefix( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL, true); - Map<String, Boolean> srcEncryptionProtocolStatus - = accountID.getBooleanPropertiesByPrefix( + Map<String,Integer> srcEncryptionProtocols + = accountID.getIntegerPropertiesByPrefix( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL, + true); + Map<String,Boolean> srcEncryptionProtocolStatus + = accountID.getBooleanPropertiesByPrefix( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS, - true, - false); + true, + false); // Load stored values. int prefixeLength - = ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + 1; - String name; - boolean enabled; - for(String protocolPropertyName : srcEncryptionProtocols.keySet()) + = ProtocolProviderFactory.ENCRYPTION_PROTOCOL.length() + 1; + + for (Map.Entry<String,Integer> e : srcEncryptionProtocols.entrySet()) { - name = protocolPropertyName.substring(prefixeLength); + String name = e.getKey().substring(prefixeLength); if (isExistingEncryptionProtocol(name)) { - // Copies the priority - encryptionProtocols.put( - name, - srcEncryptionProtocols.get(protocolPropertyName)); - // Extracts the status - enabled = srcEncryptionProtocolStatus.get( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + // Copy the priority + encryptionProtocols.put(name, e.getValue()); + // Extract the status + boolean enabled + = srcEncryptionProtocolStatus.get( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + "." + name); encryptionProtocolStatus.put(name, enabled); @@ -341,12 +331,10 @@ public abstract class SecurityAccountRegistration accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_SIPZRTP_ATTRIBUTE, true)); - setSavpOption( accountID.getAccountPropertyInt( ProtocolProviderFactory.SAVP_OPTION, ProtocolProviderFactory.SAVP_OFF)); - setSDesCipherSuites( accountID.getAccountPropertyString( ProtocolProviderFactory.SDES_CIPHER_SUITES)); @@ -369,42 +357,37 @@ public abstract class SecurityAccountRegistration Map<String, Integer> encryptionProtocols, Map<String, Boolean> encryptionProtocolStatus) { - int nbEncryptionProtocols = ENCRYPTION_PROTOCOLS.length; + int nbEncryptionProtocols = ENCRYPTION_PROTOCOLS.size(); String[] encryptions = new String[nbEncryptionProtocols]; boolean[] selectedEncryptions = new boolean[nbEncryptionProtocols]; // Load stored values. - String name; - int index; - Iterator<String> encryptionProtocolNames - = encryptionProtocols.keySet().iterator(); - while(encryptionProtocolNames.hasNext()) + for (Map.Entry<String,Integer> e : encryptionProtocols.entrySet()) { - name = encryptionProtocolNames.next(); - index = encryptionProtocols.get(name); + int index = e.getValue(); + // If the property is set. - if(index != -1) + if (index != -1) { + String name = e.getKey(); + if (isExistingEncryptionProtocol(name)) { encryptions[index] = name; selectedEncryptions[index] - = encryptionProtocolStatus.get(name); + = encryptionProtocolStatus.get(name); } } } // Load default values. - String encryptionProtocol; - boolean set; int j = 0; - for(int i = 0; i < ENCRYPTION_PROTOCOLS.length; ++i) + for (String encryptionProtocol : ENCRYPTION_PROTOCOLS) { - encryptionProtocol = ENCRYPTION_PROTOCOLS[i]; // Specify a default value only if there is no specific value set. if(!encryptionProtocols.containsKey(encryptionProtocol)) { - set = false; + boolean set = false; // Search for the first empty element. while(j < encryptions.length && !set) { @@ -418,30 +401,22 @@ public abstract class SecurityAccountRegistration } ++j; } - } } - return new Object[]{ encryptions, selectedEncryptions}; + return new Object[] { encryptions, selectedEncryptions}; } /** - * Checks if given <tt>protocol</tt> is on supported protocols list. + * Checks if a specific <tt>protocol</tt> is on the list of supported + * (encryption) protocols. + * * @param protocol the protocol name - * @return <tt>true</tt> if encryption protocol with given protocol name is - * supported. + * @return <tt>true</tt> if <tt>protocol</tt> is supported; <tt>false</tt>, + * otherwise */ private static boolean isExistingEncryptionProtocol(String protocol) { - for (String key : ENCRYPTION_PROTOCOLS) - { - if (key.equals(protocol)) - { - return true; - } - } - - return false; + return ENCRYPTION_PROTOCOLS.contains(protocol); } - } diff --git a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java index 11fcf31..9780393 100644 --- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java @@ -13,8 +13,8 @@ import java.util.*; import java.util.List; import net.java.sip.communicator.service.protocol.*; - import net.java.sip.communicator.util.*; + import org.jitsi.service.neomedia.*; import org.jitsi.service.neomedia.codec.*; import org.jitsi.service.neomedia.control.*; @@ -980,7 +980,7 @@ public abstract class CallPeerMediaHandler<T extends MediaAwareCallPeer<?,?,?>> * @return the <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this * instance */ - protected Map<MediaTypeSrtpControl, SrtpControl> getSrtpControls() + protected SrtpControls getSrtpControls() { return mediaHandler.getSrtpControls(this); } @@ -1443,6 +1443,24 @@ public abstract class CallPeerMediaHandler<T extends MediaAwareCallPeer<?,?,?>> return mediaHandler.processKeyFrameRequest(this); } + protected void removeAndCleanupOtherSrtpControls( + MediaType mediaType, + SrtpControlType srtpControlType) + { + SrtpControls srtpControls = getSrtpControls(); + + for (SrtpControlType i : SrtpControlType.values()) + { + if (!i.equals(srtpControlType)) + { + SrtpControl e = srtpControls.remove(mediaType, i); + + if (e != null) + e.cleanup(); + } + } + } + /** * Unregisters a specific <tt>VideoListener</tt> from this instance so that * it stops receiving notifications from it about changes in the diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java index 62ecfa6..4b5db9e 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java @@ -138,8 +138,8 @@ public class MediaHandler /** * The <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this instance. */ - private final SortedMap<MediaTypeSrtpControl, SrtpControl> srtpControls - = new TreeMap<MediaTypeSrtpControl, SrtpControl>(); + private final SrtpControls srtpControls + = new SrtpControls(); private final SrtpListener srtpListener = new SrtpListener() @@ -597,19 +597,7 @@ public class MediaHandler } // Clean up the SRTP controls used for the associated Call. - Iterator<Map.Entry<MediaTypeSrtpControl, SrtpControl>> iter - = srtpControls.entrySet().iterator(); - - while (iter.hasNext()) - { - Map.Entry<MediaTypeSrtpControl, SrtpControl> entry = iter.next(); - - if (entry.getKey().mediaType == mediaType) - { - entry.getValue().cleanup(); - iter.remove(); - } - } + callPeerMediaHandler.removeAndCleanupOtherSrtpControls(mediaType, null); } /** @@ -771,8 +759,8 @@ public class MediaHandler for(SrtpControlType srtpControlType : SrtpControlType.values()) { SrtpControl srtpControl - = getSrtpControls(callPeerMediaHandler).get( - new MediaTypeSrtpControl(mediaType, srtpControlType)); + = getSrtpControls(callPeerMediaHandler) + .get(mediaType, srtpControlType); if((srtpControl != null) && srtpControl.getSecureCommunicationStatus()) @@ -798,8 +786,7 @@ public class MediaHandler * @return the <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this * instance */ - Map<MediaTypeSrtpControl, SrtpControl> getSrtpControls( - CallPeerMediaHandler<?> callPeerMediaHandler) + SrtpControls getSrtpControls(CallPeerMediaHandler<?> callPeerMediaHandler) { return srtpControls; } @@ -884,17 +871,7 @@ public class MediaHandler MediaService mediaService = ProtocolMediaActivator.getMediaService(); - /* - * The default SrtpControlType is ZRTP. But if a SrtpControl exists - * already, it determines the SrtpControlType. - */ - SrtpControlType srtpControlType - = (srtpControls.size() > 0) - ? srtpControls.firstKey().srtpControlType - : SrtpControlType.ZRTP; - MediaTypeSrtpControl mediaTypeSrtpControl - = new MediaTypeSrtpControl(mediaType, srtpControlType); - SrtpControl srtpControl = srtpControls.get(mediaTypeSrtpControl); + SrtpControl srtpControl = srtpControls.findFirst(mediaType); // If a SrtpControl does not exist yet, create a default one. if (srtpControl == null) @@ -905,7 +882,9 @@ public class MediaHandler * Consequently, it needs to be linked to the srtpControls Map. */ stream = mediaService.createMediaStream(connector, device); - srtpControls.put(mediaTypeSrtpControl, stream.getSrtpControl()); + srtpControl = stream.getSrtpControl(); + if (srtpControl != null) + srtpControls.set(mediaType, srtpControl); } else { diff --git a/src/net/java/sip/communicator/service/protocol/media/SrtpControls.java b/src/net/java/sip/communicator/service/protocol/media/SrtpControls.java new file mode 100644 index 0000000..0e88007 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/media/SrtpControls.java @@ -0,0 +1,97 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.protocol.media; + +import org.jitsi.service.neomedia.*; + +/** + * Represents a sorted set of <tt>SrtpControl</tt> implementations. + * + * @author Lyubomir Marinov + */ +public class SrtpControls +{ + private static final SrtpControlType[] SORTED_SRTP_CONTROL_TYPES + = { + SrtpControlType.ZRTP, + SrtpControlType.DTLS_SRTP, + SrtpControlType.MIKEY, + SrtpControlType.SDES + }; + + /** + * The <tt>SrtpControl</tt> implementations which are the elements of this + * sorted set. + */ + private final SrtpControl[][] elements + = new SrtpControl + [MediaType.values().length] + [SrtpControlType.values().length]; + + /** + * Initializes a new <tt>SrtpControls</tt> instance. + */ + public SrtpControls() + { + } + + public SrtpControl findFirst(MediaType mediaType) + { + SrtpControl element = null; + + for (SrtpControlType srtpControlType : SORTED_SRTP_CONTROL_TYPES) + { + element = get(mediaType, srtpControlType); + if (element != null) + break; + } + return element; + } + + public SrtpControl get(MediaType mediaType, SrtpControlType srtpControlType) + { + return elements[mediaType.ordinal()][srtpControlType.ordinal()]; + } + + public SrtpControl getOrCreate( + MediaType mediaType, + SrtpControlType srtpControlType) + { + SrtpControl[] elements = this.elements[mediaType.ordinal()]; + int index = srtpControlType.ordinal(); + SrtpControl element = elements[index]; + + if (element == null) + { + element + = ProtocolMediaActivator.getMediaService().createSrtpControl( + srtpControlType); + if (element != null) + elements[index] = element; + } + return element; + } + + public SrtpControl remove( + MediaType mediaType, + SrtpControlType srtpControlType) + { + SrtpControl[] elements = this.elements[mediaType.ordinal()]; + int index = srtpControlType.ordinal(); + SrtpControl element = elements[index]; + + elements[index] = null; + return element; + } + + public void set(MediaType mediaType, SrtpControl element) + { + SrtpControlType srtpControlType = element.getSrtpControlType(); + + elements[mediaType.ordinal()][srtpControlType.ordinal()] = element; + } +} |