Merged ZRTP-related code

cusax-fix
Romain Kuntz 18 years ago
parent a1801e9f3c
commit 0fb8343cf5

@ -59,6 +59,8 @@ DIAL_BUTTON=resources/images/impl/gui/buttons/dialButton.png
HOLD_BUTTON=resources/images/impl/gui/buttons/holdButton.png
MUTE_BUTTON=resources/images/impl/gui/buttons/muteButton.png
TRANSFER_CALL_BUTTON=resources/images/impl/gui/buttons/transferCallButton.png
SECURE_BUTTON_ON=resources/images/impl/gui/buttons/secureOn.png
SECURE_BUTTON_OFF=resources/images/impl/gui/buttons/secureOff.png
INVITE_DIALOG_ICON=resources/images/impl/gui/common/inviteDialogIcon.png
SEND_SMS_ICON=resources/images/impl/gui/common/gsm.png
DIAL_BUTTON_BG=resources/images/impl/gui/buttons/dialButtonBg.png
@ -410,4 +412,4 @@ popupIcon=resources/images/plugin/notificationconfiguration/popupIcon.png
soundIcon=resources/images/plugin/notificationconfiguration/soundIcon.png
activatedIcon=resources/images/plugin/notificationconfiguration/activeIcon.png
desactivatedIcon=resources/images/plugin/notificationconfiguration/desactivatedIcon.png
foldericon=resources/images/plugin/notificationconfiguration/folder.png
foldericon=resources/images/plugin/notificationconfiguration/folder.png

@ -700,3 +700,24 @@ restore=Restore Defaults
playsound=Play a sound :
execprog=Execute a program :
displaypopup=Show a message in a pop-up window
# ZRTP Securing
toggleOffSecurity=Toggle OFF secure call mode
toggleOnSecurity=Try toggle ON secure call mode
engineInitFailure=Securing engine initialization failure
allowClearRequestFailure=Peer doesn't support unsecuring the call
defaultSASTooltip=Secure status field
defaultSASMessage= Not in call
sasSecuredTooltip=Check this string by voice with your peer to verify secure comm link
sasSecuredMessage=Secured:
sasNotSecuredTooltip=Secure mode not enabled
sasNotSecuredMessage=Not secured
sasEngineFailTooltip=Engine failed at initialization
sasEngineFailMessage=Engine failure
sasSecuringFailTooltip=Securing call failed
sasUnsecuredAtRequestTooltip=Call unsecured at request
peerUnsuportedSecurity=<html>You have enabled secure init mode<br/>You can't switch off this mode at this moment<br/>The call is not secured yet anyway</html>
sasPeerUnsuportedTooltip=Securing call not supported/enabled by the other peer
peerToggledOffSecurityMessage=Call peer toggled secure mode off
peerToggledOffSecurityCaption=Secure Off
sasUnsecuredAtPeerRequestTooltip=Call unsecured at peer request

@ -295,3 +295,25 @@ september=Sep
october=Oct
november=Nov
december=Dec
# ZRTP Securing
toggleOffSecurity=DeactiveazÄ? modul securizat
toggleOnSecurity=ActiveazÄ? modul securizat
engineInitFailure=Eroare la iniČ?ializare
allowClearRequestFailure=Nu este suportatÄ? anularea securizÄ?rii
defaultSASTooltip=CĂŽmp de stare al securizÄ?rii
defaultSASMessage= Inactiv
sasSecuredTooltip=ConsultaČ?i acest string prin voce cu interlocutorul pentru a verifica securizarea
sasSecuredMessage=Securizat:
sasNotSecuredTooltip=Mod securizat neactivat
sasNotSecuredMessage=Nesecurizat
sasEngineFailTooltip=Securizare eČ?uatÄ? la iniČ?ializare
sasEngineFailMessage=Securizare eČ?uatÄ?
sasSecuringFailTooltip=Securizarea apelului eČ?uatÄ?
sasUnsecuredAtRequestTooltip=Securizare anulatÄ? la cerere
peerUnsuportedSecurity=<html>Modul securizat a fost activat<br/>Nu puteČ?i schimba acest mod la acest moment<br/>Oricum apelul nu este ĂŽncÄ? securizat</html>
sasPeerUnsuportedTooltip=Securizarea apelului nu este suportatÄ?/activatÄ? de interlocutor
peerToggledOffSecurityMessage=Interlocutorul a anulat modul securizat
peerToggledOffSecurityCaption=Mod securizat anulat
sasUnsecuredAtPeerRequestTooltip=Securizarea apelului anulatÄ? la cererea interlocutorului

@ -0,0 +1,91 @@
/*
* SIP Communicator, 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.gui.main.call;
import java.awt.event.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.impl.gui.i18n.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.media.*;
/**
* The UI button used to toggle on or off call securing
*
* @author Emanuel Onica
*
*/
public class SecureButton
extends JButton
implements ActionListener
{
private final CallParticipant callParticipant;
public SecureButton(CallParticipant callParticipant)
{
super(new ImageIcon(ImageLoader.getImage(ImageLoader.SECURE_BUTTON_OFF)));
this.callParticipant = callParticipant;
this.addActionListener(this);
}
public void actionPerformed(ActionEvent evt)
{
Call call = callParticipant.getCall();
if (call != null)
{
String command = evt.getActionCommand();
if (command.equals("startSecureMode"))
{
OperationSetBasicTelephony telephony =
(OperationSetBasicTelephony) call.getProtocolProvider()
.getOperationSet(OperationSetBasicTelephony.class);
if (telephony.getSecured(callParticipant))
{
updateSecureButton(false);
telephony.setSecured(callParticipant,
false,
OperationSetBasicTelephony.
SecureStatusChangeSource.SECURE_STATUS_CHANGE_BY_LOCAL);
}
else
{
updateSecureButton(true);
telephony.setSecured(callParticipant,
true,
OperationSetBasicTelephony.
SecureStatusChangeSource.SECURE_STATUS_CHANGE_BY_LOCAL);
}
}
}
}
/**
* The method used to update the secure button state (pressed or not pressed)
*
* @param isSecured parameter reflecting the current button state
*/
public void updateSecureButton(boolean isSecured)
{
if(isSecured)
{
this.setIcon(
new ImageIcon(ImageLoader.getImage(ImageLoader.SECURE_BUTTON_ON)));
this.setToolTipText(Messages.getI18NString("toggleOffSecurity").getText());
}
else
{
this.setIcon(
new ImageIcon(ImageLoader.getImage(ImageLoader.SECURE_BUTTON_OFF)));
this.setToolTipText(Messages.getI18NString("toggleOnSecurity").getText());
}
}
}

@ -455,6 +455,18 @@ public class ImageLoader {
*/
public static final ImageID TRANSFER_CALL_BUTTON =
new ImageID("TRANSFER_CALL_BUTTON");
/**
* The secure button on icon. The icon shown in the CallParticipant panel.
*/
public static final ImageID SECURE_BUTTON_ON =
new ImageID("SECURE_BUTTON_ON");
/**
* The secure button off icon. The icon shown in the CallParticipant panel.
*/
public static final ImageID SECURE_BUTTON_OFF =
new ImageID("SECURE_BUTTON_OFF");
/**
* The image used, when a contact has no photo specified.
@ -462,7 +474,6 @@ public class ImageLoader {
public static final ImageID DEFAULT_USER_PHOTO
= new ImageID("DEFAULT_USER_PHOTO");
/**
* The minimize button icon in the <tt>CallPanel</tt>.
*/

@ -17,6 +17,10 @@
import javax.media.rtp.event.*;
import javax.sdp.*;
import net.java.sip.communicator.impl.media.transform.*;
import net.java.sip.communicator.impl.media.transform.srtp.*;
import net.java.sip.communicator.impl.media.transform.zrtp.*;
import net.java.sip.communicator.impl.media.keyshare.*;
import net.java.sip.communicator.service.media.*;
import net.java.sip.communicator.service.media.MediaException;
import net.java.sip.communicator.service.netaddr.*;
@ -26,6 +30,7 @@
import net.java.sip.communicator.impl.media.codec.*;
import javax.media.control.*;
import gnu.java.zrtp.*;
/**
* Contains parameters associated with a particular Call such as media (audio
@ -60,6 +65,7 @@
* @author Ken Larson
* @author Dudek Przemyslaw
* @author Lubomir Marinov
* @author Emanuel Onica
*/
public class CallSessionImpl
implements CallSession
@ -69,6 +75,7 @@ public class CallSessionImpl
, SendStreamListener
, SessionListener
, ControllerListener
, SecureEventListener
{
private static final Logger logger
@ -143,6 +150,29 @@ public class CallSessionImpl
* session.
*/
private List videoFrames = new ArrayList();
/**
* SRTP TransformConnectors corresponding to each RTPManager when SRTP
* feature is enabled.
*/
private Hashtable transConnectors = new Hashtable();
/**
* Toggles default (from the call start) activation
* of secure communication
*/
private boolean usingSRTP = false;
/**
* Vector used to hold references of various key management solutions implemented.
* For now only ZRTP and Dummy (hardcoded keys) are present.
*/
private Vector keySharingAlgorithms = null;
/**
* The key management solution type used for the current session
*/
private KeyProviderAlgorithm selectedKeyProviderAlgorithm = null;
/**
* The Custom Data Destination used for this call session.
@ -236,6 +266,8 @@ public CallSessionImpl(Call call,
call.addCallChangeListener(this);
initializePortNumbers();
initializeSupportedKeyProviders();
}
/**
@ -440,7 +472,31 @@ private void stopStreaming(RTPManager rtpManager,
}
//remove targets
rtpManager.removeTargets("Session ended.");
if (selectedKeyProviderAlgorithm != null
/* TODO: Video securing related code
* remove the next condition as part of enabling video securing
* (see comments insecureStatusChanged method for more info)
*/
&& rtpManager.equals(audioRtpManager)
)
{
TransformConnector transConnector
= (TransformConnector) this.transConnectors.get(rtpManager);
if (transConnector != null)
{
ZRTPTransformEngine engine = (ZRTPTransformEngine)transConnector.getEngine();
engine.sendInfo(ZrtpCodes.MessageSeverity.Info,
EnumSet.of(ZRTPCustomInfoCodes.ZRTPDisabledByCallEnd));
transConnector.removeTargets();
}
}
else
{
rtpManager.removeTargets("Session ended.");
}
printFlowStatistics(rtpManager);
@ -1165,7 +1221,29 @@ private void initStreamTargets(Connection globalConnParam,
try
{
rtpManager.addTarget(target);
if (selectedKeyProviderAlgorithm != null
/* TODO: Video securing related code
* remove the next condition as part of enabling video securing
* (see comments insecureStatusChanged method for more info)
*/
&& rtpManager.equals(audioRtpManager)
)
{
TransformConnector transConnector =
(TransformConnector) this.transConnectors.get(rtpManager);
if (transConnector == null)
{
throw new Exception();
}
transConnector.addTarget(target);
}
else
{
rtpManager.addTarget(target);
}
logger.trace("added target " + target
+ " for type " + type);
}
@ -1869,9 +1947,120 @@ private void initializeRtpManager(RTPManager rtpManager,
SessionAddress bindAddress)
throws MediaException
{
/* Select a key management type from the present ones to use
* for now using the zero - top priority solution (ZRTP);
* TODO: should be extended to a selection algorithm to choose the
* key management type
*/
selectedKeyProviderAlgorithm = selectKeyProviderAlgorithm(0);
try
{
rtpManager.initialize(bindAddress);
{
// Selected key management type == ZRTP branch
if (selectedKeyProviderAlgorithm != null &&
selectedKeyProviderAlgorithm.getProviderType() ==
KeyProviderAlgorithm.ProviderType.ZRTP_PROVIDER
/* TODO: Video securing related code
* remove the next condition as part of enabling video securing
* (see comments insecureStatusChanged method for more info)
*/
&& rtpManager.equals(audioRtpManager))
{
// Set a ZRTP connector to use for communication
TransformConnector transConnector = null;
TransformManager.initializeProviders();
// The connector is created based also on the crypto services provider type;
// The crypto provider solution should be queried somehow
// or taken from a resources file
transConnector = TransformManager.createZRTPConnector(bindAddress,
"BouncyCastle",
this);
rtpManager.initialize(transConnector);
this.transConnectors.put(rtpManager, transConnector);
// ZRTP engine initialization
// TODO: 1. must query/randomize/find a method for the zid file name
// 2. must define an exception for initialization failure
ZRTPTransformEngine engine = (ZRTPTransformEngine)transConnector.getEngine();
// Case 1: user toggled secure communication prior to the call
if (usingSRTP)
{
if (!engine.initialize("my_zid.zid"))
engine.sendInfo(ZrtpCodes.MessageSeverity.Info,
EnumSet.of(ZRTPCustomInfoCodes.ZRTPEngineInitFailure));
}
// Case 2: user will toggle secure communication during the call
// (it's not set on at this point)
else
{
engine.sendInfo(ZrtpCodes.MessageSeverity.Info,
EnumSet.of(ZRTPCustomInfoCodes.ZRTPNotEnabledByUser));
}
logger.trace("RTP"+
(rtpManager.equals(audioRtpManager)?" audio ":"video")+
"manager initialized through connector");
}
else
// Selected key management type == Dummy branch - hardcoded keys
if (selectedKeyProviderAlgorithm != null &&
selectedKeyProviderAlgorithm.getProviderType() ==
KeyProviderAlgorithm.ProviderType.DUMMY_PROVIDER
/* TODO: Video securing related code
* remove the next condition as part of enabling video securing
* (see comments insecureStatusChanged method for more info)
*/
&& rtpManager.equals(audioRtpManager))
{
SRTPPolicy srtpPolicy =
new SRTPPolicy(SRTPPolicy.AESF8_ENCRYPTION, 16,
SRTPPolicy.HMACSHA1_AUTHENTICATION, 20, 10, 14);
// Master key and master salt are hardcoded
byte[] masterKey = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
byte[] masterSalt = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d};
TransformConnector transConnector = null;
TransformManager.initializeProviders();
// The connector is created based also on the crypto services provider type;
// The crypto provider solution should be queried somehow
// or taken from a resources file
transConnector =
TransformManager.createSRTPConnector(bindAddress,
masterKey,
masterSalt,
srtpPolicy,
srtpPolicy,
"BouncyCastle");
rtpManager.initialize(transConnector);
this.transConnectors.put(rtpManager, transConnector);
logger.trace("RTP"+
(rtpManager.equals(audioRtpManager)?" audio ":"video")+
"manager initialized through connector");
}
// No key management solution - unsecured communication branch
else
{
rtpManager.initialize(bindAddress);
logger.trace("RTP"+
(rtpManager.equals(audioRtpManager)?" audio ":"video")+
"manager initialized normally");
}
}
catch (Exception exc)
{
@ -1977,9 +2166,9 @@ public void callStateChanged(CallChangeEvent evt)
logger.error("Failed to start streaming.", ex);
}
}
else if( evt.getNewValue() == CallState.CALL_ENDED
else if( evt.getNewValue() == CallState.CALL_ENDED
&& evt.getNewValue() != evt.getOldValue())
{
{
logger.warn("Stopping streaming.");
stopStreaming();
mediaServCallback.getMediaControl(getCall())
@ -2408,10 +2597,241 @@ public void run()
}
}
}
/**
* Method for getting the default secure status value for communication
*
* @return the default enabled/disabled status value for secure communication
*/
public boolean getSecureCommunicationStatus()
{
return usingSRTP;
}
/**
* Method for setting the default secure status value for communication
* Also has the role to trigger going secure from not secured or viceversa
* Notifies any present CallSession of change in the status value for this purpose
*
* @param activator setting for default communication securing
* @param source the source of changing the secure status (local or remote)
*/
public void setSecureCommunicationStatus(boolean activator,
OperationSetBasicTelephony.
SecureStatusChangeSource source)
{
logger.trace("Call session secure status change event request received");
// Make the change for default security enabled/disabled start option
usingSRTP = activator;
// Fire the change event to notify any present CallSession of security change status
// if not the case of a reverted secure state
// (usually case of previous change rejected due to an error)
if (source != OperationSetBasicTelephony.
SecureStatusChangeSource.SECURE_STATUS_REVERTED)
fireSecureStatusChanged(activator, source);
}
/**
* Fire the event of change in security status
*
* @param activator type of change - enable/disable communication security
* @param source the source of changing the secure status (local or remote)
*/
private synchronized void fireSecureStatusChanged(boolean activator,
OperationSetBasicTelephony.
SecureStatusChangeSource source)
{
if (activator)
{
this.secureStatusChanged(
new SecureEvent(this,
SecureEvent.SECURE_COMMUNICATION,
source));
}
else
{
this.secureStatusChanged(
new SecureEvent(this,
SecureEvent.UNSECURE_COMMUNICATION,
source));
}
}
/**
* The method for changing security status for a specific RTPManager when
* the ZRTP key sharing solution is used.
* Called when a new SecureEvent is received.
*
* @param manager The RTP manager for which the media streams
* will be secured or unsecured
* @param event The secure status changed event
*/
public void ZRTPChangeStatus(RTPManager manager, SecureEvent event)
{
int newStatus = event.getEventID();
OperationSetBasicTelephony.SecureStatusChangeSource source = event.getSource();
TransformConnector transConnector =
(TransformConnector) this.transConnectors.get(manager);
ZRTPTransformEngine engine = (ZRTPTransformEngine)transConnector.getEngine();
// Perform ZRTP engine actions only if triggered by local peer - user commands;
// If the remote peer caused the event only general call session security status
// is changed (done before event processing)
if (source == OperationSetBasicTelephony.
SecureStatusChangeSource.SECURE_STATUS_CHANGE_BY_LOCAL)
{
if (newStatus == SecureEvent.SECURE_COMMUNICATION)
{
// Secure the comm after the call begins
if (!engine.isStarted())
{
logger.trace("Normal call securing event processing");
if (!engine.initialize("my_zid.zid"))
engine.sendInfo(ZrtpCodes.MessageSeverity.Info,
EnumSet.of(ZRTPCustomInfoCodes.ZRTPEngineInitFailure));
}
else
{
logger.trace("GoSecure call event processing");
// This point isn't reached if GoClear is not enabled
SCCallback cb = (SCCallback)engine.getUserCallback();
cb.setGCGSByPeerFlag(false);
engine.requestGoSecure();
}
}
else
{
// At this moment this attempts a GoClear request but
// fails due to GoClear code disabled (failing doesn't result
// in error, only in a warning tooltip set on the button, and
// a change of it's internal state to prevent further attempts)
// to re-enable GoClear uncomment the specific code parts in ZRTP4J
logger.trace("GoClear call event processing");
SCCallback cb = (SCCallback)engine.getUserCallback();
cb.setGCGSByPeerFlag(false);
engine.requestGoClear();
}
}
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.SecureEventListener#secureStatusChanged(net.java.sip.communicator.impl.media.SecureEvent)
*/
public void secureStatusChanged(SecureEvent secureEvent)
{
// If the current selected key management solution type is ZRTP
// act accordingly
if (selectedKeyProviderAlgorithm.getProviderType() ==
KeyProviderAlgorithm.ProviderType.ZRTP_PROVIDER)
{
ZRTPChangeStatus(this.audioRtpManager, secureEvent);
/* TODO: Video securing related code
*
* We disable for the moment the video securing due to (yet) unsuported
* multistream mode in ZRTP4J; This can be re-enabled and attempted as is
* implemented, using a separate instance of ZRTP engine which will
* attempt securing through DH mode; This might result in some unexpected
* behavior at the GUI level, but in theory should work; However, due to
* incomplete standard compliance and potential problems mentioned we leave
* it disabled; To enable just check the other "Video securing related code"
* sections in this source
*
* Uncomment the next line as part of enabling video securing
*/
//ZRTPChangeStatus(this.videoRtpManager, secureEvent);
}
}
/**
* Determines whether the audio of this session is (set to) mute.
* Initializes the supported key management types and establishes
* default usage priorities for them.
* This part should be further developed (by adding a more detailed
* priority setting mechanism in case of addition of other security
* providers).
*/
public void initializeSupportedKeyProviders()
{
if (keySharingAlgorithms == null)
keySharingAlgorithms = new Vector();
DummyKeyProvider dummyProvider = new DummyKeyProvider(1);
ZRTPKeyProvider zrtpKeyProvider = new ZRTPKeyProvider(0);
keySharingAlgorithms.add(zrtpKeyProvider.getPriority(), zrtpKeyProvider);
keySharingAlgorithms.add(dummyProvider.getPriority(), dummyProvider);
}
/**
* Selects a default key management type to use in securing based
* on which the actual implementation for that solution will be started
* This part should be further developed (by adding a more detailed
* priority choosing mechanism in case of addition of other security
* providers).
*
* @return the default keymanagement type used in securing
*/
public KeyProviderAlgorithm selectDefaultKeyProviderAlgorithm()
{
KeyProviderAlgorithm defaultProvider =
(KeyProviderAlgorithm)keySharingAlgorithms.get(0);
if (defaultProvider == null)
{
return new DummyKeyProvider(0);
}
else
{
return defaultProvider;
}
}
/**
* Selects a key management type to use in securing based on priority
* For now the priorities are equal with the position in the Vector
* holding the keymanagement types.
* This part should be further developed (by adding a more detailed
* priority choosing mechanism in case of addition of other security
* providers).
*
* @param priority the priority of the selected key management type - 0 is top
* @return the selected key management type
*/
public KeyProviderAlgorithm selectKeyProviderAlgorithm(int priority)
{
KeyProviderAlgorithm selectedProvider =
(KeyProviderAlgorithm)keySharingAlgorithms.get(priority);
return selectedProvider;
}
/**
* Additional info codes for ZRTP4J.
* These could be added to the library. However they are specific for this
* implementation, needing them for various GUI changes.
*
*/
public static enum ZRTPCustomInfoCodes
{
ZRTPNotEnabledByUser,
ZRTPDisabledByCallEnd,
ZRTPEngineInitFailure;
}
/**
* Determines whether the audio of this session is (set to) mute.
*
* @return <tt>true</tt> if the audio of this session is (set to) mute;
* otherwise, <tt>false</tt>
*/
@ -2422,7 +2842,7 @@ public boolean isMute()
/**
* Sets the mute state of the audio of this session.
*
*
* @param mute <tt>true</tt> to mute the audio of this session; otherwise,
* <tt>false</tt>
*/

@ -0,0 +1,53 @@
/*
* SIP Communicator, 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.media.keyshare;
import net.java.sip.communicator.impl.media.transform.srtp.*;
/**
* DummyProvider class implements KeyProvider interface.
* Used only for testing - activates the hardcoded keys behaviour for SRTP traffic.
*
* @author Emanuel Onica (eonica@info.uaic.ro)
*
*/
public class DummyKeyProvider
implements KeyProviderAlgorithm
{
/**
* The constant provider type of this class
*/
private static final KeyProviderAlgorithm.ProviderType providerType =
KeyProviderAlgorithm.ProviderType.DUMMY_PROVIDER;
private int priority;
public DummyKeyProvider(int priority)
{
this.priority = priority;
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.keyshare.KeyProvider#getProviderType()
*/
public KeyProviderAlgorithm.ProviderType getProviderType()
{
return providerType;
}
public int getPriority()
{
return priority;
}
public void setPriority(int priority)
{
this.priority = priority;
}
}

@ -0,0 +1,51 @@
/*
* SIP Communicator, 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.media.keyshare;
import net.java.sip.communicator.impl.media.transform.srtp.*;
/**
* KeyProvider interface defines the current available provider types,
* and a method to obtain the set type for a provider.
* (Originally the interface contained setter and getter methods for keys
* and other cryptographic parameters but this were removed due to
* redundancy regarding the fact these are partial provided already inside
* directly in the SRTPTransformEngine class.
* It might still be a viable option.)
*
* @author Emanuel Onica (eonica@info.uaic.ro)
*/
public interface KeyProviderAlgorithm
{
public enum ProviderType
{
DUMMY_PROVIDER,
ZRTP_PROVIDER
};
/**
* Obtains the current provider type for the class implementing the interface
*
* @return the provider type
*/
public ProviderType getProviderType();
/**
* Gets this algorithm's priority of usage in handling the key management
*
* @return the priority of usage in handling the key management
*/
public int getPriority();
/**
* Sets this algorithm's priority of usage in handling the key management
*
* @param priority the priority of usage in handling the key management
*/
public void setPriority(int priority);
}

@ -0,0 +1,58 @@
/*
* SIP Communicator, 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.media.keyshare;
/**
* ZRTPProvider class implements KeyProvider interface.
* Used to activate the ZRTPConnector creation.
* Also could be used to provide additional info.
* (Originally the interface contained setter and getter methods for keys
* and other cryptographic parameters but this were removed due to
* redundancy regarding the fact these are partial provided already inside
* directly in the SRTPTransformEngine class.
* The ZRTPConnector originally implemented the interface to have direct
* access to these as a ZRTPProvider.
* It might still be a viable option.)
*
* @author Emanuel Onica (eonica@info.uaic.ro)
*
*/
public class ZRTPKeyProvider implements KeyProviderAlgorithm {
/**
* The constant provider type of this class
*/
private static final KeyProviderAlgorithm.ProviderType providerType
= KeyProviderAlgorithm.ProviderType.ZRTP_PROVIDER;
private int priority;
public ZRTPKeyProvider(int priority)
{
this.priority = priority;
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.keyshare.KeyProvider#getProviderType()
*/
public ProviderType getProviderType()
{
return providerType;
}
public int getPriority()
{
return priority;
}
public void setPriority(int priority)
{
this.priority = priority;
}
}

@ -13,11 +13,21 @@ Import-Package: org.osgi.framework,
javax.swing,
javax.swing.event,
javax.swing.border,
javax.imageio,
net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.resources,
javax.sound,
javax.sound.sampled,
javax.crypto,
org.xml.sax
javax.crypto.spec,
javax.crypto.interfaces,
org.xml.sax,
org.bouncycastle.jce.provider,
gnu.java.zrtp,
gnu.java.zrtp.zidfile,
gnu.java.zrtp.utils,
gnu.java.zrtp.packets
Export-Package: net.java.sip.communicator.service.media,
net.java.sip.communicator.service.media.event,
net.java.sip.communicator.impl.media,

@ -85,39 +85,39 @@ public int getOffset()
/**
* Read a integer from this packet at specified offset
*
* @param offset start offset of the integer to be read
* @param off start offset of the integer to be read
* @return the integer to be read
*/
public int readInt(int offset)
public int readInt(int off)
{
return (this.buffer[this.offset + offset + 0] << 24) |
(this.buffer[this.offset + offset + 1] << 16) |
(this.buffer[this.offset + offset + 2] << 8) |
(this.buffer[this.offset + offset + 3]);
return (this.buffer[this.offset + off + 0] << 24) |
((this.buffer[this.offset + off + 1] & 0xff) << 16) |
((this.buffer[this.offset + off + 2] & 0xff) << 8) |
(this.buffer[this.offset + off + 3] & 0xff);
}
/**
* Read a short from this packet at specified offset
*
* @param offset start offset of this short
* @param off start offset of this short
* @return short value at offset
*/
public short readShort(int offset)
public short readShort(int off)
{
return (short) ((this.buffer[this.offset + offset + 0] << 8) |
(this.buffer[this.offset + offset + 1]));
return (short) ((this.buffer[this.offset + off + 0] << 8) |
(this.buffer[this.offset + off + 1] & 0xff));
}
/**
* Read an unsigned short at specified offset as a int
*
* @param offset start offset of the unsigned short
* @param off start offset of the unsigned short
* @return the int value of the unsigned short at offset
*/
public int readUnsignedShortAsInt(int offset)
public int readUnsignedShortAsInt(int off)
{
int b1 = (0x000000FF & ((int)this.buffer[this.offset + offset + 0]));
int b2 = (0x000000FF & ((int)this.buffer[this.offset + offset + 1]));
int b1 = (0x000000FF & (this.buffer[this.offset + off + 0]));
int b2 = (0x000000FF & (this.buffer[this.offset + off + 1]));
int val = b1 << 8 | b2;
return val;
}
@ -125,49 +125,49 @@ public int readUnsignedShortAsInt(int offset)
/**
* Read a byte from this packet at specified offset
*
* @param offset start offset of the byte
* @param off start offset of the byte
* @return byte at offset
*/
public byte readByte(int offset)
public byte readByte(int off)
{
return this.buffer[this.offset + offset];
return buffer[offset + off];
}
/**
* Read an unsigned integer as long at specified offset
*
* @param offset start offset of this unsigned integer
* @param off start offset of this unsigned integer
* @return unsigned integer as long at offset
*/
public long readUnsignedIntAsLong(int offset)
public long readUnsignedIntAsLong(int off)
{
int b0 = (0x000000FF & ((int)this.buffer[this.offset + offset + 0]));
int b1 = (0x000000FF & ((int)this.buffer[this.offset + offset + 1]));
int b2 = (0x000000FF & ((int)this.buffer[this.offset + offset + 2]));
int b3 = (0x000000FF & ((int)this.buffer[this.offset + offset + 3]));
int b0 = (0x000000FF & (this.buffer[this.offset + off + 0]));
int b1 = (0x000000FF & (this.buffer[this.offset + off + 1]));
int b2 = (0x000000FF & (this.buffer[this.offset + off + 2]));
int b3 = (0x000000FF & (this.buffer[this.offset + off + 3]));
return ((long) (b0 << 24 | b1 << 16 | b2 << 8 | b3)) & 0xFFFFFFFFL;
return ((b0 << 24 | b1 << 16 | b2 << 8 | b3)) & 0xFFFFFFFFL;
}
/**
* Read a byte region from specified offset with specified length
*
* @param offset start offset of the region to be read
* @param length length of the region to be read
* @param off start offset of the region to be read
* @param len length of the region to be read
* @return byte array of [offset, offset + length)
*/
public byte[] readRegion(int offset, int length)
public byte[] readRegion(int off, int len)
{
int startOffset = this.offset + offset;
if (offset < 0 || length <= 0
|| startOffset + length > this.buffer.length)
int startOffset = this.offset + off;
if (off < 0 || len <= 0
|| startOffset + len > this.buffer.length)
{
return null;
}
byte[] region = new byte[length];
byte[] region = new byte[len];
System.arraycopy(this.buffer, startOffset, region, 0, length);
System.arraycopy(this.buffer, startOffset, region, 0, len);
return region;
}
@ -177,35 +177,36 @@ public byte[] readRegion(int offset, int length)
* buffer of this packet.
*
* @param data byte array to append
* @param len the number of bytes to append
*/
public void append(byte[] data)
public void append(byte[] data, int len)
{
if (data == null || data.length == 0)
if (data == null || len == 0)
{
return;
}
byte[] newBuffer = new byte[this.length + data.length];
byte[] newBuffer = new byte[this.length + len];
System.arraycopy(this.buffer, this.offset, newBuffer, 0, this.length);
System.arraycopy(data, 0, newBuffer, this.length, data.length);
System.arraycopy(data, 0, newBuffer, this.length, len);
this.offset = 0;
this.length = this.length + data.length;
this.length = this.length + len;
this.buffer = newBuffer;
}
/**
* Shrink the buffer of this packet by specified length
*
* @param length length to shrink
* @param len length to shrink
*/
public void shrink(int length)
public void shrink(int len)
{
if (length <= 0)
if (len <= 0)
{
return;
}
this.length -= length;
this.length -= len;
if (this.length < 0)
{
this.length = 0;

@ -41,7 +41,7 @@ public class TransformConnector
* The customized TransformEngine object, which contains the concrete
* transform logic.
*/
private TransformEngine engine;
protected TransformEngine engine;
/**
* Local RTP session listen address.
@ -329,4 +329,103 @@ public void setSendBufferSize(int size)
{
// Nothing should be done here :-)
}
/**
* Getter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @return the control input stream
*/
public TransformInputStream getCtrlInputStream()
{
return ctrlInputStream;
}
/**
* Getter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @return the control output stream
*/
public TransformOutputStream getCtrlOutputStream()
{
return ctrlOutputStream;
}
/**
* Setter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @param ctrlOutputStream the control output stream to be set
*/
public void setCtrlOutputStream(TransformOutputStream ctrlOutputStream)
{
this.ctrlOutputStream = ctrlOutputStream;
}
/**
* Setter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @param dataInputStream the data input stream to be set
*/
public void setDataInputStream(TransformInputStream dataInputStream)
{
this.dataInputStream = dataInputStream;
}
/**
* Setter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @param ctrlInputStream the control input stream to be set
*/
public void setCtrlInputStream(TransformInputStream ctrlInputStream)
{
this.ctrlInputStream = ctrlInputStream;
}
/**
* Setter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @param dataOutputStream the data output stream to be set
*/
public void setDataOutputStream(TransformOutputStream dataOutputStream)
{
this.dataOutputStream = dataOutputStream;
}
/**
* Getter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @return the data socket
*/
public DatagramSocket getDataSocket()
{
return dataSocket;
}
/**
* Getter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @return the control socket
*/
public DatagramSocket getCtrlSocket()
{
return ctrlSocket;
}
/**
* Getter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @return the engine
*/
public TransformEngine getEngine()
{
return engine;
}
}

@ -12,18 +12,19 @@
import javax.media.protocol.*;
/**
* TransformInputStream implements PushSourceStream. It is used by RTPManager
* to receive RTP/RTCP packet datas.
* TransformInputStream implements PushSourceStream. It is used by RTPManager to
* receive RTP/RTCP packet datas.
*
* In this implementation, we use UDP sockets to receive RTP/RTCP. We listen
* on the address / port specified by local session address. When one packet is
* In this implementation, we use UDP sockets to receive RTP/RTCP. We listen on
* the address / port specified by local session address. When one packet is
* received, it is first reverse transformed through PacketTransformer defined
* by user. And then returned as normal RTP/RTCP packets to RTPManager.
*
* @author Bing SU (nova.su@gmail.com)
*/
public class TransformInputStream
implements PushSourceStream, Runnable
public class TransformInputStream
implements PushSourceStream,
Runnable
{
/**
* UDP socket used to receive data.
@ -41,17 +42,12 @@ public class TransformInputStream
*/
private SourceTransferHandler transferHandler;
/**
* Whether we received some data.
*/
private boolean gotData;
/**
* Whether this stream is closed. Used to control the termination of worker
* thread.
*/
private boolean closed;
/**
* Worker thread we use to call transfer handle to received the data
*/
@ -63,21 +59,29 @@ public class TransformInputStream
private byte[] buffer = new byte[65535];
/**
* Construct a TransformInputStream based on the receiving socket and
* Caught an IO exception during read from socket
*/
private boolean ioError = false;
private RawPacket pkt = null;
/**
* Construct a TransformInputStream based on the receiving socket and
* PacketTransformer
*
* @param socket data receiving socket
* @param transformer packet transformer used
* @param socket
* data receiving socket
* @param transformer
* packet transformer used
*/
public TransformInputStream(DatagramSocket socket,
PacketTransformer transformer)
PacketTransformer transformer)
{
this.socket = socket;
this.transformer = transformer;
this.closed = false;
this.gotData = false;
this.recvThread = new Thread(this);
this.recvThread.start();
@ -86,158 +90,140 @@ public TransformInputStream(DatagramSocket socket,
/**
* Close this stream, stops the worker thread.
*/
public synchronized void close()
public synchronized void close()
{
this.closed = true;
notifyAll();
this.socket.close();
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.protocol.PushSourceStream#read(byte[], int, int)
*/
public int read(byte[] buffer, int offset, int length)
throws IOException
public int read(byte[] inBuffer, int offset, int length)
throws IOException
{
// loop until we get a valid packet
while (true)
if (ioError)
{
DatagramPacket p = new DatagramPacket(this.buffer, 0, 65535);
try
{
this.socket.receive(p);
}
catch (IOException e)
{
return -1;
}
RawPacket pkt =
this.transformer.reverseTransform(new RawPacket(this.buffer,
0,
p.getLength()));
// If the reverse transformed result is not valid,
// then we will not deliver this packet.
if (pkt == null)
{
continue;
}
else
{
if (length < pkt.getLength())
{
throw new IOException("Input buffer not big enough for "
+ String.valueOf(pkt.getLength()));
}
System.arraycopy(pkt.getBuffer(), pkt.getOffset(), buffer, offset,
pkt.getLength());
synchronized (this)
{
this.gotData = true;
notifyAll();
}
return pkt.getLength();
}
return -1;
}
if (length < pkt.getLength()) {
throw new IOException("Input buffer not big enough for "
+ String.valueOf(pkt.getLength()));
}
System.arraycopy(pkt.getBuffer(), pkt.getOffset(), inBuffer, offset,
pkt.getLength());
return pkt.getLength();
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.protocol.PushSourceStream#setTransferHandler
* (javax.media.protocol.SourceTransferHandler)
* (javax.media.protocol.SourceTransferHandler)
*/
public synchronized void setTransferHandler(SourceTransferHandler handler)
public void setTransferHandler(SourceTransferHandler handler)
{
if (this.closed) return;
if (this.closed)
return;
this.transferHandler = handler;
if (this.transferHandler != null)
{
this.gotData = true;
notifyAll();
}
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.protocol.PushSourceStream#getMinimumTransferSize()
*/
public int getMinimumTransferSize()
public int getMinimumTransferSize()
{
return 2 * 1024; // twice the MTU size, just to be safe.
}
// ----- Not applicable methods -----
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.protocol.SourceStream#endOfStream()
*/
public boolean endOfStream()
public boolean endOfStream()
{
return false;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.protocol.SourceStream#getContentDescriptor()
*/
public ContentDescriptor getContentDescriptor()
public ContentDescriptor getContentDescriptor()
{
return null;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.protocol.SourceStream#getContentLength()
*/
public long getContentLength()
public long getContentLength()
{
return LENGTH_UNKNOWN;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.Controls#getControl(java.lang.String)
*/
public Object getControl(String controlType)
public Object getControl(String controlType)
{
return null;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see javax.media.Controls#getControls()
*/
public Object[] getControls()
public Object[] getControls()
{
return new Object[0];
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
public void run()
public void run()
{
while (!this.closed)
while (!this.closed)
{
synchronized (TransformInputStream.this)
DatagramPacket p = new DatagramPacket(this.buffer, 0, 65535);
try
{
this.socket.receive(p);
}
catch (IOException e)
{
while (!this.gotData && !this.closed)
{
try
{
TransformInputStream.this.wait();
}
catch (InterruptedException e)
{
// nothing should be done here ?
}
}
this.gotData = false;
ioError = true;
break;
}
pkt = new RawPacket(this.buffer, 0, p.getLength());
pkt = this.transformer.reverseTransform(pkt);
if (this.transferHandler != null && !this.closed)
// If the reverse transformed result is not valid,
// then we will not deliver this packet.
if (pkt == null)
{
continue;
}
if (this.transferHandler != null && !this.closed)
{
this.transferHandler.transferData(TransformInputStream.this);
}

@ -7,9 +7,15 @@
package net.java.sip.communicator.impl.media.transform;
import javax.media.rtp.*;
import java.security.*;
import net.java.sip.communicator.impl.media.transform.dummy.*;
import net.java.sip.communicator.impl.media.transform.srtp.*;
import net.java.sip.communicator.impl.media.transform.zrtp.*;
import net.java.sip.communicator.impl.media.keyshare.*;
import java.util.*;
import org.bouncycastle.jce.provider.*;
/**
* TransformManager class encapsulate the logic of creating different kinds of
@ -17,9 +23,64 @@
* TransformManager class.
*
* @author Bing SU (nova.su@gmail.com)
* @author Emanuel Onica (eonica@info.uaic.ro)
*/
public class TransformManager
{
/**
* Map of supported cryptography services providers
*/
public static HashMap cryptoProviders = null;
/**
* Initialize the supported cryptography services providers
*/
public static void initializeProviders()
{
if (cryptoProviders == null)
{
cryptoProviders = new HashMap();
Provider cryptoProvider = null ;
try
{
Class<?> c = Class
.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
cryptoProvider = (Provider) c.newInstance();
}
catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InstantiationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
cryptoProviders.put("BouncyCastle", cryptoProvider);
}
}
/**
* Select a cryptography services provider to use from the supported ones
*
* @param cryptoProvider cryptography provider selection string
* @return the actual cryptography provider to use
*/
public static Provider selectProvider(String cryptoProvider)
{
return (Provider)cryptoProviders.get(cryptoProvider);
}
/**
* Create a SRTP TransformConnector, which will provide SRTP encryption /
* decryption functionality, using algorithms defined in RFC3711.
@ -29,28 +90,84 @@ public class TransformManager
* @param masterSalt master salt of this SRTP session
* @param srtpPolicy SRTP policy for this SRTP session
* @param srtcpPolicy SRTCP policy for this SRTP session
* @param cryptoProvider the cryptography services provider selection string
* should be obtained from a resource file or by querying
* @return the TransformConnector used for SRTP encyption/decryption
* @throws InvalidSessionAddressException if the local RTP session address
* is invalid
*/
*/
public static TransformConnector createSRTPConnector(SessionAddress addr,
byte[] masterKey,
byte[] masterSalt,
byte[] masterKey,
byte[] masterSalt,
SRTPPolicy srtpPolicy,
SRTPPolicy srtcpPolicy)
throws InvalidSessionAddressException
SRTPPolicy srtcpPolicy,
String cryptoProvider)
throws InvalidSessionAddressException
{
SRTPTransformEngine engine = new SRTPTransformEngine(masterKey,
masterSalt,
srtpPolicy,
srtcpPolicy);
SRTPTransformEngine engine = null;
Provider cp = selectProvider(cryptoProvider);
try
{
engine = new SRTPTransformEngine(masterKey,
masterSalt,
srtpPolicy,
srtcpPolicy,
cp);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
return null;
}
TransformConnector connector = null;
connector = new TransformConnector(addr, engine);
return connector;
}
/**
* Creates a connector specific for use in case of ZRTP key management
*
* @param addr local RTP session listen address
* @param cryptoProvider the cryptography services provider selection string
* should be obtained from a resource file or by querying
* @return the TransformConnector used for SRTP encyption/decryption
* @throws InvalidSessionAddressException
*/
public static TransformConnector createZRTPConnector(SessionAddress addr,
String cryptoProvider)
throws InvalidSessionAddressException
{
//for adding multistream support the engine should be instantiated
//once as a static variable of this class and passed to every ZRTP
//connector as a parameter
ZRTPTransformEngine engine = new ZRTPTransformEngine();
Provider cp = selectProvider(cryptoProvider);
TransformConnector connector = null;
connector = new ZrtpTransformConnector(addr, engine);
//for adding multistream support this method should be replaced with
//an addConnector, which should add the connector to an internal engine
//connector array; supporting multistream mode by the engine implies
//the proper management of this connector array - practically every
//stream has it's own connector
engine.setConnector(connector);
//for adding multistream support also the SCCallback should be instantiates
//only once as a static variable of this class and passed to the engine
engine.setUserCallback(new SCCallback());
engine.setCryptoProvider(cp);
return connector;
}
/**
* Create a dummy TransformConnector. A dummy TransformConnector does no
* modification (transformation) to RTP/RTCP packets. Its main purpose is to

@ -38,13 +38,13 @@ public class TransformOutputStream
/**
* Stream targets' ip addresses
*/
private Vector remoteAddrs;
private Vector<InetAddress> remoteAddrs;
/**
* Stream targets' ports, corresponding to their ip addresses.
*/
private Vector remotePorts;
private Vector<Integer> remotePorts;
/**
* Construct a TransformOutputStream based on the given UDP socket and
* PacketTransformer
@ -57,8 +57,8 @@ public TransformOutputStream(DatagramSocket socket,
{
this.socket = socket;
this.transformer = transformer;
this.remoteAddrs = new Vector();
this.remotePorts = new Vector();
this.remoteAddrs = new Vector<InetAddress>();
this.remotePorts = new Vector<Integer>();
}
/**
@ -103,13 +103,24 @@ public void removeTargets()
* @see javax.media.rtp.OutputDataStream#write(byte[], int, int)
*/
public int write(byte[] buffer, int offset, int length)
{
{
// Transformation could be non-inplace, we shall not modify the the old
// buffer
RawPacket pkt = this.transformer.transform(new RawPacket(buffer,
offset,
length));
// This is for the case when the ZRTP engine stops the media stream
// allowing only ZRTP packets
/* TODO GoClear
* To uncomment in order to use the GoClear feature
*/
/*
if (pkt == null)
return length;
*/
for (int i = 0; i < this.remoteAddrs.size(); ++i)
{
InetAddress remoteAddr =
@ -118,7 +129,7 @@ public int write(byte[] buffer, int offset, int length)
((Integer) this.remotePorts.elementAt(i)).intValue();
try
{
{
this.socket.send(new DatagramPacket(pkt.getBuffer(),
pkt.getOffset(),
pkt.getLength(),

@ -25,6 +25,9 @@
*/
package net.java.sip.communicator.impl.media.transform.srtp;
import javax.crypto.*;
import java.security.*;
/**
* SRTPCipherCTR implements SRTP Counter Mode AES Encryption (AES-CM).
* Counter Mode AES Encryption algorithm is defined in RFC3711, section 4.1.1.
@ -51,28 +54,19 @@
*
* @author Bing SU (nova.su@gmail.com)
*/
public class SRTPCipherCTR implements SRTPCipher
public class SRTPCipherCTR
{
/**
* The AESCihper object we used to do basic AES encryption / decryption
*/
private AESCipher aesCipher;
/**
* Construct a SRTPCipherCTR object using given encryption key
*
* @param key the encryption key for this session
*/
public SRTPCipherCTR(byte[] key)
{
this.aesCipher = new AESCipher(key);
}
/* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.transform.srtp.
* SRTPCipher#process(byte[], int, int, byte[])
* Process (encrypt / decrypt) a byte stream, using the supplied
* initial vector.
*
* @param aesCipher the AESCihper object we use to do basic AES encryption / decryption
* @param data byte array containing the byte stream to be processed
* @param offset byte stream star offset with data byte array
* @param length byte stream length in bytes
* @param iv initial vector for this operation
*/
public void process(byte[] data, int off, int len, byte[] iv)
public static void process(Cipher aesCipher, byte[] data, int off, int len, byte[] iv)
{
if (off + len > data.length)
{
@ -82,7 +76,7 @@ public void process(byte[] data, int off, int len, byte[] iv)
byte[] cipherStream = new byte[len];
getCipherStream(cipherStream, len, iv);
getCipherStream(aesCipher, cipherStream, len, iv);
for (int i = 0; i < len; i++)
{
@ -94,34 +88,42 @@ public void process(byte[] data, int off, int len, byte[] iv)
* Computes the cipher stream for AES CM mode.
* See section 4.1.1 in RFC3711 for detailed description.
*
* @param aesCipher the AESCihper object we use to do basic AES encryption / decryption
* @param out byte array holding the output cipher stream
* @param length length of the cipher stream to produce, in bytes
* @param iv initialization vector used to generate this cipher stream
*/
public void getCipherStream(byte[] out, int length, byte[] iv)
public static void getCipherStream(Cipher aesCipher, byte[] out, int length, byte[] iv)
{
final int BLKLEN = AESCipher.BLOCK_SIZE;
final int BLKLEN = 16;
byte[] in = new byte[BLKLEN];
byte[] tmp = new byte[BLKLEN];
System.arraycopy(iv, 0, in, 0, 14);
int ctr;
for (ctr = 0; ctr < length / BLKLEN; ctr++)
try
{
// compute the cipher stream
int ctr;
for (ctr = 0; ctr < length / BLKLEN; ctr++)
{
// compute the cipher stream
in[14] = (byte) ((ctr & 0xFF00) >> 8);
in[15] = (byte) ((ctr & 0x00FF));
aesCipher.update(in, 0, BLKLEN, out, ctr * BLKLEN);
}
// Treat the last bytes:
in[14] = (byte) ((ctr & 0xFF00) >> 8);
in[15] = (byte) ((ctr & 0x00FF));
this.aesCipher.encryptBlock(in, 0, out, ctr * BLKLEN);
aesCipher.doFinal(in, 0, BLKLEN, tmp, 0);
System.arraycopy(tmp, 0, out, ctr * BLKLEN, length % BLKLEN);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
// Treat the last bytes:
in[14] = (byte) ((ctr & 0xFF00) >> 8);
in[15] = (byte) ((ctr & 0x00FF));
this.aesCipher.encryptBlock(in, 0, tmp, 0);
System.arraycopy(tmp, 0, out, ctr * BLKLEN, length % BLKLEN);
}
}

@ -26,6 +26,9 @@
package net.java.sip.communicator.impl.media.transform.srtp;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
/**
* SRTPCipherF8 implements SRTP F8 Mode AES Encryption (AES-f8).
@ -53,28 +56,13 @@
*
* @author Bing SU (nova.su@gmail.com)
*/
public class SRTPCipherF8 implements SRTPCipher
public class SRTPCipherF8
{
/**
* The AESCipher used to perform basic AES encryption / decryption
*/
private AESCipher aesCipher;
/**
* Encryption key used in this session. F8 mode encryption will use this
* data.
*/
private byte[] key;
/**
* Salting key used in this session. F8 mode encryption will use this data.
*/
private byte[] salt;
/**
* AES block size, just a short name.
*/
private final static int BLKLEN = AESCipher.BLOCK_SIZE;
private final static int BLKLEN = 16;
/**
* F8 mode encryption context, see RFC3711 section 4.1.2 for detailed
@ -88,30 +76,22 @@ class F8Context
}
/**
* Construct a SRTPCipherF8 object using given encryption key and salting
* key.
* Process (encrypt / decrypt) a byte stream, using the supplied
* initial vector.
*
* @param key the encryption key used in this session
* @param salt the salting key used in this session
*/
public SRTPCipherF8(byte[] key, byte[] salt)
{
this.aesCipher = new AESCipher(key);
this.key = new byte[key.length];
System.arraycopy(key, 0, this.key, 0, key.length);
this.salt = new byte[salt.length];
System.arraycopy(salt, 0, this.salt, 0, salt.length);
}
/* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.transform.srtp.
* SRTPCipher#process(byte[], int, int, byte[])
* @param aesCipher the AES cipher object used for block processing
* @param data byte array containing the byte stream to be processed
* @param offset byte stream star offset with data byte array
* @param length byte stream length in bytes
* @param iv initial vector for this operation
* @param key the encryption key
* @param salt the salt key
* @param f8Cipher the F8 cipher object used for iv processing
*/
public void process(byte[] data, int off, int len, byte[] iv)
public static void process(Cipher aesCipher, byte[] data, int off, int len, byte[] iv,
byte[] key, byte[] salt, Cipher f8Cipher)
{
F8Context f8ctx = new F8Context();
F8Context f8ctx = new SRTPCipherF8().new F8Context();
/*
* Get memory for the derived IV (IV')
@ -122,15 +102,15 @@ public void process(byte[] data, int off, int len, byte[] iv)
* Get memory for the special key. This is the key to compute the
* derived IV (IV').
*/
byte[] saltMask = new byte[this.key.length];
byte[] maskedKey = new byte[this.key.length];
byte[] saltMask = new byte[key.length];
byte[] maskedKey = new byte[key.length];
/*
* First copy the salt into the mask field, then fill with 0x55 to
* get a full key.
*/
System.arraycopy(this.salt, 0, saltMask, 0, this.salt.length);
for (int i = this.salt.length; i < saltMask.length; ++i)
System.arraycopy(salt, 0, saltMask, 0, salt.length);
for (int i = salt.length; i < saltMask.length; ++i)
{
saltMask[i] = 0x55;
}
@ -139,20 +119,31 @@ public void process(byte[] data, int off, int len, byte[] iv)
* XOR the original key with the above created mask to
* get the special key.
*/
for (int i = 0; i < this.key.length; i++)
for (int i = 0; i < key.length; i++)
{
maskedKey[i] = (byte) (this.key[i] ^ saltMask[i]);
maskedKey[i] = (byte) (key[i] ^ saltMask[i]);
}
/*
* Prepare the a new AES cipher with the special key to compute IV'
* Prepare the f8Cipher with the special key to compute IV'
*/
AESCipher cipher = new AESCipher(maskedKey);
SecretKey encryptionKey = new SecretKeySpec(maskedKey, 0,
maskedKey.length, "AES");
try
{
f8Cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
/*
* Use the masked key to encrypt the original IV to produce IV'.
*/
f8Cipher.doFinal(iv, 0, BLKLEN, f8ctx.ivAccent, 0);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* Use the masked key to encrypt the original IV to produce IV'.
*/
cipher.encryptBlock(iv, 0, f8ctx.ivAccent, 0);
saltMask = null;
maskedKey = null;
@ -166,14 +157,16 @@ public void process(byte[] data, int off, int len, byte[] iv)
while (inLen >= BLKLEN)
{
processBlock(f8ctx, data, off, data, off, BLKLEN);
processBlock(aesCipher, f8ctx, data, off, data, off, BLKLEN);
inLen -= BLKLEN;
off += BLKLEN;
}
if (inLen > 0)
{
processBlock(f8ctx, data, off, data, off, inLen);
processBlock(aesCipher, f8ctx, data, off, data, off, inLen);
}
}
@ -181,6 +174,7 @@ public void process(byte[] data, int off, int len, byte[] iv)
* Encrypt / Decrypt a block using F8 Mode AES algorithm, read len bytes
* data from in at inOff and write the output into out at outOff
*
* @param aesCipher the AES cipher object used for block processing
* @param f8ctx F8 encryption context
* @param in byte array holding the data to be processed
* @param inOff start offset of the data to be processed inside in array
@ -188,8 +182,8 @@ public void process(byte[] data, int off, int len, byte[] iv)
* @param outOff start offset of output data in out
* @param len length of the input data
*/
private void processBlock(F8Context f8ctx, byte[] in, int inOff,
byte[] out, int outOff, int len)
private static void processBlock(Cipher aesCipher, F8Context f8ctx, byte[] in, int inOff,
byte[] out, int outOff, int len)
{
/*
@ -214,7 +208,16 @@ private void processBlock(F8Context f8ctx, byte[] in, int inOff,
/*
* Now compute the new key stream using AES encrypt
*/
this.aesCipher.encryptBlock(f8ctx.S, 0, f8ctx.S, 0);
try
{
aesCipher.doFinal(f8ctx.S, 0, BLKLEN, f8ctx.S, 0);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* As the last step XOR the plain text with the key stream to produce

@ -29,6 +29,9 @@
import java.util.*;
import net.java.sip.communicator.impl.media.transform.*;
import java.security.*;
import javax.crypto.spec.*;
import javax.crypto.*;
/**
* SRTPCryptoContext class is the core class of SRTP implementation.
@ -135,6 +138,26 @@ public class SRTPCryptoContext
* The SRTPDigest object we used to do packet authentication
*/
private SRTPDigest digest;
/**
* The cryptographic services provider
*/
private Provider cryptoProvider;
/**
* Used for various HMAC computations
*/
private Mac hmacSha1;
/**
* The AES cipher for counter mode
*/
private Cipher AEScipher = null;
/**
* The AES cipher for F8 mode
*/
private Cipher AEScipherF8 = null;
/**
* Construct an empty SRTPCryptoContext using ssrc.
@ -157,6 +180,7 @@ public SRTPCryptoContext(long ssrc)
this.saltKey = null;
this.seqNumSet = false;
this.policy = null;
this.cryptoProvider = null;
}
/**
@ -177,13 +201,16 @@ public SRTPCryptoContext(long ssrc)
* authentication key and the session salt.
* @param policy SRTP policy for this SRTP cryptographic context, defined
* the encryption algorithm, the authentication algorithm, etc
* @param cryptoProvider cryptographic services provider
*/
public SRTPCryptoContext(long ssrc,
int roc,
long keyDerivationRate,
byte[] masterKey,
byte[] masterSalt,
SRTPPolicy policy)
SRTPPolicy policy,
Provider cryptoProvider)
throws GeneralSecurityException
{
this.ssrc = ssrc;
this.mki = null;
@ -192,14 +219,16 @@ public SRTPCryptoContext(long ssrc,
this.seqNum = 0;
this.keyDerivationRate = keyDerivationRate;
this.seqNumSet = false;
this.cryptoProvider = cryptoProvider;
this.policy = policy;
this.masterKey = new byte[masterKey.length];
System.arraycopy(masterKey, 0, this.masterKey, 0, this.masterKey.length);
this.masterKey = new byte[this.policy.getEncKeyLength()];
System.arraycopy(masterKey, 0, this.masterKey, 0, this.policy.getEncKeyLength());
this.masterSalt = new byte[masterSalt.length];
System.arraycopy(masterSalt, 0, this.masterSalt, 0, this.masterSalt.length);
this.masterSalt = new byte[this.policy.getSaltKeyLength()];
System.arraycopy(masterSalt, 0, this.masterSalt, 0, this.policy.getSaltKeyLength());
switch (policy.getEncType())
{
@ -209,7 +238,16 @@ public SRTPCryptoContext(long ssrc,
break;
case SRTPPolicy.AESCM_ENCRYPTION:
hmacSha1 = Mac.getInstance("HMACSHA1", cryptoProvider);
AEScipher = Cipher.getInstance("AES/ECB/NOPADDING", cryptoProvider);
this.encKey = new byte[this.policy.getEncKeyLength()];
this.saltKey = new byte[this.policy.getSaltKeyLength()];
break;
case SRTPPolicy.AESF8_ENCRYPTION:
hmacSha1 = Mac.getInstance("HMACSHA1", cryptoProvider);
AEScipher = Cipher.getInstance("AES/ECB/NOPADDING", cryptoProvider);
AEScipherF8 = Cipher.getInstance("AES/ECB/NOPADDING", cryptoProvider);
this.encKey = new byte[this.policy.getEncKeyLength()];
this.saltKey = new byte[this.policy.getSaltKeyLength()];
break;
@ -319,7 +357,7 @@ else if (this.policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION)
if (this.policy.getAuthType() == SRTPPolicy.HMACSHA1_AUTHENTICATION)
{
byte[] tag = authenticatePacketHMCSHA1(pkt);
pkt.append(tag);
pkt.append(tag, policy.getAuthTagLength());
}
/* Update the ROC if necessary */
@ -362,9 +400,11 @@ public boolean reverseTransformPacket(RawPacket pkt)
byte[] calculatedTag = authenticatePacketHMCSHA1(pkt);
if (!Arrays.equals(originalTag, calculatedTag))
{
return false;
for (int i = 0; i < tagLength; i++) {
if ((originalTag[i]&0xff) == (calculatedTag[i]&0xff))
continue;
else
return false;
}
}
@ -420,13 +460,11 @@ public void processPacketAESCM(RawPacket pkt)
iv[14] = iv[15] = 0;
SRTPCipher cipher = new SRTPCipherCTR(this.encKey);
final int payloadOffset = PacketManipulator.GetRTPHeaderLength(pkt);
final int payloadLength = PacketManipulator.GetRTPPayloadLength(pkt);
cipher.process(pkt.getBuffer(), pkt.getOffset() + payloadOffset,
payloadLength, iv);
SRTPCipherCTR.process(AEScipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
payloadLength, iv);
}
/**
@ -436,42 +474,45 @@ public void processPacketAESCM(RawPacket pkt)
*/
public void processPacketAESF8(RawPacket pkt)
{
long ssrc = PacketManipulator.GetRTPSSRC(pkt);
boolean isMarked = PacketManipulator.IsPacketMarked(pkt);
int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
byte payload = PacketManipulator.GetRTPPayloadType(pkt);
//long ssrc = PacketManipulator.GetRTPSSRC(pkt);
//boolean isMarked = PacketManipulator.IsPacketMarked(pkt);
//int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
//byte payload = PacketManipulator.GetRTPPayloadType(pkt);
byte[] iv = new byte[16];
iv[0] = 0;
iv[1] = (byte) (isMarked ? 0x80 : 0x00);
iv[1] |= payload & 0x7f;
iv[2] = (byte) (seqNum >> 8);
iv[3] = (byte) seqNum;
//iv[0] = 0;
//iv[1] = (byte) (isMarked ? 0x80 : 0x00);
//iv[1] |= payload & 0x7f;
//iv[2] = (byte) (seqNum >> 8);
//iv[3] = (byte) seqNum;
// set the TimeStamp in network order into IV
byte[] timeStamp = PacketManipulator.ReadTimeStampIntoByteArray(pkt);
System.arraycopy(timeStamp, 0, iv, 4, 4);
//byte[] timeStamp = PacketManipulator.ReadTimeStampIntoByteArray(pkt);
//System.arraycopy(timeStamp, 0, iv, 4, 4);
// set the SSRC in network order into IV
iv[8] = (byte) (ssrc >> 24);
iv[9] = (byte) (ssrc >> 16);
iv[10] = (byte) (ssrc >> 8);
iv[11] = (byte) ssrc;
//iv[8] = (byte) (ssrc >> 24);
//iv[9] = (byte) (ssrc >> 16);
//iv[10] = (byte) (ssrc >> 8);
//iv[11] = (byte) ssrc;
// set the ROC in network order into IV
// 11 bytes of the RTP header are the 11 bytes of the iv
// the first byte of the RTP header is not used.
System.arraycopy(pkt.getBuffer(), pkt.getOffset(), iv, 0, 12);
iv[0] = 0;
iv[12] = (byte) (this.roc >> 24);
iv[13] = (byte) (this.roc >> 16);
iv[14] = (byte) (this.roc >> 8);
iv[15] = (byte) this.roc;
SRTPCipher cipher = new SRTPCipherF8(this.encKey, this.saltKey);
final int payloadOffset = PacketManipulator.GetRTPHeaderLength(pkt);
final int payloadLength = PacketManipulator.GetRTPPayloadLength(pkt);
cipher.process(pkt.getBuffer(), pkt.getOffset() + payloadOffset,
payloadLength, iv);
SRTPCipherF8.process(AEScipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
payloadLength, iv, encKey, saltKey, AEScipherF8);
}
/**
@ -483,26 +524,15 @@ public void processPacketAESF8(RawPacket pkt)
*/
private byte[] authenticatePacketHMCSHA1(RawPacket pkt)
{
byte[][] chunks = new byte[2][];
int[] chunkLength = new int[2];
chunks[0] = pkt.getBuffer();
chunkLength[0] = pkt.getLength();
chunks[1] = new byte[4];
chunks[1][0] = (byte) (this.roc >> 24);
chunks[1][1] = (byte) (this.roc >> 16);
chunks[1][2] = (byte) (this.roc >> 8);
chunks[1][3] = (byte) this.roc;
chunkLength[1] = 4;
byte[] result = this.digest.authHMACSHA1(chunks, chunkLength);
byte[] tag = new byte[this.policy.getAuthTagLength()];
System.arraycopy(result, 0, tag, 0, this.policy.getAuthTagLength());
hmacSha1.update(pkt.getBuffer(), 0, pkt.getLength());
byte[] rb = new byte[4];
rb[0] = (byte) (this.roc >> 24);
rb[1] = (byte) (this.roc >> 16);
rb[2] = (byte) (this.roc >> 8);
rb[3] = (byte) this.roc;
hmacSha1.update(rb);
return tag;
return hmacSha1.doFinal();
}
/**
@ -613,25 +643,61 @@ public void deriveSrtpKeys(long index)
// compute the session encryption key
long label = 0;
computeIv(iv, label, index, this.keyDerivationRate, this.masterSalt);
SRTPCipherCTR aes = new SRTPCipherCTR(this.masterKey);
aes.getCipherStream(this.encKey, this.policy.getEncKeyLength(), iv);
SecretKey encryptionKey = new SecretKeySpec(masterKey, 0, policy.getEncKeyLength(), "AES");
try
{
AEScipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
}
catch (InvalidKeyException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
SRTPCipherCTR.getCipherStream(AEScipher, encKey, policy.getEncKeyLength(), iv);
// compute the session authentication key
if (this.authKey != null)
{
label = 0x01;
computeIv(iv, label, index, this.keyDerivationRate, this.masterSalt);
aes = new SRTPCipherCTR(this.masterKey);
aes.getCipherStream(this.authKey, this.policy.getAuthKeyLength(), iv);
SRTPCipherCTR.getCipherStream(AEScipher, authKey, policy.getAuthKeyLength(), iv);
SecretKey key = new SecretKeySpec(authKey, "HMAC");
try
{
hmacSha1.init(key);
}
catch (InvalidKeyException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
this.digest = new SRTPDigest(this.authKey);
}
// compute the session salt
label = 0x02;
computeIv(iv, label, index, this.keyDerivationRate, this.masterSalt);
aes = new SRTPCipherCTR(this.masterKey);
aes.getCipherStream(this.saltKey, this.policy.getSaltKeyLength(), iv);
SRTPCipherCTR.getCipherStream(AEScipher, saltKey, policy.getSaltKeyLength(), iv);
// As last step: initialize AES cipher with derived encryption key.
encryptionKey = new SecretKeySpec(encKey, 0, policy.getEncKeyLength(), "AES");
try
{
AEScipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
}
catch (InvalidKeyException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
@ -717,9 +783,19 @@ private void update(int seqNum)
*/
public SRTPCryptoContext deriveContext(long ssrc, int roc, long deriveRate)
{
SRTPCryptoContext pcc =
new SRTPCryptoContext(ssrc, roc, deriveRate, this.masterKey,
this.masterSalt, this.policy);
SRTPCryptoContext pcc = null;
try
{
pcc = new SRTPCryptoContext(ssrc, roc, deriveRate,
this.masterKey, this.masterSalt, this.policy,
this.cryptoProvider);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return pcc;
}
}

@ -7,6 +7,7 @@
package net.java.sip.communicator.impl.media.transform.srtp;
import net.java.sip.communicator.impl.media.transform.*;
import java.security.*;
/**
* SRTPTransformEngine class implements TransformEngine interface.
@ -54,9 +55,12 @@ public class SRTPTransformEngine
* @param masterSalt the master salt key
* @param srtpPolicy SRTP policy
* @param srtcpPolicy SRTCP policy
* @param cryptoProvider cryptography services provider
*/
public SRTPTransformEngine(byte[] masterKey, byte[] masterSalt,
SRTPPolicy srtpPolicy, SRTPPolicy srtcpPolicy)
SRTPPolicy srtpPolicy, SRTPPolicy srtcpPolicy,
Provider cryptoProvider)
throws GeneralSecurityException
{
this.masterKey = new byte[masterKey.length];
System.arraycopy(masterKey, 0, this.masterKey, 0, masterKey.length);
@ -70,7 +74,8 @@ public SRTPTransformEngine(byte[] masterKey, byte[] masterSalt,
this.defaultContext = new SRTPCryptoContext(0, 0, 0,
this.masterKey,
this.masterSalt,
this.srtpPolicy);
this.srtpPolicy,
cryptoProvider);
}
/* (non-Javadoc)

@ -51,7 +51,7 @@ public class SRTPTransformer
/**
* All the known SSRC's corresponding SRTPCryptoContexts
*/
private Hashtable contexts;
private Hashtable<Long, SRTPCryptoContext> contexts;
/**
* Construct a SRTPTransformer
@ -61,7 +61,7 @@ public class SRTPTransformer
public SRTPTransformer(SRTPTransformEngine engine)
{
this.engine = engine;
this.contexts = new Hashtable();
this.contexts = new Hashtable<Long, SRTPCryptoContext>();
}
/* (non-Javadoc)
@ -78,11 +78,17 @@ public RawPacket transform(RawPacket pkt)
if (context == null)
{
context = this.engine.getDefaultContext().deriveContext(ssrc, 0, 0);
context.deriveSrtpKeys(0);
this.contexts.put(new Long(ssrc), context);
if (context != null)
{
context.deriveSrtpKeys(0);
this.contexts.put(new Long(ssrc), context);
}
}
context.transformPacket(pkt);
if (context != null)
{
context.transformPacket(pkt);
}
return pkt;
}
@ -101,16 +107,35 @@ public RawPacket reverseTransform(RawPacket pkt)
if (context == null)
{
context = this.engine.getDefaultContext().deriveContext(ssrc, 0, 0);
context.deriveSrtpKeys(seqNum);
this.contexts.put(new Long(ssrc), context);
if (context != null)
{
context.deriveSrtpKeys(seqNum);
this.contexts.put(new Long(ssrc), context);
}
}
boolean validPacket = context.reverseTransformPacket(pkt);
if (!validPacket)
if (context != null)
{
return null;
boolean validPacket = context.reverseTransformPacket(pkt);
if (!validPacket)
{
return null;
}
}
return pkt;
}
/**
* Getter to use in derived classes.
* (Could modify the member variable to protected instead for direct access)
*
* @return the engine
*/
public SRTPTransformEngine getEngine()
{
return engine;
}
}

@ -684,5 +684,23 @@ public void sessionMediaReceived(JingleSession jingleSession,
{
logger.info("session media received ");
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetBasicTelephony#setSecured(net.java.sip.communicator.service.protocol.CallParticipant, boolean, net.java.sip.communicator.service.media.CallSession.SecureStatusChangeSource)
*/
public void setSecured(CallParticipant participant, boolean secured,
OperationSetBasicTelephony.SecureStatusChangeSource source)
{
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetBasicTelephony#getSecured(net.java.sip.communicator.service.protocol.CallParticipant)
*/
public boolean getSecured(CallParticipant participant)
{
return false;
}
}

@ -231,4 +231,22 @@ public void callStateChanged(CallChangeEvent evt)
fireCallEvent(CallEvent.CALL_ENDED, sourceCall);
}
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetBasicTelephony#setSecured(net.java.sip.communicator.service.protocol.CallParticipant, boolean, net.java.sip.communicator.service.media.CallSession.SecureStatusChangeSource)
*/
public void setSecured(CallParticipant participant, boolean secured,
OperationSetBasicTelephony.SecureStatusChangeSource source)
{
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetBasicTelephony#getSecured(net.java.sip.communicator.service.protocol.CallParticipant)
*/
public boolean getSecured(CallParticipant participant)
{
return false;
}
}

@ -26,6 +26,7 @@
* @author Emil Ivov
* @author Lubomir Marinov
* @author Alan Kelly
* @author Emanuel Onica
*/
public class OperationSetBasicTelephonySipImpl
extends AbstractOperationSetBasicTelephony
@ -2962,6 +2963,27 @@ public void setMute(CallParticipant participant, boolean mute)
((CallSipImpl) participant.getCall()).getMediaCallSession().setMute(
mute);
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetBasicTelephony#setSecured(net.java.sip.communicator.service.protocol.CallParticipant, boolean, net.java.sip.communicator.service.media.CallSession.SecureStatusChangeSource)
*/
public void setSecured(CallParticipant participant, boolean secured,
SecureStatusChangeSource source)
{
((CallSipImpl) participant.getCall()).getMediaCallSession().
setSecureCommunicationStatus(secured, source);
}
/*
* (non-Javadoc)
* @see net.java.sip.communicator.service.protocol.OperationSetBasicTelephony#getSecured(net.java.sip.communicator.service.protocol.CallParticipant)
*/
public boolean getSecured(CallParticipant participant)
{
return ((CallSipImpl) participant.getCall()).getMediaCallSession().
getSecureCommunicationStatus();
}
/**
* Transfers (in the sense of call transfer) a specific

@ -25,6 +25,7 @@
*
* @author Emil Ivov
* @author Lubomir Marinov
* @author Emanuel Onica
*/
public interface CallSession
{
@ -190,4 +191,30 @@ public void processSdpAnswer(CallParticipant responder, String sdpAnswer)
* session.
*/
public void stopStreaming();
/**
* Sets the default secure/unsecure communication status for the supported
* call sessions.
*
* @param activator value of default secure communication status
* @param source the initiator of the secure status change (can be local or remote)
*/
public void setSecureCommunicationStatus(boolean activator,
OperationSetBasicTelephony.
SecureStatusChangeSource source);
/**
* Gets the default secure/unsecure communication status for the supported
* call sessions.
*
* @return default secure communication status for the supported call sessions
*/
public boolean getSecureCommunicationStatus();
/**
* Gets the call associated with this session
*
* @return the call associated with this session
*/
public Call getCall();
}

@ -19,6 +19,7 @@
* or H323Call or AnyOtherTelephonyProtocolCall
*
* @author Emil Ivov
* @author Emanuel Onica
*/
public abstract class Call
{
@ -39,6 +40,13 @@ public abstract class Call
* A reference to the ProtocolProviderService instance that created us.
*/
private ProtocolProviderService protocolProvider = null;
/**
* A collection of various GUI components used for a call management that might
* be needed inside specific layers of the call securing, depending on the
* securing algorithm used
*/
private Hashtable secureGUIComponents;
/**
* Creates a new Call instance.
@ -240,4 +248,40 @@ protected void fireCallChangeEvent( String type,
* currently in.
*/
public abstract CallState getCallState();
/**
* This method is used to add references to various GUI components related to
* securing the call that might be used in different way in by various securing
* algorithms, and consequently might be needed for particular usage at the layers
* where the specified algorithms operate
*
* @param key a key used by a securing algorithm implementation
* to identify the GUI item needed
* @param value the GUI object
*/
public void addSecureGUIComponent(Object key, Object value)
{
if (secureGUIComponents == null)
secureGUIComponents = new Hashtable();
secureGUIComponents.put(key, value);
}
/**
* This method is used to obtain the reference to various GUI components related to
* securing the call that might be used in different way in by various securing
* algorithms, and consequently might be needed for particular usage at the layers
* where the specified algorithms operate
*
* @param key a key used by a securing algorithm implementation
* to identify the GUI item needed
* @return the GUI object
*/
public Object getSecureGUIComponent(Object key)
{
if (secureGUIComponents == null)
return null;
else
return secureGUIComponents.get(key);
}
}

@ -18,6 +18,7 @@
*
* @author Emil Ivov
* @author Lubomir Marinov
* @author Emanuel Onica
*/
public interface OperationSetBasicTelephony
extends OperationSet
@ -135,4 +136,35 @@ public void hangupCallParticipant(CallParticipant participant)
* <tt>participant</tt>; otherwise, <tt>false</tt>
*/
public void setMute(CallParticipant participant, boolean mute);
/**
* Use this to indicate the source of setting the secure status
* of the communication as being the local or remote peer or reverted by local
*/
public static enum SecureStatusChangeSource {
SECURE_STATUS_CHANGE_BY_LOCAL,
SECURE_STATUS_CHANGE_BY_REMOTE,
SECURE_STATUS_REVERTED;
}
/**
* Sets the secured state of the call session in which a specific participant
* is involved
*
* @param participant the participant who toggled (or for whom is remotely toggled)
* the secure status change for the call
* @param secured the new secure status
* @param source the source who generated the call change
*/
public void setSecured(CallParticipant participant, boolean secured,
SecureStatusChangeSource source);
/**
* Gets the secured state of the call session in which a specific participant
* is involved
*
* @param participant the participant for who the call state is required
* @return the call state
*/
public boolean getSecured(CallParticipant participant);
}

@ -0,0 +1,79 @@
/*
* SIP Communicator, 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;
import java.util.*;
import net.java.sip.communicator.impl.media.*;
/**
* SecureEvent class extends EventObject
* This is the event type sent to current call sessions running
* when the user changes the secure state of communication in the GUI,
* to inform about the go secure / go clear change in communication.
* The event is actual triggered by a modification of the usingSRTP
* static secure communication status in the CallSessionImpl class.
*
* @author Emanuel Onica (eonica@info.uaic.ro)
*
*/
public class SecureEvent
extends EventObject
{
/**
* Constant value defining that the user triggered secure communication.
*/
public static final int SECURE_COMMUNICATION = 1;
/**
* Constant value defining that the user triggered unsecure communication.
*/
public static final int UNSECURE_COMMUNICATION = 2;
/**
* The actual event value - secure or unsecure, set at one of the above constants
*/
private int eventID;
/**
* The source that triggered the event - local or remote peer
*/
private OperationSetBasicTelephony.SecureStatusChangeSource source;
/**
* The event constructor
*
* @param callSession the event source - the call session for which this event applies
* @param eventID the change value - going secure or stopping secure communication
*/
public SecureEvent(CallSessionImpl callSession,
int eventID,
OperationSetBasicTelephony.SecureStatusChangeSource source)
{
super(callSession);
this.eventID = eventID;
this.source = source;
}
/**
* Retrieves the value of change - secure or unsecure
*
* @return the actual event value
*/
public int getEventID()
{
return eventID;
}
/**
* Retrieves the source that triggered the event
* (change by local peer or remote peer or reverting a previous change)
*/
public OperationSetBasicTelephony.SecureStatusChangeSource getSource()
{
return source;
}
}

@ -0,0 +1,57 @@
/*
* SIP Communicator, 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;
import java.util.*;
/**
* SecureEventListener interface extends EventListener
* This is the listener interface used to handle an event
* related with a change in security status. It is required
* to be implemented by a CallSession for this purpose.
* The change in security status is triggered at the GUI level
* by the user, who can toggle on or off securing the communication.
* This modifies the current security status static indicator for
* the call sessions, action that is the one which actually triggers
* sending the changed secure event to the current call sessions in
* progress.
* There are two different cases related to when the user could
* toggle secure communication on and off at the GUI level.
*
* 1. No active call session is in progress and
* subsequently no listeners are registered.
* Result: Only the usingSRTP static general status of security
* is changed and any following call sessions will use
* that as default for going secure or not from the start.
*
* 2. Active call sessions (actually one according to the current
* media service implementation) are in progress and have
* registered their listeners.
* Result: The usingSRTP default start status is changed and
* triggers successfully the changed secure status event
* sending to the call sessions, which handle it modifying
* the session secure state (going secure or back to normal
* state during the actual session) according to the used key
* management system.
*
* @author Emanuel Onica (eonica@info.uaic.ro)
*
*/
public interface SecureEventListener
extends EventListener
{
/**
* The handler for the secure event received.
* The secure event represents an indication of change in the secure
* communication status of the implementor of this interface.
* The implementor should modify it's internal state if supported.
*
* @param secureEvent the secureEvent received
*/
public void secureStatusChanged(SecureEvent secureEvent);
}
Loading…
Cancel
Save