Modify security relevant code to get rid of Java JCE dependence. This simplifies distribution

and istallation of SC - no Sun/Java crypto policy files are required anymore. This check-in
also include a new zrtp4j lib (1.4.3) that implements draft 13 and has no JCE dependency.
cusax-fix
Werner Dittmann 17 years ago
parent 27e83ca696
commit ded3a87b1f

@ -1916,13 +1916,13 @@ javax.swing.event, javax.swing.border"/>
<jar compress="true" destfile="${bundles.dest}/zrtp4j.jar"
filesetmanifest="merge">
<zipfileset src="${lib.noinst}/zrtp4j-1.4.2.jar" prefix=""/>
<zipfileset src="${lib.noinst}/zrtp4j-1.4.3.jar" prefix=""/>
<manifest>
<attribute name="Export-Package" value="gnu.java.zrtp,gnu.java.zrtp.packets,gnu.java.zrtp.utils,gnu.java.zrtp.zidfile"/>
<attribute name="Import-Package" value="javax.crypto,javax.crypto.spec,javax.crypto.interfaces"/>
<attribute name="Bundle-Name" value="ZRTP4J"/>
<attribute name="Bundle-Description" value="ZRTP for Java library."/>
<attribute name="Bundle-Version" value="1.4.2"/>
<attribute name="Bundle-Version" value="1.4.3"/>
<attribute name="System-Bundle" value="yes"/>
</manifest>
</jar>

@ -46,7 +46,7 @@
<classpathentry kind="lib" path="lib/installer-exclude/swing-worker-1.2.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/transparency.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/ymsg_network_v0_63.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/zrtp4j-1.4.2.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/zrtp4j-1.4.3.jar"/>
<classpathentry kind="lib" path="lib/os-specific/linux/installer-exclude/jmf.jar"/>
<classpathentry kind="lib" path="lib/os-specific/linux/jdic_stub.jar"/>
<classpathentry kind="lib" path="lib/os-specific/mac/installer-exclude/jmf.jar"/>

@ -122,7 +122,7 @@
<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
<compilation-unit>
<package-root>src</package-root>
<classpath mode="compile">lib/bcprovider.jar:lib/felix.jar:lib/jdic-all.jar:lib/bundle/junit.jar:lib/bundle/log4j.jar:lib/installer-exclude/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/dict4j.jar:lib/installer-exclude/dnsjava-2.0.3.jar:lib/installer-exclude/jain-sip-api-1.2.jar:lib/installer-exclude/jain-sip-ri-1.2.91.jar:lib/installer-exclude/jcalendar-1.3.2.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/jdom.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b2.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/junit.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/rome-0.9.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/smackx-jingle.jar:lib/installer-exclude/Stun4J.jar:lib/installer-exclude/ymsg_network_v0_63.jar:lib/installer-exclude/fmj.jar:lib/installer-exclude/jna.jar:lib/installer-exclude/lti-civil-no_s_w_t.jar:lib/installer-exclude/swing-worker-1.2.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/AppleJavaExtensions.jar:lib/os-specific/mac/growl.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/mac/installer-exclude/dock.jar:lib/os-specific/windows/jdic_stub.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar:lib/installer-exclude/aclibico-2.1.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/pircbot.jar:lib/os-specific/solaris/jdic_stub.jar:lib/os-specific/solaris/installer-exclude/jmf.jar:lib/installer-exclude/jsch-0.1.36.jar:lib/installer-exclude/apache-ant-1.7.0.jar:lib/installer-exclude/izpack-shortcut-link.jar:lib/installer-exclude/jfontchooser-1.0.5.jar:lib/installer-exclude/KeybindingUtil.jar:lib/installer-exclude/laf-widget.jar:lib/installer-exclude/transparency.jar:lib/installer-exclude/zrtp4j-1.4.2.jar:lib/installer-exclude/profiler4j-1.0-beta3-SC.jar</classpath>
<classpath mode="compile">lib/bcprovider.jar:lib/felix.jar:lib/jdic-all.jar:lib/bundle/junit.jar:lib/bundle/log4j.jar:lib/installer-exclude/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/dict4j.jar:lib/installer-exclude/dnsjava-2.0.3.jar:lib/installer-exclude/jain-sip-api-1.2.jar:lib/installer-exclude/jain-sip-ri-1.2.91.jar:lib/installer-exclude/jcalendar-1.3.2.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/jdom.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b2.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/junit.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/rome-0.9.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/smackx-jingle.jar:lib/installer-exclude/Stun4J.jar:lib/installer-exclude/ymsg_network_v0_63.jar:lib/installer-exclude/fmj.jar:lib/installer-exclude/jna.jar:lib/installer-exclude/lti-civil-no_s_w_t.jar:lib/installer-exclude/swing-worker-1.2.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/AppleJavaExtensions.jar:lib/os-specific/mac/growl.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/mac/installer-exclude/dock.jar:lib/os-specific/windows/jdic_stub.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar:lib/installer-exclude/aclibico-2.1.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/pircbot.jar:lib/os-specific/solaris/jdic_stub.jar:lib/os-specific/solaris/installer-exclude/jmf.jar:lib/installer-exclude/jsch-0.1.36.jar:lib/installer-exclude/apache-ant-1.7.0.jar:lib/installer-exclude/izpack-shortcut-link.jar:lib/installer-exclude/jfontchooser-1.0.5.jar:lib/installer-exclude/KeybindingUtil.jar:lib/installer-exclude/laf-widget.jar:lib/installer-exclude/transparency.jar:lib/installer-exclude/zrtp4j-1.4.3.jar:lib/installer-exclude/profiler4j-1.0-beta3-SC.jar</classpath>
<built-to>classes</built-to>
<source-level>1.5</source-level>
</compilation-unit>

@ -2103,13 +2103,11 @@ private void initializeRtpManager(RTPManager rtpManager,
selectedKeyProviderAlgorithm.getProviderType()
== KeyProviderAlgorithm.ProviderType.ZRTP_PROVIDER)
{
TransformManager.initializeProviders();
// The connector is created based also on the crypto services
// The crypto provider solution should be queried somehow
// or taken from a resources file
TransformConnector transConnector = TransformManager.createZRTPConnector(
bindAddress, "BouncyCastle", this);
bindAddress, this);
rtpManager.initialize(transConnector);
this.transConnectors.put(rtpManager, transConnector);

@ -17,6 +17,7 @@
* Besides packet info storage, RawPacket also provides some other operations
* such as readInt() to ease the development process.
*
* @author Werner Dittmann (Werner.Dittmann@t-online.de)
* @author Bing SU (nova.su@gmail.com)
*/
public class RawPacket
@ -172,6 +173,29 @@ public byte[] readRegion(int off, int len)
return region;
}
/**
* Read a byte region from specified offset with specified length in given buffer
*
* @param off start offset of the region to be read
* @param len length of the region to be read
* @param outBuff output buffer
*/
public void readRegionToBuff(int off, int len, byte[] outBuff)
{
int startOffset = this.offset + off;
if (off < 0 || len <= 0
|| startOffset + len > this.buffer.length)
{
return;
}
if (outBuff.length < len)
{
return;
}
System.arraycopy(this.buffer, startOffset, outBuff, 0, len);
}
/**
* Append a byte array to then end of the packet. This will change the data
* buffer of this packet.

@ -6,9 +6,6 @@
*/
package net.java.sip.communicator.impl.media.transform;
import java.security.*;
import java.util.*;
import javax.media.rtp.*;
import net.java.sip.communicator.impl.media.transform.dummy.*;
@ -25,60 +22,7 @@
* @author Emanuel Onica (eonica@info.uaic.ro)
*/
public class TransformManager
{
/**
* Map of supported cryptography services providers
*/
public static HashMap cryptoProviders = null;
/**
* Initialize the supported cryptography services providers
*/
public static void initializeProviders()
{
if (cryptoProviders == null)
{
cryptoProviders = new HashMap();
Provider cryptoProvider = null ;
try
{
Class<?> c = Class
.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
cryptoProvider = (Provider) c.newInstance();
}
catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InstantiationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
cryptoProviders.put("BouncyCastle", cryptoProvider);
}
}
/**
* Select a cryptography services provider to use from the supported ones
*
* @param cryptoProvider cryptography provider selection string
* @return the actual cryptography provider to use
*/
public static Provider selectProvider(String cryptoProvider)
{
return (Provider)cryptoProviders.get(cryptoProvider);
}
{
/**
* Create a SRTP TransformConnector, which will provide SRTP encryption /
@ -99,30 +43,16 @@ public static TransformConnector createSRTPConnector(SessionAddress addr,
byte[] masterKey,
byte[] masterSalt,
SRTPPolicy srtpPolicy,
SRTPPolicy srtcpPolicy,
String cryptoProvider)
SRTPPolicy srtcpPolicy)
throws InvalidSessionAddressException
{
SRTPTransformEngine engine = null;
Provider cp = selectProvider(cryptoProvider);
try
{
engine = new SRTPTransformEngine(masterKey,
masterSalt,
srtpPolicy,
srtcpPolicy,
cp);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
return null;
}
engine = new SRTPTransformEngine(masterKey, masterSalt, srtpPolicy,
srtcpPolicy);
TransformConnector connector = null;
connector = new TransformConnector(addr, engine);
return connector;
@ -138,7 +68,6 @@ public static TransformConnector createSRTPConnector(SessionAddress addr,
* @throws InvalidSessionAddressException
*/
public static TransformConnector createZRTPConnector(SessionAddress addr,
String cryptoProvider,
CallSession callSession)
throws InvalidSessionAddressException
{
@ -147,8 +76,6 @@ public static TransformConnector createZRTPConnector(SessionAddress addr,
//connector as a parameter
ZRTPTransformEngine engine = new ZRTPTransformEngine();
Provider cp = selectProvider(cryptoProvider);
TransformConnector connector = null;
connector = new ZrtpTransformConnector(addr, engine);
@ -160,8 +87,6 @@ public static TransformConnector createZRTPConnector(SessionAddress addr,
//stream has it's own connector
engine.setConnector(connector);
engine.setCryptoProvider(cp);
return connector;
}

@ -1,99 +0,0 @@
/*
* 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.media.transform.srtp;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.params.*;
/**
* AESCipher encapsulate basic AES encryption / decryption algorithm.
* It encrypts and decrypts data in 128bit blocks. This implementation is based
* on Bouncy Castle (http://www.bouncycastle.org).
*
* @author Bing SU (nova.su@gmail.com)
*/
public class AESCipher
{
/**
* AES cipher block size in bytes
*/
public final static int BLOCK_SIZE = 16;
/**
* The encryption key we are using
*/
private KeyParameter keyParam;
/**
* Bouncy Castle's AES cipher object
*/
private AESFastEngine aesCipher;
/**
* Construct an AESCipher using specified key, which is 128bit long
*
* @param key encryption key for this AESCipher
*/
public AESCipher(byte[] key)
{
// Currently we only support 128 bit AES encryption
if (key == null || key.length != 16)
{
throw new RuntimeException("Invalid key length (BUG).");
}
this.aesCipher = new AESFastEngine();
this.keyParam = new KeyParameter(key);
}
/**
* Encrypt a 128bit block using AES cipher
*
* @param in byte array containing the block to encrypted
* @param inOff start offset of the block inside byte array
* @param out output byte array storing the encrypted block
* @param outOff start offset of encrypted block inside output array
*/
public void encryptBlock(byte[] in, int inOff, byte[] out, int outOff)
{
this.aesCipher.init(true, this.keyParam);
this.aesCipher.processBlock(in, inOff, out, outOff);
}
/**
* Decrypt a 128bit block using AES cipher
*
* @param in byte array containing the block to be decrypted
* @param inOff start offset of the block inside byte array
* @param out output byte array holding the decrypted block
* @param outOff start offset of decrypted block inside output array
*/
public void decryptBlock(byte[] in, int inOff, byte[] out, int outOff)
{
this.aesCipher.init(false, this.keyParam);
this.aesCipher.processBlock(in, inOff, out, outOff);
}
}

@ -25,8 +25,8 @@
*/
package net.java.sip.communicator.impl.media.transform.srtp;
import javax.crypto.*;
import java.security.*;
import org.bouncycastle.crypto.engines.AESFastEngine;
/**
* SRTPCipherCTR implements SRTP Counter Mode AES Encryption (AES-CM).
@ -56,74 +56,75 @@
*/
public class SRTPCipherCTR
{
/**
* Process (encrypt / decrypt) a byte stream, using the supplied
* initial vector.
*
* @param aesCipher the AESCihper object we use to do basic AES encryption / decryption
* @param data byte array containing the byte stream to be processed
* @param off byte stream star offset with data byte array
* @param len byte stream length in bytes
* @param iv initial vector for this operation
private final static int BLKLEN = 16;
private final static int MAX_BUFFER_LENGTH = 10*1024;
private final byte[] cipherInBlock = new byte[BLKLEN];
private final byte[] tmpCipherBlock = new byte[BLKLEN];
private byte[] streamBuf = new byte[1024];
public SRTPCipherCTR() {
}
/* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.transform.srtp.
* SRTPCipher#process(byte[], int, int, byte[])
*/
public static void process(Cipher aesCipher, byte[] data, int off, int len, byte[] iv)
{
if (off + len > data.length)
{
// TODO this is invalid, need error handling
public void process(AESFastEngine aesCipher, byte[] data, int off, int len,
byte[] iv) {
if (off + len > data.length) {
return;
}
byte[] cipherStream = new byte[len];
// if data fits in inter buffer - use it. Otherwise allocate bigger
// buffer store it to use it for later processing - up to a defined
// maximum size.
byte[] cipherStream = null;
if (len > streamBuf.length) {
cipherStream = new byte[len];
if (cipherStream.length <= MAX_BUFFER_LENGTH) {
streamBuf = cipherStream;
}
}
else {
cipherStream = streamBuf;
}
getCipherStream(aesCipher, cipherStream, len, iv);
for (int i = 0; i < len; i++)
{
for (int i = 0; i < len; i++) {
data[i + off] ^= cipherStream[i];
}
}
/**
* Computes the cipher stream for AES CM mode.
* See section 4.1.1 in RFC3711 for detailed description.
* Computes the cipher stream for AES CM mode. See section 4.1.1 in RFC3711
* for detailed description.
*
* @param aesCipher the AESCihper object we use to do basic AES encryption / decryption
* @param out byte array holding the output cipher stream
* @param length length of the cipher stream to produce, in bytes
* @param iv initialization vector used to generate this cipher stream
* @param out
* byte array holding the output cipher stream
* @param length
* length of the cipher stream to produce, in bytes
* @param iv
* initialization vector used to generate this cipher stream
*/
public static void getCipherStream(Cipher aesCipher, byte[] out, int length, byte[] iv)
public void getCipherStream(AESFastEngine aesCipher, byte[] out, int length, byte[] iv)
{
final int BLKLEN = 16;
byte[] in = new byte[BLKLEN];
byte[] tmp = new byte[BLKLEN];
System.arraycopy(iv, 0, cipherInBlock, 0, 14);
System.arraycopy(iv, 0, in, 0, 14);
int ctr;
for (ctr = 0; ctr < length / BLKLEN; ctr++) {
// compute the cipher stream
cipherInBlock[14] = (byte) ((ctr & 0xFF00) >> 8);
cipherInBlock[15] = (byte) ((ctr & 0x00FF));
try
{
int ctr;
for (ctr = 0; ctr < length / BLKLEN; ctr++)
{
// compute the cipher stream
in[14] = (byte) ((ctr & 0xFF00) >> 8);
in[15] = (byte) ((ctr & 0x00FF));
aesCipher.update(in, 0, BLKLEN, out, ctr * BLKLEN);
}
aesCipher.processBlock(cipherInBlock, 0, out, ctr * BLKLEN);
}
// Treat the last bytes:
in[14] = (byte) ((ctr & 0xFF00) >> 8);
in[15] = (byte) ((ctr & 0x00FF));
// Treat the last bytes:
cipherInBlock[14] = (byte) ((ctr & 0xFF00) >> 8);
cipherInBlock[15] = (byte) ((ctr & 0x00FF));
aesCipher.doFinal(in, 0, BLKLEN, tmp, 0);
System.arraycopy(tmp, 0, out, ctr * BLKLEN, length % BLKLEN);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
aesCipher.processBlock(cipherInBlock, 0, tmpCipherBlock, 0);
System.arraycopy(tmpCipherBlock, 0, out, ctr * BLKLEN, length % BLKLEN);
}
}

@ -26,9 +26,9 @@
package net.java.sip.communicator.impl.media.transform.srtp;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.params.KeyParameter;
/**
* SRTPCipherF8 implements SRTP F8 Mode AES Encryption (AES-f8).
@ -54,6 +54,7 @@
*
* We use AESCipher to handle basic AES encryption / decryption.
*
* @author Werner Dittmann (Werner.Dittmann@t-online.de)
* @author Bing SU (nova.su@gmail.com)
*/
public class SRTPCipherF8
@ -75,22 +76,12 @@ class F8Context
long J;
}
/**
* Process (encrypt / decrypt) a byte stream, using the supplied
* initial vector.
*
* @param aesCipher the AES cipher object used for block processing
* @param data byte array containing the byte stream to be processed
* @param off byte stream star offset with data byte array
* @param len byte stream length in bytes
* @param iv initial vector for this operation
* @param key the encryption key
* @param salt the salt key
* @param f8Cipher the F8 cipher object used for iv processing
/* (non-Javadoc)
* @see net.java.sip.communicator.impl.media.transform.srtp.
* SRTPCipher#process(byte[], int, int, byte[])
*/
public static void process(Cipher aesCipher, byte[] data, int off, int len, byte[] iv,
byte[] key, byte[] salt, Cipher f8Cipher)
{
public static void process(AESFastEngine aesCipher, byte[] data, int off, int len,
byte[] iv, byte[] key, byte[] salt, AESFastEngine f8Cipher) {
F8Context f8ctx = new SRTPCipherF8().new F8Context();
/*
@ -102,96 +93,81 @@ public static void process(Cipher aesCipher, byte[] data, int off, int len, byte
* Get memory for the special key. This is the key to compute the
* derived IV (IV').
*/
byte[] saltMask = new byte[key.length];
byte[] saltMask = new byte[key.length];
byte[] maskedKey = new byte[key.length];
/*
* First copy the salt into the mask field, then fill with 0x55 to
* get a full key.
* First copy the salt into the mask field, then fill with 0x55 to get a
* full key.
*/
System.arraycopy(salt, 0, saltMask, 0, salt.length);
for (int i = salt.length; i < saltMask.length; ++i)
{
for (int i = salt.length; i < saltMask.length; ++i) {
saltMask[i] = 0x55;
}
/*
* XOR the original key with the above created mask to
* get the special key.
* XOR the original key with the above created mask to get the special
* key.
*/
for (int i = 0; i < key.length; i++)
{
for (int i = 0; i < key.length; i++) {
maskedKey[i] = (byte) (key[i] ^ saltMask[i]);
}
/*
* Prepare the f8Cipher with the special key to compute IV'
*/
SecretKey encryptionKey = new SecretKeySpec(maskedKey, 0,
maskedKey.length, "AES");
try
{
f8Cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
/*
* Use the masked key to encrypt the original IV to produce IV'.
*/
f8Cipher.doFinal(iv, 0, BLKLEN, f8ctx.ivAccent, 0);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
KeyParameter encryptionKey = new KeyParameter(maskedKey);
f8Cipher.init(true, encryptionKey);
/*
* Use the masked key to encrypt the original IV to produce IV'.
*/
f8Cipher.processBlock(iv, 0, f8ctx.ivAccent, 0);
saltMask = null;
maskedKey = null;
f8ctx.J = 0; // initialize the counter
f8ctx.S = new byte[BLKLEN]; // get the key stream buffer
f8ctx.J = 0; // initialize the counter
f8ctx.S = new byte[BLKLEN]; // get the key stream buffer
Arrays.fill(f8ctx.S, (byte) 0);
int inLen = len;
while (inLen >= BLKLEN)
{
processBlock(aesCipher, f8ctx, data, off, data, off, BLKLEN);
while (inLen >= BLKLEN) {
processBlock(aesCipher, f8ctx, data, off, data, off, BLKLEN);
inLen -= BLKLEN;
off += BLKLEN;
}
if (inLen > 0)
{
processBlock(aesCipher, f8ctx, data, off, data, off, inLen);
if (inLen > 0) {
processBlock(aesCipher, f8ctx, data, off, data, off, inLen);
}
}
/**
* Encrypt / Decrypt a block using F8 Mode AES algorithm, read len bytes
* Encrypt / Decrypt a block using F8 Mode AES algorithm, read len bytes
* data from in at inOff and write the output into out at outOff
*
* @param aesCipher the AES cipher object used for block processing
* @param f8ctx F8 encryption context
* @param in byte array holding the data to be processed
* @param inOff start offset of the data to be processed inside in array
* @param out byte array that will hold the processed data
* @param outOff start offset of output data in out
* @param len length of the input data
* @param f8ctx
* F8 encryption context
* @param in
* byte array holding the data to be processed
* @param inOff
* start offset of the data to be processed inside in array
* @param out
* byte array that will hold the processed data
* @param outOff
* start offset of output data in out
* @param len
* length of the input data
*/
private static void processBlock(Cipher aesCipher, F8Context f8ctx, byte[] in, int inOff,
byte[] out, int outOff, int len)
{
private static void processBlock(AESFastEngine aesCipher, F8Context f8ctx,
byte[] in, int inOff, byte[] out, int outOff, int len) {
/*
* XOR the previous key stream with IV'
* ( S(-1) xor IV' )
*/
for (int i = 0; i < BLKLEN; i++)
{
for (int i = 0; i < BLKLEN; i++) {
f8ctx.S[i] ^= f8ctx.ivAccent[i];
}
@ -208,23 +184,13 @@ private static void processBlock(Cipher aesCipher, F8Context f8ctx, byte[] in,
/*
* Now compute the new key stream using AES encrypt
*/
try
{
aesCipher.doFinal(f8ctx.S, 0, BLKLEN, f8ctx.S, 0);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
aesCipher.processBlock(f8ctx.S, 0, f8ctx.S, 0);
/*
* As the last step XOR the plain text with the key stream to produce
* the cipher text.
*/
for (int i = 0; i < len; i++)
{
for (int i = 0; i < len; i++) {
out[outOff + i] = (byte) (in[inOff + i] ^ f8ctx.S[i]);
}
}

@ -26,10 +26,11 @@
*/
package net.java.sip.communicator.impl.media.transform.srtp;
import java.security.*;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import javax.crypto.*;
import javax.crypto.spec.*;
import org.bouncycastle.crypto.engines.AESFastEngine;
import net.java.sip.communicator.impl.media.transform.*;
@ -135,29 +136,27 @@ public class SRTPCryptoContext
private final SRTPPolicy policy;
/**
* The SRTPDigest object we used to do packet authentication
* The HMAC object we used to do packet authentication
*/
private SRTPDigest digest;
private HMac hmacSha1; // used for various HMAC computations
/**
* The cryptographic services provider
*/
private Provider cryptoProvider;
// The symmetric cipher engines we need here
private AESFastEngine AEScipher = null;
private AESFastEngine AEScipherF8 = null; // used inside F8 mode only
/**
* Used for various HMAC computations
*/
private Mac hmacSha1;
// 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
/**
* The AES cipher for counter mode
*/
private Cipher AEScipher = null;
private final byte[] tagStore;
private final byte[] ivStore = new byte[16];
private final byte[] rbStore = new byte[4];
/**
* The AES cipher for F8 mode
*/
private Cipher AEScipherF8 = null;
// 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.
@ -165,129 +164,122 @@ public class SRTPCryptoContext
*
* @param ssrc SSRC of this SRTPCryptoContext
*/
public SRTPCryptoContext(long ssrc)
public SRTPCryptoContext(long ssrcIn)
{
this.ssrc = ssrc;
this.mki = null;
this.roc = 0;
this.guessedROC = 0;
this.seqNum = 0;
this.keyDerivationRate = 0;
this.masterKey = null;
this.masterSalt = null;
this.encKey = null;
this.authKey = null;
this.saltKey = null;
this.seqNumSet = false;
this.policy = null;
this.cryptoProvider = null;
ssrc = ssrcIn;
mki = null;
roc = 0;
guessedROC = 0;
seqNum = 0;
keyDerivationRate = 0;
masterKey = null;
masterSalt = null;
encKey = null;
authKey = null;
saltKey = null;
seqNumSet = false;
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 roc the initial Roll-Over-Counter according to RFC 3711.
* These are the upper 32 bit of the overall 48 bit SRTP packet index.
* Refer to chapter 3.2.1 of the RFC.
* @param keyDerivationRate the key derivation rate defines when to recompute
* the SRTP session keys. Refer to chapter 4.3.1 in the RFC.
* @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
* @param cryptoProvider cryptographic services provider
*/
public SRTPCryptoContext(long ssrc,
int roc,
long keyDerivationRate,
byte[] masterKey,
byte[] masterSalt,
SRTPPolicy policy,
Provider cryptoProvider)
throws GeneralSecurityException
*
* @param ssrc
* the RTP SSRC that this SRTP cryptographic context protects.
* @param roc
* the initial Roll-Over-Counter according to RFC 3711. These are
* the upper 32 bit of the overall 48 bit SRTP packet index.
* Refer to chapter 3.2.1 of the RFC.
* @param keyDerivationRate
* the key derivation rate defines when to recompute the SRTP
* session keys. Refer to chapter 4.3.1 in the RFC.
* @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 SRTPCryptoContext(long ssrcIn, int rocIn, long kdr,
byte[] masterK, byte[] masterS, SRTPPolicy policyIn)
{
this.ssrc = ssrc;
this.mki = null;
this.roc = roc;
this.guessedROC = 0;
this.seqNum = 0;
this.keyDerivationRate = keyDerivationRate;
this.seqNumSet = false;
this.cryptoProvider = cryptoProvider;
this.policy = policy;
this.masterKey = new byte[this.policy.getEncKeyLength()];
System.arraycopy(masterKey, 0, this.masterKey, 0, this.policy.getEncKeyLength());
this.masterSalt = new byte[this.policy.getSaltKeyLength()];
System.arraycopy(masterSalt, 0, this.masterSalt, 0, this.policy.getSaltKeyLength());
switch (policy.getEncType())
{
case SRTPPolicy.NULL_ENCRYPTION:
this.encKey = null;
this.saltKey = null;
break;
case SRTPPolicy.AESCM_ENCRYPTION:
hmacSha1 = Mac.getInstance("HMACSHA1", cryptoProvider);
AEScipher = Cipher.getInstance("AES/ECB/NOPADDING", cryptoProvider);
this.encKey = new byte[this.policy.getEncKeyLength()];
this.saltKey = new byte[this.policy.getSaltKeyLength()];
break;
case SRTPPolicy.AESF8_ENCRYPTION:
hmacSha1 = Mac.getInstance("HMACSHA1", cryptoProvider);
AEScipher = Cipher.getInstance("AES/ECB/NOPADDING", cryptoProvider);
AEScipherF8 = Cipher.getInstance("AES/ECB/NOPADDING", cryptoProvider);
this.encKey = new byte[this.policy.getEncKeyLength()];
this.saltKey = new byte[this.policy.getSaltKeyLength()];
break;
}
switch (policy.getAuthType())
{
case SRTPPolicy.NULL_AUTHENTICATION:
this.authKey = null;
break;
case SRTPPolicy.HMACSHA1_AUTHENTICATION:
this.authKey = new byte[policy.getAuthKeyLength()];
break;
ssrc = ssrcIn;
mki = null;
roc = rocIn;
guessedROC = 0;
seqNum = 0;
keyDerivationRate = kdr;
seqNumSet = false;
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());
hmacSha1 = new HMac(new SHA1Digest());
AEScipher = new AESFastEngine();
switch (policy.getEncType()) {
case SRTPPolicy.NULL_ENCRYPTION:
encKey = null;
saltKey = null;
break;
case SRTPPolicy.AESF8_ENCRYPTION:
AEScipherF8 = new AESFastEngine();
case SRTPPolicy.AESCM_ENCRYPTION:
encKey = new byte[policy.getEncKeyLength()];
saltKey = new byte[policy.getSaltKeyLength()];
break;
}
switch (policy.getAuthType()) {
case SRTPPolicy.NULL_AUTHENTICATION:
authKey = null;
tagStore = null;
break;
case SRTPPolicy.HMACSHA1_AUTHENTICATION:
authKey = new byte[policy.getAuthKeyLength()];
tagStore = new byte[hmacSha1.getMacSize()];
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 this.policy.getAuthTagLength();
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 (this.mki != null)
{
return this.mki.length;
}
else
{
public int getMKILength() {
if (mki != null) {
return mki.length;
} else {
return 0;
}
}
@ -297,19 +289,17 @@ public int getMKILength()
*
* @return the SSRC of this SRTP cryptographic context
*/
public long getSSRC()
{
return this.ssrc;
public long getSSRC() {
return ssrc;
}
/**
* Get the Roll-Over-Counter of this SRTP cryptographic context
*
* @return the Roll-Over-Counter of this SRTP cryptographic context
*/
public int getROC()
{
return this.roc;
public int getROC() {
return roc;
}
/**
@ -317,9 +307,8 @@ public int getROC()
*
* @param roc the Roll-Over-Counter of this SRTP cryptographic context
*/
public void setROC(int roc)
{
this.roc = roc;
public void setROC(int rocIn) {
roc = rocIn;
}
/**
@ -339,32 +328,27 @@ public void setROC(int roc)
*
* @param pkt the RTP packet that is going to be sent out
*/
public void transformPacket(RawPacket pkt)
{
public void transformPacket(RawPacket pkt) {
/* Encrypt the packet using Counter Mode encryption */
if (this.policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION)
{
if (policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION) {
processPacketAESCM(pkt);
}
/* Encrypt the packet using F8 Mode encryption */
else if (this.policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION)
{
else if (policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION) {
processPacketAESF8(pkt);
}
/* Authenticate the packet */
if (this.policy.getAuthType() == SRTPPolicy.HMACSHA1_AUTHENTICATION)
{
byte[] tag = authenticatePacketHMCSHA1(pkt, this.roc);
pkt.append(tag, policy.getAuthTagLength());
if (policy.getAuthType() == SRTPPolicy.HMACSHA1_AUTHENTICATION) {
authenticatePacketHMCSHA1(pkt, roc);
pkt.append(tagStore, policy.getAuthTagLength());
}
/* Update the ROC if necessary */
int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
if (seqNum == 0xFFFF)
{
this.roc++;
int seqNo = PacketManipulator.GetRTPSequenceNumber(pkt);
if (seqNo == 0xFFFF) {
roc++;
}
}
@ -386,51 +370,53 @@ else if (this.policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION)
* @return true if the packet can be accepted
* false if the packet failed authentication or failed replay check
*/
public boolean reverseTransformPacket(RawPacket pkt)
{
int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
public boolean reverseTransformPacket(RawPacket pkt) {
int seqNo = PacketManipulator.GetRTPSequenceNumber(pkt);
if (!seqNumSet) {
seqNumSet = true;
seqNum = seqNo;
}
// Guess the SRTP index (48 bit), see rFC 3711, 3.3.1
// Stores the guessed roc in this.guessedROC
long guessedIndex = guessIndex(seqNo);
/* Replay control */
if (!checkReplay(seqNum)) {
if (!checkReplay(seqNo, guessedIndex)) {
return false;
}
/* Authenticate the packet */
if (this.policy.getAuthType() == SRTPPolicy.HMACSHA1_AUTHENTICATION)
{
int tagLength = this.policy.getAuthTagLength();
if (policy.getAuthType() == SRTPPolicy.HMACSHA1_AUTHENTICATION) {
int tagLength = policy.getAuthTagLength();
// get original authentication and store in tempStore
pkt.readRegionToBuff(pkt.getLength() - tagLength,
tagLength, tempStore);
byte[] originalTag =
pkt.readRegion(pkt.getLength() - tagLength, tagLength);
pkt.shrink(tagLength);
/* Guess the SRTP index (48 bit) */
long guessedIndex = guessIndex(seqNum);
int guessedRoc = (int)(guessedIndex >> 16); // extract the roc (32 bit)
byte[] calculatedTag = authenticatePacketHMCSHA1(pkt, guessedRoc);
// save computed authentication in tagStore
authenticatePacketHMCSHA1(pkt, guessedROC);
for (int i = 0; i < tagLength; i++) {
if ((originalTag[i]&0xff) == (calculatedTag[i]&0xff))
if ((tempStore[i]&0xff) == (tagStore[i]&0xff))
continue;
else
return false;
}
}
/* Decrypt the packet using Counter Mode encryption*/
if (this.policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION)
{
if (policy.getEncType() == SRTPPolicy.AESCM_ENCRYPTION) {
processPacketAESCM(pkt);
}
/* Decrypt the packet using F8 Mode encryption*/
else if (this.policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION)
{
else if (policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION) {
processPacketAESF8(pkt);
}
update(seqNum);
update(seqNo, guessedIndex);
return true;
}
@ -439,82 +425,59 @@ else if (this.policy.getEncType() == SRTPPolicy.AESF8_ENCRYPTION)
* Perform Counter Mode AES encryption / decryption
* @param pkt the RTP packet to be encrypted / decrypted
*/
public void processPacketAESCM(RawPacket pkt)
{
long ssrc = PacketManipulator.GetRTPSSRC(pkt);
int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
long index = ((long) this.roc << 16) | (long) seqNum;
public void processPacketAESCM(RawPacket pkt) {
long ssrc = PacketManipulator.GetRTPSSRC(pkt);
int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
long index = ((long) this.roc << 16) | (long) seqNum;
byte[] iv = new byte[16];
System.arraycopy(this.saltKey, 0, iv, 0, 4);
// byte[] iv = new byte[16];
ivStore[0] = saltKey[0];
ivStore[1] = saltKey[1];
ivStore[2] = saltKey[2];
ivStore[3] = saltKey[3];
int i;
for (i = 4; i < 8; i++)
{
iv[i] = (byte)((0xFF & (ssrc >> ((7 - i) * 8))) ^ this.saltKey[i]);
for (i = 4; i < 8; i++) {
ivStore[i] = (byte) ((0xFF & (ssrc >> ((7 - i) * 8))) ^ this.saltKey[i]);
}
for (i = 8; i < 14; i++)
{
iv[i] =
(byte)((0xFF & (byte)(index >> ((13 - i) * 8))) ^ this.saltKey[i]);
for (i = 8; i < 14; i++) {
ivStore[i] = (byte) ((0xFF & (byte) (index >> ((13 - i) * 8))) ^ this.saltKey[i]);
}
iv[14] = iv[15] = 0;
ivStore[14] = ivStore[15] = 0;
final int payloadOffset = PacketManipulator.GetRTPHeaderLength(pkt);
final int payloadLength = PacketManipulator.GetRTPPayloadLength(pkt);
SRTPCipherCTR.process(AEScipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
payloadLength, iv);
cipherCtr.process(AEScipher, 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)
{
//long ssrc = PacketManipulator.GetRTPSSRC(pkt);
//boolean isMarked = PacketManipulator.IsPacketMarked(pkt);
//int seqNum = PacketManipulator.GetRTPSequenceNumber(pkt);
//byte payload = PacketManipulator.GetRTPPayloadType(pkt);
public void processPacketAESF8(RawPacket pkt) {
// byte[] iv = new byte[16];
byte[] iv = new byte[16];
//iv[0] = 0;
//iv[1] = (byte) (isMarked ? 0x80 : 0x00);
//iv[1] |= payload & 0x7f;
//iv[2] = (byte) (seqNum >> 8);
//iv[3] = (byte) seqNum;
// set the TimeStamp in network order into IV
//byte[] timeStamp = PacketManipulator.ReadTimeStampIntoByteArray(pkt);
//System.arraycopy(timeStamp, 0, iv, 4, 4);
// set the SSRC in network order into IV
//iv[8] = (byte) (ssrc >> 24);
//iv[9] = (byte) (ssrc >> 16);
//iv[10] = (byte) (ssrc >> 8);
//iv[11] = (byte) ssrc;
// set the ROC in network order into IV
// 11 bytes of the RTP header are the 11 bytes of the iv
// the first byte of the RTP header is not used.
System.arraycopy(pkt.getBuffer(), pkt.getOffset(), iv, 0, 12);
iv[0] = 0;
iv[12] = (byte) (this.roc >> 24);
iv[13] = (byte) (this.roc >> 16);
iv[14] = (byte) (this.roc >> 8);
iv[15] = (byte) this.roc;
System.arraycopy(pkt.getBuffer(), pkt.getOffset(), ivStore, 0, 12);
ivStore[0] = 0;
// set the ROC in network order into IV
ivStore[12] = (byte) (this.roc >> 24);
ivStore[13] = (byte) (this.roc >> 16);
ivStore[14] = (byte) (this.roc >> 8);
ivStore[15] = (byte) this.roc;
final int payloadOffset = PacketManipulator.GetRTPHeaderLength(pkt);
final int payloadLength = PacketManipulator.GetRTPPayloadLength(pkt);
SRTPCipherF8.process(AEScipher, pkt.getBuffer(), pkt.getOffset() + payloadOffset,
payloadLength, iv, encKey, saltKey, AEScipherF8);
payloadLength, ivStore, encKey, saltKey, AEScipherF8);
}
/**
@ -524,19 +487,18 @@ public void processPacketAESF8(RawPacket pkt)
* @param pkt the RTP packet to be authenticated
* @return authentication tag of pkt
*/
private byte[] authenticatePacketHMCSHA1(RawPacket pkt, int rocIn)
{
private void authenticatePacketHMCSHA1(RawPacket pkt, int rocIn) {
hmacSha1.update(pkt.getBuffer(), 0, pkt.getLength());
byte[] rb = new byte[4];
rb[0] = (byte) (rocIn >> 24);
rb[1] = (byte) (rocIn >> 16);
rb[2] = (byte) (rocIn >> 8);
rb[3] = (byte) rocIn;
hmacSha1.update(rb);
return hmacSha1.doFinal();
// byte[] rb = new byte[4];
rbStore[0] = (byte) (rocIn >> 24);
rbStore[1] = (byte) (rocIn >> 16);
rbStore[2] = (byte) (rocIn >> 8);
rbStore[3] = (byte) rocIn;
hmacSha1.update(rbStore, 0, rbStore.length);
hmacSha1.doFinal(tagStore, 0);
}
/**
* Checks if a packet is a replayed on based on its sequence number.
*
@ -550,43 +512,24 @@ private byte[] authenticatePacketHMCSHA1(RawPacket pkt, int rocIn)
* @return true if this sequence number indicates the packet is not a
* replayed one, false if not
*/
boolean checkReplay(int seqNum)
{
/*
* Initialize the sequences number on first call that uses the
* sequence number. Either guessIndex() or checkReplay().
*/
if (!this.seqNumSet)
{
this.seqNumSet = true;
this.seqNum = seqNum;
}
long guessedIndex = guessIndex( seqNum );
long localIndex = ((long)this.roc) << 16 | this.seqNum;
boolean checkReplay(int seqNo, long guessedIndex) {
// compute the index of previously received packet and its
// delta to the new received packet
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
{
if( -delta > REPLAY_WINDOW_SIZE )
{
} 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;
}
@ -594,7 +537,6 @@ boolean checkReplay(int seqNum)
}
}
/**
* Compute the initialization vector, used later by encryption algorithms,
* based on the lable, the packet index, key derivation rate and master
@ -606,141 +548,83 @@ boolean checkReplay(int seqNum)
* @param kdv key derivation rate of this SRTPCryptoContext
* @param masterSalt master salt key
*/
private static void computeIv(byte[] iv, long label, long index,
long kdv, byte[] masterSalt)
{
private void computeIv(long label, long index) {
long key_id;
if (kdv == 0)
{
if (keyDerivationRate == 0) {
key_id = label << 48;
} else {
key_id = ((label << 48) | (index / keyDerivationRate));
}
else
{
key_id = ((label << 48) | (index / kdv));
for (int i = 0; i < 7; i++) {
ivStore[i] = masterSalt[i];
}
for (int i = 0; i < 7; i++)
{
iv[i] = masterSalt[i];
}
for (int i = 7; i < 14; i++)
{
iv[i] = (byte)
((byte)(0xFF & (key_id >> (8 * (13 - i)))) ^ masterSalt[i]);
for (int i = 7; i < 14; i++) {
ivStore[i] = (byte) ((byte) (0xFF & (key_id >> (8 * (13 - i)))) ^ masterSalt[i]);
}
iv[14] = iv[15] = 0;
ivStore[14] = ivStore[15] = 0;
}
/**
* Derives the srtp session keys from the master key
* @param index the 48 bit SRTP packet index
*
* @param index
* the 48 bit SRTP packet index
*/
public void deriveSrtpKeys(long index)
{
byte[] iv = new byte[16];
public void deriveSrtpKeys(long index) {
// compute the session encryption key
long label = 0;
computeIv(iv, label, index, this.keyDerivationRate, this.masterSalt);
SecretKey encryptionKey = new SecretKeySpec(masterKey, 0, policy.getEncKeyLength(), "AES");
try
{
AEScipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
}
catch (InvalidKeyException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
SRTPCipherCTR.getCipherStream(AEScipher, encKey, policy.getEncKeyLength(), iv);
computeIv(label, index);
KeyParameter encryptionKey = new KeyParameter(masterKey);
AEScipher.init(true, encryptionKey);
cipherCtr.getCipherStream(AEScipher, encKey, policy.getEncKeyLength(), ivStore);
// compute the session authentication key
if (this.authKey != null)
{
if (authKey != null) {
label = 0x01;
computeIv(iv, label, index, this.keyDerivationRate, this.masterSalt);
SRTPCipherCTR.getCipherStream(AEScipher, authKey, policy.getAuthKeyLength(), iv);
SecretKey key = new SecretKeySpec(authKey, "HMAC");
try
{
hmacSha1.init(key);
}
catch (InvalidKeyException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
computeIv(label, index);
cipherCtr.getCipherStream(AEScipher, authKey, policy.getAuthKeyLength(), ivStore);
KeyParameter key = new KeyParameter(authKey);
hmacSha1.init(key);
}
// compute the session salt
label = 0x02;
computeIv(iv, label, index, this.keyDerivationRate, this.masterSalt);
SRTPCipherCTR.getCipherStream(AEScipher, saltKey, policy.getSaltKeyLength(), iv);
computeIv(label, index);
cipherCtr.getCipherStream(AEScipher, saltKey, policy.getSaltKeyLength(), ivStore);
// As last step: initialize AES cipher with derived encryption key.
encryptionKey = new SecretKeySpec(encKey, 0, policy.getEncKeyLength(), "AES");
try
{
AEScipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
}
catch (InvalidKeyException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
encryptionKey = new KeyParameter(encKey);
AEScipher.init(true, encryptionKey);
}
/**
* Compute (guess) the new SRTP index based on the sequence number of
* a received RTP packet.
* Compute (guess) the new SRTP index based on the sequence number of a
* received RTP packet.
*
* @param seqNum sequence number of the received RTP packet
* @param seqNum
* sequence number of the received RTP packet
* @return the new SRTP packet index
*/
private long guessIndex(int seqNum)
{
if (!this.seqNumSet)
{
this.seqNumSet = true;
this.seqNum = seqNum;
}
private long guessIndex(int seqNo) {
if (this.seqNum < 32768)
{
if (seqNum - this.seqNum > 32768)
{
this.guessedROC = this.roc - 1;
if (this.seqNum < 32768) {
if (seqNo - this.seqNum > 32768) {
guessedROC = roc - 1;
} else {
guessedROC = roc;
}
else
{
this.guessedROC = this.roc;
}
}
else
{
if (this.seqNum - 32768 > seqNum)
{
this.guessedROC = this.roc + 1;
}
else
{
this.guessedROC = this.roc;
} else {
if (seqNum - 32768 > seqNo) {
guessedROC = roc + 1;
} else {
guessedROC = roc;
}
}
return ((long) this.guessedROC) << 16 | seqNum;
return ((long) guessedROC) << 16 | seqNo;
}
/**
@ -751,9 +635,8 @@ private long guessIndex(int seqNum)
*
* @param seqNum sequence number of the accepted packet
*/
private void update(int seqNum)
{
long delta = guessIndex(seqNum) - (((long) this.roc) << 16 | this.seqNum);
private void update(int seqNo, long guessedIndex) {
long delta = guessedIndex - (((long) this.roc) << 16 | this.seqNum);
/* update the replay bit mask */
if( delta > 0 ){
@ -764,49 +647,39 @@ private void update(int seqNum)
replayWindow |= ( 1 << delta );
}
if (seqNum > this.seqNum)
{
this.seqNum = seqNum & 0xffff; // make short
if (seqNo > seqNum) {
seqNum = seqNo & 0xffff;
}
if (this.guessedROC > this.roc)
{
this.roc = this.guessedROC;
this.seqNum = seqNum & 0xffff; // make short
if (this.guessedROC > this.roc) {
roc = guessedROC;
seqNum = seqNo & 0xffff;
}
}
/**
* 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.
*
*
* 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
* @param roc The Roll-Over-Counter for this context
* @param deriveRate The key derivation rate for this context
*
* @param ssrc
* The SSRC for this context
* @param roc
* The Roll-Over-Counter for this context
* @param deriveRate
* 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;
try
{
pcc = new SRTPCryptoContext(ssrc, roc, deriveRate,
this.masterKey, this.masterSalt, this.policy,
this.cryptoProvider);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
pcc = new SRTPCryptoContext(ssrc, roc, deriveRate, masterKey,
masterSalt, policy);
return pcc;
}
}

@ -1,61 +0,0 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.media.transform.srtp;
import org.bouncycastle.crypto.digests.*;
import org.bouncycastle.crypto.macs.*;
import org.bouncycastle.crypto.params.*;
/**
* SRTPDigest is used to calculate the digest of a specified byte stream using
* HMC SHA1 algorithm.
*
* This class uses bouncy castle's digest algorithm.
*
* @author Bing SU (nova.su@gmail.com)
*/
public class SRTPDigest
{
/**
* Bouncy Castle's HMAC algorithm provider
*/
private HMac hmac;
/**
* Construct a SRTPDigest based on given authentication key
*
* @param key the authentication key
*/
public SRTPDigest(byte[] key)
{
this.hmac = new HMac(new SHA1Digest());
this.hmac.init(new KeyParameter(key));
}
/**
* Calculate HMC SHA1 digests for a set of byte streams. These byte streams
* are considered to be continuous and the digest is calculated based on all
* these byte streams.
*
* @param chunks an array holding the byte streams to be calculated
* @param chunkLengths lengths of the byte streams to be calculated
* @return byte array holding the digest result
*/
public byte[] authHMACSHA1(byte[][] chunks, int[] chunkLengths)
{
this.hmac.reset();
byte[] result = new byte[this.hmac.getMacSize()];
for (int i = 0; i < chunks.length; i++)
{
this.hmac.update(chunks[i], 0, chunkLengths[i]);
}
this.hmac.doFinal(result, 0);
return result;
}
}

@ -7,7 +7,6 @@
package net.java.sip.communicator.impl.media.transform.srtp;
import net.java.sip.communicator.impl.media.transform.*;
import java.security.*;
/**
* SRTPTransformEngine class implements TransformEngine interface.
@ -58,9 +57,7 @@ public class SRTPTransformEngine
* @param cryptoProvider cryptography services provider
*/
public SRTPTransformEngine(byte[] masterKey, byte[] masterSalt,
SRTPPolicy srtpPolicy, SRTPPolicy srtcpPolicy,
Provider cryptoProvider)
throws GeneralSecurityException
SRTPPolicy srtpPolicy, SRTPPolicy srtcpPolicy)
{
this.masterKey = new byte[masterKey.length];
System.arraycopy(masterKey, 0, this.masterKey, 0, masterKey.length);
@ -74,8 +71,7 @@ public SRTPTransformEngine(byte[] masterKey, byte[] masterSalt,
this.defaultContext = new SRTPCryptoContext(0, 0, 0,
this.masterKey,
this.masterSalt,
this.srtpPolicy,
cryptoProvider);
this.srtpPolicy);
}
/* (non-Javadoc)

@ -17,8 +17,6 @@
import net.java.sip.communicator.service.fileaccess.*;
import java.io.*;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.util.EnumSet;
@ -301,12 +299,6 @@ public void run()
*/
private short senderZrtpSeqNo = 0;
/**
* The cryptographic algorithms provider
* (currently BouncyCastle is used)
*/
private Provider cryptoProvider= null;
/**
* The number of sent packets
*/
@ -416,11 +408,6 @@ public synchronized boolean initialize(String zidFilename,
e.printStackTrace();
}
if (cryptoProvider == null)
{
return false;
}
ZidFile zf = ZidFile.getInstance();
if (!zf.isOpen())
{
@ -442,17 +429,7 @@ public synchronized boolean initialize(String zidFilename,
return false;
}
}
try
{
zrtpEngine = new ZRtp(
zf.getZid(), this, clientIdString, cryptoProvider);
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
zrtpEngine = new ZRtp(zf.getZid(), this, clientIdString);
if (timeoutProvider == null)
{
@ -674,19 +651,10 @@ public boolean srtpSecretsReady(ZrtpSrtpSecrets secrets,
secrets.getSrtpAuthTagLen() / 8,// auth tag length
secrets.getInitSaltLen() / 8 // salt length
);
try
{
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyInitiator(), secrets.getSaltInitiator(),
srtpPolicy, srtpPolicy, cryptoProvider);
srtpPolicy, srtpPolicy);
srtpOutTransformer = engine.getRTPTransformer();
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
else
{
@ -698,19 +666,10 @@ public boolean srtpSecretsReady(ZrtpSrtpSecrets secrets,
secrets.getRespSaltLen() / 8 // salt length
);
try
{
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyResponder(), secrets.getSaltResponder(),
srtpPolicy, srtpPolicy, cryptoProvider);
srtpOutTransformer = engine.getRTPTransformer();
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyResponder(), secrets.getSaltResponder(),
srtpPolicy, srtpPolicy);
srtpOutTransformer = engine.getRTPTransformer();
}
}
if (part == EnableSecurity.ForReceiver)
@ -728,19 +687,10 @@ public boolean srtpSecretsReady(ZrtpSrtpSecrets secrets,
secrets.getRespSaltLen() / 8 // salt length
);
try
{
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyResponder(), secrets.getSaltResponder(),
srtpPolicy, srtpPolicy, cryptoProvider);
srtpInTransformer = engine.getRTPTransformer();
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyResponder(), secrets.getSaltResponder(),
srtpPolicy, srtpPolicy);
srtpInTransformer = engine.getRTPTransformer();
}
else
{
@ -752,19 +702,10 @@ public boolean srtpSecretsReady(ZrtpSrtpSecrets secrets,
secrets.getInitSaltLen() / 8 // salt length
);
try
{
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyInitiator(), secrets.getSaltInitiator(),
srtpPolicy, srtpPolicy, cryptoProvider);
srtpInTransformer = engine.getRTPTransformer();
}
catch (GeneralSecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
SRTPTransformEngine engine = new SRTPTransformEngine(secrets
.getKeyInitiator(), secrets.getSaltInitiator(),
srtpPolicy, srtpPolicy);
srtpInTransformer = engine.getRTPTransformer();
}
}
return true;
@ -1177,17 +1118,6 @@ public void setUserCallback(SCCallback ub)
userCallback = ub;
}
/**
* Sets the cryptography provider responsible with the crypto algorithms
* (Currently BouncyCastle is used)
*
* @param cryptoProvider the cryptoProvider to set
*/
public void setCryptoProvider(Provider cryptoProvider)
{
this.cryptoProvider = cryptoProvider;
}
/**
* Returns the current status of the ZRTP engine
*

Loading…
Cancel
Save