From 45d35e5d3422994c2c4d4ece991000e41b5ef6cd Mon Sep 17 00:00:00 2001 From: Werner Dittmann Date: Tue, 2 Dec 2008 10:20:02 +0000 Subject: [PATCH] Insert code to enable secure video streams via ZRTP key negotiation (ZRTP multi-stream mode). --- .../impl/media/CallSessionImpl.java | 115 +++++++++++++----- .../impl/media/transform/zrtp/SCCallback.java | 88 ++++++++++++-- .../transform/zrtp/ZRTPTransformEngine.java | 4 +- 3 files changed, 161 insertions(+), 46 deletions(-) diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java index 22035fd92..98fbd999a 100644 --- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java +++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java @@ -243,7 +243,7 @@ public class CallSessionImpl }; /** - * Additional info codes for ZRTP4J. + * Additional info codes for and data to support ZRTP4J. * These could be added to the library. However they are specific for this * implementation, needing them for various GUI changes. */ @@ -254,6 +254,14 @@ public static enum ZRTPCustomInfoCodes ZRTPEngineInitFailure; } + /** + * Holds the "Master" ZRTP session. + * + * This session must be started first and must have negotiated the key material + * before any other media session to the same client can be started. See the + * ZRTP specification, topic multi-streaming mode. + */ + private TransformConnector zrtpDHSession = null; /** * JMF stores CUSTOM_CODEC_FORMATS statically, so they only need to be * registered once. FMJ does this dynamically (per instance), so it needs @@ -509,12 +517,8 @@ private boolean stopStreaming(RTPManager rtpManager) } //remove targets - if (selectedKeyProviderAlgorithm != null - /* TODO: Video securing related code - * remove the next condition as part of enabling video securing - * (see comments in secureStatusChanged method for more info) - */ - && rtpManager.equals(audioRtpManager)) + if (selectedKeyProviderAlgorithm != null && selectedKeyProviderAlgorithm.getProviderType() + == KeyProviderAlgorithm.ProviderType.ZRTP_PROVIDER) { TransformConnector transConnector = this.transConnectors.get(rtpManager); @@ -523,10 +527,8 @@ private boolean stopStreaming(RTPManager rtpManager) { ZRTPTransformEngine engine = (ZRTPTransformEngine)transConnector.getEngine(); - - engine.sendInfo( - ZrtpCodes.MessageSeverity.Info, - EnumSet.of(ZRTPCustomInfoCodes.ZRTPDisabledByCallEnd)); + engine.stopZrtp(); + engine.cleanup(); transConnector.removeTargets(); } @@ -1257,13 +1259,7 @@ private void initStreamTargets(Connection globalConnParam, try { - if (selectedKeyProviderAlgorithm != null - /* TODO: Video securing related code - * remove the next condition as part of enabling video - * securing (see comments in secureStatusChanged method - * for more info) - */ - && rtpManager.equals(audioRtpManager)) + if (selectedKeyProviderAlgorithm != null) { TransformConnector transConnector = this.transConnectors.get(rtpManager); @@ -2050,26 +2046,40 @@ private void initializeRtpManager(RTPManager rtpManager, // 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 in secureStatusChanged method for more info) - */ - && rtpManager.equals(audioRtpManager)) + == KeyProviderAlgorithm.ProviderType.ZRTP_PROVIDER) { - // Set a ZRTP connector to use for communication - TransformConnector transConnector = null; - TransformManager.initializeProviders(); // The connector is created based also on the crypto services // The crypto provider solution should be queried somehow // or taken from a resources file - transConnector = TransformManager.createZRTPConnector( + TransformConnector transConnector = TransformManager.createZRTPConnector( bindAddress, "BouncyCastle", this); rtpManager.initialize(transConnector); this.transConnectors.put(rtpManager, transConnector); + SCCallback callback = new SCCallback(this); + boolean zrtpAutoStart = false; + + // Decide if this will become the ZRTP Master session: + // - Statement: audio media session will be started before video media session + // - if no other audio session was started before then this will become + // ZRTP Master session + // - only the ZRTP master sessions start in "aut-sensing" mode to + // immedialtely catch ZRTP communication fro other client + // - after the master session has completed key negotiations it will + // start other media sessions (see SCCallback) + if (rtpManager.equals(audioRtpManager)) { + if (zrtpDHSession == null) { + zrtpDHSession = transConnector; + zrtpAutoStart = true; + callback.setDHSession(true); + } + callback.setType(SecurityGUIEventZrtp.AUDIO); + } + else if (rtpManager.equals(videoRtpManager)) { + callback.setType(SecurityGUIEventZrtp.VIDEO); + } // ZRTP engine initialization // TODO: 1. must query/randomize/find a method for the zid file // name @@ -2077,12 +2087,12 @@ private void initializeRtpManager(RTPManager rtpManager, ZRTPTransformEngine engine = (ZRTPTransformEngine)transConnector.getEngine(); - engine.setUserCallback(new SCCallback(this)); + engine.setUserCallback(callback); // Case 1: user toggled secure communication prior to the call if (usingSRTP) { - if (!engine.initialize("GNUZRTP4J.zid")) + if (!engine.initialize("GNUZRTP4J.zid", zrtpAutoStart)) engine.sendInfo( ZrtpCodes.MessageSeverity.Info, EnumSet.of( @@ -2164,6 +2174,7 @@ private void initializeRtpManager(RTPManager rtpManager, } catch (Exception exc) { + exc.printStackTrace(); logger.error("Failed to init an RTP manager.", exc); throw new MediaException("Failed to init an RTP manager." , MediaException.IO_ERROR @@ -2786,10 +2797,15 @@ public void setSecureCommunicationStatus(boolean activator, } } + /* + * The following methods are specific to ZRTP key management implementation. + */ + /* + * (non-Javadoc) + * @see net.java.sip.communicator.service.media.CallSession#setZrtpSASVerification(boolean) + */ public boolean setZrtpSASVerification(boolean verified) { - TransformConnector transConnector = this.transConnectors - .get(audioRtpManager); - ZRTPTransformEngine engine = (ZRTPTransformEngine) transConnector + ZRTPTransformEngine engine = (ZRTPTransformEngine) zrtpDHSession .getEngine(); if (verified) { engine.SASVerified(); @@ -2844,6 +2860,39 @@ private void zrtpChangeStatus(RTPManager manager, SecureEvent event) } } + /** + * Start multi-stream ZRTP sessions. + * + * After the ZRTP Master (DH) session reached secure state the SCCallback calls + * this method to start the multi-stream ZRTP sessions. + * + * First get the multi-stream data from the ZRTP DH session. Then iterate over + * all known connectors, set multi-stream mode data, and enable auto-start + * mode (auto-sensing). + * + * @return Number of started ZRTP multi-stream mode sessions + */ + public int startZrtpMultiStreams() { + ZRTPTransformEngine engine + = (ZRTPTransformEngine)zrtpDHSession.getEngine(); + + int counter = 0; + byte[] multiStreamData = engine.getMultiStrParams(); + + Enumeration tcs = transConnectors.elements(); + + while (tcs.hasMoreElements()) { + TransformConnector tc = tcs.nextElement(); + if (tc.equals(zrtpDHSession)) { + continue; + } + engine = (ZRTPTransformEngine)tc.getEngine(); + engine.setMultiStrParams(multiStreamData); + engine.setEnableZrtp(true); + counter++; + } + return counter; + } /** * Initializes the supported key management types and establishes * default usage priorities for them. diff --git a/src/net/java/sip/communicator/impl/media/transform/zrtp/SCCallback.java b/src/net/java/sip/communicator/impl/media/transform/zrtp/SCCallback.java index a0d937944..f43fb17e3 100644 --- a/src/net/java/sip/communicator/impl/media/transform/zrtp/SCCallback.java +++ b/src/net/java/sip/communicator/impl/media/transform/zrtp/SCCallback.java @@ -8,6 +8,7 @@ import java.util.*; +import net.java.sip.communicator.impl.media.CallSessionImpl; import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.Logger; @@ -36,7 +37,17 @@ public class SCCallback private SecurityGUIListener guiListener = null; private CallParticipant participant; + + /* + * Is this a ZRTP DH (Master) session? + */ + private boolean dhSession = false; + /* + * Type of session. See class SecurityGUIEventZrtp which types are + * supported. + */ + private String sessionType = null; /** * The class constructor. */ @@ -57,7 +68,8 @@ public void init() { SecurityGUIEvent.NONE, SecurityGUIEvent.SECURITY_ENABLED); - logger.info("initialize SCCallback"); + if (logger.isInfoEnabled()) + logger.info(sessionType +": initialize SCCallback"); fireStateChangedEvent(evt); } @@ -73,16 +85,41 @@ private void fireStateChangedEvent(SecurityGUIEvent evt) { } } + + /** + * Set the type of this session. + * + * @param type + */ + public void setType(String type) { + sessionType = type; + } + + /** + * Set the DH session flag. + * + * @param yesNo + */ + public void setDHSession(boolean yesNo) { + dhSession = yesNo; + } + + /* + * The following methods implement the ZrtpUserCallback interface + */ + /* * (non-Javadoc) * @see gnu.java.zrtp.ZrtpUserCallback#secureOn(java.lang.String) */ public void secureOn(String cipher) { - logger.info("Cipher: " + cipher); + if (logger.isInfoEnabled()) + logger.info(sessionType + ": cipher enabled: " + cipher); + HashMap state = new HashMap(3); - state.put(SecurityGUIEventZrtp.SESSION_TYPE, SecurityGUIEventZrtp.AUDIO); + state.put(SecurityGUIEventZrtp.SESSION_TYPE, sessionType); state.put(SecurityGUIEventZrtp.SECURITY_CHANGE, Boolean.TRUE); state.put(SecurityGUIEventZrtp.CIPHER, cipher); @@ -96,7 +133,9 @@ public void secureOn(String cipher) */ public void showSAS(String sas, boolean verified) { - logger.info("SAS: " + sas); + if (logger.isInfoEnabled()) + logger.info(sessionType + ": SAS is: " + sas); + HashMap state = new HashMap(3); state.put(SecurityGUIEventZrtp.SESSION_TYPE, SecurityGUIEventZrtp.AUDIO); @@ -118,11 +157,33 @@ public void showSAS(String sas, boolean verified) * gnu.java.zrtp.ZrtpCodes.MessageSeverity, * java.util.EnumSet) */ - public void showMessage(ZrtpCodes.MessageSeverity sev, EnumSet subCode) - { + public void showMessage(ZrtpCodes.MessageSeverity sev, EnumSet subCode) { + + ZrtpCodes.InfoCodes inf; + int multiStreams = 0; + Iterator ii = subCode.iterator(); Object msgCode = ii.next(); - logger.info("Show message sub code: " + msgCode); + + if (sev == ZrtpCodes.MessageSeverity.Info) { + if (msgCode instanceof ZrtpCodes.InfoCodes) { + inf = (ZrtpCodes.InfoCodes) msgCode; + + // If the ZRTP Master session (DH mode) signals "security on" + // then start multi-stream sessions. + if (dhSession && inf == ZrtpCodes.InfoCodes.InfoSecureStateOn) { + multiStreams = ((CallSessionImpl) callSession) + .startZrtpMultiStreams(); + } + } + } + + if (logger.isInfoEnabled()) { + logger.info(sessionType + ": " + "ZRTP message: severity: " + sev + + ", sub code: " + msgCode + ", DH session: " + dhSession + + ", multi: " + multiStreams); + } + } /* @@ -136,7 +197,9 @@ public void zrtpNegotiationFailed(ZrtpCodes.MessageSeverity severity, { Iterator ii = subCode.iterator(); Object msgCode = ii.next(); - logger.warn("Negotiation failed sub code: " + msgCode); + + if (logger.isInfoEnabled()) + logger.warn(sessionType + ": ZRTP key negotiation failed, sub code: " + msgCode); } /* @@ -145,7 +208,8 @@ public void zrtpNegotiationFailed(ZrtpCodes.MessageSeverity severity, */ public void secureOff() { - logger.info("Security off"); + if (logger.isInfoEnabled()) + logger.info(sessionType + ": Security off"); HashMap state = new HashMap(2); @@ -162,7 +226,8 @@ public void secureOff() */ public void zrtpNotSuppOther() { - logger.info("ZRTP not supported"); + if (logger.isInfoEnabled()) + logger.info(sessionType + ": Other party does not support ZRTP key negotiation protocol, no secure calls possible"); } /* @@ -171,6 +236,7 @@ public void zrtpNotSuppOther() */ public void confirmGoClear() { - logger.info("GoClear confirmation requested"); + if (logger.isInfoEnabled()) + logger.info(sessionType + ": GoClear confirmation requested"); } } diff --git a/src/net/java/sip/communicator/impl/media/transform/zrtp/ZRTPTransformEngine.java b/src/net/java/sip/communicator/impl/media/transform/zrtp/ZRTPTransformEngine.java index 3dbddb035..2af2ea57b 100644 --- a/src/net/java/sip/communicator/impl/media/transform/zrtp/ZRTPTransformEngine.java +++ b/src/net/java/sip/communicator/impl/media/transform/zrtp/ZRTPTransformEngine.java @@ -362,7 +362,7 @@ public PacketTransformer getRTPTransformer() /** * Default engine initialization method. - * Calling this for engine initialization starts it with auto-sensing. + * Calling this for engine initialization and start it with auto-sensing. * * @param zidFilename The ZID file name * @return true if initialization fails, false if succeeds @@ -445,7 +445,6 @@ public synchronized boolean initialize(String zidFilename, if (zf.open(zidFilename) < 0) { - enableZrtp = false; return false; } } @@ -459,6 +458,7 @@ public synchronized boolean initialize(String zidFilename, { // TODO Auto-generated catch block e.printStackTrace(); + return false; } userCallback.init();