diff options
author | Werner Dittmann <wernerd@java.net> | 2011-10-16 07:31:50 +0000 |
---|---|---|
committer | Werner Dittmann <wernerd@java.net> | 2011-10-16 07:31:50 +0000 |
commit | ad3bd7796af611a03201097988711fa729138ac5 (patch) | |
tree | c6269ebe163fc32951f3ee6f9415e9244c86cc54 /src/net | |
parent | 261520dfcc611c913eaadba26e8bab4386ab42ac (diff) | |
download | jitsi-ad3bd7796af611a03201097988711fa729138ac5.zip jitsi-ad3bd7796af611a03201097988711fa729138ac5.tar.gz jitsi-ad3bd7796af611a03201097988711fa729138ac5.tar.bz2 |
New ZRTP library that supports trusted MitM/PBX feature and implement SRTCP handling.
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/java/sip/communicator/impl/neomedia/RawPacket.java | 64 | ||||
-rw-r--r-- | src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPCryptoContext.java | 648 | ||||
-rw-r--r-- | src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPTransformer.java | 43 | ||||
-rw-r--r-- | src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformEngine.java | 19 | ||||
-rw-r--r-- | src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTCPTransformer.java (renamed from src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPCTransformer.java) | 38 | ||||
-rw-r--r-- | src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java | 136 |
6 files changed, 906 insertions, 42 deletions
diff --git a/src/net/java/sip/communicator/impl/neomedia/RawPacket.java b/src/net/java/sip/communicator/impl/neomedia/RawPacket.java index 55e01fd..3ddc276 100644 --- a/src/net/java/sip/communicator/impl/neomedia/RawPacket.java +++ b/src/net/java/sip/communicator/impl/neomedia/RawPacket.java @@ -310,26 +310,47 @@ public class RawPacket }
/**
+ * Grow the internal packet buffer.
+ *
+ * This will change the data buffer of this packet but not the
+ * length of the valid data. Use this to grow the internal buffer
+ * to avoid buffer re-allocations when appending data.
+ *
+ * @param howMuch number of bytes to grow
+ */
+ public void grow(int howMuch) {
+ if (howMuch == 0) {
+ return;
+ }
+ byte[] newBuffer = new byte[this.length + howMuch];
+ System.arraycopy(this.buffer, this.offset, newBuffer, 0, this.length);
+ offset = 0;
+ buffer = newBuffer;
+ }
+
+ /**
* Append a byte array to the end of the packet. This may change the data
* buffer of this packet.
*
* @param data byte array to append
* @param len the number of bytes to append
*/
- public void append(byte[] data, int len)
- {
- if (data == null || len == 0)
+ public void append(byte[] data, int len) {
+ if (data == null || len == 0) {
return;
-
- // check if old buffer can hold all data. If not allocate a new one.
- if ((length + offset + len) > buffer.length)
- {
- byte[] newBuffer = new byte[length + offset + len];
- System.arraycopy(this.buffer, 0, newBuffer, 0, length+offset);
+ }
+
+ // re-allocate internal buffer if it is too small
+ if ((this.length + len) > (buffer.length - this.offset)) {
+ byte[] newBuffer = new byte[this.length + len];
+ System.arraycopy(this.buffer, this.offset, newBuffer, 0, this.length);
+ this.offset = 0;
this.buffer = newBuffer;
}
- System.arraycopy(data, 0, buffer, length + offset, len);
- this.length += len;
+ // append data
+ System.arraycopy(data, 0, this.buffer, this.length, len);
+ this.length = this.length + len;
+
}
/**
@@ -490,6 +511,16 @@ public class RawPacket }
/**
+ * Get RTCP SSRC from a RTCP packet
+ *
+ * @param pkt the source RTP packet
+ * @return RTP SSRC from source RTP packet
+ */
+ public long GetRTCPSSRC()
+ {
+ return (int)(readUnsignedIntAsLong(4) & 0xffffffff);
+ }
+ /**
* Get RTP sequence number from a RTP packet
*
* @return RTP sequence num from source packet
@@ -500,6 +531,17 @@ public class RawPacket }
/**
+ * Get SRTCP sequence number from a SRTCP packet
+ *
+ * @param pkt the source SRTCP packet
+ * @return SRTCP sequence num from source packet
+ */
+ public int getSRTCPIndex(int authTagLen)
+ {
+ int offset = getLength() - (4 + authTagLen);
+ return readInt(offset);
+ }
+ /**
* Test whether if a RTP packet is padded
*
* @return whether if source RTP packet is padded
diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPCryptoContext.java b/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPCryptoContext.java new file mode 100644 index 0000000..63e982d --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPCryptoContext.java @@ -0,0 +1,648 @@ +/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * 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:
+ *
+ Copyright (C) 2004-2006 the Minisip Team
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+package net.java.sip.communicator.impl.neomedia.transform.srtp;
+
+import net.java.sip.communicator.impl.neomedia.*;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.macs.*;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersForSkein;
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Mac;
+
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.TwofishEngine;
+
+
+/**
+ * SRTPCryptoContext class is the core class of SRTP implementation.
+ * There can be multiple SRTP sources in one SRTP session. And each SRTP stream
+ * has a corresponding SRTPCryptoContext object, identified by SSRC. In this
+ * way, different sources can be protected independently.
+ *
+ * SRTPCryptoContext class acts as a manager class and maintains all the
+ * information used in SRTP transformation. It is responsible for deriving
+ * encryption keys / salting keys / authentication keys from master keys. And
+ * it will invoke certain class to encrypt / decrypt (transform / reverse
+ * transform) RTP packets. It will hold a replay check db and do replay check
+ * against incoming packets.
+ *
+ * Refer to section 3.2 in RFC3711 for detailed description of cryptographic
+ * context.
+ *
+ * Cryptographic related parameters, i.e. encryption mode / authentication mode,
+ * master encryption key and master salt key are determined outside the scope
+ * of SRTP implementation. They can be assigned manually, or can be assigned
+ * automatically using some key management protocol, such as MIKEY (RFC3880) or
+ * Phil Zimmermann's ZRTP protocol.
+ *
+ * @author Bing SU (nova.su@gmail.com)
+ */
+public class SRTCPCryptoContext
+{
+ /**
+ * The replay check windows size
+ */
+ private static final long REPLAY_WINDOW_SIZE = 64;
+
+ /**
+ * RTCP SSRC of this cryptographic context
+ */
+ private long ssrc;
+
+ /**
+ * Master key identifier
+ */
+ private byte[] mki;
+
+ /**
+ * Index received so far
+ */
+ private int receivedIndex = 0;
+
+ /**
+ * Index sent so far
+ */
+ private int sentIndex = 0;
+
+ /**
+ * Bit mask for replay check
+ */
+ private long replayWindow;
+
+ /**
+ * Master encryption key
+ */
+ private byte[] masterKey;
+
+ /**
+ * Master salting key
+ */
+ private byte[] masterSalt;
+
+ /**
+ * Derived session encryption key
+ */
+ private byte[] encKey;
+
+ /**
+ * Derived session authentication key
+ */
+ private byte[] authKey;
+
+ /**
+ * Derived session salting key
+ */
+ private byte[] saltKey;
+
+ /**
+ * Encryption / Authentication policy for this session
+ */
+ private final SRTPPolicy policy;
+
+ /**
+ * The HMAC object we used to do packet authentication
+ */
+ private Mac mac; // used for various HMAC computations
+
+ // The symmetric cipher engines we need here
+ private BlockCipher cipher = null;
+ private BlockCipher cipherF8 = null; // used inside F8 mode only
+
+ // implements the counter cipher mode for RTP according to RFC 3711
+ private final SRTPCipherCTR cipherCtr = new SRTPCipherCTR();
+
+ // Here some fields that a allocated here or in constructor. The methods
+ // use these fields to avoid too many new operations
+
+ private final byte[] tagStore;
+ private final byte[] ivStore = new byte[16];
+ private final byte[] rbStore = new byte[4];
+
+ // this is some working store, used by some methods to avoid new operations
+ // the methods must use this only to store some reults for immediate processing
+ private final byte[] tempStore = new byte[100];
+
+ /**
+ * Construct an empty SRTPCryptoContext using ssrc.
+ * The other parameters are set to default null value.
+ *
+ * @param ssrc SSRC of this SRTPCryptoContext
+ */
+ public SRTCPCryptoContext(long ssrcIn)
+ {
+ ssrc = ssrcIn;
+ mki = null;
+ masterKey = null;
+ masterSalt = null;
+ encKey = null;
+ authKey = null;
+ saltKey = null;
+ policy = null;
+ tagStore = null;
+ }
+
+ /**
+ * Construct a normal SRTPCryptoContext based on the given parameters.
+ *
+ * @param ssrc
+ * the RTP SSRC that this SRTP cryptographic context protects.
+ * @param masterKey
+ * byte array holding the master key for this SRTP cryptographic
+ * context. Refer to chapter 3.2.1 of the RFC about the role of
+ * the master key.
+ * @param masterSalt
+ * byte array holding the master salt for this SRTP cryptographic
+ * context. It is used to computer the initialization vector that
+ * in turn is input to compute the session key, session
+ * authentication key and the session salt.
+ * @param policy
+ * SRTP policy for this SRTP cryptographic context, defined the
+ * encryption algorithm, the authentication algorithm, etc
+ */
+ public SRTCPCryptoContext(long ssrcIn,
+ byte[] masterK, byte[] masterS, SRTPPolicy policyIn)
+ {
+ ssrc = ssrcIn;
+ mki = null;
+
+ policy = policyIn;
+
+ masterKey = new byte[policy.getEncKeyLength()];
+ System.arraycopy(masterK, 0, masterKey, 0, policy
+ .getEncKeyLength());
+
+ masterSalt = new byte[policy.getSaltKeyLength()];
+ System.arraycopy(masterS, 0, masterSalt, 0, policy
+ .getSaltKeyLength());
+
+ switch (policy.getEncType()) {
+ case SRTPPolicy.NULL_ENCRYPTION:
+ encKey = null;
+ saltKey = null;
+ break;
+
+ case SRTPPolicy.AESCM_ENCRYPTION:
+ cipher = new AESFastEngine();
+ encKey = new byte[this.policy.getEncKeyLength()];
+ saltKey = new byte[this.policy.getSaltKeyLength()];
+
+ case SRTPPolicy.AESF8_ENCRYPTION:
+ cipherF8 = new AESFastEngine();
+ break;
+
+ case SRTPPolicy.TWOFISH_ENCRYPTION:
+ 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()) {
+ case SRTPPolicy.NULL_AUTHENTICATION:
+ authKey = null;
+ tagStore = null;
+ break;
+
+ case SRTPPolicy.HMACSHA1_AUTHENTICATION:
+ mac = new HMac(new SHA1Digest());
+ authKey = new byte[policy.getAuthKeyLength()];
+ tagStore = new byte[mac.getMacSize()];
+ break;
+
+ case SRTPPolicy.SKEIN_AUTHENTICATION:
+ mac = new SkeinMac();
+ authKey = new byte[policy.getAuthKeyLength()];
+ tagStore = new byte[policy.getAuthTagLength()];
+ break;
+
+ default:
+ tagStore = null;
+ }
+ }
+
+ /**
+ * Get the authentication tag length of this SRTP cryptographic context
+ *
+ * @return the authentication tag length of this SRTP cryptographic context
+ */
+ public int getAuthTagLength() {
+ return policy.getAuthTagLength();
+ }
+
+ /**
+ * Get the MKI length of this SRTP cryptographic context
+ *
+ * @return the MKI length of this SRTP cryptographic context
+ */
+ public int getMKILength() {
+ if (mki != null) {
+ return mki.length;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Get the SSRC of this SRTP cryptographic context
+ *
+ * @return the SSRC of this SRTP cryptographic context
+ */
+ public long getSSRC() {
+ return ssrc;
+ }
+
+ /**
+ * Transform a RTP packet into a SRTP packet.
+ * This method is called when a normal RTP packet ready to be sent.
+ *
+ * Operations done by the transformation may include: encryption, using
+ * either Counter Mode encryption, or F8 Mode encryption, adding
+ * authentication tag, currently HMC SHA1 method.
+ *
+ * Both encryption and authentication functionality can be turned off
+ * as long as the SRTPPolicy used in this SRTPCryptoContext is requires no
+ * encryption and no authentication. Then the packet will be sent out
+ * untouched. However this is not encouraged. If no SRTP feature is enabled,
+ * then we shall not use SRTP TransformConnector. We should use the original
+ * method (RTPManager managed transportation) instead.
+ *
+ * @param pkt the RTP packet that is going to be sent out
+ */
+ public void transformPacket(RawPacket pkt) {
+
+ boolean encrypt = false;
+ /* Encrypt the packet using Counter Mode encryption */
+ if (policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION ||
+ policy.getEncType() == SRTPPolicy.TWOFISH_ENCRYPTION) {
+ processPacketAESCM(pkt, sentIndex);
+ encrypt = true;
+ }
+
+ /* Encrypt the packet using F8 Mode encryption */
+ else if (policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION ||
+ policy.getEncType() == SRTPPolicy.TWOFISHF8_ENCRYPTION) {
+ processPacketAESF8(pkt, sentIndex);
+ encrypt = true;
+ }
+ int index = 0;
+ if (encrypt)
+ index = sentIndex | 0x80000000;
+
+ // Grow packet storage in one step
+ pkt.grow(4 + policy.getAuthTagLength());
+
+ // Authenticate the packet
+ // The authenticate method gets the index via parameter and stores
+ // it in network order in rbStore variable.
+ if (policy.getAuthType() != SRTPPolicy.NULL_AUTHENTICATION) {
+ authenticatePacket(pkt, index);
+ pkt.append(rbStore, 4);
+ pkt.append(tagStore, policy.getAuthTagLength());
+ }
+ sentIndex++;
+ sentIndex &= ~0x80000000; // clear possible overflow
+ }
+
+ /**
+ * Transform a SRTCP packet into a RTCP packet.
+ * This method is called when a SRTCP packet was received.
+ *
+ * Operations done by the this operation include:
+ * Authentication check, Packet replay check and decryption.
+ *
+ * Both encryption and authentication functionality can be turned off
+ * as long as the SRTPPolicy used in this SRTPCryptoContext requires no
+ * encryption and no authentication. Then the packet will be sent out
+ * untouched. However this is not encouraged. If no SRTCP feature is enabled,
+ * then we shall not use SRTP TransformConnector. We should use the original
+ * method (RTPManager managed transportation) instead.
+ *
+ * @param pkt the received RTCP packet
+ * @return true if the packet can be accepted
+ * false if authentication or replay check failed
+ */
+ public boolean reverseTransformPacket(RawPacket pkt) {
+
+ boolean decrypt = false;
+ int tagLength = policy.getAuthTagLength();
+ int indexEflag = pkt.getSRTCPIndex(tagLength);
+
+ if ((indexEflag & 0x80000000) == 0x80000000)
+ decrypt = true;
+
+ int index = indexEflag & ~0x80000000;
+
+ /* Replay control */
+ if (!checkReplay(index)) {
+ return false;
+
+ }
+ /* Authenticate the packet */
+ if (policy.getAuthType() != SRTPPolicy.NULL_AUTHENTICATION) {
+
+ // get original authentication data and store in tempStore
+ pkt.readRegionToBuff(pkt.getLength() - tagLength, tagLength,
+ tempStore);
+
+ // Shrink packet to remove the authentication tag and index
+ // because this is part of authenicated data
+ pkt.shrink(tagLength + 4);
+
+ // compute, then save authentication in tagStore
+ authenticatePacket(pkt, indexEflag);
+
+ for (int i = 0; i < tagLength; i++) {
+ if ((tempStore[i] & 0xff) == (tagStore[i] & 0xff))
+ continue;
+ else
+ return false;
+ }
+ }
+
+ if (decrypt) {
+ /* Decrypt the packet using Counter Mode encryption */
+ if (policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION
+ || policy.getEncType() == SRTPPolicy.TWOFISH_ENCRYPTION) {
+ processPacketAESCM(pkt, index);
+ }
+
+ /* Decrypt the packet using F8 Mode encryption */
+ else if (policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION
+ || policy.getEncType() == SRTPPolicy.TWOFISHF8_ENCRYPTION) {
+ processPacketAESF8(pkt, index);
+ }
+ }
+ update(index);
+
+ return true;
+ }
+
+ /**
+ * Perform Counter Mode AES encryption / decryption
+ * @param pkt the RTP packet to be encrypted / decrypted
+ */
+ public void processPacketAESCM(RawPacket pkt, int index) {
+ long ssrc = pkt.GetRTCPSSRC();
+
+ /* Compute the CM IV (refer to chapter 4.1.1 in RFC 3711):
+ *
+ * k_s XX XX XX XX XX XX XX XX XX XX XX XX XX XX
+ * SSRC XX XX XX XX
+ * index XX XX XX XX
+ * ------------------------------------------------------XOR
+ * IV XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ */
+ ivStore[0] = saltKey[0];
+ ivStore[1] = saltKey[1];
+ ivStore[2] = saltKey[2];
+ ivStore[3] = saltKey[3];
+
+ // The shifts transform the ssrc and index into network order
+ ivStore[4] = (byte) (((ssrc >> 24) & 0xff) ^ this.saltKey[4]);
+ ivStore[5] = (byte) (((ssrc >> 16) & 0xff) ^ this.saltKey[5]);
+ ivStore[6] = (byte) (((ssrc >> 8) & 0xff) ^ this.saltKey[6]);
+ ivStore[7] = (byte) ((ssrc & 0xff) ^ this.saltKey[7]);
+
+ ivStore[8] = saltKey[8];
+ ivStore[9] = saltKey[9];
+
+ ivStore[10] = (byte) (((index >> 24) & 0xff) ^ this.saltKey[10]);
+ ivStore[11] = (byte) (((index >> 16) & 0xff) ^ this.saltKey[11]);
+ ivStore[12] = (byte) (((index >> 8) & 0xff) ^ this.saltKey[12]);
+ ivStore[13] = (byte) ((index & 0xff) ^ this.saltKey[13]);
+
+ ivStore[14] = ivStore[15] = 0;
+
+ // Encrypted part excludes fixed header (8 bytes)
+ final int payloadOffset = 8;
+ final int payloadLength = pkt.getLength() - payloadOffset;
+
+ cipherCtr.process(cipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
+ payloadLength, ivStore);
+ }
+
+ /**
+ * Perform F8 Mode AES encryption / decryption
+ *
+ * @param pkt the RTP packet to be encrypted / decrypted
+ */
+ public void processPacketAESF8(RawPacket pkt, int index) {
+ // byte[] iv = new byte[16];
+
+ // 4 bytes of the iv are zero
+ // the first byte of the RTP header is not used.
+ ivStore[0] = 0;
+ ivStore[1] = 0;
+ ivStore[2] = 0;
+ ivStore[3] = 0;
+
+ // Need the encryption flag
+ index = index | 0x80000000;
+
+ // set the index and the encrypt flag in network order into IV
+ ivStore[4] = (byte) (index >> 24);
+ ivStore[5] = (byte) (index >> 16);
+ ivStore[6] = (byte) (index >> 8);
+ ivStore[7] = (byte) index;
+
+ // The fixed header follows and fills the rest of the IV
+ System.arraycopy(pkt.getBuffer(), pkt.getOffset(), ivStore, 8, 8);
+
+ // Encrypted part excludes fixed header (8 bytes), index (4 bytes), and
+ // authentication tag (variable according to policy)
+ final int payloadOffset = 8;
+ final int payloadLength = pkt.getLength() - (4 + policy.getAuthTagLength());
+
+ SRTPCipherF8.process(cipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
+ payloadLength, ivStore, encKey, saltKey, cipherF8);
+ }
+
+ /**
+ * Authenticate a packet.
+ *
+ * Calculated authentication tag is stored in tagStore area.
+ *
+ * @param pkt the RTP packet to be authenticated
+ */
+ private void authenticatePacket(RawPacket pkt, int index) {
+
+ mac.update(pkt.getBuffer(), 0, pkt.getLength());
+ // byte[] rb = new byte[4];
+ rbStore[0] = (byte) (index >> 24);
+ rbStore[1] = (byte) (index >> 16);
+ rbStore[2] = (byte) (index >> 8);
+ rbStore[3] = (byte) index;
+ mac.update(rbStore, 0, rbStore.length);
+ mac.doFinal(tagStore, 0);
+ }
+
+ /**
+ * Checks if a packet is a replayed on based on its sequence number.
+ *
+ * This method supports a 64 packet history relative the the given
+ * sequence number.
+ *
+ * Sequence Number is guaranteed to be real (not faked) through
+ * authentication.
+ *
+ * @param index index number of the SRTCP packet
+ * @return true if this sequence number indicates the packet is not a
+ * replayed one, false if not
+ */
+ boolean checkReplay(int index) {
+ // compute the index of previously received packet and its
+ // delta to the new received packet
+ long delta = index - receivedIndex;
+
+ if (delta > 0) {
+ /* Packet not yet received */
+ return true;
+ } else {
+ if (-delta > REPLAY_WINDOW_SIZE) {
+ /* Packet too old */
+ return false;
+ } else {
+ if (((this.replayWindow >> (-delta)) & 0x1) != 0) {
+ /* Packet already received ! */
+ return false;
+ } else {
+ /* Packet not yet received */
+ return true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute the initialization vector, used later by encryption algorithms,
+ * based on the label.
+ *
+ * @param label label specified for each type of iv
+ */
+ private void computeIv(byte label) {
+
+ for (int i = 0; i < 14; i++) {
+ ivStore[i] = masterSalt[i];
+ }
+ ivStore[7] ^= label;
+ ivStore[14] = ivStore[15] = 0;
+ }
+
+ /**
+ * Derives the srtcp session keys from the master key.
+ *
+ */
+ public void deriveSrtcpKeys() {
+ // compute the session encryption key
+ byte label = 3;
+ computeIv(label);
+
+ KeyParameter encryptionKey = new KeyParameter(masterKey);
+ cipher.init(true, encryptionKey);
+ cipherCtr.getCipherStream(cipher, encKey, policy.getEncKeyLength(), ivStore);
+
+ if (authKey != null) {
+ label = 4;
+ computeIv(label);
+ cipherCtr.getCipherStream(cipher, authKey, policy.getAuthKeyLength(), ivStore);
+
+ switch ((policy.getAuthType())) {
+ case SRTPPolicy.HMACSHA1_AUTHENTICATION:
+ KeyParameter key = new KeyParameter(authKey);
+ mac.init(key);
+ break;
+
+ case SRTPPolicy.SKEIN_AUTHENTICATION:
+ // Skein MAC uses number of bits as MAC size, not just bytes
+ ParametersForSkein pfs = new ParametersForSkein(new KeyParameter(authKey),
+ ParametersForSkein.Skein512, tagStore.length*8);
+ mac.init(pfs);
+ break;
+ }
+ }
+ // compute the session salt
+ label = 5;
+ computeIv(label);
+ cipherCtr.getCipherStream(cipher, saltKey, policy.getSaltKeyLength(), ivStore);
+
+ // As last step: initialize cipher with derived encryption key.
+ encryptionKey = new KeyParameter(encKey);
+ cipher.init(true, encryptionKey);
+ }
+
+
+ /**
+ * Update the SRTP packet index.
+ *
+ * This method is called after all checks were successful.
+ *
+ * @param index index number of the accepted packet
+ */
+ private void update(int index) {
+ int delta = receivedIndex - index;
+
+ /* update the replay bit mask */
+ if( delta > 0 ){
+ replayWindow = replayWindow << delta;
+ replayWindow |= 1;
+ }
+ else {
+ replayWindow |= ( 1 << delta );
+ }
+
+ receivedIndex = index;
+ }
+
+ /**
+ * Derive a new SRTPCryptoContext for use with a new SSRC
+ *
+ * This method returns a new SRTPCryptoContext initialized with the data of
+ * this SRTPCryptoContext. Replacing the SSRC, Roll-over-Counter, and the
+ * key derivation rate the application cab use this SRTPCryptoContext to
+ * encrypt / decrypt a new stream (Synchronization source) inside one RTP
+ * session.
+ *
+ * Before the application can use this SRTPCryptoContext it must call the
+ * deriveSrtpKeys method.
+ *
+ * @param ssrc
+ * The SSRC for this context
+ * @return a new SRTPCryptoContext with all relevant data set.
+ */
+ public SRTCPCryptoContext deriveContext(long ssrc) {
+ SRTCPCryptoContext pcc = null;
+ pcc = new SRTCPCryptoContext(ssrc, masterKey,
+ masterSalt, policy);
+ return pcc;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPTransformer.java b/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPTransformer.java index 876b5bb..169aea2 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPTransformer.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTCPTransformer.java @@ -6,6 +6,8 @@ */ package net.java.sip.communicator.impl.neomedia.transform.srtp; +import java.util.Hashtable; + import net.java.sip.communicator.impl.neomedia.*; import net.java.sip.communicator.impl.neomedia.transform.*; @@ -21,6 +23,13 @@ public class SRTCPTransformer implements PacketTransformer { + private SRTPTransformEngine engine; + + /** + * All the known SSRC's corresponding SRTCPCryptoContexts + */ + private Hashtable<Long,SRTCPCryptoContext> contexts; + /** * Constructs a SRTCPTransformer object * @@ -28,6 +37,8 @@ public class SRTCPTransformer */ public SRTCPTransformer(SRTPTransformEngine engine) { + this.engine = engine; + this.contexts = new Hashtable<Long,SRTCPCryptoContext>(); } /** @@ -41,6 +52,21 @@ public class SRTCPTransformer */ public RawPacket transform(RawPacket pkt) { + long ssrc = pkt.GetRTCPSSRC(); + + SRTCPCryptoContext context = this.contexts + .get(new Long(ssrc)); + + if (context == null) { + context = this.engine.getDefaultContextControl().deriveContext(ssrc); + if (context != null) { + context.deriveSrtcpKeys(); + contexts.put(new Long(ssrc), context); + } + } + if (context != null) { + context.transformPacket(pkt); + } return pkt; } @@ -55,6 +81,23 @@ public class SRTCPTransformer */ public RawPacket reverseTransform(RawPacket pkt) { + long ssrc = pkt.GetRTCPSSRC(); + SRTCPCryptoContext context = this.contexts.get(new Long(ssrc)); + + if (context == null) { + context = this.engine.getDefaultContextControl().deriveContext(ssrc); + if (context != null) { + context.deriveSrtcpKeys(); + this.contexts.put(new Long(ssrc), context); + } + } + + if (context != null) { + boolean validPacket = context.reverseTransformPacket(pkt); + if (!validPacket) { + return null; + } + } return pkt; } } diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformEngine.java index 48aedfb..a69752c 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformEngine.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/srtp/SRTPTransformEngine.java @@ -47,6 +47,12 @@ public class SRTPTransformEngine private SRTPCryptoContext defaultContext; /** + * The default SRTPCryptoContext, which will be used to derive other + * contexts. + */ + private SRTCPCryptoContext defaultContextControl; + + /** * Construct a SRTPTransformEngine based on given master encryption key, * master salt key and SRTP/SRTCP policy. * @@ -71,6 +77,10 @@ public class SRTPTransformEngine this.masterKey, this.masterSalt, this.srtpPolicy); + this.defaultContextControl = new SRTCPCryptoContext(0, + this.masterKey, + this.masterSalt, + this.srtpPolicy); } /** @@ -142,4 +152,13 @@ public class SRTPTransformEngine { return this.defaultContext; } + + /** + * Get the default SRTPCryptoContext + * + * @return the default SRTPCryptoContext + */ + public SRTCPCryptoContext getDefaultContextControl() { + return this.defaultContextControl; + } } diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPCTransformer.java b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTCPTransformer.java index dc854ca..17f7a44 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPCTransformer.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTCPTransformer.java @@ -14,15 +14,25 @@ import net.java.sip.communicator.impl.neomedia.transform.*; * * @author Werner Dittmann <Werner.Dittmann@t-online.de> */ -public class ZRTPCTransformer +public class ZRTCPTransformer implements PacketTransformer { /** + * We support different SRTCP contexts for input and output traffic: + * + * Transform() uses the srtcpOut to perform encryption + * reverseTransform() uses srtcpIn to perform decryption + */ + private PacketTransformer srtcpIn = null; + + private PacketTransformer srtcpOut = null; + + /** * Constructs a ZRTCPTransformer object * * @param engine The associated ZRTPTransformEngine object */ - public ZRTPCTransformer(ZRTPTransformEngine engine) + public ZRTCPTransformer(ZRTPTransformEngine engine) { } @@ -37,7 +47,10 @@ public class ZRTPCTransformer */ public RawPacket transform(RawPacket pkt) { - return pkt; + if (srtcpOut == null) { + return pkt; + } + return srtcpOut.transform(pkt); } /** @@ -51,6 +64,23 @@ public class ZRTPCTransformer */ public RawPacket reverseTransform(RawPacket pkt) { - return pkt; + if (srtcpIn == null) { + return pkt; + } + return srtcpIn.reverseTransform(pkt); + + } + /** + * @param srtcpIn the srtcpIn to set + */ + public void setSrtcpIn(PacketTransformer srtcpIn) { + this.srtcpIn = srtcpIn; + } + + /** + * @param srtcpOut the srtcpOut to set + */ + public void setSrtcpOut(PacketTransformer srtcpOut) { + this.srtcpOut = srtcpOut; } } diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java index b42f5f4..d4a1345 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/ZRTPTransformEngine.java @@ -356,6 +356,10 @@ public class ZRTPTransformEngine */ private boolean muted = false; + private boolean mitmMode = false; + + private ZRTCPTransformer zrtcpTransformer = null; + /** * Construct a ZRTPTransformEngine. * @@ -372,7 +376,7 @@ public class ZRTPTransformEngine */ public PacketTransformer getRTCPTransformer() { - return new ZRTPCTransformer(this); + return new ZRTCPTransformer(this); } /** @@ -503,7 +507,7 @@ public class ZRTPTransformEngine config.setStandardConfig(); } - zrtpEngine = new ZRtp(zf.getZid(), this, clientIdString, config); + zrtpEngine = new ZRtp(zf.getZid(), this, clientIdString, config, mitmMode); if (timeoutProvider == null) { @@ -796,6 +800,7 @@ public class ZRTPTransformEngine srtpPolicy, srtpPolicy); srtpOutTransformer = engine.getRTPTransformer(); + zrtcpTransformer.setSrtcpOut(engine.getRTCPTransformer()); } else { @@ -810,6 +815,7 @@ public class ZRTPTransformEngine .getKeyResponder(), secrets.getSaltResponder(), srtpPolicy, srtpPolicy); srtpOutTransformer = engine.getRTPTransformer(); + zrtcpTransformer.setSrtcpOut(engine.getRTCPTransformer()); } } @@ -831,6 +837,7 @@ public class ZRTPTransformEngine .getKeyResponder(), secrets.getSaltResponder(), srtpPolicy, srtpPolicy); srtpInTransformer = engine.getRTPTransformer(); + zrtcpTransformer.setSrtcpIn(engine.getRTCPTransformer()); this.muted = false; } else @@ -846,6 +853,7 @@ public class ZRTPTransformEngine .getKeyInitiator(), secrets.getSaltInitiator(), srtpPolicy, srtpPolicy); srtpInTransformer = engine.getRTPTransformer(); + zrtcpTransformer.setSrtcpIn(engine.getRTCPTransformer()); this.muted = false; } } @@ -979,7 +987,7 @@ public class ZRTPTransformEngine * Zrtp ask for Enrollment. * @param info supplied info. */ - public void zrtpAskEnrollment(String info) + public void zrtpAskEnrollment(ZrtpCodes.InfoEnrollment info) { if (securityEventManager != null) { @@ -992,7 +1000,7 @@ public class ZRTPTransformEngine * @param info * @see gnu.java.zrtp.ZrtpCallback#zrtpInformEnrollment(java.lang.String) */ - public void zrtpInformEnrollment(String info) + public void zrtpInformEnrollment(ZrtpCodes.InfoEnrollment info) { if (securityEventManager != null) { @@ -1096,17 +1104,6 @@ public class ZRTPTransformEngine zrtpEngine.setAuxSecret(data); } - - /** - * Sets the PBX secret data - * - * @param data The PBX secret data - */ - public void setPbxSecret(byte[] data) { - if (zrtpEngine != null) - zrtpEngine.setPbxSecret(data); - } - /** * Sets the client ID * @@ -1192,6 +1189,101 @@ public class ZRTPTransformEngine } /** + * Get the commited SAS rendering algorithm for this ZRTP session. + * + * @return the commited SAS rendering algorithm + */ + public ZrtpConstants.SupportedSASTypes getSasType() { + if (zrtpEngine != null) + return zrtpEngine.getSasType(); + else + return null; + } + + /** + * Get the computed SAS hash for this ZRTP session. + * + * @return a refernce to the byte array that contains the full + * SAS hash. + */ + public byte[] getSasHash() { + if (zrtpEngine != null) + return zrtpEngine.getSasHash(); + else + return null; + } + + /** + * Send the SAS relay packet. + * + * The method creates and sends a SAS relay packet according to the ZRTP + * specifications. Usually only a MitM capable user agent (PBX) uses this + * function. + * + * @param sh the full SAS hash value + * @param render the SAS rendering algorithm + */ + public boolean sendSASRelayPacket(byte[] sh, ZrtpConstants.SupportedSASTypes render) { + if (zrtpEngine != null) + return zrtpEngine.sendSASRelayPacket(sh, render); + else + return false; + } + /** + * Check the state of the MitM mode flag. + * + * If true then this ZRTP session acts as MitM, usually enabled by a PBX + * based client (user agent) + * + * @return state of mitmMode + */ + public boolean isMitmMode() { + return mitmMode; + } + + /** + * Set the state of the MitM mode flag. + * + * If MitM mode is set to true this ZRTP session acts as MitM, usually + * enabled by a PBX based client (user agent). + * + * @param mitmMode defines the new state of the mitmMode flag + */ + public void setMitmMode(boolean mitmMode) { + this.mitmMode = mitmMode; + } + + /** + * Check the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and performs the enrollment actions. A MitM (PBX) enrollment service sets this flagstarted this ZRTP + * session. Can be set to true only if mitmMode is also true. + * @return status of the enrollmentMode flag. + */ + public boolean isEnrollmentMode() { + if (zrtpEngine != null) + return zrtpEngine.isEnrollmentMode(); + else + return false; + } + + /** + * Set the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and perform the enrollment actions. A MitM (PBX) enrollment + * service must sets this mode to true. + * + * Can be set to true only if mitmMode is also true. + * + * @param enrollmentMode defines the new state of the enrollmentMode flag + */ + public void setEnrollmentMode(boolean enrollmentMode) { + if (zrtpEngine != null) + zrtpEngine.setEnrollmentMode(enrollmentMode); + } + /** * Sets signature data for the Confirm packets * * @param data the signature data @@ -1226,16 +1318,6 @@ public class ZRTPTransformEngine return ((zrtpEngine != null) ? zrtpEngine.getSignatureLength() : 0); } - /** - * Sets the PBX enrollment flag (see chapter 8.3 of ZRTP standards) - * (The PBX part needs further development) - * @param yesNo The PBX enrollment flag - */ - public void setPBXEnrollment(boolean yesNo) - { - if (zrtpEngine != null) - zrtpEngine.setPBXEnrollment(yesNo); - } /** * Method called by the Zrtp class as result of a GoClear request from the @@ -1302,8 +1384,8 @@ public class ZRTPTransformEngine * * @return the ZID data as byte array. */ - public byte[] getZid() + public byte[] getPeerZid() { - return ((zrtpEngine != null) ? zrtpEngine.getZid() : null); + return ((zrtpEngine != null) ? zrtpEngine.getPeerZid() : null); } } |