[jitsi-dev] Re: [jitsi~svn:8560] If ZRTP engine see that RTP packet SSRC has changed and if the packet is


#1

Hi all,

I just saw this on the commit mailing list.

I just browsed the modifications and I'm not yet sure about the security implications.

IMHO this could introduce some serious security risk:
- SRTP will not be active for a different remote SSRC anymore. IIRC the RTP RFC
  says that one RTP sessions shall use one SSRC only and it is a bad
  practice to multiplex several SSRC streams on one RTP session.

ZRTP and SRTP were desinged with this in mind, AFAIK.

Thus simply returning the packets that contain different SSRCs would allow
a bad guy (Mallory) to feed in packets which will then processed normaly by Jitsi.
Thus Mallory can massively disturb the content. Also because of this the user
does not get any warnings - feeding packets with different SSRC on a otherwise
secure RTP session will go unnoticed.

My proposal here:
Stay to the rule "one RTP session - one SSRC" in particular if this RTP session
uses SRTP.

NB: if Jitsi requires SSRC multiplexing then we probably need to introduce an
additional SRTP Crypto Context management the can hold several Crypto Contexts
indexed by their respective SSRC - however, this is no supported by the ZRTP
implementation.

Best regards,
Werner

···

Am 05.05.2011 18:21, schrieb s_vincent@java.net:

Project: jitsi
Repository: svn
Revision: 8560
Author: s_vincent
Date: 2011-05-05 16:21:40 UTC
Link:

Log Message:
------------
If ZRTP engine see that RTP packet SSRC has changed and if the packet is not encrypted, it returns the packet as is. Disable also the muted mode of ZRTP which drop unencrypted packets.

Revisions:
----------
8560

Modified Paths:
---------------
trunk/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java
trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformer.java
trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPCryptoContext.java
trunk/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
trunk/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java

Diffs:
------
Index: trunk/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java

--- trunk/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java (revision 8559)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java (revision 8560)
@@ -1109,7 +1109,11 @@
          * for us to hear. So we mute it till a secure connection is again
          * established.
          */
- zrtpControl.getZrtpEngine().setStartMuted(true);
+ // disable it because it can cause problems when switching from camera
+ // to desktop sharing. Moreover if ZRTP see unencrypted packets, it will
+ // not try to decrypt them (maybe the noise come from the decryption of
+ // unencrypted data).
+ //zrtpControl.getZrtpEngine().setStartMuted(true);

         RTPTransformConnector rtpConnector = getRTPConnector();

Index: trunk/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java

--- trunk/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java (revision 8559)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java (revision 8560)
@@ -40,7 +40,7 @@
  * RTP/SRTP stack and the operating system:
  * <ul>
  * <li> The GNU ZRTP core is independent of a specific RTP/SRTP stack and the
- * operationg system and consists of the ZRTP protocol state engine, the ZRTP
+ * operating system and consists of the ZRTP protocol state engine, the ZRTP
  * protocol messages, and the GNU ZRTP4J engine. The GNU ZRTP4J engine provides
  * methods to setup ZRTP message and to analyze received ZRTP messages, to
  * compute the crypto data required for SRTP, and to maintain the required
@@ -328,6 +328,11 @@
     private int ownSSRC = 0;

     /**
+ * Remote SSRC identifier.
+ */
+ private int remoteSSRC = 0;
+
+ /**
      * ZRTP packet sequence number
      */
     private short senderZrtpSeqNo = 0;
@@ -408,7 +413,8 @@
      * @param autoEnable If true start with auto-sensing mode.
      * @return true if initialization fails, false if succeeds
      */
- public boolean initialize(String zidFilename, boolean autoEnable) {
+ public boolean initialize(String zidFilename, boolean autoEnable)
+ {
         return initialize(zidFilename, autoEnable, null);
     }

@@ -421,7 +427,8 @@
      * @param zidFilename The ZID file name
      * @return true if initialization fails, false if succeeds
      */
- public boolean initialize(String zidFilename) {
+ public boolean initialize(String zidFilename)
+ {
         return initialize(zidFilename, true, null);
     }

@@ -495,7 +502,8 @@
                 return false;
             }
         }
- if (config == null) {
+ if (config == null)
+ {
             config = new ZrtpConfigure();
             config.setStandardConfig();
         }
@@ -590,7 +598,8 @@
      *
      * @param ssrc SSRC to set
      */
- public void setOwnSSRC(long ssrc) {
+ public void setOwnSSRC(long ssrc)
+ {
         ownSSRC = (int)(ssrc & 0xffffffff);
     }

@@ -638,8 +647,13 @@
         if (!started && enableZrtp && ownSSRC != 0)
             startZrtp();

+ if(remoteSSRC == 0)
+ {
+ remoteSSRC = pkt.getSSRC();
+ }
+
         /*
- * Check if incoming packt is a ZRTP packet, if not treat
+ * Check if incoming packet is a ZRTP packet, if not treat
          * it as normal RTP packet and handle it accordingly.
          */
         if (!ZrtpRawPacket.isZrtpData(pkt))
@@ -647,23 +661,32 @@
             if (srtpInTransformer == null)
             {
                 if(muted)
+ {
                     return null;
+ }
                 else
                     return pkt;
             }

- pkt = srtpInTransformer.reverseTransform(pkt);
+ RawPacket pkt2 = srtpInTransformer.reverseTransform(pkt);
             // if packet was valid (i.e. not null) and ZRTP engine started and
             // in Wait for Confirm2 Ack then emulate a Conf2Ack packet.
             // See ZRTP specification chap. 5.6
- if ((pkt != null)
+ if ((pkt2 != null)
                     && started
                     && zrtpEngine
                         .inState(ZrtpStateClass.ZrtpStates.WaitConfAck))
             {
                 zrtpEngine.conf2AckSecure();
             }
- return pkt;
+
+ // if we change SSRC, returns the original packet
+ if(remoteSSRC != pkt.getSSRC())
+ {
+ return pkt;
+ }
+
+ return pkt2;
         }

         /*
@@ -753,19 +776,19 @@
         SRTPPolicy srtpPolicy = null;
         int cipher = 0, authn = 0, authKeyLen = 0;

- if (secrets.getAuthAlgorithm() == ZrtpConstants.SupportedAuthAlgos.HS)
+ if (secrets.getAuthAlgorithm() == ZrtpConstants.SupportedAuthAlgos.HS)
         {
             authn = SRTPPolicy.HMACSHA1_AUTHENTICATION;
             authKeyLen = 20;
         }
- if (secrets.getAuthAlgorithm() == ZrtpConstants.SupportedAuthAlgos.SK)
+ if (secrets.getAuthAlgorithm() == ZrtpConstants.SupportedAuthAlgos.SK)
         {
             authn = SRTPPolicy.SKEIN_AUTHENTICATION;
             authKeyLen = 32;
         }
         if (secrets.getSymEncAlgorithm() == ZrtpConstants.SupportedSymAlgos.AES)
             cipher = SRTPPolicy.AESCM_ENCRYPTION;
-
+
         if (secrets.getSymEncAlgorithm() == ZrtpConstants.SupportedSymAlgos.TwoFish)
             cipher = SRTPPolicy.TWOFISH_ENCRYPTION;

Index: trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformer.java

--- trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformer.java (revision 8559)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformer.java (revision 8560)
@@ -3,9 +3,9 @@
  *
  * Distributable under LGPL license.
  * See terms of license at gnu.org.
- *
+ *
  * Some of the code in this class is derived from ccRtp's SRTP implementation,
- * which has the following copyright notice:
+ * which has the following copyright notice:
  *
   Copyright (C) 2004-2006 the Minisip Team

@@ -34,11 +34,11 @@
  * SRTPTransformer implements PacketTransformer and provides implementations
  * for RTP packet to SRTP packet transformation and SRTP packet to RTP packet
  * transformation logic.
- *
+ *
  * It will first find the corresponding SRTPCryptoContext for each packet based
  * on their SSRC and then invoke the context object to perform the
- * transformation and reverse transformation operation.
- *
+ * transformation and reverse transformation operation.
+ *
  * @author Bing SU (nova.su@gmail.com)
  */
public class SRTPTransformer
@@ -48,7 +48,7 @@
      * The SRTPTransformEngine we are using
      */
     private SRTPTransformEngine engine;
-
+
     /**
      * All the known SSRC's corresponding SRTPCryptoContexts
      */
@@ -76,7 +76,7 @@
         long ssrc = pkt.getSSRC();

         SRTPCryptoContext context = this.contexts.get(ssrc);
-
+
         if (context == null)
         {
             context = this.engine.getDefaultContext().deriveContext(ssrc, 0, 0);
@@ -86,7 +86,7 @@
                     this.contexts.put(ssrc, context);
                 }
         }
-
+
         if (context != null)
         {
             context.transformPacket(pkt);
@@ -107,11 +107,11 @@
         long ssrc = pkt.getSSRC();
         int seqNum = pkt.getSequenceNumber();
         SRTPCryptoContext context = this.contexts.get(ssrc);
-
+
         if (context == null)
         {
             context = this.engine.getDefaultContext().deriveContext(ssrc, 0, 0);
-
+
             if (context != null)
             {
                 context.deriveSrtpKeys(seqNum);
@@ -122,7 +122,7 @@
         if (context != null)
         {
             boolean validPacket = context.reverseTransformPacket(pkt);
-
+
             if (!validPacket)
             {
                 return null;
@@ -134,10 +134,10 @@

     /**
      * Getter to use in derived classes.
- *
+ *
      * @return the engine
      */
- public SRTPTransformEngine getEngine()
+ public SRTPTransformEngine getEngine()
     {
         return engine;
     }
Index: trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPCryptoContext.java

--- trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPCryptoContext.java (revision 8559)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPCryptoContext.java (revision 8560)
@@ -249,7 +249,8 @@

         mac = new HMac(new SHA1Digest());

- switch (policy.getEncType()) {
+ switch (policy.getEncType())
+ {
         case SRTPPolicy.NULL_ENCRYPTION:
             encKey = null;
             saltKey = null;
@@ -260,7 +261,7 @@
             encKey = new byte[policy.getEncKeyLength()];
             saltKey = new byte[policy.getSaltKeyLength()];
             //$FALL-THROUGH$
-
+
         case SRTPPolicy.AESF8_ENCRYPTION:
             cipherF8 = new AESFastEngine();
             break;
@@ -269,13 +270,14 @@
             cipher = new TwofishEngine();
             encKey = new byte[this.policy.getEncKeyLength()];
             saltKey = new byte[this.policy.getSaltKeyLength()];
-
+
         case SRTPPolicy.TWOFISHF8_ENCRYPTION:
             cipherF8 = new TwofishEngine();
             break;
         }

- switch (policy.getAuthType()) {
+ switch (policy.getAuthType())
+ {
         case SRTPPolicy.NULL_AUTHENTICATION:
             authKey = null;
             tagStore = null;
@@ -286,7 +288,7 @@
             authKey = new byte[policy.getAuthKeyLength()];
             tagStore = new byte[mac.getMacSize()];
             break;
-
+
         case SRTPPolicy.SKEIN_AUTHENTICATION:
             mac = new SkeinMac();
             authKey = new byte[policy.getAuthKeyLength()];
@@ -303,7 +305,8 @@
      *
      * @return the authentication tag length of this SRTP cryptographic context
      */
- public int getAuthTagLength() {
+ public int getAuthTagLength()
+ {
         return policy.getAuthTagLength();
     }

@@ -312,10 +315,14 @@
      *
      * @return the MKI length of this SRTP cryptographic context
      */
- public int getMKILength() {
- if (mki != null) {
+ public int getMKILength()
+ {
+ if (mki != null)
+ {
             return mki.length;
- } else {
+ }
+ else
+ {
             return 0;
         }
     }
@@ -325,7 +332,8 @@
      *
      * @return the SSRC of this SRTP cryptographic context
      */
- public long getSSRC() {
+ public long getSSRC()
+ {
         return ssrc;
     }

@@ -334,7 +342,8 @@
      *
      * @return the Roll-Over-Counter of this SRTP cryptographic context
      */
- public int getROC() {
+ public int getROC()
+ {
         return roc;
     }

@@ -343,7 +352,8 @@
      *
      * @param rocIn the Roll-Over-Counter of this SRTP cryptographic context
      */
- public void setROC(int rocIn) {
+ public void setROC(int rocIn)
+ {
         roc = rocIn;
     }

@@ -367,7 +377,7 @@
     public void transformPacket(RawPacket pkt)
     {
         /* Encrypt the packet using Counter Mode encryption */
- if (policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION ||
+ if (policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION ||
                 policy.getEncType() == SRTPPolicy.TWOFISH_ENCRYPTION)
         {
             processPacketAESCM(pkt);
@@ -416,7 +426,8 @@
     {
         int seqNo = pkt.getSequenceNumber();

- if (!seqNumSet) {
+ if (!seqNumSet)
+ {
             seqNumSet = true;
             seqNum = seqNo;
         }
@@ -425,11 +436,13 @@
         long guessedIndex = guessIndex(seqNo);

         /* Replay control */
- if (!checkReplay(seqNo, guessedIndex)) {
+ if (!checkReplay(seqNo, guessedIndex))
+ {
             return false;
         }
         /* Authenticate the packet */
- if (policy.getAuthType() != SRTPPolicy.NULL_AUTHENTICATION) {
+ if (policy.getAuthType() != SRTPPolicy.NULL_AUTHENTICATION)
+ {
             int tagLength = policy.getAuthTagLength();

             // get original authentication and store in tempStore
@@ -441,11 +454,14 @@
             // save computed authentication in tagStore
             authenticatePacketHMCSHA1(pkt, guessedROC);

- for (int i = 0; i < tagLength; i++) {
+ for (int i = 0; i < tagLength; i++)
+ {
                 if ((tempStore[i]&0xff) == (tagStore[i]&0xff))
                     continue;
                 else
+ {
                     return false;
+ }
             }
         }

@@ -457,7 +473,8 @@

         /* Decrypt the packet using F8 Mode encryption*/
         else if (policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION ||
- policy.getEncType() == SRTPPolicy.TWOFISHF8_ENCRYPTION) {
+ policy.getEncType() == SRTPPolicy.TWOFISHF8_ENCRYPTION)
+ {
             processPacketAESF8(pkt);
         }

@@ -470,7 +487,8 @@
      * Perform Counter Mode AES encryption / decryption
      * @param pkt the RTP packet to be encrypted / decrypted
      */
- public void processPacketAESCM(RawPacket pkt) {
+ public void processPacketAESCM(RawPacket pkt)
+ {
         long ssrc = pkt.getSSRC();
         int seqNo = pkt.getSequenceNumber();
         long index = ((long) this.roc << 16) | seqNo;
@@ -482,11 +500,13 @@
         ivStore[3] = saltKey[3];

         int i;
- for (i = 4; i < 8; i++) {
+ for (i = 4; i < 8; i++)
+ {
             ivStore[i] = (byte) ((0xFF & (ssrc >> ((7 - i) * 8))) ^ this.saltKey[i]);
         }

- for (i = 8; i < 14; i++) {
+ for (i = 8; i < 14; i++)
+ {
             ivStore[i] = (byte) ((0xFF & (byte) (index >> ((13 - i) * 8))) ^ this.saltKey[i]);
         }

@@ -495,8 +515,8 @@
         final int payloadOffset = pkt.getHeaderLength();
         final int payloadLength = pkt.getPayloadLength();

- cipherCtr.process(cipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
- payloadLength, ivStore);
+ cipherCtr.process(cipher, pkt.getBuffer(), pkt.getOffset() +
+ payloadOffset, payloadLength, ivStore);
     }

     /**
@@ -504,7 +524,8 @@
      *
      * @param pkt the RTP packet to be encrypted / decrypted
      */
- public void processPacketAESF8(RawPacket pkt) {
+ public void processPacketAESF8(RawPacket pkt)
+ {
         // byte[] iv = new byte[16];

         // 11 bytes of the RTP header are the 11 bytes of the iv
@@ -521,8 +542,9 @@
         final int payloadOffset = pkt.getHeaderLength();
         final int payloadLength = pkt.getPayloadLength();

- SRTPCipherF8.process(cipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
- payloadLength, ivStore, encKey, saltKey, cipherF8);
+ SRTPCipherF8.process(cipher, pkt.getBuffer(), pkt.getOffset() +
+ payloadOffset, payloadLength, ivStore, encKey, saltKey,
+ cipherF8);
     }

     /**
@@ -532,7 +554,7 @@
      * @param pkt the RTP packet to be authenticated
      * @param rocIn Roll-Over-Counter
      */
- private void authenticatePacketHMCSHA1(RawPacket pkt, int rocIn)
+ private void authenticatePacketHMCSHA1(RawPacket pkt, int rocIn)
     {
         mac.update(pkt.getBuffer(), pkt.getOffset(), pkt.getLength());
         // byte[] rb = new byte[4];
@@ -565,18 +587,26 @@
         long localIndex = (((long) this.roc) << 16) | this.seqNum;
         long delta = guessedIndex - localIndex;

- if (delta > 0) {
+ if (delta > 0)
+ {
             /* Packet not yet received */
             return true;
- } else {
+ }
+ else
+ {
             if (-delta > REPLAY_WINDOW_SIZE) {
                 /* Packet too old */
                 return false;
- } else {
- if (((this.replayWindow >> (-delta)) & 0x1) != 0) {
+ }
+ else
+ {
+ if (((this.replayWindow >> (-delta)) & 0x1) != 0)
+ {
                     /* Packet already received ! */
                     return false;
- } else {
+ }
+ else
+ {
                     /* Packet not yet received */
                     return true;
                 }
@@ -596,15 +626,20 @@
     {
         long key_id;

- if (keyDerivationRate == 0) {
+ if (keyDerivationRate == 0)
+ {
             key_id = label << 48;
- } else {
+ }
+ else
+ {
             key_id = ((label << 48) | (index / keyDerivationRate));
         }
- for (int i = 0; i < 7; i++) {
+ for (int i = 0; i < 7; i++)
+ {
             ivStore[i] = masterSalt[i];
         }
- for (int i = 7; i < 14; i++) {
+ for (int i = 7; i < 14; i++)
+ {
             ivStore[i] = (byte) ((byte) (0xFF & (key_id >> (8 * (13 - i)))) ^ masterSalt[i]);
         }
         ivStore[14] = ivStore[15] = 0;
@@ -616,22 +651,27 @@
      * @param index
      * the 48 bit SRTP packet index
      */
- public void deriveSrtpKeys(long index) {
+ public void deriveSrtpKeys(long index)
+ {
         // compute the session encryption key
         long label = 0;
         computeIv(label, index);

         KeyParameter encryptionKey = new KeyParameter(masterKey);
         cipher.init(true, encryptionKey);
- cipherCtr.getCipherStream(cipher, encKey, policy.getEncKeyLength(), ivStore);
+ cipherCtr.getCipherStream(cipher, encKey, policy.getEncKeyLength(),
+ ivStore);

         // compute the session authentication key
- if (authKey != null) {
+ if (authKey != null)
+ {
             label = 0x01;
             computeIv(label, index);
- cipherCtr.getCipherStream(cipher, authKey, policy.getAuthKeyLength(), ivStore);
+ cipherCtr.getCipherStream(cipher, authKey,
+ policy.getAuthKeyLength(), ivStore);

- switch ((policy.getAuthType())) {
+ switch ((policy.getAuthType()))
+ {
             case SRTPPolicy.HMACSHA1_AUTHENTICATION:
                 KeyParameter key = new KeyParameter(authKey);
                 mac.init(key);
@@ -639,7 +679,8 @@

             case SRTPPolicy.SKEIN_AUTHENTICATION:
                 // Skein MAC uses number of bits as MAC size, not just bytes
- ParametersForSkein pfs = new ParametersForSkein(new KeyParameter(authKey),
+ ParametersForSkein pfs = new ParametersForSkein(
+ new KeyParameter(authKey),
                         ParametersForSkein.Skein512, tagStore.length*8);
                 mac.init(pfs);
                 break;
@@ -648,7 +689,8 @@
         // compute the session salt
         label = 0x02;
         computeIv(label, index);
- cipherCtr.getCipherStream(cipher, saltKey, policy.getSaltKeyLength(), ivStore);
+ cipherCtr.getCipherStream(cipher, saltKey, policy.getSaltKeyLength(),
+ ivStore);

         // As last step: initialize cipher with derived encryption key.
         encryptionKey = new KeyParameter(encKey);
@@ -664,16 +706,25 @@
      */
     private long guessIndex(int seqNo)
     {
- if (this.seqNum < 32768) {
- if (seqNo - this.seqNum > 32768) {
+ if (this.seqNum < 32768)
+ {
+ if (seqNo - this.seqNum > 32768)
+ {
                 guessedROC = roc - 1;
- } else {
+ }
+ else
+ {
                 guessedROC = roc;
             }
- } else {
- if (seqNum - 32768 > seqNo) {
+ }
+ else
+ {
+ if (seqNum - 32768 > seqNo)
+ {
                 guessedROC = roc + 1;
- } else {
+ }
+ else
+ {
                 guessedROC = roc;
             }
         }
@@ -695,18 +746,22 @@
         long delta = guessedIndex - (((long) this.roc) << 16 | this.seqNum);

         /* update the replay bit mask */
- if( delta > 0 ){
+ if( delta > 0 )
+ {
           replayWindow = replayWindow << delta;
           replayWindow |= 1;
         }
- else {
+ else
+ {
           replayWindow |= ( 1 << delta );
         }

- if (seqNo > seqNum) {
+ if (seqNo > seqNum)
+ {
             seqNum = seqNo & 0xffff;
         }
- if (this.guessedROC > this.roc) {
+ if (this.guessedROC > this.roc)
+ {
             roc = guessedROC;
             seqNum = seqNo & 0xffff;
         }
@@ -732,7 +787,8 @@
      * The key derivation rate for this context
      * @return a new SRTPCryptoContext with all relevant data set.
      */
- public SRTPCryptoContext deriveContext(long ssrc, int roc, long deriveRate) {
+ public SRTPCryptoContext deriveContext(long ssrc, int roc, long deriveRate)
+ {
         SRTPCryptoContext pcc = null;
         pcc = new SRTPCryptoContext(ssrc, roc, deriveRate, masterKey,
                 masterSalt, policy);
Index: trunk/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java

--- trunk/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java (revision 8559)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java (revision 8560)
@@ -202,8 +202,8 @@
     /**
      * 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.
+ * After the ZRTP Master (DH) session reached secure state the SCCallback
+ * calls this method to start the multi-stream ZRTP sessions.
      *
      * enable auto-start mode (auto-sensing) to the engine.
      * @param multiStreamData


#2

Hi Werner,

Thank you for the feedback. I will try to explain a use-case which causes problem to us.

A and B: two registraless SIP account with ZRTP enabled on same LAN.

- A do a videocall to B
- B enable video
- A switch to desktop streaming
     -> A send an RTP Bye
     -> A begins to send new RTP packet with different SSRC, sequence number and reset timestamp (because it is a new video stream) on the same RTP port as previously.
- B receives the BYE but during its processing (by JMF) B receive also first new unencrypted packets which will be dropped because the current SRTPCryptoContext cannot authenticate them.
- Finally our code process the ByeEvent which restart ZRTP (restartZrtpControl() in MediaStreamImpl)
- The next RTP video packets can now be transmitted to JMF for processing
     -> but as we miss the first packets of the video keyframe (or the entire keyframe), image is not displayed immediately and B can wait 20 or more seconds to have something displayed on its screen.

Of course if both peers has disable ZRTP, this problem does not appears.

What do you think ?

Best regards,

···

--
Seb

Le 05/05/11 21:28, Werner Dittmann a écrit :

Hi all,

I just saw this on the commit mailing list.

I just browsed the modifications and I'm not yet sure about the security implications.

IMHO this could introduce some serious security risk:
- SRTP will not be active for a different remote SSRC anymore. IIRC the RTP RFC
   says that one RTP sessions shall use one SSRC only and it is a bad
   practice to multiplex several SSRC streams on one RTP session.

ZRTP and SRTP were desinged with this in mind, AFAIK.

Thus simply returning the packets that contain different SSRCs would allow
a bad guy (Mallory) to feed in packets which will then processed normaly by Jitsi.
Thus Mallory can massively disturb the content. Also because of this the user
does not get any warnings - feeding packets with different SSRC on a otherwise
secure RTP session will go unnoticed.

My proposal here:
Stay to the rule "one RTP session - one SSRC" in particular if this RTP session
uses SRTP.

NB: if Jitsi requires SSRC multiplexing then we probably need to introduce an
additional SRTP Crypto Context management the can hold several Crypto Contexts
indexed by their respective SSRC - however, this is no supported by the ZRTP
implementation.

Best regards,
Werner


#3

Hi Seb,

just a thought:
- why does A send an RTP bye (via RTCP) or do you mean a SIP bye here?

- if you switch the RTP parameters, for example to use a different codec
  or somthing like that, then Jitsi should perform a SIP Re-Invite similar to
  the processing that is done when adding a video stream to an existing
  audio stream. In this case A should not send RTP packets before the re-invite
  was processed.

Or do I miss something important here?

In any case: "silently" accepting and processing RTP packets with different/new SSRC
opens up a security hole. A bad guy can feed RTP data and may communicate with
either A or B, or both. If the bad guy uses a different SSRC then this attack goes
unnotified because Jitsi happily processes RTP packets with every SSRC that it
receives on the RTP connection (if I interpret the patch correctly).

Best regards,
Werner

···

Am 06.05.2011 09:26, schrieb Sebastien Vincent:

Hi Werner,

Thank you for the feedback. I will try to explain a use-case which causes problem to us.

A and B: two registraless SIP account with ZRTP enabled on same LAN.

- A do a videocall to B
- B enable video
- A switch to desktop streaming
    -> A send an RTP Bye
    -> A begins to send new RTP packet with different SSRC, sequence number and reset timestamp (because it is a new video stream) on the same RTP port as
previously.
- B receives the BYE but during its processing (by JMF) B receive also first new unencrypted packets which will be dropped because the current SRTPCryptoContext
cannot authenticate them.
- Finally our code process the ByeEvent which restart ZRTP (restartZrtpControl() in MediaStreamImpl)
- The next RTP video packets can now be transmitted to JMF for processing
    -> but as we miss the first packets of the video keyframe (or the entire keyframe), image is not displayed immediately and B can wait 20 or more seconds to
have something displayed on its screen.

Of course if both peers has disable ZRTP, this problem does not appears.

What do you think ?

Best regards,
--
Seb

Le 05/05/11 21:28, Werner Dittmann a écrit :

Hi all,

I just saw this on the commit mailing list.

I just browsed the modifications and I'm not yet sure about the security implications.

IMHO this could introduce some serious security risk:
- SRTP will not be active for a different remote SSRC anymore. IIRC the RTP RFC
   says that one RTP sessions shall use one SSRC only and it is a bad
   practice to multiplex several SSRC streams on one RTP session.

ZRTP and SRTP were desinged with this in mind, AFAIK.

Thus simply returning the packets that contain different SSRCs would allow
a bad guy (Mallory) to feed in packets which will then processed normaly by Jitsi.
Thus Mallory can massively disturb the content. Also because of this the user
does not get any warnings - feeding packets with different SSRC on a otherwise
secure RTP session will go unnoticed.

My proposal here:
Stay to the rule "one RTP session - one SSRC" in particular if this RTP session
uses SRTP.

NB: if Jitsi requires SSRC multiplexing then we probably need to introduce an
additional SRTP Crypto Context management the can hold several Crypto Contexts
indexed by their respective SSRC - however, this is no supported by the ZRTP
implementation.

Best regards,
Werner


#4

Hi Werner,

Hi Seb,

just a thought:
- why does A send an RTP bye (via RTCP) or do you mean a SIP bye here?

It is a RTCP Bye and it is send internally by JMF.

- if you switch the RTP parameters, for example to use a different codec
   or somthing like that, then Jitsi should perform a SIP Re-Invite similar to
   the processing that is done when adding a video stream to an existing
   audio stream. In this case A should not send RTP packets before the re-invite
   was processed.

When I say "switch to desktop streaming", effectively I mean A send SIP RE-INVITE.
Next A receive SIP/OK from B, then A send SIP/ACK to B and begins to send new packets (when capture device is ready).

Or do I miss something important here?

In any case: "silently" accepting and processing RTP packets with different/new SSRC
opens up a security hole. A bad guy can feed RTP data and may communicate with
either A or B, or both. If the bad guy uses a different SSRC then this attack goes
unnotified because Jitsi happily processes RTP packets with every SSRC that it
receives on the RTP connection (if I interpret the patch correctly).

Yes we understand this issue.

···

Le 06/05/11 17:00, Werner Dittmann a écrit :

--
Seb

Best regards,
Werner

Am 06.05.2011 09:26, schrieb Sebastien Vincent:

Hi Werner,

Thank you for the feedback. I will try to explain a use-case which causes problem to us.

A and B: two registraless SIP account with ZRTP enabled on same LAN.

- A do a videocall to B
- B enable video
- A switch to desktop streaming
     -> A send an RTP Bye
     -> A begins to send new RTP packet with different SSRC, sequence number and reset timestamp (because it is a new video stream) on the same RTP port as
previously.
- B receives the BYE but during its processing (by JMF) B receive also first new unencrypted packets which will be dropped because the current SRTPCryptoContext
cannot authenticate them.
- Finally our code process the ByeEvent which restart ZRTP (restartZrtpControl() in MediaStreamImpl)
- The next RTP video packets can now be transmitted to JMF for processing
     -> but as we miss the first packets of the video keyframe (or the entire keyframe), image is not displayed immediately and B can wait 20 or more seconds to
have something displayed on its screen.

Of course if both peers has disable ZRTP, this problem does not appears.

What do you think ?

Best regards,
--
Seb

Le 05/05/11 21:28, Werner Dittmann a écrit :

Hi all,

I just saw this on the commit mailing list.

I just browsed the modifications and I'm not yet sure about the security implications.

IMHO this could introduce some serious security risk:
- SRTP will not be active for a different remote SSRC anymore. IIRC the RTP RFC
    says that one RTP sessions shall use one SSRC only and it is a bad
    practice to multiplex several SSRC streams on one RTP session.

ZRTP and SRTP were desinged with this in mind, AFAIK.

Thus simply returning the packets that contain different SSRCs would allow
a bad guy (Mallory) to feed in packets which will then processed normaly by Jitsi.
Thus Mallory can massively disturb the content. Also because of this the user
does not get any warnings - feeding packets with different SSRC on a otherwise
secure RTP session will go unnoticed.

My proposal here:
Stay to the rule "one RTP session - one SSRC" in particular if this RTP session
uses SRTP.

NB: if Jitsi requires SSRC multiplexing then we probably need to introduce an
additional SRTP Crypto Context management the can hold several Crypto Contexts
indexed by their respective SSRC - however, this is no supported by the ZRTP
implementation.

Best regards,
Werner


#5

Hey Werner,

I meant to write earlier but got caught up in other things so apologies
for the delay.

I definitely wanted to discuss this with you though. So, as Seb already
mentioned we discovered the problem in a scenario where A streams video
to B and then switches to a different device (e.g. the desktop).

We do send a reINVITE in this case although we don't really need to and
we can't expect to always receive one when we are on the other end. Once
an RTP stream has been negotiatied the sender can switch from one codec
to another as they see fit, as long as the new codec also belongs to the
list that was negotiated in the offer/answer exchange.

RTP 3550 however does say that all packets carrying the same SSRC need
to use the same timing and sequence space. So, when changing devices we
kind of need to also change the SSRC which, as you say it yourself,
means a new RTP session.

Now, when we do this and we start the new send stream we first close the
previous send stream. This causes an RTCP BYE which, once processed
causes a ZRTP cleanup at the receiving end. After a cleanup, we are out
in the open until the ZRTP exchange is handled again but that should be
OK because the user sees it.

The problem is that BYE processing sometimes takes a while and we end up
cleaning up only after the new stream has started. All packets that we
received during that time were previously dropped by ZRTP.

While this may not be a problem for audio as it only implies a short
cut, it does imply a noticeable interruption for video. Dropping the
initial video packets means that we would lose a significant chunk of
the initial keyframe and the user would endup without video for quite a
while.

We therefore thought that instead of waiting for the BYE we could simply
take the appearance of a new SSRC as an indication for a new stream and
the end of the old one.

The part that we haven't committed yet is notifying the user that they
are in the clear when that happens. We will do that in the following few
days.

So. That's the plan.

I am not sure that this is really the best way to handle that kind of
situation but so far I can't see a better approach. As I said, we can't
rely on reINVITEs because there may be none in some cases.

Do you think there's something I am missing?

Emil

На 06.05.11 18:07, Sebastien Vincent написа:

···

Hi Werner,

Le 06/05/11 17:00, Werner Dittmann a écrit :

Hi Seb,

just a thought:
- why does A send an RTP bye (via RTCP) or do you mean a SIP bye here?

It is a RTCP Bye and it is send internally by JMF.

- if you switch the RTP parameters, for example to use a different codec
   or somthing like that, then Jitsi should perform a SIP Re-Invite similar to
   the processing that is done when adding a video stream to an existing
   audio stream. In this case A should not send RTP packets before the re-invite
   was processed.

When I say "switch to desktop streaming", effectively I mean A send SIP
RE-INVITE.
Next A receive SIP/OK from B, then A send SIP/ACK to B and begins to
send new packets (when capture device is ready).

Or do I miss something important here?

In any case: "silently" accepting and processing RTP packets with different/new SSRC
opens up a security hole. A bad guy can feed RTP data and may communicate with
either A or B, or both. If the bad guy uses a different SSRC then this attack goes
unnotified because Jitsi happily processes RTP packets with every SSRC that it
receives on the RTP connection (if I interpret the patch correctly).

Yes we understand this issue.

--
Seb

Best regards,
Werner

Am 06.05.2011 09:26, schrieb Sebastien Vincent:

Hi Werner,

Thank you for the feedback. I will try to explain a use-case which causes problem to us.

A and B: two registraless SIP account with ZRTP enabled on same LAN.

- A do a videocall to B
- B enable video
- A switch to desktop streaming
     -> A send an RTP Bye
     -> A begins to send new RTP packet with different SSRC, sequence number and reset timestamp (because it is a new video stream) on the same RTP port as
previously.
- B receives the BYE but during its processing (by JMF) B receive also first new unencrypted packets which will be dropped because the current SRTPCryptoContext
cannot authenticate them.
- Finally our code process the ByeEvent which restart ZRTP (restartZrtpControl() in MediaStreamImpl)
- The next RTP video packets can now be transmitted to JMF for processing
     -> but as we miss the first packets of the video keyframe (or the entire keyframe), image is not displayed immediately and B can wait 20 or more seconds to
have something displayed on its screen.

Of course if both peers has disable ZRTP, this problem does not appears.

What do you think ?

Best regards,
--
Seb

Le 05/05/11 21:28, Werner Dittmann a écrit :

Hi all,

I just saw this on the commit mailing list.

I just browsed the modifications and I'm not yet sure about the security implications.

IMHO this could introduce some serious security risk:
- SRTP will not be active for a different remote SSRC anymore. IIRC the RTP RFC
    says that one RTP sessions shall use one SSRC only and it is a bad
    practice to multiplex several SSRC streams on one RTP session.

ZRTP and SRTP were desinged with this in mind, AFAIK.

Thus simply returning the packets that contain different SSRCs would allow
a bad guy (Mallory) to feed in packets which will then processed normaly by Jitsi.
Thus Mallory can massively disturb the content. Also because of this the user
does not get any warnings - feeding packets with different SSRC on a otherwise
secure RTP session will go unnoticed.

My proposal here:
Stay to the rule "one RTP session - one SSRC" in particular if this RTP session
uses SRTP.

NB: if Jitsi requires SSRC multiplexing then we probably need to introduce an
additional SRTP Crypto Context management the can hold several Crypto Contexts
indexed by their respective SSRC - however, this is no supported by the ZRTP
implementation.

Best regards,
Werner

--
Emil Ivov, Ph.D. 67000 Strasbourg,
Project Lead France
Jitsi
emcho@jitsi.org PHONE: +33.1.77.62.43.30
http://jitsi.org FAX: +33.1.77.62.47.31


#6

Emil, Seb,

fully understood :-).

The current patch allows to receive packets with a different SSRC
in parallel to the SRTP unconditionally. That's my real concern.

Somehow this "unconditional" processing of a different SSRC needs
to be limited, IMHO. I do not have an fully fledge idea yet how to
do it, only a vague idea:
- when we see a new remote SSRC I think we can setup a new SRTP crypto
  context immediately with the existing key material.

The CryptoContext class already supports a function to derive a new
crypto context from a a "half-baked" context. We can create such a half-baked
context with all necessary data, only with SSRC set to 0 and store this context.
If we see that the SSRC changes (either the first time from 0 to the first SSRC
or from an existing SSRC to a new one) then we just clone (on the fly) another
context with the new SSRC. The crypto class provides the "deriveContext(...)"
method to do this. Then just call "deriveSrtpKeys(...)" on the new context
and voila - ready to go. The "roc" and "deriveRate" parameters of "deriveContext"
should be set to 0.

Also the sender side must do this immediately when it requires a new SSRC
but uses the same RTP connection. This has to be done if the sender gets
a new SSRC - need to extend/check if the old SSRC is replaced/overwritten.
This could be done at "setOwnSSRC(...)" somehow similar to:

  if existing own SSRC is not 0 and new SSRC is different from old SSRC; then
      clone a half-baked crypto context with new SSRC and store it (maybe in a
      map that uses SSRC as key, thus we can have more the one active crypto
      context, identified with SSRC)
      Clone a new context only if the SSRC is not 0 at "setOwnSSRC(...)"

If we build in such a mechanism then no ZRTP reset is necessary on RTCP BYE
processing because the SRTP context is already up and running using the
previously negotiated keys, or do I miss something at this point?

Also, as the code stands now, only one such switch-over is possible
on one RTP connection - once the remoteSSRC variable is not 0 then such
a "seamless" switch is not possible anymore.

Let me have a look during the weekend - IMHO this on-the-fly management
of the SRTP crypto contexts could solve the problem. The C++ ccRTP code
contains such a mechanism to manage SRTP crypto contexts.

Best regards,
Werner

···

Am 06.05.2011 18:18, schrieb Emil Ivov:

Hey Werner,

I meant to write earlier but got caught up in other things so apologies
for the delay.

I definitely wanted to discuss this with you though. So, as Seb already
mentioned we discovered the problem in a scenario where A streams video
to B and then switches to a different device (e.g. the desktop).

We do send a reINVITE in this case although we don't really need to and
we can't expect to always receive one when we are on the other end. Once
an RTP stream has been negotiatied the sender can switch from one codec
to another as they see fit, as long as the new codec also belongs to the
list that was negotiated in the offer/answer exchange.

RTP 3550 however does say that all packets carrying the same SSRC need
to use the same timing and sequence space. So, when changing devices we
kind of need to also change the SSRC which, as you say it yourself,
means a new RTP session.

Now, when we do this and we start the new send stream we first close the
previous send stream. This causes an RTCP BYE which, once processed
causes a ZRTP cleanup at the receiving end. After a cleanup, we are out
in the open until the ZRTP exchange is handled again but that should be
OK because the user sees it.

The problem is that BYE processing sometimes takes a while and we end up
cleaning up only after the new stream has started. All packets that we
received during that time were previously dropped by ZRTP.

While this may not be a problem for audio as it only implies a short
cut, it does imply a noticeable interruption for video. Dropping the
initial video packets means that we would lose a significant chunk of
the initial keyframe and the user would endup without video for quite a
while.

We therefore thought that instead of waiting for the BYE we could simply
take the appearance of a new SSRC as an indication for a new stream and
the end of the old one.

The part that we haven't committed yet is notifying the user that they
are in the clear when that happens. We will do that in the following few
days.

So. That's the plan.

I am not sure that this is really the best way to handle that kind of
situation but so far I can't see a better approach. As I said, we can't
rely on reINVITEs because there may be none in some cases.

Do you think there's something I am missing?

Emil

На 06.05.11 18:07, Sebastien Vincent написа:

Hi Werner,

Le 06/05/11 17:00, Werner Dittmann a écrit :

Hi Seb,

just a thought:
- why does A send an RTP bye (via RTCP) or do you mean a SIP bye here?

It is a RTCP Bye and it is send internally by JMF.

- if you switch the RTP parameters, for example to use a different codec
   or somthing like that, then Jitsi should perform a SIP Re-Invite similar to
   the processing that is done when adding a video stream to an existing
   audio stream. In this case A should not send RTP packets before the re-invite
   was processed.

When I say "switch to desktop streaming", effectively I mean A send SIP
RE-INVITE.
Next A receive SIP/OK from B, then A send SIP/ACK to B and begins to
send new packets (when capture device is ready).

Or do I miss something important here?

In any case: "silently" accepting and processing RTP packets with different/new SSRC
opens up a security hole. A bad guy can feed RTP data and may communicate with
either A or B, or both. If the bad guy uses a different SSRC then this attack goes
unnotified because Jitsi happily processes RTP packets with every SSRC that it
receives on the RTP connection (if I interpret the patch correctly).

Yes we understand this issue.

--
Seb

Best regards,
Werner

Am 06.05.2011 09:26, schrieb Sebastien Vincent:

Hi Werner,

Thank you for the feedback. I will try to explain a use-case which causes problem to us.

A and B: two registraless SIP account with ZRTP enabled on same LAN.

- A do a videocall to B
- B enable video
- A switch to desktop streaming
     -> A send an RTP Bye
     -> A begins to send new RTP packet with different SSRC, sequence number and reset timestamp (because it is a new video stream) on the same RTP port as
previously.
- B receives the BYE but during its processing (by JMF) B receive also first new unencrypted packets which will be dropped because the current SRTPCryptoContext
cannot authenticate them.
- Finally our code process the ByeEvent which restart ZRTP (restartZrtpControl() in MediaStreamImpl)
- The next RTP video packets can now be transmitted to JMF for processing
     -> but as we miss the first packets of the video keyframe (or the entire keyframe), image is not displayed immediately and B can wait 20 or more seconds to
have something displayed on its screen.

Of course if both peers has disable ZRTP, this problem does not appears.

What do you think ?

Best regards,
--
Seb

Le 05/05/11 21:28, Werner Dittmann a écrit :

Hi all,

I just saw this on the commit mailing list.

I just browsed the modifications and I'm not yet sure about the security implications.

IMHO this could introduce some serious security risk:
- SRTP will not be active for a different remote SSRC anymore. IIRC the RTP RFC
    says that one RTP sessions shall use one SSRC only and it is a bad
    practice to multiplex several SSRC streams on one RTP session.

ZRTP and SRTP were desinged with this in mind, AFAIK.

Thus simply returning the packets that contain different SSRCs would allow
a bad guy (Mallory) to feed in packets which will then processed normaly by Jitsi.
Thus Mallory can massively disturb the content. Also because of this the user
does not get any warnings - feeding packets with different SSRC on a otherwise
secure RTP session will go unnoticed.

My proposal here:
Stay to the rule "one RTP session - one SSRC" in particular if this RTP session
uses SRTP.

NB: if Jitsi requires SSRC multiplexing then we probably need to introduce an
additional SRTP Crypto Context management the can hold several Crypto Contexts
indexed by their respective SSRC - however, this is no supported by the ZRTP
implementation.

Best regards,
Werner


#7

Hi Emil, Seb,

the idea I sketched out below is already implemented. Currently
the SRTPTransformer already manages several SRTP crypto contexts
and indexes then with the packet's SSRC, please refer to the
transform and reverseTransform methods of SRTPTransformer. Also
the ZRTPTransformer sets it up correctly via the SRTPTransformerEngine.

Thus having two (or even more) SSRC on one SRTP connection in parallel
should work without the patch. Maybe there is some interference with JMF
and the RTP close of JMF that introduces a problem.

Thus introducing a new SSRC on the same RTP connection should not pose
a problem for the current SRTP stuff.

Maybe we can have a chat about this if the problems persist?

Best regards,
Werner

···

Am 06.05.2011 20:15, schrieb Werner Dittmann:

Emil, Seb,

fully understood :-).

The current patch allows to receive packets with a different SSRC
in parallel to the SRTP unconditionally. That's my real concern.

Somehow this "unconditional" processing of a different SSRC needs
to be limited, IMHO. I do not have an fully fledge idea yet how to
do it, only a vague idea:
- when we see a new remote SSRC I think we can setup a new SRTP crypto
  context immediately with the existing key material.

The CryptoContext class already supports a function to derive a new
crypto context from a a "half-baked" context. We can create such a half-baked
context with all necessary data, only with SSRC set to 0 and store this context.
If we see that the SSRC changes (either the first time from 0 to the first SSRC
or from an existing SSRC to a new one) then we just clone (on the fly) another
context with the new SSRC. The crypto class provides the "deriveContext(...)"
method to do this. Then just call "deriveSrtpKeys(...)" on the new context
and voila - ready to go. The "roc" and "deriveRate" parameters of "deriveContext"
should be set to 0.

Also the sender side must do this immediately when it requires a new SSRC
but uses the same RTP connection. This has to be done if the sender gets
a new SSRC - need to extend/check if the old SSRC is replaced/overwritten.
This could be done at "setOwnSSRC(...)" somehow similar to:

  if existing own SSRC is not 0 and new SSRC is different from old SSRC; then
      clone a half-baked crypto context with new SSRC and store it (maybe in a
      map that uses SSRC as key, thus we can have more the one active crypto
      context, identified with SSRC)
      Clone a new context only if the SSRC is not 0 at "setOwnSSRC(...)"

If we build in such a mechanism then no ZRTP reset is necessary on RTCP BYE
processing because the SRTP context is already up and running using the
previously negotiated keys, or do I miss something at this point?

Also, as the code stands now, only one such switch-over is possible
on one RTP connection - once the remoteSSRC variable is not 0 then such
a "seamless" switch is not possible anymore.

Let me have a look during the weekend - IMHO this on-the-fly management
of the SRTP crypto contexts could solve the problem. The C++ ccRTP code
contains such a mechanism to manage SRTP crypto contexts.

Best regards,
Werner

Am 06.05.2011 18:18, schrieb Emil Ivov:

Hey Werner,

I meant to write earlier but got caught up in other things so apologies
for the delay.

I definitely wanted to discuss this with you though. So, as Seb already
mentioned we discovered the problem in a scenario where A streams video
to B and then switches to a different device (e.g. the desktop).

We do send a reINVITE in this case although we don't really need to and
we can't expect to always receive one when we are on the other end. Once
an RTP stream has been negotiatied the sender can switch from one codec
to another as they see fit, as long as the new codec also belongs to the
list that was negotiated in the offer/answer exchange.

RTP 3550 however does say that all packets carrying the same SSRC need
to use the same timing and sequence space. So, when changing devices we
kind of need to also change the SSRC which, as you say it yourself,
means a new RTP session.

Now, when we do this and we start the new send stream we first close the
previous send stream. This causes an RTCP BYE which, once processed
causes a ZRTP cleanup at the receiving end. After a cleanup, we are out
in the open until the ZRTP exchange is handled again but that should be
OK because the user sees it.

The problem is that BYE processing sometimes takes a while and we end up
cleaning up only after the new stream has started. All packets that we
received during that time were previously dropped by ZRTP.

While this may not be a problem for audio as it only implies a short
cut, it does imply a noticeable interruption for video. Dropping the
initial video packets means that we would lose a significant chunk of
the initial keyframe and the user would endup without video for quite a
while.

We therefore thought that instead of waiting for the BYE we could simply
take the appearance of a new SSRC as an indication for a new stream and
the end of the old one.

The part that we haven't committed yet is notifying the user that they
are in the clear when that happens. We will do that in the following few
days.

So. That's the plan.

I am not sure that this is really the best way to handle that kind of
situation but so far I can't see a better approach. As I said, we can't
rely on reINVITEs because there may be none in some cases.

Do you think there's something I am missing?

Emil

На 06.05.11 18:07, Sebastien Vincent написа:

Hi Werner,

Le 06/05/11 17:00, Werner Dittmann a écrit :

Hi Seb,

just a thought:
- why does A send an RTP bye (via RTCP) or do you mean a SIP bye here?

It is a RTCP Bye and it is send internally by JMF.

- if you switch the RTP parameters, for example to use a different codec
   or somthing like that, then Jitsi should perform a SIP Re-Invite similar to
   the processing that is done when adding a video stream to an existing
   audio stream. In this case A should not send RTP packets before the re-invite
   was processed.

When I say "switch to desktop streaming", effectively I mean A send SIP
RE-INVITE.
Next A receive SIP/OK from B, then A send SIP/ACK to B and begins to
send new packets (when capture device is ready).

Or do I miss something important here?

In any case: "silently" accepting and processing RTP packets with different/new SSRC
opens up a security hole. A bad guy can feed RTP data and may communicate with
either A or B, or both. If the bad guy uses a different SSRC then this attack goes
unnotified because Jitsi happily processes RTP packets with every SSRC that it
receives on the RTP connection (if I interpret the patch correctly).

Yes we understand this issue.

--
Seb

Best regards,
Werner

SNIP --- SNAP


#8

Hi Werner,

Thanks for pointing that SRTP can handle more than one SSRC.

So for the record, here a more detailed scenario that expose the problem.

Problem involved two peers A and B on the same LAN.
Both peers use a registrarless SIP account.

- A do a videocall to B
         -> SIP INVITE from A to B
         -> When A receive SIP/OK, A creates a audio and video stream and begins to send RTP packet

- B enable video
         -> SIP REINVITE from B to A
         -> When B receive SIP/OK, B creates its video stream and begins to send RTP packet

- A switch to desktop streaming
         -> SIP REINVITE from A to B
         -> When A receive SIP/OK, A closes its video stream (internally JMF sends an RTP BYE)
         -> A recreates a new video stream with different SSRC
         -> A begins to send new RTP packets (from the new video stream) on the same RTP port as before
         -> First packets sent belongs to a video keyframe

- B receives the RTP BYE
         -> JMF takes some time to process the BYE internally and then our code is notified to do
            a zrtpRestartControl()

- During the BYE processing by JMF, B receive new RTP packets (with the different SSRC),
         -> because the ZRTP stuff has not finished to reconfigure, these packets will be dropped
            (SRTPCryptoContext failed to authenticate the packets)

- zrtpControlRestart() has finished on B
         -> new packets can now be processed _but_ as first packets has been dropped, the keyframe is
            incomplete and B has to wait for the next keyframe (which can be 20 or more seconds) to have
            something displayed on the screen

So as you said we will look more closely at what can happen before ZRTP/SRTP stuff with JMF.

Regards,

···

--
Seb

Le 07/05/11 09:49, Werner Dittmann a écrit :

Hi Emil, Seb,

the idea I sketched out below is already implemented. Currently
the SRTPTransformer already manages several SRTP crypto contexts
and indexes then with the packet's SSRC, please refer to the
transform and reverseTransform methods of SRTPTransformer. Also
the ZRTPTransformer sets it up correctly via the SRTPTransformerEngine.

Thus having two (or even more) SSRC on one SRTP connection in parallel
should work without the patch. Maybe there is some interference with JMF
and the RTP close of JMF that introduces a problem.

Thus introducing a new SSRC on the same RTP connection should not pose
a problem for the current SRTP stuff.

Maybe we can have a chat about this if the problems persist?

Best regards,
Werner

Am 06.05.2011 20:15, schrieb Werner Dittmann:

Emil, Seb,

fully understood :-).

The current patch allows to receive packets with a different SSRC
in parallel to the SRTP unconditionally. That's my real concern.

Somehow this "unconditional" processing of a different SSRC needs
to be limited, IMHO. I do not have an fully fledge idea yet how to
do it, only a vague idea:
- when we see a new remote SSRC I think we can setup a new SRTP crypto
   context immediately with the existing key material.

The CryptoContext class already supports a function to derive a new
crypto context from a a "half-baked" context. We can create such a half-baked
context with all necessary data, only with SSRC set to 0 and store this context.
If we see that the SSRC changes (either the first time from 0 to the first SSRC
or from an existing SSRC to a new one) then we just clone (on the fly) another
context with the new SSRC. The crypto class provides the "deriveContext(...)"
method to do this. Then just call "deriveSrtpKeys(...)" on the new context
and voila - ready to go. The "roc" and "deriveRate" parameters of "deriveContext"
should be set to 0.

Also the sender side must do this immediately when it requires a new SSRC
but uses the same RTP connection. This has to be done if the sender gets
a new SSRC - need to extend/check if the old SSRC is replaced/overwritten.
This could be done at "setOwnSSRC(...)" somehow similar to:

   if existing own SSRC is not 0 and new SSRC is different from old SSRC; then
       clone a half-baked crypto context with new SSRC and store it (maybe in a
       map that uses SSRC as key, thus we can have more the one active crypto
       context, identified with SSRC)
       Clone a new context only if the SSRC is not 0 at "setOwnSSRC(...)"

If we build in such a mechanism then no ZRTP reset is necessary on RTCP BYE
processing because the SRTP context is already up and running using the
previously negotiated keys, or do I miss something at this point?

Also, as the code stands now, only one such switch-over is possible
on one RTP connection - once the remoteSSRC variable is not 0 then such
a "seamless" switch is not possible anymore.

Let me have a look during the weekend - IMHO this on-the-fly management
of the SRTP crypto contexts could solve the problem. The C++ ccRTP code
contains such a mechanism to manage SRTP crypto contexts.

Best regards,
Werner

Am 06.05.2011 18:18, schrieb Emil Ivov:

Hey Werner,

I meant to write earlier but got caught up in other things so apologies
for the delay.

I definitely wanted to discuss this with you though. So, as Seb already
mentioned we discovered the problem in a scenario where A streams video
to B and then switches to a different device (e.g. the desktop).

We do send a reINVITE in this case although we don't really need to and
we can't expect to always receive one when we are on the other end. Once
an RTP stream has been negotiatied the sender can switch from one codec
to another as they see fit, as long as the new codec also belongs to the
list that was negotiated in the offer/answer exchange.

RTP 3550 however does say that all packets carrying the same SSRC need
to use the same timing and sequence space. So, when changing devices we
kind of need to also change the SSRC which, as you say it yourself,
means a new RTP session.

Now, when we do this and we start the new send stream we first close the
previous send stream. This causes an RTCP BYE which, once processed
causes a ZRTP cleanup at the receiving end. After a cleanup, we are out
in the open until the ZRTP exchange is handled again but that should be
OK because the user sees it.

The problem is that BYE processing sometimes takes a while and we end up
cleaning up only after the new stream has started. All packets that we
received during that time were previously dropped by ZRTP.

While this may not be a problem for audio as it only implies a short
cut, it does imply a noticeable interruption for video. Dropping the
initial video packets means that we would lose a significant chunk of
the initial keyframe and the user would endup without video for quite a
while.

We therefore thought that instead of waiting for the BYE we could simply
take the appearance of a new SSRC as an indication for a new stream and
the end of the old one.

The part that we haven't committed yet is notifying the user that they
are in the clear when that happens. We will do that in the following few
days.

So. That's the plan.

I am not sure that this is really the best way to handle that kind of
situation but so far I can't see a better approach. As I said, we can't
rely on reINVITEs because there may be none in some cases.

Do you think there's something I am missing?

Emil

На 06.05.11 18:07, Sebastien Vincent написа:

Hi Werner,

Le 06/05/11 17:00, Werner Dittmann a écrit :

Hi Seb,

just a thought:
- why does A send an RTP bye (via RTCP) or do you mean a SIP bye here?

It is a RTCP Bye and it is send internally by JMF.

- if you switch the RTP parameters, for example to use a different codec
    or somthing like that, then Jitsi should perform a SIP Re-Invite similar to
    the processing that is done when adding a video stream to an existing
    audio stream. In this case A should not send RTP packets before the re-invite
    was processed.

When I say "switch to desktop streaming", effectively I mean A send SIP
RE-INVITE.
Next A receive SIP/OK from B, then A send SIP/ACK to B and begins to
send new packets (when capture device is ready).

Or do I miss something important here?

In any case: "silently" accepting and processing RTP packets with different/new SSRC
opens up a security hole. A bad guy can feed RTP data and may communicate with
either A or B, or both. If the bad guy uses a different SSRC then this attack goes
unnotified because Jitsi happily processes RTP packets with every SSRC that it
receives on the RTP connection (if I interpret the patch correctly).

Yes we understand this issue.

--
Seb

Best regards,
Werner

SNIP --- SNAP


#9

Hi Werner, Damian,

One solution that can work is to not restart ZRTP (restartZrtpControl) when we recreate the JMF SendStream (for example when we switch camera to desktop streaming) _and_ when we receive an RTP Bye. In other words don't use restartZrtpControl() at all for the duration of the call. So we will use the same key materials for the video stream (camera and then desktop sharing).

What do you think ? Do you see any security risks or other problem that could happen with that ?

Regards,

···

--
Seb

Le 11/05/11 11:53, Sebastien Vincent a écrit :

Hi Werner,

Thanks for pointing that SRTP can handle more than one SSRC.

So for the record, here a more detailed scenario that expose the problem.

Problem involved two peers A and B on the same LAN.
Both peers use a registrarless SIP account.

- A do a videocall to B
        -> SIP INVITE from A to B
        -> When A receive SIP/OK, A creates a audio and video stream and begins to send RTP packet

- B enable video
        -> SIP REINVITE from B to A
        -> When B receive SIP/OK, B creates its video stream and begins to send RTP packet

- A switch to desktop streaming
        -> SIP REINVITE from A to B
        -> When A receive SIP/OK, A closes its video stream (internally JMF sends an RTP BYE)
        -> A recreates a new video stream with different SSRC
        -> A begins to send new RTP packets (from the new video stream) on the same RTP port as before
        -> First packets sent belongs to a video keyframe

- B receives the RTP BYE
        -> JMF takes some time to process the BYE internally and then our code is notified to do
           a zrtpRestartControl()

- During the BYE processing by JMF, B receive new RTP packets (with the different SSRC),
        -> because the ZRTP stuff has not finished to reconfigure, these packets will be dropped
           (SRTPCryptoContext failed to authenticate the packets)

- zrtpControlRestart() has finished on B
        -> new packets can now be processed _but_ as first packets has been dropped, the keyframe is
           incomplete and B has to wait for the next keyframe (which can be 20 or more seconds) to have
           something displayed on the screen

So as you said we will look more closely at what can happen before ZRTP/SRTP stuff with JMF.

Regards,
--
Seb


#10

Hi again,

I have tested this solution today and it works fine.

I just detect a bug when we switch from a simple call to a conference call, the first participant is not considered as encrypted (the padlock is open) but I suspect tat it is a GUI bug as it appears in older revision (when there was no SSRC and ZRTP changes).

Werner, Damian, what do you think about this solution ?

···

--
Seb

Le 11/05/11 13:15, Sebastien Vincent a écrit :

Hi Werner, Damian,

One solution that can work is to not restart ZRTP (restartZrtpControl) when we recreate the JMF SendStream (for example when we switch camera to desktop streaming) _and_ when we receive an RTP Bye. In other words don't use restartZrtpControl() at all for the duration of the call. So we will use the same key materials for the video stream (camera and then desktop sharing).

What do you think ? Do you see any security risks or other problem that could happen with that ?

Regards,
--
Seb

Le 11/05/11 11:53, Sebastien Vincent a écrit :

Hi Werner,

Thanks for pointing that SRTP can handle more than one SSRC.

So for the record, here a more detailed scenario that expose the problem.

Problem involved two peers A and B on the same LAN.
Both peers use a registrarless SIP account.

- A do a videocall to B
        -> SIP INVITE from A to B
        -> When A receive SIP/OK, A creates a audio and video stream and begins to send RTP packet

- B enable video
        -> SIP REINVITE from B to A
        -> When B receive SIP/OK, B creates its video stream and begins to send RTP packet

- A switch to desktop streaming
        -> SIP REINVITE from A to B
        -> When A receive SIP/OK, A closes its video stream (internally JMF sends an RTP BYE)
        -> A recreates a new video stream with different SSRC
        -> A begins to send new RTP packets (from the new video stream) on the same RTP port as before
        -> First packets sent belongs to a video keyframe

- B receives the RTP BYE
        -> JMF takes some time to process the BYE internally and then our code is notified to do
           a zrtpRestartControl()

- During the BYE processing by JMF, B receive new RTP packets (with the different SSRC),
        -> because the ZRTP stuff has not finished to reconfigure, these packets will be dropped
           (SRTPCryptoContext failed to authenticate the packets)

- zrtpControlRestart() has finished on B
        -> new packets can now be processed _but_ as first packets has been dropped, the keyframe is
           incomplete and B has to wait for the next keyframe (which can be 20 or more seconds) to have
           something displayed on the screen

So as you said we will look more closely at what can happen before ZRTP/SRTP stuff with JMF.

Regards,
--
Seb


#11

Hi all,

just some thoughts - see inline please

Best regards,
Werner

Hi Werner, Damian,

One solution that can work is to not restart ZRTP (restartZrtpControl) when we recreate the JMF SendStream (for example when we switch camera to desktop
streaming) _and_ when we receive an RTP Bye. In other words don't use restartZrtpControl() at all for the duration of the call. So we will use the same key
materials for the video stream (camera and then desktop sharing).

This is how it should work - just switching the SSRC for an existing SRTP session because
you change the "codec" (video --> desktop streaming) is ok. No need to reset the
ZRTP/SRTP transport, just reuse the existing stuff for the new SSRC and SRTP kicks in
automatically.

Create or restart ZRTP only if you setup a brand new RTP connection, for example to another
port.

What do you think ? Do you see any security risks or other problem that could happen with that ?

Regards,
--
Seb

Hi Werner,

Thanks for pointing that SRTP can handle more than one SSRC.

So for the record, here a more detailed scenario that expose the problem.

Problem involved two peers A and B on the same LAN.
Both peers use a registrarless SIP account.

- A do a videocall to B
        -> SIP INVITE from A to B
        -> When A receive SIP/OK, A creates a audio and video stream and begins to send RTP packet

- B enable video
        -> SIP REINVITE from B to A
        -> When B receive SIP/OK, B creates its video stream and begins to send RTP packet

- A switch to desktop streaming
        -> SIP REINVITE from A to B
        -> When A receive SIP/OK, A closes its video stream (internally JMF sends an RTP BYE)
        -> A recreates a new video stream with different SSRC
        -> A begins to send new RTP packets (from the new video stream) on the same RTP port as before
        -> First packets sent belongs to a video keyframe

At this point it is important that the packets use the same stream transport setup, i.e. the same setup
for the Transformers that already hold the key material, the pre-set crypto contexts etc. If
this new stream uses a different transport then the packets will not be encrypted and this leads to ...

- B receives the RTP BYE
        -> JMF takes some time to process the BYE internally and then our code is notified to do
           a zrtpRestartControl()

- During the BYE processing by JMF, B receive new RTP packets (with the different SSRC),
        -> because the ZRTP stuff has not finished to reconfigure, these packets will be dropped
           (SRTPCryptoContext failed to authenticate the packets)

... exactly this error. At the receiving end SRTP first performs an authentication check. And
this check obviously fails if the packets were not encrypted by the sender.

···

Am 11.05.2011 13:15, schrieb Sebastien Vincent:

Le 11/05/11 11:53, Sebastien Vincent a écrit :

- zrtpControlRestart() has finished on B
        -> new packets can now be processed _but_ as first packets has been dropped, the keyframe is
           incomplete and B has to wait for the next keyframe (which can be 20 or more seconds) to have
           something displayed on the screen

So as you said we will look more closely at what can happen before ZRTP/SRTP stuff with JMF.

Regards,
--
Seb


#12

Hi Werner,

Thanks.

Damian reports me offlist to also test the call transfer (blind and attended) and see if it works correctly.

Regards,

···

--
Seb

Le 11/05/11 17:48, Werner Dittmann a écrit :

Hi all,

just some thoughts - see inline please

Best regards,
Werner

Am 11.05.2011 13:15, schrieb Sebastien Vincent:

Hi Werner, Damian,

One solution that can work is to not restart ZRTP (restartZrtpControl) when we recreate the JMF SendStream (for example when we switch camera to desktop
streaming) _and_ when we receive an RTP Bye. In other words don't use restartZrtpControl() at all for the duration of the call. So we will use the same key
materials for the video stream (camera and then desktop sharing).

This is how it should work - just switching the SSRC for an existing SRTP session because
you change the "codec" (video --> desktop streaming) is ok. No need to reset the
ZRTP/SRTP transport, just reuse the existing stuff for the new SSRC and SRTP kicks in
automatically.

Create or restart ZRTP only if you setup a brand new RTP connection, for example to another
port.

What do you think ? Do you see any security risks or other problem that could happen with that ?

Regards,
--
Seb

Le 11/05/11 11:53, Sebastien Vincent a écrit :

Hi Werner,

Thanks for pointing that SRTP can handle more than one SSRC.

So for the record, here a more detailed scenario that expose the problem.

Problem involved two peers A and B on the same LAN.
Both peers use a registrarless SIP account.

- A do a videocall to B
         -> SIP INVITE from A to B
         -> When A receive SIP/OK, A creates a audio and video stream and begins to send RTP packet

- B enable video
         -> SIP REINVITE from B to A
         -> When B receive SIP/OK, B creates its video stream and begins to send RTP packet

- A switch to desktop streaming
         -> SIP REINVITE from A to B
         -> When A receive SIP/OK, A closes its video stream (internally JMF sends an RTP BYE)
         -> A recreates a new video stream with different SSRC
         -> A begins to send new RTP packets (from the new video stream) on the same RTP port as before
         -> First packets sent belongs to a video keyframe

At this point it is important that the packets use the same stream transport setup, i.e. the same setup
for the Transformers that already hold the key material, the pre-set crypto contexts etc. If
this new stream uses a different transport then the packets will not be encrypted and this leads to ...

- B receives the RTP BYE
         -> JMF takes some time to process the BYE internally and then our code is notified to do
            a zrtpRestartControl()

- During the BYE processing by JMF, B receive new RTP packets (with the different SSRC),
         -> because the ZRTP stuff has not finished to reconfigure, these packets will be dropped
            (SRTPCryptoContext failed to authenticate the packets)

... exactly this error. At the receiving end SRTP first performs an authentication check. And
this check obviously fails if the packets were not encrypted by the sender.

- zrtpControlRestart() has finished on B
         -> new packets can now be processed _but_ as first packets has been dropped, the keyframe is
            incomplete and B has to wait for the next keyframe (which can be 20 or more seconds) to have
            something displayed on the screen

So as you said we will look more closely at what can happen before ZRTP/SRTP stuff with JMF.

Regards,
--
Seb


#13

Hi Werner,

Thanks.

Damian reports me offlist to also test the call transfer (blind and attended) and see if it works correctly.

call transfer requires a new RTP connect because you transfer the call to
a new client - thus you then need to perform a whole new ZRTP negotiation
via the new RTP connection. Thus it should be not different to a normal
call :slight_smile: .

Regards,
Werner

···

Am 12.05.2011 10:11, schrieb Sebastien Vincent:

Regards,
--
Seb

Le 11/05/11 17:48, Werner Dittmann a écrit :

Hi all,

just some thoughts - see inline please

Best regards,
Werner

Am 11.05.2011 13:15, schrieb Sebastien Vincent:

Hi Werner, Damian,

One solution that can work is to not restart ZRTP (restartZrtpControl) when we recreate the JMF SendStream (for example when we switch camera to desktop
streaming) _and_ when we receive an RTP Bye. In other words don't use restartZrtpControl() at all for the duration of the call. So we will use the same key
materials for the video stream (camera and then desktop sharing).

This is how it should work - just switching the SSRC for an existing SRTP session because
you change the "codec" (video --> desktop streaming) is ok. No need to reset the
ZRTP/SRTP transport, just reuse the existing stuff for the new SSRC and SRTP kicks in
automatically.