SDES Integration: Enable fallback to ZRTP, add Javadoc, more renames

cusax-fix
Ingo Bauersachs 14 years ago
parent b0ad56ad82
commit 81d63ab672

@ -80,3 +80,5 @@ plugin.provisioning.EXIT_ON_FAIL=false
net.java.sip.communicator.util.dns.BACKUP_RESOLVER_FALLBACK_IP=8.8.8.8
plugin.jabberaccregwizz.NEW_ACCOUNT_DEFAULT_SERVER=jit.si
net.java.sip.communicator.service.neomedia.SDES_CIPHER_SUITES=AES_CM_128_HMAC_SHA1_80,AES_CM_128_HMAC_SHA1_32

@ -196,9 +196,9 @@ else if (MediaDeviceSession.SSRC_LIST.equals(propertyName))
new Hashtable<String, String>();
/**
* The current <tt>ZrtpControl</tt>.
* The current <tt>SrtpControl</tt>.
*/
private final SrtpControl zrtpControl;
private final SrtpControl srtpControl;
/**
* Needed when restarting zrtp control.
@ -256,14 +256,14 @@ public MediaStreamImpl(MediaDevice device, SrtpControl srtpControl)
* @param device the <tt>MediaDevice</tt> the new instance is to use for
* both capture and playback of media exchanged via the specified
* <tt>StreamConnector</tt>
* @param zrtpControl an existing control instance to control the ZRTP
* @param srtpControl an existing control instance to control the ZRTP
* operations or <tt>null</tt> if a new control instance is to be created by
* the new <tt>MediaStreamImpl</tt>
*/
public MediaStreamImpl(
StreamConnector connector,
MediaDevice device,
SrtpControl zrtpControl)
SrtpControl srtpControl)
{
/*
* XXX Set the device early in order to make sure that it is of the
@ -271,8 +271,9 @@ public MediaStreamImpl(
*/
setDevice(device);
this.zrtpControl
= (zrtpControl == null) ? new ZrtpControlImpl() : zrtpControl;
//TODO add option to disable ZRTP, e.g. by implementing a NullControl
this.srtpControl
= (srtpControl == null) ? new ZrtpControlImpl() : srtpControl;
if (connector != null)
setConnector(connector);
@ -351,8 +352,8 @@ private TransformEngineChain createTransformEngineChain()
if (dtmfEngine != null)
engineChain.add(dtmfEngine);
// ZRTP
engineChain.add(zrtpControl.getTransformEngine());
// SRTP
engineChain.add(srtpControl.getTransformEngine());
// RTCP Statistics
if(statisticsEngine == null)
@ -506,7 +507,7 @@ public void close()
stop();
closeSendStreams();
zrtpControl.cleanup();
srtpControl.cleanup();
zrtpRestarted = false;
if(csrcEngine != null)
@ -646,7 +647,7 @@ else if (dataSource instanceof PullDataSource)
// If a ZRTP engine is available then set the SSRC of this
// stream
// currently ZRTP supports only one SSRC per engine
TransformEngine engine = zrtpControl.getTransformEngine();
TransformEngine engine = srtpControl.getTransformEngine();
if (engine != null && engine instanceof ZRTPTransformEngine)
((ZRTPTransformEngine)engine)
@ -1096,13 +1097,13 @@ private RTPManager getRTPManager()
}
/**
* Gets the <tt>ZrtpControl</tt> which controls the ZRTP of this stream.
* Gets the <tt>SrtpControl</tt> which controls the SRTP of this stream.
*
* @return the <tt>ZrtpControl</tt> which controls the ZRTP of this stream
* @return the <tt>SrtpControl</tt> which controls the SRTP of this stream
*/
public SrtpControl getZrtpControl()
public SrtpControl getSrtpControl()
{
return zrtpControl;
return srtpControl;
}
/**
@ -1115,10 +1116,10 @@ private void restartZrtpControl()
* If there is no current secure communication, we don't need to do
* that.
*/
if(!zrtpControl.getSecureCommunicationStatus())
if(!srtpControl.getSecureCommunicationStatus())
return;
zrtpControl.cleanup();
srtpControl.cleanup();
/*
* As we are recreating this stream and it was obviously secured, it may
@ -1134,7 +1135,7 @@ private void restartZrtpControl()
AbstractRTPConnector rtpConnector = getRTPConnector();
zrtpControl.setConnector(rtpConnector);
srtpControl.setConnector(rtpConnector);
if(rtpConnector instanceof RTPTransformUDPConnector)
((RTPTransformUDPConnector)rtpConnector)
.setEngine(createTransformEngineChain());
@ -1250,7 +1251,7 @@ protected void rtpConnectorChanged(
AbstractRTPConnector oldValue,
AbstractRTPConnector newValue)
{
zrtpControl.setConnector(newValue);
srtpControl.setConnector(newValue);
if (newValue != null)
{

@ -1,42 +1,68 @@
package net.java.sip.communicator.impl.neomedia;
import java.util.*;
import ch.imvs.sdes4j.srtp.*;
import net.java.sip.communicator.impl.neomedia.transform.*;
import net.java.sip.communicator.impl.neomedia.transform.sdes.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.service.protocol.event.CallPeerSecurityStatusEvent;
/**
* Default implementation of {@link SDesControl}.
*
* @author Ingo Bauersachs
*/
public class SDesControlImpl
implements SDesControl
{
private String[] supportedCryptoSuites = new String[]
{
SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_80,
//SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_32,
//SrtpCryptoSuite.F8_128_HMAC_SHA1_80
};
private List<String> enabledCryptoSuites = new ArrayList<String>(3)
{{
add(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_80);
add(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_32);
add(SrtpCryptoSuite.F8_128_HMAC_SHA1_80);
}};
private final List<String> supportedCryptoSuites = new ArrayList<String>(3)
{{
add(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_80);
add(SrtpCryptoSuite.AES_CM_128_HMAC_SHA1_32);
add(SrtpCryptoSuite.F8_128_HMAC_SHA1_80);
}};
private SrtpSDesFactory sdesFactory = new SrtpSDesFactory();
private SrtpCryptoAttribute[] attributes;
private SDesTransformEngine engine;
private SrtpCryptoAttribute selectedInAttribute;
private SrtpCryptoAttribute selectedOutAttribute;
private SrtpListener srtpListener;
public SDesControlImpl()
public void setEnabledCiphers(Iterable<String> ciphers)
{
enabledCryptoSuites.clear();
for(String c : ciphers)
enabledCryptoSuites.add(c);
}
public Iterable<String> getSupportedCryptoSuites()
{
return Collections.unmodifiableList(supportedCryptoSuites);
}
public void cleanup()
{
}
public void setZrtpListener(SrtpListener zrtpListener)
public void setSrtpListener(SrtpListener srtpListener)
{
this.srtpListener = srtpListener;
}
public SrtpListener getSrtpListener()
{
return null;
return srtpListener;
}
public boolean getSecureCommunicationStatus()
@ -50,6 +76,11 @@ public void setSASVerification(boolean verified)
public void start(boolean masterSession)
{
srtpListener.securityTurnedOn(
masterSession ?
CallPeerSecurityStatusEvent.AUDIO_SESSION :
CallPeerSecurityStatusEvent.VIDEO_SESSION,
selectedInAttribute.getCryptoSuite().encode(), null, true, null);
}
public void setMultistream(byte[] multiStreamData)
@ -65,12 +96,15 @@ public TransformEngine getTransformEngine()
public String[] getInitiatorCryptoAttributes()
{
attributes = new SrtpCryptoAttribute[supportedCryptoSuites.length];
for (int i = 0; i < attributes.length; i++)
if(attributes == null)
{
attributes[i] =
sdesFactory.createCryptoAttribute(i + 1,
supportedCryptoSuites[i]);
attributes = new SrtpCryptoAttribute[enabledCryptoSuites.size()];
for (int i = 0; i < attributes.length; i++)
{
attributes[i] =
sdesFactory.createCryptoAttribute(i + 1,
enabledCryptoSuites.get(i));
}
}
String[] result = new String[attributes.length];
for(int i = 0; i < attributes.length; i++)
@ -78,9 +112,9 @@ public String[] getInitiatorCryptoAttributes()
return result;
}
public String responderSelectAttribute(String[] peerAttributes)
public String responderSelectAttribute(Iterable<String> peerAttributes)
{
for (String suite : supportedCryptoSuites)
for (String suite : enabledCryptoSuites)
{
for (String ea : peerAttributes)
{
@ -97,7 +131,7 @@ public String responderSelectAttribute(String[] peerAttributes)
return null;
}
public void initiatorSelectAttribute(String[] peerAttributes)
public boolean initiatorSelectAttribute(Iterable<String> peerAttributes)
{
for (SrtpCryptoAttribute localCA : attributes)
{
@ -108,10 +142,11 @@ public void initiatorSelectAttribute(String[] peerAttributes)
{
selectedInAttribute = peerCA;
selectedOutAttribute = localCA;
return;
return true;
}
}
}
return false;
}
public SrtpCryptoAttribute getInAttribute()

@ -78,7 +78,7 @@ public void cleanup()
*
* @param zrtpListener the <tt>ZrtpListener</tt> to set
*/
public void setZrtpListener(SrtpListener zrtpListener)
public void setSrtpListener(SrtpListener zrtpListener)
{
this.zrtpListener = zrtpListener;
}

@ -6,6 +6,11 @@
import net.java.sip.communicator.impl.neomedia.transform.*;
import net.java.sip.communicator.impl.neomedia.transform.srtp.*;
/**
* PacketTransformer for SDES based SRTP encryption.
*
* @author Ingo Bauersachs
*/
public class SDesTransformEngine
implements TransformEngine, PacketTransformer
{
@ -14,6 +19,10 @@ public class SDesTransformEngine
private SrtpCryptoAttribute inAttribute;
private SrtpCryptoAttribute outAttribute;
/**
* Creates a new instance of this class.
* @param sDesControl The control that supplies the key material.
*/
public SDesTransformEngine(SDesControlImpl sDesControl)
{
inAttribute = sDesControl.getInAttribute();
@ -53,11 +62,23 @@ private byte[] getSalt(SrtpCryptoAttribute attribute)
return salt;
}
/*
* (non-Javadoc)
*
* @see net.java.sip.communicator.impl.neomedia.transform.TransformEngine#
* getRTPTransformer()
*/
public PacketTransformer getRTPTransformer()
{
return this;
}
/*
* (non-Javadoc)
*
* @see net.java.sip.communicator.impl.neomedia.transform.TransformEngine#
* getRTCPTransformer()
*/
public PacketTransformer getRTCPTransformer()
{
return null;
@ -70,23 +91,23 @@ private SRTPCryptoContext createContext(long ssrc,
SrtpCryptoSuite cs = attribute.getCryptoSuite();
switch (cs.getEncryptionAlgorithm())
{
case SrtpCryptoSuite.ENCRYPTION_AES128_CM:
encType = SRTPPolicy.AESCM_ENCRYPTION;
break;
case SrtpCryptoSuite.ENCRYPTION_AES128_F8:
encType = SRTPPolicy.AESF8_ENCRYPTION;
break;
default:
throw new IllegalArgumentException("Unsupported cipher");
case SrtpCryptoSuite.ENCRYPTION_AES128_CM:
encType = SRTPPolicy.AESCM_ENCRYPTION;
break;
case SrtpCryptoSuite.ENCRYPTION_AES128_F8:
encType = SRTPPolicy.AESF8_ENCRYPTION;
break;
default:
throw new IllegalArgumentException("Unsupported cipher");
}
int authType;
switch (cs.getHashAlgorithm())
{
case SrtpCryptoSuite.HASH_HMAC_SHA1:
authType = SRTPPolicy.HMACSHA1_AUTHENTICATION;
break;
default:
throw new IllegalArgumentException("Unsupported hash");
case SrtpCryptoSuite.HASH_HMAC_SHA1:
authType = SRTPPolicy.HMACSHA1_AUTHENTICATION;
break;
default:
throw new IllegalArgumentException("Unsupported hash");
}
SRTPPolicy policy =
new SRTPPolicy(
@ -103,6 +124,13 @@ private SRTPCryptoContext createContext(long ssrc,
);
}
/*
* (non-Javadoc)
*
* @see
* net.java.sip.communicator.impl.neomedia.transform.PacketTransformer
* #transform(net.java.sip.communicator.impl.neomedia.RawPacket)
*/
public RawPacket transform(RawPacket pkt)
{
if (outContext == null)
@ -115,6 +143,12 @@ public RawPacket transform(RawPacket pkt)
return pkt;
}
/*
* (non-Javadoc)
*
* @see net.java.sip.communicator.impl.neomedia.transform.PacketTransformer#
* reverseTransform(net.java.sip.communicator.impl.neomedia.RawPacket)
*/
public RawPacket reverseTransform(RawPacket pkt)
{
long ssrc = pkt.getSSRC();

@ -343,12 +343,14 @@ public void processOffer(List<ContentPacketExtension> offer)
// ZRTP
if(getPeer().getCall().isSipZrtpAttribute())
{
SrtpControl control = getZrtpControls().get(mediaType);
if(control == null || !(control instanceof ZrtpControl))
MediaTypeSrtpControl key =
new MediaTypeSrtpControl(mediaType, SrtpControlType.ZRTP);
SrtpControl control = getSrtpControls().get(key);
if(control == null)
{
control = JabberActivator.getMediaService()
.createZrtpControl();
getZrtpControls().put(mediaType, control);
getSrtpControls().put(key, control);
}
String helloHash[] = ((ZrtpControl)control).getHelloHashSep();
@ -550,13 +552,15 @@ private ContentPacketExtension createContent(MediaDevice dev)
//ZRTP
if(getPeer().getCall().isSipZrtpAttribute())
{
SrtpControl control = getZrtpControls().get(dev.getMediaType());
if(control == null || !(control instanceof ZrtpControl))
MediaTypeSrtpControl key =
new MediaTypeSrtpControl(dev.getMediaType(),
SrtpControlType.ZRTP);
SrtpControl control = getSrtpControls().get(key);
if(control == null)
{
control
= JabberActivator.getMediaService().createZrtpControl();
getZrtpControls().put(dev.getMediaType(), control);
getSrtpControls().put(key, control);
}
String helloHash[] = ((ZrtpControl)control).getHelloHashSep();
@ -689,12 +693,15 @@ public List<ContentPacketExtension> createContentList()
//ZRTP
if(getPeer().getCall().isSipZrtpAttribute())
{
SrtpControl control = getZrtpControls().get(mediaType);
if(control == null || !(control instanceof ZrtpControl))
MediaTypeSrtpControl key =
new MediaTypeSrtpControl(mediaType,
SrtpControlType.ZRTP);
SrtpControl control = getSrtpControls().get(key);
if(control == null)
{
control = JabberActivator.getMediaService()
.createZrtpControl();
getZrtpControls().put(mediaType, control);
getSrtpControls().put(key, control);
}
String helloHash[] =

@ -46,12 +46,6 @@ public class CallPeerMediaHandlerSipImpl
*/
private SessionDescription localSess = null;
/**
* The last ( and maybe only ) session description that we received from
* the remote party.
*/
private SessionDescription remoteSess = null;
/**
* A <tt>URL</tt> pointing to a location with call information or a call
* control web interface related to the <tt>CallPeer</tt> that we are
@ -179,19 +173,23 @@ private Vector<MediaDescription> createMediaDescriptions()
{
MediaDevice dev = getDefaultDevice(mediaType);
if (dev != null)
{
MediaDirection direction = dev.getDirection().and(
getDirectionUserPreference(mediaType));
if (dev == null)
continue;
if(isLocallyOnHold())
direction = direction.and(MediaDirection.SENDONLY);
MediaDirection direction = dev.getDirection().and(
getDirectionUserPreference(mediaType));
if(direction != MediaDirection.INACTIVE)
if(isLocallyOnHold())
direction = direction.and(MediaDirection.SENDONLY);
if(direction != MediaDirection.INACTIVE)
{
boolean hadSavp = false;
for (String profileName : getRtpTransports())
{
MediaDescription md =
createMediaDescription(
true, //TODO base on settings
profileName,
dev.getSupportedFormats(
sendQualityPreset,
receiveQualityPreset),
@ -216,10 +214,16 @@ private Vector<MediaDescription> createMediaDescriptions()
// do nothing in case of error.
}
//updateMediaDescriptionForZrtp(mediaType, md); //TODO: base on setting
updateMediaDescriptionForSDes(mediaType, md, null);
if(!hadSavp)
{
updateMediaDescriptionForZrtp(mediaType, md);
updateMediaDescriptionForSDes(mediaType, md, null);
}
mediaDescs.add(md);
if(!hadSavp && profileName.contains("SAVP"))
hadSavp = true;
}
}
}
@ -321,8 +325,6 @@ private SessionDescription processFirstOffer(SessionDescription offer)
throws OperationFailedException,
IllegalArgumentException
{
this.remoteSess = offer;
Vector<MediaDescription> answerDescriptions
= createMediaDescriptionsForAnswer(offer);
@ -358,8 +360,6 @@ private SessionDescription processUpdateOffer(
throws OperationFailedException,
IllegalArgumentException
{
this.remoteSess = newOffer;
Vector<MediaDescription> answerDescriptions
= createMediaDescriptionsForAnswer(newOffer);
@ -403,12 +403,17 @@ private Vector<MediaDescription> createMediaDescriptionsForAnswer(
boolean atLeastOneValidDescription = false;
List<MediaType> seenMediaTypes = new ArrayList<MediaType>();
for (MediaDescription mediaDescription : remoteDescriptions)
{
MediaType mediaType = null;
try
{
mediaType = SdpUtils.getMediaType(mediaDescription);
//don't process a second media of the same type
if(seenMediaTypes.contains(mediaType))
continue;
seenMediaTypes.add(mediaType);
}
catch (IllegalArgumentException iae)
{
@ -524,11 +529,23 @@ private Vector<MediaDescription> createMediaDescriptionsForAnswer(
}
}
MediaDescription md = createMediaDescription(true, //TODO base on settings
mutuallySupportedFormats, connector, direction, rtpExtensions);
MediaDescription md;
try
{
md =
createMediaDescription(mediaDescription.getMedia()
.getProtocol(), mutuallySupportedFormats, connector,
direction, rtpExtensions);
}
catch (SdpParseException e)
{
throw new OperationFailedException(
"unable to create the media description",
OperationFailedException.ILLEGAL_ARGUMENT, e);
}
//updateMediaDescriptionForZrtp(mediaType, md); //TODO base on setting
updateMediaDescriptionForSDes(mediaType, md, mediaDescription);
if(!updateMediaDescriptionForSDes(mediaType, md, mediaDescription))
updateMediaDescriptionForZrtp(mediaType, md);
// create the corresponding stream...
MediaFormat fmt = findMediaFormat(remoteFormats,
@ -562,12 +579,14 @@ private void updateMediaDescriptionForZrtp(
{
try
{
SrtpControl scontrol = getZrtpControls().get(mediaType);
if(scontrol == null || !(scontrol instanceof ZrtpControl))
MediaTypeSrtpControl key =
new MediaTypeSrtpControl(mediaType, SrtpControlType.ZRTP);
SrtpControl scontrol = getSrtpControls().get(key);
if(scontrol == null)
{
scontrol = SipActivator.getMediaService()
.createZrtpControl();
getZrtpControls().put(mediaType, scontrol);
getSrtpControls().put(key, scontrol);
}
ZrtpControl zcontrol = (ZrtpControl) scontrol;
@ -587,72 +606,139 @@ private void updateMediaDescriptionForZrtp(
* Updates the supplied description with SDES attributes if necessary.
*
* @param mediaType the media type.
* @param localMd the description to be updated.
* @param peerMd
* @throws OperationFailedException
* @param localMd the description of the local peer.
* @param peerMd the description of the remote peer.
*/
@SuppressWarnings("unchecked") //jain-sip legacy
private void updateMediaDescriptionForSDes(
private boolean updateMediaDescriptionForSDes(
MediaType mediaType, MediaDescription localMd, MediaDescription peerMd)
throws OperationFailedException
{
//if(getPeer().getCall().isSipZrtpAttribute()) //TODO: check SDES config
//check if SDES is enabled at all
if (!getPeer()
.getProtocolProvider()
.getAccountID()
.getAccountPropertyBoolean(
ProtocolProviderServiceSipImpl.SDES_ENABLED, true))
{
return false;
}
// get or create the control
MediaTypeSrtpControl key =
new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES);
SrtpControl scontrol = getSrtpControls().get(key);
if (scontrol == null)
{
scontrol = SipActivator.getMediaService().createSDesControl();
getSrtpControls().put(key, scontrol);
}
// set the enabled ciphers suites
SDesControl sdcontrol = (SDesControl) scontrol;
String ciphers =
getPeer()
.getProtocolProvider()
.getAccountID()
.getAccountPropertyString(
ProtocolProviderServiceSipImpl.SDES_CIPHER_SUITES);
if (ciphers == null)
{
SrtpControl scontrol = getZrtpControls().get(mediaType);
if (scontrol == null || !(scontrol instanceof SDesControl))
ciphers =
SipActivator.getResources().getSettingsString(
SDesControl.SDES_CIPHER_SUITES);
}
sdcontrol.setEnabledCiphers(Arrays.asList(ciphers.split(",")));
// act as initiator
if (peerMd == null)
{
Vector<Attribute> atts = localMd.getAttributes(true);
for (String ca : sdcontrol.getInitiatorCryptoAttributes())
{
scontrol = SipActivator.getMediaService().createSDesControl();
getZrtpControls().put(mediaType, scontrol);
Attribute a = SdpUtils.createAttribute("crypto", ca);
atts.add(a);
}
SDesControl sdcontrol = (SDesControl) scontrol;
if (peerMd == null)
return true;
}
// act as responder
else
{
Vector<Attribute> atts = peerMd.getAttributes(true);
List<String> peerAttributes = new LinkedList<String>();
for (Attribute a : atts)
{
Vector<Attribute> atts = localMd.getAttributes(true);
for (String ca : sdcontrol.getInitiatorCryptoAttributes())
try
{
if (a.getName().equals("crypto"))
{
peerAttributes.add(a.getValue());
}
}
catch (SdpParseException e)
{
Attribute a = SdpUtils.createAttribute("crypto", ca);
atts.add(a);
logger.error("received an uparsable sdp attribute", e);
}
}
else
if (peerAttributes.size() > 0)
{
Vector<Attribute> atts = peerMd.getAttributes(true);
List<String> peerAttributes = new LinkedList<String>();
for (Attribute a : atts)
String localAttr =
sdcontrol.responderSelectAttribute(peerAttributes);
if (localAttr != null)
{
try
{
if (a.getName().equals("crypto"))
{
peerAttributes.add(a.getValue());
}
localMd.setAttribute("crypto", localAttr);
return true;
}
catch (SdpParseException e)
catch (SdpException e)
{
logger.error("received an uparsable sdp attribute", e);
logger.error("unable to add crypto to answer", e);
}
}
if (peerAttributes.size() > 0)
else
{
String localAttr =
sdcontrol.responderSelectAttribute(peerAttributes
.toArray(new String[peerAttributes.size()]));
if(localAttr != null)
{
try
{
localMd.setAttribute("crypto", localAttr);
}
catch (SdpException e)
{
logger.error("unable to add crypto to answer", e);
}
}
// none of the offered suites match, destroy the sdes
// control
getSrtpControls().remove(key);
logger.warn("Received unsupported sdes crypto attribute "
+ peerAttributes.toString());
}
}
else
{
// peer doesn't offer any SDES attribute, destroy the sdes
// control
getSrtpControls().remove(key);
}
return false;
}
}
private List<String> getRtpTransports() throws OperationFailedException
{
List<String> result = new ArrayList<String>(2);
int savpOption =
getPeer()
.getProtocolProvider()
.getAccountID()
.getAccountPropertyInt(
ProtocolProviderServiceSipImpl.SAVP_OPTION,
ProtocolProviderServiceSipImpl.SAVP_OFF);
if(savpOption == ProtocolProviderServiceSipImpl.SAVP_MANDATORY)
result.add("RTP/SAVP");
else if(savpOption == ProtocolProviderServiceSipImpl.SAVP_OFF)
result.add(SdpConstants.RTP_AVP);
else if(savpOption == ProtocolProviderServiceSipImpl.SAVP_OPTIONAL)
{
result.add("RTP/SAVP");
result.add(SdpConstants.RTP_AVP);
}
else
throw new OperationFailedException("invalid value for SAVP_OPTION",
OperationFailedException.GENERAL_ERROR);
return result;
}
/**
* Handles the specified <tt>answer</tt> by creating and initializing the
* corresponding <tt>MediaStream</tt>s.
@ -690,19 +776,22 @@ private synchronized void processAnswer(SessionDescription answer)
throws OperationFailedException,
IllegalArgumentException
{
this.remoteSess = answer;
List<MediaDescription> remoteDescriptions
= SdpUtils.extractMediaDescriptions(answer);
this.setCallInfoURL(SdpUtils.getCallInfoURL(answer));
for ( MediaDescription mediaDescription : remoteDescriptions)
List<MediaType> seenMediaTypes = new ArrayList<MediaType>();
for (MediaDescription mediaDescription : remoteDescriptions)
{
MediaType mediaType;
try
{
mediaType = SdpUtils.getMediaType(mediaDescription);
//don't process a second media of the same type
if(seenMediaTypes.contains(mediaType))
continue;
seenMediaTypes.add(mediaType);
}
catch(IllegalArgumentException iae)
{
@ -787,8 +876,10 @@ private synchronized void processAnswer(SessionDescription answer)
}
// select the crypto key the peer has chosen from our proposal
SrtpControl scontrol = getZrtpControls().get(mediaType);
if(scontrol != null && scontrol instanceof SDesControl)
MediaTypeSrtpControl key =
new MediaTypeSrtpControl(mediaType, SrtpControlType.SDES);
SrtpControl scontrol = getSrtpControls().get(key);
if(scontrol != null)
{
List<String> peerAttributes = new LinkedList<String>();
@SuppressWarnings("unchecked")
@ -807,9 +898,28 @@ private synchronized void processAnswer(SessionDescription answer)
logger.error("received an uparsable sdp attribute", e);
}
}
((SDesControl) scontrol)
.initiatorSelectAttribute(peerAttributes
.toArray(new String[peerAttributes.size()]));
if(!((SDesControl) scontrol)
.initiatorSelectAttribute(peerAttributes))
{
getSrtpControls().remove(key);
if(peerAttributes.size() > 0)
logger
.warn("Received unsupported sdes crypto attribute: "
+ peerAttributes);
}
else
{
//found an SDES answer, remove all other controls
Iterator<MediaTypeSrtpControl> it =
getSrtpControls().keySet().iterator();
while (it.hasNext())
{
MediaTypeSrtpControl mtc = it.next();
if (mtc.mediaType == mediaType
&& mtc.srtpControlType != SrtpControlType.SDES)
it.remove();
}
}
}
// create the corresponding stream...
@ -835,7 +945,7 @@ private String getUserName()
* taking account the local streaming preference for the corresponding
* media type.
*
* @param secure when true, the profile is RTP/SAVP instead of RTP/AVP
* @param transport the profile name (RTP/SAVP or RTP/AVP)
* @param formats the list of <tt>MediaFormats</tt> that we'd like to
* advertise.
* @param connector the <tt>StreamConnector</tt> that we will be using
@ -852,14 +962,14 @@ private String getUserName()
* <tt>MediaDescription</tt> fails for some reason.
*/
private MediaDescription createMediaDescription(
boolean secure,
String transport,
List<MediaFormat> formats,
StreamConnector connector,
MediaDirection direction,
List<RTPExtension> extensions )
throws OperationFailedException
{
return SdpUtils.createMediaDescription(secure, formats, connector,
return SdpUtils.createMediaDescription(transport, formats, connector,
direction, extensions,
getDynamicPayloadTypes(), getRtpExtensionsRegistry());
}

@ -159,7 +159,7 @@ public class ProtocolProviderServiceSipImpl
/**
* The name of the property under which the user may specify whether to use
* original sip creadetials for the XCAP.
* original sip credentials for the XCAP.
*/
public static final String XCAP_USE_SIP_CREDETIALS =
"XCAP_USE_SIP_CREDETIALS";
@ -181,6 +181,43 @@ public class ProtocolProviderServiceSipImpl
*/
public static final String XCAP_PASSWORD = "XCAP_PASSWORD";
/**
* The name of the property that indicates if SDES is enabled for this
* account.
*/
public static final String SDES_ENABLED = "SDES_ENABLED";
/**
* The name of the property that defines the enabled SDES cipher suites.
* Enabled suites are listed as CSV by their RFC name.
*/
public static final String SDES_CIPHER_SUITES = "SDES_CIPHER_SUITES";
/**
* The name of the property that indicates the AVP type.
* <ul>
* <li>{@link #SAVP_OPTION_AVP}</li>
* <li>{@link #SAVP_OPTION_SAVP}</li>
* <li>{@link #SAVP_OPTION_AVP_OR_SAVP}</li>
* </ul>
*/
public static final String SAVP_OPTION = "SAVP_OPTION";
/**
* Always use RTP/AVP
*/
public static final int SAVP_OFF = 0;
/**
* Always use RTP/SAVP
*/
public static final int SAVP_MANDATORY = 1;
/**
* Sends two media description, with RTP/SAVP being first.
*/
public static final int SAVP_OPTIONAL = 2;
/**
* Presence content for image.
*/

@ -1284,7 +1284,7 @@ public static URL getCallInfoURL(SessionDescription sessDesc)
* description is determined via from the type of the first
* <tt>MediaFormat</tt> in the <tt>formats</tt> list.
*
* @param secure when true, the profile is RTP/SAVP instead of RTP/AVP
* @param transport the profile name (RTP/SAVP or RTP/AVP)
* @param formats the list of formats that should be advertised in the newly
* created <tt>MediaDescription</tt>.
* @param connector the socket couple that will be used for the media stream
@ -1307,7 +1307,7 @@ public static URL getCallInfoURL(SessionDescription sessDesc)
* some other reason.
*/
public static MediaDescription createMediaDescription(
boolean secure,
String transport,
List<MediaFormat> formats,
StreamConnector connector,
MediaDirection direction,
@ -1437,8 +1437,7 @@ public static MediaDescription createMediaDescription(
{
mediaDesc = sdpFactory.createMediaDescription(mediaType.toString(),
connector.getDataSocket().getLocalPort(), 1,
secure ? "RTP/SAVP" : SdpConstants.RTP_AVP,
payloadTypesArray);
transport, payloadTypesArray);
// add all the attributes we have created above
mediaDesc.setAttributes(mediaAttributes);

@ -319,5 +319,5 @@ public void addDynamicRTPPayloadType(
*
* @return the <tt>ZrtpControl</tt> which controls the ZRTP for this stream
*/
public SrtpControl getZrtpControl();
public SrtpControl getSrtpControl();
}

@ -0,0 +1,65 @@
package net.java.sip.communicator.service.neomedia;
public class MediaTypeSrtpControl implements Comparable<MediaTypeSrtpControl>
{
public MediaType mediaType;
public SrtpControlType srtpControlType;
public MediaTypeSrtpControl(MediaType mt, SrtpControlType sct)
{
mediaType = mt;
srtpControlType = sct;
}
@Override
public boolean equals(Object obj)
{
if(obj == null || obj.getClass() != MediaTypeSrtpControl.class)
return false;
MediaTypeSrtpControl other = (MediaTypeSrtpControl)obj;
return mediaType == other.mediaType && srtpControlType == other.srtpControlType;
}
@Override
public int hashCode()
{
return mediaType.hashCode() ^ srtpControlType.hashCode();
}
public int compareTo(MediaTypeSrtpControl o)
{
return getWeight() == o.getWeight() ?
0 :
getWeight() < o.getWeight() ?
-1 : 1;
}
private int getWeight()
{
int mtWeight = 0;
switch(mediaType)
{
case AUDIO:
mtWeight = 1;
break;
case VIDEO:
mtWeight = 2;
break;
}
int stWeight = 0;
switch(srtpControlType)
{
case ZRTP:
stWeight = 1;
break;
case MIKEY:
stWeight = 2;
break;
case SDES:
stWeight = 3;
break;
}
return mtWeight * 10 + stWeight;
}
}

@ -2,12 +2,73 @@
import ch.imvs.sdes4j.srtp.SrtpCryptoAttribute;
/**
* SDES based SRTP MediaStream encryption control.
*
* @author Ingo Bauersachs
*/
public interface SDesControl
extends SrtpControl
{
/**
* Name of the config setting that supplies the default enabled cipher
* suites. Cipher suites are comma-separated.
*/
public static final String SDES_CIPHER_SUITES =
"net.java.sip.communicator.service.neomedia.SDES_CIPHER_SUITES";
/**
* Set the enabled SDES ciphers.
*
* @param ciphers The list of enabled ciphers.
*/
public void setEnabledCiphers(Iterable<String> ciphers);
/**
* Gets all supported cipher suites.
*
* @return all supported cipher suites.
*/
public Iterable<String> getSupportedCryptoSuites();
/**
* Gets the encoded SDES crypto-attributes for all enabled ciphers when the
* control is used as the initiator.
*
* @return the encoded SDES crypto-attributes for all enabled ciphers.
*/
public String[] getInitiatorCryptoAttributes();
public String responderSelectAttribute(String[] peerAttributes);
public void initiatorSelectAttribute(String[] peerAttributes);
/**
* Chooses a supported crypto attribute from the peer's list of supplied
* attributes and creates the local crypto attribute. Used when the control
* is running in the role as responder.
*
* @param peerAttributes The peer's crypto attribute offering.
* @return The local crypto attribute for the answer of the offer or null if
* no matching cipher suite could be found.
*/
public String responderSelectAttribute(Iterable<String> peerAttributes);
/**
* Select the local crypto attribute from the initial offering (@see
* {@link #getInitiatorCryptoAttributes()}) based on the peer's first
* matching cipher suite.
*
* @param peerAttributes The peer's crypto offers.
* @return True when a matching cipher suite was found, false otherwise.
*/
public boolean initiatorSelectAttribute(Iterable<String> peerAttributes);
/**
* Gets the crypto attribute of the incoming MediaStream.
* @return the crypto attribute of the incoming MediaStream.
*/
public SrtpCryptoAttribute getInAttribute();
/**
* Gets the crypto attribute of the outgoing MediaStream.
* @return the crypto attribute of the outgoing MediaStream.
*/
public SrtpCryptoAttribute getOutAttribute();
}

@ -11,29 +11,29 @@
import net.java.sip.communicator.service.neomedia.event.*;
/**
* Controls zrtp in the MediaStream.
* Controls SRTP encryption in the MediaStream.
*
* @author Damian Minkov
*/
public interface SrtpControl
{
/**
* Cleans up the current zrtp control and its engine.
* Cleans up the current SRTP control and its engine.
*/
public void cleanup();
/**
* Sets a <tt>ZrtpListener</tt> that will listen for
* zrtp security events.
* Sets a <tt>SrtpListener</tt> that will listen for
* srtp security events.
*
* @param zrtpListener the <tt>ZrtpListener</tt> to set
* @param srtpListener the <tt>SrtpListener</tt> to set
*/
public void setZrtpListener(SrtpListener zrtpListener);
public void setSrtpListener(SrtpListener srtpListener);
/**
* Returns the <tt>ZrtpListener</tt> which listens for security events.
* Returns the <tt>SrtpListener</tt> which listens for security events.
*
* @return the <tt>ZrtpListener</tt> which listens for security events
* @return the <tt>SrtpListener</tt> which listens for security events
*/
public SrtpListener getSrtpListener();
@ -76,5 +76,11 @@ public interface SrtpControl
*/
public TransformEngine getTransformEngine();
/**
* Sets the <tt>RTPConnector</tt> which is to use or uses this SRTP engine.
*
* @param connector the <tt>RTPConnector</tt> which is to use or uses this
* SRTP engine
*/
public void setConnector(AbstractRTPConnector newValue);
}

@ -0,0 +1,25 @@
package net.java.sip.communicator.service.neomedia;
/**
* The <tt>SrtpControlType</tt> enumeration contains all currently known
* <tt>SrtpControl</tt> implementations.
*
* @author Ingo Bauersachs
*/
public enum SrtpControlType
{
/**
* Session Description Protocol (SDP) Security Descriptions for Media Streams (RFC 4568)
*/
SDES,
/**
* ZRTP: Media Path Key Agreement for Unicast Secure RTP (RFC 6189)
*/
ZRTP,
/**
* Multimedia Internet KEYing (RFC 3830)
*/
MIKEY
}

@ -94,10 +94,10 @@ public abstract class CallPeerMediaHandler<
private final T peer;
/**
* A reference to the object that would be responsible for ZRTP control
* A reference to the object that would be responsible for SRTP control
* and which most often would be the peer itself.
*/
public final SrtpListener zrtpController;
public final SrtpListener srtpListener;
/**
* The RTP stream that this media handler uses to send audio.
@ -191,10 +191,10 @@ public abstract class CallPeerMediaHandler<
= new DynamicRTPExtensionsRegistry();
/**
* Holds the ZRTP controls used for the current call.
* Holds the SRTP controls used for the current call.
*/
private Map<MediaType, SrtpControl> srtpControls =
new Hashtable<MediaType, SrtpControl>();
private SortedMap<MediaTypeSrtpControl, SrtpControl> srtpControls =
new TreeMap<MediaTypeSrtpControl, SrtpControl>();
/**
* The <tt>KeyFrameControl</tt> currently known to this
@ -330,14 +330,13 @@ public void videoUpdate(
*
* @param peer that <tt>CallPeer</tt> instance that we will be managing
* media for.
* @param zrtpController the object that would be responsible for
* controlling zrtp, and which most often would be the peer itself.
* @param srtpListener the object that receives SRTP security events.
*/
public CallPeerMediaHandler(T peer,
SrtpListener zrtpController)
SrtpListener srtpListener)
{
this.peer = peer;
this.zrtpController = zrtpController;
this.srtpListener = srtpListener;
}
/**
@ -415,20 +414,23 @@ public synchronized void close()
*/
protected void closeStream(MediaType type)
{
if( type == MediaType.AUDIO)
if (type == MediaType.AUDIO)
setAudioStream(null);
else
setVideoStream(null);
getTransportManager().closeStreamConnector(type);
// Clear the ZRTP controls used for the associated Call.
SrtpControl zrtpCtrl = srtpControls.get(type);
if (zrtpCtrl != null)
// Clear the SRTP controls used for the associated Call.
Iterator<MediaTypeSrtpControl> it = srtpControls.keySet().iterator();
while (it.hasNext())
{
zrtpCtrl.cleanup();
srtpControls.remove(type);
MediaTypeSrtpControl mct = it.next();
if (mct.mediaType == type)
{
srtpControls.get(mct).cleanup();
it.remove();
}
}
}
@ -675,10 +677,10 @@ private void setAudioRemoteSSRC(long audioRemoteSSRC)
public void setSasVerified(boolean isVerified )
{
if(audioStream != null)
audioStream.getZrtpControl().setSASVerification(isVerified);
audioStream.getSrtpControl().setSASVerification(isVerified);
if(videoStream != null)
videoStream.getZrtpControl().setSASVerification(isVerified);
videoStream.getSrtpControl().setSASVerification(isVerified);
}
/**
@ -694,14 +696,14 @@ public boolean isSecure()
*/
boolean isAudioSecured
= (audioStream == null)
|| audioStream.getZrtpControl().getSecureCommunicationStatus();
|| audioStream.getSrtpControl().getSecureCommunicationStatus();
if (!isAudioSecured)
return false;
boolean isVideoSecured
= (videoStream == null)
|| videoStream.getZrtpControl().getSecureCommunicationStatus();
|| videoStream.getSrtpControl().getSecureCommunicationStatus();
if (!isVideoSecured)
return false;
@ -711,16 +713,16 @@ public boolean isSecure()
/**
* Passes <tt>multiStreamData</tt> to the video stream that we are using
* in this media handler (if any) so that the underlying ZRTP lib could
* in this media handler (if any) so that the underlying SRTP lib could
* properly handle stream security.
*
* @param multiStreamData the data that we are supposed to pass to our
* video stream.
*/
public void startZrtpMultistream(byte[] multiStreamData)
public void startSrtpMultistream(byte[] multiStreamData)
{
if(videoStream != null)
videoStream.getZrtpControl().setMultistream(multiStreamData);
videoStream.getSrtpControl().setMultistream(multiStreamData);
}
/**
@ -1157,11 +1159,11 @@ public void setCsrcAudioLevelListener(
}
/**
* Returns the currently valid <tt>ZrtpControls</tt> map.
* Returns the currently valid <tt>SrtpControls</tt> map.
*
* @return the currently valid <tt>ZrtpControls</tt> map.
* @return the currently valid <tt>SrtpControls</tt> map.
*/
protected Map<MediaType, SrtpControl> getZrtpControls()
protected Map<MediaTypeSrtpControl, SrtpControl> getSrtpControls()
{
return this.srtpControls;
}
@ -1206,16 +1208,21 @@ protected MediaStream initStream(StreamConnector connector,
logger.trace("The media types of device and format differ.");
// check whether a control already exists
SrtpControl control = srtpControls.get(mediaType);
MediaService mediaService
= ProtocolMediaActivator.getMediaService();
SrtpControl control = srtpControls.size() > 0 ?
srtpControls.get(new MediaTypeSrtpControl(mediaType,
srtpControls.firstKey().srtpControlType)) : null;
if(control == null)
{
// this creates the default control, currently ZRTP without
// the hello-hash
stream = mediaService.createMediaStream(connector, device);
}
else
{
stream
= mediaService.createMediaStream(
stream = mediaService.createMediaStream(
connector, device, control);
}
}
@ -1281,11 +1288,11 @@ protected MediaStream configureStream( MediaDevice device,
if(peer.getCall().isDefaultEncrypted())
{
// we use the audio stream for master stream
// when using ZRTP multistreams.
SrtpControl zrtpControl = stream.getZrtpControl();
// when using SRTP multistreams.
SrtpControl srtpControl = stream.getSrtpControl();
zrtpControl.setZrtpListener(zrtpController);
zrtpControl.start(stream instanceof AudioMediaStream);
srtpControl.setSrtpListener(srtpListener);
srtpControl.start(stream instanceof AudioMediaStream);
}
return stream;

@ -730,7 +730,7 @@ public void securityTurnedOn(
{
if(multiStreamData != null)
{
getMediaHandler().startZrtpMultistream(multiStreamData);
getMediaHandler().startSrtpMultistream(multiStreamData);
}
fireCallPeerSecurityOnEvent(

Loading…
Cancel
Save