diff --git a/.classpath b/.classpath index 237be5f81..9a9bf4cae 100755 --- a/.classpath +++ b/.classpath @@ -80,7 +80,7 @@ - + diff --git a/build.xml b/build.xml index 96f19f8d5..043060e67 100644 --- a/build.xml +++ b/build.xml @@ -1286,6 +1286,7 @@ + @@ -2517,7 +2518,7 @@ javax.swing.event, javax.swing.border"/> - + diff --git a/lib/installer-exclude/bcpkix-jdk15on-149.jar b/lib/installer-exclude/bcpkix-jdk15on-149.jar new file mode 100644 index 000000000..96d198556 Binary files /dev/null and b/lib/installer-exclude/bcpkix-jdk15on-149.jar differ diff --git a/lib/installer-exclude/bcprov-jdk15on-148.jar b/lib/installer-exclude/bcprov-jdk15on-149.jar similarity index 55% rename from lib/installer-exclude/bcprov-jdk15on-148.jar rename to lib/installer-exclude/bcprov-jdk15on-149.jar index 3fcb136da..e1d4bb31c 100644 Binary files a/lib/installer-exclude/bcprov-jdk15on-148.jar and b/lib/installer-exclude/bcprov-jdk15on-149.jar differ diff --git a/lib/installer-exclude/bcprov.manifest.mf b/lib/installer-exclude/bcprov.manifest.mf index 95fd77ed7..402723b79 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 index 3cda590db..8f4b7a6e4 100644 Binary files a/lib/installer-exclude/jain-sdp.jar and b/lib/installer-exclude/jain-sdp.jar differ diff --git a/lib/installer-exclude/libjitsi.jar b/lib/installer-exclude/libjitsi.jar index c7124b29a..3b3eeb10b 100644 Binary files a/lib/installer-exclude/libjitsi.jar and b/lib/installer-exclude/libjitsi.jar differ 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 8ba3f6155..49bbe47d6 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 void run() 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 f1876f715..744de28bd 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 void securityOn(CallPeerSecurityOnEvent evt) 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 da60db370..2cf9ab5c1 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 6d2d242ee..7c5c69024 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.util.*; import org.jitsi.service.neomedia.*; - import org.jivesoftware.smack.packet.*; /** @@ -104,7 +103,8 @@ protected void addZRTPAdvertisedEncryptions( if (accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("ZRTP") + && accountID.isEncryptionProtocolEnabled( + ZrtpControl.PROTO_NAME) && getPeer().getCall().isSipZrtpAttribute()) { // ZRTP @@ -151,55 +151,33 @@ protected void addSDESAdvertisedEncryptions( if(accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("SDES")) + && accountID.isEncryptionProtocolEnabled( + SDesControl.PROTO_NAME)) { - Map 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> iter - = srtpControls.entrySet().iterator(); - - while (iter.hasNext()) - { - Map.Entry 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 @@ protected void addSDESAdvertisedEncryptions( // 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 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 @@ protected boolean isRemoteZrtpCapable( for(int i = 0; i < packetExtensions.size(); ++i) { if(packetExtensions.get(i) instanceof ZrtpHashPacketExtension) - { return true; - } } - return false; } @@ -338,29 +298,21 @@ protected boolean setZrtpEncryptionToDescription( if(accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("ZRTP") + && accountID.isEncryptionProtocolEnabled( + ZrtpControl.PROTO_NAME) && peer.getCall().isSipZrtpAttribute()) { - Map 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 @@ protected boolean setSDesEncryptionToDescription( if (accountID.getAccountPropertyBoolean( ProtocolProviderFactory.DEFAULT_ENCRYPTION, true) - && accountID.isEncryptionProtocolEnabled("SDES")) + && accountID.isEncryptionProtocolEnabled( + SDesControl.PROTO_NAME)) { // get or create the control - Map 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 @@ protected boolean setSDesEncryptionToDescription( 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 @@ protected boolean setSDesEncryptionToDescription( localDescription.addChildExtension(localEncryption); } for(SrtpCryptoAttribute ca: - sdcontrol.getInitiatorCryptoAttributes()) + sdesControl.getInitiatorCryptoAttributes()) { CryptoPacketExtension crypto = new CryptoPacketExtension(ca); @@ -479,7 +423,7 @@ protected boolean setSDesEncryptionToDescription( { SrtpCryptoAttribute selectedSdes = selectSdesCryptoSuite( false, - sdcontrol, + sdesControl, remoteEncryption); if(selectedSdes != null) @@ -505,8 +449,8 @@ protected boolean setSDesEncryptionToDescription( { // 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 @@ protected boolean setSDesEncryptionToDescription( { // 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 @@ protected void setAndAddPreferredEncryptionProtocol( 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 @@ protected void setAndAddPreferredEncryptionProtocol( 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 4185a6da7..6c4e190d3 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 void sendCandidates( * @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 lst = localContentMap.get("audio"); description.setNamespace(SessionIQProvider.GTALK_AUDIO_NAMESPACE); @@ -282,11 +282,10 @@ public RtpDescriptionPacketExtension generateSessionAccept( continue; // stream target - MediaStreamTarget target = transportManager.getStreamTarget( - mediaType); + MediaStreamTarget target + = transportManager.getStreamTarget(mediaType); - List rtpExtensions = - new ArrayList(); + List rtpExtensions = new ArrayList(); 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 052e64563..727b735bf 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 @@ && isFeatureSupported( * 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 b3f877d71..2e70e4879 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 void startCandidateHarvest( * * @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 9364d3804..af336596e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountIDImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountIDImpl.java @@ -31,19 +31,6 @@ public class JabberAccountIDImpl super( id, accountProperties ); } - /** - * 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 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 50f0a687e..546ce99d2 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 @@ protected PacketExtension getTransportPacketExtension() * @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 928487ec5..17bd9c8dd 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 @@ private RawUdpTransportPacketExtension createTransport( 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 000000000..440cca478 --- /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 4b36b5672..583906d91 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 JingleIQProvider() new DefaultPacketExtensionProvider( CoinPacketExtension.class)); + // DTLS-SRTP + providerManager.addExtensionProvider( + DtlsFingerprintPacketExtension.ELEMENT_NAME, + DtlsFingerprintPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + ( + DtlsFingerprintPacketExtension.class)); + /* * XEP-0251: Jingle Session Transfer and * 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 7cbd5f76a..246a26b76 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java @@ -32,11 +32,26 @@ * both classes are only separated for reasons of readability. * * @author Emil Ivov - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class CallPeerMediaHandlerSipImpl extends CallPeerMediaHandler { + /** + * 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. */ @@ -197,9 +212,7 @@ private Vector createMediaDescriptions() 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 @@ private Vector createMediaDescriptions() = direction.allowsSending() ? sendQualityPreset : null; MediaDescription md = createMediaDescription( - profileName, + proto, getLocallySupportedFormats( dev, effectiveSendQualityPreset, @@ -247,16 +260,36 @@ private Vector createMediaDescriptions() // 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 @@ private SessionDescription processUpdateOffer( * or semantics of newOffer. */ private Vector createMediaDescriptionsForAnswer( - SessionDescription offer) + SessionDescription offer) throws OperationFailedException, IllegalArgumentException { - List remoteDescriptions = SdpUtils - .extractMediaDescriptions(offer); - + List remoteDescriptions + = SdpUtils.extractMediaDescriptions(offer); // prepare to generate answers to all the incoming descriptions Vector answerDescriptions - = new Vector( remoteDescriptions.size() ); + = new Vector(remoteDescriptions.size()); this.setCallInfoURL(SdpUtils.getCallInfoURL(offer)); @@ -457,22 +489,26 @@ private Vector createMediaDescriptionsForAnswer( 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 @@ else if(mediaType.equals(MediaType.VIDEO) MediaDescription md = createMediaDescription( - transportProtocol, + proto, mutuallySupportedFormats, connector, direction, @@ -676,7 +712,6 @@ else if(mediaType.equals(MediaType.VIDEO) atLeastOneValidDescription = true; } - if (!atLeastOneValidDescription) { if (rejectedAvpOfferDueToSavpMandatory) @@ -699,72 +734,270 @@ else if(mediaType.equals(MediaType.VIDEO) } /** - * 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 MediaDescription 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 MediaType of the media described by + * localMd and remoteMd + * @param localMd the local MediaDescription to be updated + * @param remoteMd the remote MediaDescription, if any, associated + * with localMd + * @return true if the specified localMd and/or the state + * of this instance was updated for the purposes of DTLS-SRTP or + * false if the specified localMd (and remoteMd) + * 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 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 SrtpControls of this instance in accord with a + * specific MediaDescription presented by a remote peer. + * + * @param mediaType the MediaType of the specified + * MediaDescription to be analyzed + * @param localMd the MediaDescription of the local peer that is + * the answer to the offer presented by a remote peer represented by + * remoteMd or null if the specified remoteMd is + * an answer to an offer of the local peer + * @param remoteMd the MediaDescription 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 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 attrs = remoteMd.getAttributes(false); + Map remoteFingerprints + = new LinkedHashMap(); - 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 true if SDES is added to the media description; + * @return true if SDES has been added to the media description; * false, otherwise. */ private boolean updateMediaDescriptionForSDes( @@ -774,29 +1007,22 @@ private boolean updateMediaDescriptionForSDes( { 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 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 @@ private boolean updateMediaDescriptionForSDes( = 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 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 @@ private boolean updateMediaDescriptionForSDes( } 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 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 true if ZRTP is added to the media description; + * false, 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. <proto>) to + * be announced in a SDP media description (i.e. m= line). + * + * @return a List of (RTP) transport protocols to be announced in a + * SDP media description + * @throws OperationFailedException if the value of the AccountID + * property {@link ProtocolProviderFactory#SAVP_OPTION} is invalid + */ + private List getRtpTransports() + throws OperationFailedException { AccountID accountID = getPeer().getProtocolProvider().getAccountID(); int savpOption @@ -862,25 +1154,65 @@ private List getRtpTransports() throws OperationFailedException ProtocolProviderFactory.SAVP_OPTION, ProtocolProviderFactory.SAVP_OFF) : ProtocolProviderFactory.SAVP_OFF; - List result = new ArrayList(2); + List result = new ArrayList(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 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 @@ private void doNonSynchronisedProcessAnswer(SessionDescription answer) = SdpUtils.containsAttribute(mediaDescription, "imageattr"); } + + // DTLS-SRTP + updateSrtpControlsForDtls(mediaType, null, mediaDescription); + + // SDES // select the crypto key the peer has chosen from our proposal - Map 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> iter - = srtpControls.entrySet().iterator(); - - while (iter.hasNext()) - { - Map.Entry 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 @@ protected void setAndAddPreferredEncryptionProtocol( .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 9b3949336..b838da40e 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 void mouseClicked(MouseEvent e) 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 a66f1558f..e4a62ac67 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 @@ /** * The SecurityAccountRegistration 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 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(1); - this.encryptionProtocols.put("ZRTP", 0); - this.encryptionProtocolStatus = new HashMap(1); - this.encryptionProtocolStatus.put("ZRTP", true); + encryptionProtocols = new HashMap(1); + encryptionProtocols.put("ZRTP", 0); + encryptionProtocolStatus = new HashMap(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 void setEncryptionProtocolStatus( private void addEncryptionProtocolsToProperties( Map properties) { - Map encryptionProtocols - = this.getEncryptionProtocols(); - Iterator encryptionProtocolIterator - = encryptionProtocols.keySet().iterator(); - String encryptionProtocol; - while(encryptionProtocolIterator.hasNext()) + for (Map.Entry 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 @@ private void addEncryptionProtocolsToProperties( private void addEncryptionProtocolStatusToProperties( Map properties) { - Map encryptionProtocolStatus - = this.getEncryptionProtocolStatus(); - Iterator encryptionProtocolStatusIterator - = encryptionProtocolStatus.keySet().iterator(); - String encryptionProtocol; - while(encryptionProtocolStatusIterator.hasNext()) + for (Map.Entry 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 void storeProperties(Map propertiesMap) // 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 void loadAccount(AccountID accountID) encryptionProtocols = new HashMap(); encryptionProtocolStatus = new HashMap(); - Map srcEncryptionProtocols - = accountID.getIntegerPropertiesByPrefix( - ProtocolProviderFactory.ENCRYPTION_PROTOCOL, true); - Map srcEncryptionProtocolStatus - = accountID.getBooleanPropertiesByPrefix( + Map srcEncryptionProtocols + = accountID.getIntegerPropertiesByPrefix( + ProtocolProviderFactory.ENCRYPTION_PROTOCOL, + true); + Map 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 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 void loadAccount(AccountID accountID) 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 static Object[] loadEncryptionProtocols( Map encryptionProtocols, Map 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 encryptionProtocolNames - = encryptionProtocols.keySet().iterator(); - while(encryptionProtocolNames.hasNext()) + for (Map.Entry 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 static Object[] loadEncryptionProtocols( } ++j; } - } } - return new Object[]{ encryptions, selectedEncryptions}; + return new Object[] { encryptions, selectedEncryptions}; } /** - * Checks if given protocol is on supported protocols list. + * Checks if a specific protocol is on the list of supported + * (encryption) protocols. + * * @param protocol the protocol name - * @return true if encryption protocol with given protocol name is - * supported. + * @return true if protocol is supported; false, + * 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 11fcf3113..9780393c5 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.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 @@ protected DynamicRTPExtensionsRegistry getRtpExtensionsRegistry() * @return the SrtpControls of the MediaStreams of this * instance */ - protected Map getSrtpControls() + protected SrtpControls getSrtpControls() { return mediaHandler.getSrtpControls(this); } @@ -1443,6 +1443,24 @@ public boolean processKeyFrameRequest() 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 VideoListener 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 62ecfa67f..4b5db9e3b 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 void audioLevelChanged(int level) /** * The SrtpControls of the MediaStreams of this instance. */ - private final SortedMap srtpControls - = new TreeMap(); + private final SrtpControls srtpControls + = new SrtpControls(); private final SrtpListener srtpListener = new SrtpListener() @@ -597,19 +597,7 @@ protected void closeStream( } // Clean up the SRTP controls used for the associated Call. - Iterator> iter - = srtpControls.entrySet().iterator(); - - while (iter.hasNext()) - { - Map.Entry entry = iter.next(); - - if (entry.getKey().mediaType == mediaType) - { - entry.getValue().cleanup(); - iter.remove(); - } - } + callPeerMediaHandler.removeAndCleanupOtherSrtpControls(mediaType, null); } /** @@ -771,8 +759,8 @@ SrtpControl getEncryptionMethod( 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 @@ long getRemoteSSRC( * @return the SrtpControls of the MediaStreams of this * instance */ - Map getSrtpControls( - CallPeerMediaHandler callPeerMediaHandler) + SrtpControls getSrtpControls(CallPeerMediaHandler callPeerMediaHandler) { return srtpControls; } @@ -884,17 +871,7 @@ MediaStream initStream( 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 @@ MediaStream initStream( * 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 000000000..0e8800778 --- /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 SrtpControl 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 SrtpControl implementations which are the elements of this + * sorted set. + */ + private final SrtpControl[][] elements + = new SrtpControl + [MediaType.values().length] + [SrtpControlType.values().length]; + + /** + * Initializes a new SrtpControls 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; + } +}