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