/* * 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.transform.zrtp; import gnu.java.zrtp.*; import java.util.*; import net.java.sip.communicator.impl.media.*; import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; /** * The user callback class for ZRTP4J. * * This class constructs and sends events to the ZRTP GUI implementation. The * <code>showMessage()<code> function implements a specific check to start * associated ZRTP multi-stream sessions. * * Coordinate this callback class with the associated GUI implementation class * net.java.sip.communicator.impl.gui.main.call.ZrtpPanel * * @see net.java.sip.communicator.impl.gui.main.call.SecurityPanel * * @author Emanuel Onica * @author Werner Dittmann * @author Yana Stamcheva */ public class SecurityEventManager extends ZrtpUserCallback { private static final Logger logger = Logger.getLogger(SecurityEventManager.class); public static final String WARNING_NO_RS_MATCH = "impl.media.security.WARNING_NO_RS_MATCH"; public static final String WARNING_NO_EXPECTED_RS_MATCH = "impl.media.security.WARNING_NO_EXPECTED_RS_MATCH"; private CallParticipant callParticipant; private final CallSession callSession; /** * Is this a ZRTP DH (Master) session? */ private boolean isDHSession = false; /** * Type of session */ private int sessionType; /** * SAS string. */ private String sas; /** * Cipher. */ private String cipher; /** * Indicates if the SAS has already been verified in a previous session. */ private boolean isSasVerified; /** * The class constructor. */ public SecurityEventManager(CallSession callSession) { this.callSession = callSession; // At this moment we're supporting a security call between only two // participants. In the future the call participant would be passed // as a parameter to the SecurityEventManager. Iterator<CallParticipant> callParticipants = callSession.getCall().getCallParticipants(); while (callParticipants.hasNext()) { this.callParticipant = callParticipants.next(); } } /** * Set the type of this session. * * @param type the session type. The session type could be either * CallSessionImpl.AUDIO_SESSION or CallSessionImpl.VIDEO_SESSION. */ public void setSessionType(String type) { if (type.equals(CallSessionImpl.AUDIO_SESSION)) sessionType = CallParticipantSecurityStatusEvent.AUDIO_SESSION; else if (type.equals(CallSessionImpl.VIDEO_SESSION)) sessionType = CallParticipantSecurityStatusEvent.VIDEO_SESSION; } /** * Set the DH session flag. * * @param isDHSession the DH session flag. */ public void setDHSession(boolean isDHSession) { this.isDHSession = isDHSession; } /* * The following methods implement the ZrtpUserCallback interface */ /** * Reports the security algorithm that the ZRTP protocol negotiated. * * @see gnu.java.zrtp.ZrtpUserCallback#secureOn(java.lang.String) */ public void secureOn(String cipher) { if (logger.isInfoEnabled()) logger.info(sessionTypeToString(sessionType) + ": cipher enabled: " + cipher); this.cipher = cipher; } /** * ZRTP computes the SAS string after nearly all the negotiation * and computations are done internally. * * @see gnu.java.zrtp.ZrtpUserCallback#showSAS(java.lang.String, boolean) */ public void showSAS(String sas, boolean isVerified) { if (logger.isInfoEnabled()) logger.info(sessionTypeToString(sessionType) + ": SAS is: " + sas); this.sas = sas; this.isSasVerified = isVerified; } /** * @see gnu.java.zrtp.ZrtpUserCallback#showMessage( * gnu.java.zrtp.ZrtpCodes.MessageSeverity, java.util.EnumSet) */ public void showMessage(ZrtpCodes.MessageSeverity sev, EnumSet<?> subCode) { int multiStreams = 0; Iterator<?> ii = subCode.iterator(); Object msgCode = ii.next(); String messageType = null; String message = null; String i18nMessage = null; if (msgCode instanceof ZrtpCodes.InfoCodes) { ZrtpCodes.InfoCodes inf = (ZrtpCodes.InfoCodes) msgCode; // If the ZRTP Master session (DH mode) signals "security on" // then start multi-stream sessions. // Signal SAS to GUI only if this is a DH mode session. // Multi-stream session don't have own SAS data if (inf == ZrtpCodes.InfoCodes.InfoSecureStateOn) { if (isDHSession) { multiStreams = ((CallSessionImpl) callSession) .startZrtpMultiStreams(); ((AbstractCallParticipant) callParticipant).setSecurityOn(true, sessionType, cipher, sas, isSasVerified); } else { ((AbstractCallParticipant) callParticipant).setSecurityOn(true, sessionType, cipher, null, false); } } } else if (msgCode instanceof ZrtpCodes.WarningCodes) { // Warning codes usually do not affect encryption or security. Only // in few cases inform the user and ask to verify SAS. ZrtpCodes.WarningCodes warn = (ZrtpCodes.WarningCodes) msgCode; if (warn == ZrtpCodes.WarningCodes.WarningNoRSMatch) { messageType = CallParticipantSecurityMessageEvent .SECURITY_AUTHENTICATION_REQUIRED; message = "No retained shared secret available."; i18nMessage = WARNING_NO_RS_MATCH; } else if (warn == ZrtpCodes.WarningCodes.WarningNoExpectedRSMatch) { messageType = CallParticipantSecurityMessageEvent .RETAINED_SECURITY_AUTHENTICATION_FAILED; message = "An expected retained shared secret is missing."; i18nMessage = WARNING_NO_EXPECTED_RS_MATCH; } else if (warn == ZrtpCodes.WarningCodes.WarningCRCmismatch) { messageType = CallParticipantSecurityMessageEvent .CHECKSUM_MISMATCH; message = "Internal ZRTP packet checksum mismatch."; i18nMessage = "impl.media.security.CHECKSUM_MISMATCH"; } } else if (msgCode instanceof ZrtpCodes.SevereCodes) { ZrtpCodes.SevereCodes severe = (ZrtpCodes.SevereCodes) msgCode; if (severe == ZrtpCodes.SevereCodes.SevereCannotSend) { messageType = CallParticipantSecurityMessageEvent .DATA_SEND_FAILED; message = "Failed to send data." + "Internet data connection or peer is down."; i18nMessage = "impl.media.security.DATA_SEND_FAILED"; } else if (severe == ZrtpCodes.SevereCodes.SevereTooMuchRetries) { messageType = CallParticipantSecurityMessageEvent .RETRY_RATE_EXCEEDED; message = "Too much retries during ZRTP negotiation."; i18nMessage = "impl.media.security.RETRY_RATE_EXCEEDED"; } else if (severe == ZrtpCodes.SevereCodes.SevereProtocolError) { messageType = CallParticipantSecurityMessageEvent .INTERNAL_PROTOCOL_ERROR; message = "Internal protocol error occured."; i18nMessage = "impl.media.security.INTERNAL_PROTOCOL_ERROR"; } else { messageType = CallParticipantSecurityMessageEvent.GENERAL_ERROR; message = "General error has occurred."; i18nMessage = "impl.media.security.ZRTP_GENERIC_MSG"; } } else if (msgCode instanceof ZrtpCodes.ZrtpErrorCodes) { messageType = CallParticipantSecurityMessageEvent.NOT_COMPATIBLE; message = "Indicates compatibility problems like for example:" + "unsupported protocol version, unsupported hash type," + "cypher type, SAS scheme, etc."; i18nMessage = "impl.media.security.ZRTP_GENERIC_MSG"; } if (messageType != null) ((AbstractCallParticipant) callParticipant) .setSecurityMessage(messageType, message, i18nMessage); if (logger.isInfoEnabled()) { logger.info(sessionTypeToString(sessionType) + ": " + "ZRTP message: severity: " + sev + ", sub code: " + msgCode + ", DH session: " + isDHSession + ", multi: " + multiStreams); } } /** * @see gnu.java.zrtp.ZrtpUserCallback#zrtpNegotiationFailed( * gnu.java.zrtp.ZrtpCodes.MessageSeverity, java.util.EnumSet) */ public void zrtpNegotiationFailed( ZrtpCodes.MessageSeverity severity, EnumSet<?> subCode) { Iterator<?> ii = subCode.iterator(); Object msgCode = ii.next(); if (logger.isInfoEnabled()) logger.info(sessionTypeToString(sessionType) + ": ZRTP key negotiation failed, sub code: " + msgCode); } /** * @see gnu.java.zrtp.ZrtpUserCallback#secureOff() */ public void secureOff() { if (logger.isInfoEnabled()) logger.info(sessionTypeToString(sessionType) + ": Security off"); ((AbstractCallParticipant) callParticipant) .setSecurityOff(sessionType); } /** * @see gnu.java.zrtp.ZrtpUserCallback#zrtpNotSuppOther() */ public void zrtpNotSuppOther() { if (logger.isInfoEnabled()) logger .info(sessionTypeToString(sessionType) + ": Other party does not support ZRTP key negotiation protocol," + " no secure calls possible."); } /** * @see gnu.java.zrtp.ZrtpUserCallback#confirmGoClear() */ public void confirmGoClear() { if (logger.isInfoEnabled()) logger.info(sessionTypeToString(sessionType) + ": GoClear confirmation requested."); } private String sessionTypeToString(int sessionType) { switch (sessionType) { case CallParticipantSecurityStatusEvent.AUDIO_SESSION: return "AUDIO_SESSION"; case CallParticipantSecurityStatusEvent.VIDEO_SESSION: return "VIDEO_SESSION"; default: throw new IllegalArgumentException("sessionType"); } } }