diff --git a/resources/images/images.properties b/resources/images/images.properties
index 86884200e..0cd7ed6f2 100644
--- a/resources/images/images.properties
+++ b/resources/images/images.properties
@@ -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
\ No newline at end of file
+foldericon=resources/images/plugin/notificationconfiguration/folder.png
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index c8dc551f0..4c70edb66 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -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=You have enabled secure init mode
You can't switch off this mode at this moment
The call is not secured yet anyway
+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
diff --git a/resources/languages/resources_ro.properties b/resources/languages/resources_ro.properties
index d6245c38b..020a8879b 100644
--- a/resources/languages/resources_ro.properties
+++ b/resources/languages/resources_ro.properties
@@ -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=Modul securizat a fost activat
Nu puteÈ?i schimba acest mod la acest moment
Oricum apelul nu este încÄ? securizat
+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
+
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/SecureButton.java b/src/net/java/sip/communicator/impl/gui/main/call/SecureButton.java
new file mode 100644
index 000000000..402b39b6d
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/SecureButton.java
@@ -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());
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
index a802cd029..81274207a 100644
--- a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
+++ b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
@@ -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 CallPanel.
*/
diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java
index 5be615273..0e4b02c71 100644
--- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java
+++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java
@@ -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 true if the audio of this session is (set to) mute;
* otherwise, false
*/
@@ -2422,7 +2842,7 @@ public boolean isMute()
/**
* Sets the mute state of the audio of this session.
- *
+ *
* @param mute true to mute the audio of this session; otherwise,
* false
*/
diff --git a/src/net/java/sip/communicator/impl/media/keyshare/DummyKeyProvider.java b/src/net/java/sip/communicator/impl/media/keyshare/DummyKeyProvider.java
new file mode 100644
index 000000000..5451e3af3
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/keyshare/DummyKeyProvider.java
@@ -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;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/media/keyshare/KeyProviderAlgorithm.java b/src/net/java/sip/communicator/impl/media/keyshare/KeyProviderAlgorithm.java
new file mode 100644
index 000000000..c32a00ebc
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/keyshare/KeyProviderAlgorithm.java
@@ -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);
+
+}
diff --git a/src/net/java/sip/communicator/impl/media/keyshare/ZRTPKeyProvider.java b/src/net/java/sip/communicator/impl/media/keyshare/ZRTPKeyProvider.java
new file mode 100644
index 000000000..3a3319961
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/keyshare/ZRTPKeyProvider.java
@@ -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;
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/media/media.manifest.mf b/src/net/java/sip/communicator/impl/media/media.manifest.mf
index d0acb37ca..5a85efe24 100644
--- a/src/net/java/sip/communicator/impl/media/media.manifest.mf
+++ b/src/net/java/sip/communicator/impl/media/media.manifest.mf
@@ -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,
diff --git a/src/net/java/sip/communicator/impl/media/transform/RawPacket.java b/src/net/java/sip/communicator/impl/media/transform/RawPacket.java
index 574176dd3..cfd6f044a 100755
--- a/src/net/java/sip/communicator/impl/media/transform/RawPacket.java
+++ b/src/net/java/sip/communicator/impl/media/transform/RawPacket.java
@@ -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;
diff --git a/src/net/java/sip/communicator/impl/media/transform/TransformConnector.java b/src/net/java/sip/communicator/impl/media/transform/TransformConnector.java
index 242c299c0..09d88d26b 100755
--- a/src/net/java/sip/communicator/impl/media/transform/TransformConnector.java
+++ b/src/net/java/sip/communicator/impl/media/transform/TransformConnector.java
@@ -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;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/media/transform/TransformInputStream.java b/src/net/java/sip/communicator/impl/media/transform/TransformInputStream.java
index 4eb82cb92..d465d7d92 100755
--- a/src/net/java/sip/communicator/impl/media/transform/TransformInputStream.java
+++ b/src/net/java/sip/communicator/impl/media/transform/TransformInputStream.java
@@ -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);
}
diff --git a/src/net/java/sip/communicator/impl/media/transform/TransformManager.java b/src/net/java/sip/communicator/impl/media/transform/TransformManager.java
index a85e5703f..86a3543de 100755
--- a/src/net/java/sip/communicator/impl/media/transform/TransformManager.java
+++ b/src/net/java/sip/communicator/impl/media/transform/TransformManager.java
@@ -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
diff --git a/src/net/java/sip/communicator/impl/media/transform/TransformOutputStream.java b/src/net/java/sip/communicator/impl/media/transform/TransformOutputStream.java
index 7e40e2e5b..384b04204 100755
--- a/src/net/java/sip/communicator/impl/media/transform/TransformOutputStream.java
+++ b/src/net/java/sip/communicator/impl/media/transform/TransformOutputStream.java
@@ -38,13 +38,13 @@ public class TransformOutputStream
/**
* Stream targets' ip addresses
*/
- private Vector remoteAddrs;
+ private Vector remoteAddrs;
/**
* Stream targets' ports, corresponding to their ip addresses.
*/
- private Vector remotePorts;
-
+ private Vector 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();
+ this.remotePorts = new Vector();
}
/**
@@ -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(),
diff --git a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherCTR.java b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherCTR.java
index 9b740d283..8d89cd903 100755
--- a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherCTR.java
+++ b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherCTR.java
@@ -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);
}
}
diff --git a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherF8.java b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherF8.java
index f9a68f41d..4bdca7638 100755
--- a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherF8.java
+++ b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCipherF8.java
@@ -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
diff --git a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCryptoContext.java b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCryptoContext.java
index 5d5470312..e23b857f3 100755
--- a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCryptoContext.java
+++ b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPCryptoContext.java
@@ -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;
+
}
}
diff --git a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformEngine.java b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformEngine.java
index 54a7f8480..6194c76c7 100755
--- a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformEngine.java
+++ b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformEngine.java
@@ -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)
diff --git a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformer.java b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformer.java
index 0bc2c4a8c..0aecaa9bd 100755
--- a/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformer.java
+++ b/src/net/java/sip/communicator/impl/media/transform/srtp/SRTPTransformer.java
@@ -51,7 +51,7 @@ public class SRTPTransformer
/**
* All the known SSRC's corresponding SRTPCryptoContexts
*/
- private Hashtable contexts;
+ private Hashtable 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();
}
/* (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;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
index 158483db7..54acb4907 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
@@ -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;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
index 96b37d308..3cd525903 100644
--- a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
@@ -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;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index 8067e550f..9a85c7a3e 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -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
diff --git a/src/net/java/sip/communicator/service/media/CallSession.java b/src/net/java/sip/communicator/service/media/CallSession.java
index e6ab6f2c0..163d55b1c 100644
--- a/src/net/java/sip/communicator/service/media/CallSession.java
+++ b/src/net/java/sip/communicator/service/media/CallSession.java
@@ -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();
}
diff --git a/src/net/java/sip/communicator/service/protocol/Call.java b/src/net/java/sip/communicator/service/protocol/Call.java
index d5367aaf4..120afd49b 100644
--- a/src/net/java/sip/communicator/service/protocol/Call.java
+++ b/src/net/java/sip/communicator/service/protocol/Call.java
@@ -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);
+ }
}
diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java
index c8bba6b1d..2959837d2 100644
--- a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java
+++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java
@@ -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)
* participant; otherwise, false
*/
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);
}
diff --git a/src/net/java/sip/communicator/service/protocol/SecureEvent.java b/src/net/java/sip/communicator/service/protocol/SecureEvent.java
new file mode 100644
index 000000000..5ff419d7d
--- /dev/null
+++ b/src/net/java/sip/communicator/service/protocol/SecureEvent.java
@@ -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;
+ }
+}
diff --git a/src/net/java/sip/communicator/service/protocol/SecureEventListener.java b/src/net/java/sip/communicator/service/protocol/SecureEventListener.java
new file mode 100644
index 000000000..faa1504d1
--- /dev/null
+++ b/src/net/java/sip/communicator/service/protocol/SecureEventListener.java
@@ -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);
+}