Adds support for DTLS-SRTP with SIP.

cusax-fix
Lyubomir Marinov 12 years ago
parent 5568ab8f89
commit 7a957a5ad5

@ -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>

@ -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

@ -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)
{

@ -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)
{

@ -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,

@ -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<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 @@ 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<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 @@ 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<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 @@ protected boolean setSDesEncryptionToDescription(
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 @@ 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;
}
}

@ -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<PayloadTypePacketExtension> 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<RTPExtension> rtpExtensions =
new ArrayList<RTPExtension>();
List<RTPExtension> rtpExtensions = new ArrayList<RTPExtension>();
MediaDirection direction = MediaDirection.SENDRECV;
boolean masterStream = false;

@ -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)
{
/*

@ -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();

@ -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<String, String> accountProperties)
{
return accountProperties.get(ProtocolProviderFactory.SERVER_ADDRESS);
}
/**
* Returns the list of STUN servers that this account is currently
* configured to use.

@ -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();

@ -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();

@ -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));
}
}

@ -142,6 +142,14 @@ public JingleIQProvider()
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

@ -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<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.
*/
@ -197,9 +212,7 @@ private Vector<MediaDescription> 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<MediaDescription> createMediaDescriptions()
= direction.allowsSending() ? sendQualityPreset : null;
MediaDescription md
= createMediaDescription(
profileName,
proto,
getLocallySupportedFormats(
dev,
effectiveSendQualityPreset,
@ -247,16 +260,36 @@ private Vector<MediaDescription> 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 <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 @@ private Vector<MediaDescription> 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 <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 @@ 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<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 @@ 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<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 @@ 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<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>&lt;proto&gt;</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 @@ private List<String> getRtpTransports() throws OperationFailedException
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 @@ 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<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 @@ 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;
}
}

@ -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];

@ -13,11 +13,12 @@
/**
* 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 void setEncryptionProtocolStatus(
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 @@ private void addEncryptionProtocolsToProperties(
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 void storeProperties(Map<String, String> 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<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 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<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 static Object[] loadEncryptionProtocols(
}
++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);
}
}

@ -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 <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 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 <tt>VideoListener</tt> from this instance so that
* it stops receiving notifications from it about changes in the

@ -138,8 +138,8 @@ public void audioLevelChanged(int level)
/**
* 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 @@ protected void closeStream(
}
// 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 @@ 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 <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 @@ 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
{

@ -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;
}
}
Loading…
Cancel
Save