Skip to content
Snippets Groups Projects
Commit dbf8e263 authored by Lyubomir Marinov's avatar Lyubomir Marinov
Browse files

Adds support for DTLS-SRTP with Jitsi Videobridge.

parent 21921862
No related branches found
No related tags found
No related merge requests found
......@@ -306,6 +306,16 @@ public MediaStream createMediaStream(
return createMediaStream(connector, device, null);
}
/**
* {@inheritDoc}
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaType mediaType)
{
return createMediaStream(connector, mediaType, null);
}
/**
* Creates a new <tt>MediaStream</tt> instance which will use the specified
* <tt>MediaDevice</tt> for both capture and playback of media exchanged
......@@ -330,6 +340,17 @@ public MediaStream createMediaStream(
return createMediaStream(null, connector, device, srtpControl);
}
/**
* {@inheritDocs}
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaType mediaType,
SrtpControl srtpControl)
{
return createMediaStream(mediaType, connector, null, srtpControl);
}
/**
* Initializes a new <tt>MediaStream</tt> instance. The method is the actual
* implementation to which the public <tt>createMediaStream</tt> methods of
......
......@@ -239,15 +239,23 @@ public int receive(byte[] buf, int off, int len, int waitMillis)
}
}
if (receiveQ.isEmpty() && (timeout >= 0))
if (receiveQ.isEmpty())
{
try
if (timeout >= 0)
{
receiveQ.wait(timeout);
try
{
receiveQ.wait(timeout);
}
catch (InterruptedException ie)
{
interrupted = true;
}
}
catch (InterruptedException ie)
else
{
interrupted = true;
// The specified waitMillis has been exceeded.
break;
}
}
}
......
......@@ -566,7 +566,7 @@ private static String toHex(byte[] fingerprint)
* and validate against the fingerprints presented by the remote endpoint
* via the signaling path
*/
void verifyAndValidateCertificate(
private void verifyAndValidateCertificate(
org.bouncycastle.asn1.x509.Certificate certificate)
throws Exception
{
......@@ -592,11 +592,32 @@ void verifyAndValidateCertificate(
synchronized (this)
{
if (disposed)
{
throw new IllegalStateException("disposed");
}
else
remoteFingerprint = remoteFingerprints.get(hashFunction);
{
Map<String,String> remoteFingerprints = this.remoteFingerprints;
if (remoteFingerprints == null)
{
throw new IOException(
"No fingerprints declared over the signaling"
+ " path!");
}
else
{
remoteFingerprint = remoteFingerprints.get(hashFunction);
}
}
}
if (!remoteFingerprint.equals(fingerprint))
if (remoteFingerprint == null)
{
throw new IOException(
"No fingerprint declared over the signaling path with"
+ " hash function: " + hashFunction + "!");
}
else if (!remoteFingerprint.equals(fingerprint))
{
throw new IOException(
"Fingerprint " + remoteFingerprint
......@@ -611,24 +632,61 @@ void verifyAndValidateCertificate(
*
* @param certificate the certificate to be verified and validated against
* the fingerprints presented by the remote endpoint via the signaling path
* @return <tt>true</tt> if the specified <tt>certificate</tt> was
* successfully verified and validated against the fingerprints presented by
* the remote endpoint over the signaling path
* @throws Exception if the specified <tt>certificate</tt> failed to verify
* and validate against the fingerprints presented by the remote endpoint
* via the signaling path
* over the signaling path
*/
void verifyAndValidateCertificate(
boolean verifyAndValidateCertificate(
org.bouncycastle.crypto.tls.Certificate certificate)
throws Exception
{
org.bouncycastle.asn1.x509.Certificate[] certificateList
= certificate.getCertificateList();
boolean b = false;
if (certificateList.length == 0)
throw new IllegalArgumentException("certificate.certificateList");
try
{
org.bouncycastle.asn1.x509.Certificate[] certificateList
= certificate.getCertificateList();
for (org.bouncycastle.asn1.x509.Certificate x509Certificate
: certificateList)
if (certificateList.length == 0)
{
throw new IllegalArgumentException(
"certificate.certificateList");
}
else
{
for (org.bouncycastle.asn1.x509.Certificate x509Certificate
: certificateList)
{
verifyAndValidateCertificate(x509Certificate);
}
b = true;
}
}
catch (Exception e)
{
verifyAndValidateCertificate(x509Certificate);
/*
* XXX Contrary to RFC 5763 "Framework for Establishing a Secure
* Real-time Transport Protocol (SRTP) Security Context Using
* Datagram Transport Layer Security (DTLS)", we do NOT want to tear
* down the media session if the fingerprint does not match the
* hashed certificate. We want to notify the user via the
* SrtpListener.
*/
// TODO Auto-generated method stub
String message
= "Failed to verify and/or validate a certificate offered over"
+ " the media path against fingerprints declared over the"
+ " signaling path!";
String throwableMessage = e.getMessage();
if ((throwableMessage == null) || (throwableMessage.length() == 0))
logger.warn(message, e);
else
logger.warn(message + " " + throwableMessage);
}
return b;
}
}
......@@ -477,6 +477,10 @@ else if (delta < 0)
if (delta > 0)
pkt.shrink(delta);
/*
* In DTLS-SRTP no application data is transmitted
* over the DTLS channel.
*/
pkt = null;
}
}
......@@ -491,7 +495,7 @@ else if (delta < 0)
{
/*
* The specified pkt looks like a DTLS record but it is
* unexpected in the current state of the secure channels
* unexpected in the current state of the secure channel
* represented by this PacketTransformer.
*/
pkt = null;
......@@ -499,11 +503,15 @@ else if (delta < 0)
}
else
{
/*
* XXX If DTLS-SRTP has not been initialized yet or has failed to
* initialize, it is our explicit policy to let the received packet
* pass through and rely on the SrtpListener to notify the user that
* the session is not secured.
*/
PacketTransformer srtpTransformer = this.srtpTransformer;
if (srtpTransformer == null)
pkt = null;
else
if (srtpTransformer != null)
pkt = srtpTransformer.reverseTransform(pkt);
}
return pkt;
......@@ -522,8 +530,8 @@ private void runInConnectThread(
DatagramTransport datagramTransport)
{
DTLSTransport dtlsTransport = null;
int srtpProtectionProfile;
TlsContext tlsContext;
int srtpProtectionProfile = 0;
TlsContext tlsContext = null;
if (dtlsProtocol instanceof DTLSClientProtocol)
{
......@@ -544,8 +552,11 @@ private void runInConnectThread(
"Failed to connect this DTLS client to a DTLS server!",
ioe);
}
srtpProtectionProfile = tlsClient.getChosenProtectionProfile();
tlsContext = tlsClient.getContext();
if (dtlsTransport != null)
{
srtpProtectionProfile = tlsClient.getChosenProtectionProfile();
tlsContext = tlsClient.getContext();
}
}
else if (dtlsProtocol instanceof DTLSServerProtocol)
{
......@@ -566,14 +577,19 @@ else if (dtlsProtocol instanceof DTLSServerProtocol)
"Failed to accept a connection from a DTLS client!",
ioe);
}
srtpProtectionProfile = tlsServer.getChosenProtectionProfile();
tlsContext = tlsServer.getContext();
if (dtlsTransport != null)
{
srtpProtectionProfile = tlsServer.getChosenProtectionProfile();
tlsContext = tlsServer.getContext();
}
}
else
throw new IllegalStateException("dtlsProtocol");
PacketTransformer srtpTransformer
= initializeSRTPTransformer(srtpProtectionProfile, tlsContext);
= (dtlsTransport == null)
? null
: initializeSRTPTransformer(srtpProtectionProfile, tlsContext);
boolean closeSRTPTransformer;
synchronized (this)
......@@ -588,7 +604,7 @@ else if (dtlsProtocol instanceof DTLSServerProtocol)
closeSRTPTransformer
= (this.srtpTransformer != srtpTransformer);
}
if (closeSRTPTransformer)
if (closeSRTPTransformer && (srtpTransformer != null))
srtpTransformer.close();
}
......@@ -799,11 +815,15 @@ public RawPacket transform(RawPacket pkt)
*/
if (!isDtlsRecord(buf, off, len))
{
/*
* XXX If DTLS-SRTP has not been initialized yet or has failed to
* initialize, it is our explicit policy to let the received packet
* pass through and rely on the SrtpListener to notify the user that
* the session is not secured.
*/
PacketTransformer srtpTransformer = this.srtpTransformer;
if (srtpTransformer == null)
pkt = null;
else
if (srtpTransformer != null)
pkt = srtpTransformer.transform(pkt);
}
return pkt;
......
......@@ -22,17 +22,10 @@
public class ZrtpRawPacket extends RawPacket
{
/**
* Each ZRTP packet contains this magic number.
* Each ZRTP packet contains this magic number/cookie.
*/
public static byte[] zrtpMagic;
static {
zrtpMagic = new byte[4];
zrtpMagic[0]= 0x5a;
zrtpMagic[1]= 0x52;
zrtpMagic[2]= 0x54;
zrtpMagic[3]= 0x50;
}
public static final byte[] ZRTP_MAGIC
= new byte[] { 0x5a, 0x52, 0x54, 0x50 };
/**
* Construct an input ZrtpRawPacket using a received RTP raw packet.
......@@ -41,7 +34,7 @@ public class ZrtpRawPacket extends RawPacket
*/
public ZrtpRawPacket(RawPacket pkt)
{
super (pkt.getBuffer(), pkt.getOffset(), pkt.getLength());
super(pkt.getBuffer(), pkt.getOffset(), pkt.getLength());
}
/**
......@@ -61,10 +54,10 @@ public ZrtpRawPacket(byte[] buf, int off, int len)
writeByte(1, (byte)0);
int at = 4;
writeByte(at++, zrtpMagic[0]);
writeByte(at++, zrtpMagic[1]);
writeByte(at++, zrtpMagic[2]);
writeByte(at, zrtpMagic[3]);
writeByte(at++, ZRTP_MAGIC[0]);
writeByte(at++, ZRTP_MAGIC[1]);
writeByte(at++, ZRTP_MAGIC[2]);
writeByte(at, ZRTP_MAGIC[3]);
}
/**
......@@ -88,13 +81,8 @@ protected boolean isZrtpPacket()
*/
static boolean isZrtpData(RawPacket pkt)
{
if(!pkt.getExtensionBit())
return false;
if(pkt.getHeaderExtensionType() == 0x505a)
return true;
return false;
return
pkt.getExtensionBit() && (pkt.getHeaderExtensionType() == 0x505a);
}
/**
......@@ -108,10 +96,10 @@ static boolean isZrtpData(RawPacket pkt)
protected boolean hasMagic()
{
return
(readByte(4) == zrtpMagic[0])
&& (readByte(5) == zrtpMagic[1])
&& (readByte(6) == zrtpMagic[2])
&& (readByte(7) == zrtpMagic[3]);
(readByte(4) == ZRTP_MAGIC[0])
&& (readByte(5) == ZRTP_MAGIC[1])
&& (readByte(6) == ZRTP_MAGIC[2])
&& (readByte(7) == ZRTP_MAGIC[3]);
}
/**
......
......@@ -102,8 +102,27 @@ public interface MediaService
*
* @return the newly created <tt>MediaStream</tt>.
*/
public MediaStream createMediaStream(StreamConnector connector,
MediaDevice device);
public MediaStream createMediaStream(
StreamConnector connector,
MediaDevice device);
/**
* Initializes a new <tt>MediaStream</tt> instance which is to exchange
* media of a specific <tt>MediaType</tt> via a specific
* <tt>StreamConnector</tt>.
*
* @param connector the <tt>StreamConnector</tt> the stream should use for
* sending and receiving media or <tt>null</tt> if the stream is to not have
* a <tt>StreamConnector</tt> configured at initialization time and a
* <tt>StreamConnector</tt> is to be specified later on
* @param mediaType the <tt>MediaType</tt> of the media to be exchanged by
* the new instance via the specified <tt>connector</tt>
* @return a new <tt>MediaStream</tt> instance which is to exchange media of
* the specified <tt>mediaType</tt> via the specified <tt>connector</tt>
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaType mediaType);
/**
* Creates a <tt>MediaStream</tt> that will be using the specified
......@@ -116,14 +135,37 @@ public MediaStream createMediaStream(StreamConnector connector,
* <tt>StreamConnector</tt> is to be specified later on
* @param device the device to be used for both capture and playback of
* media exchanged via the specified <tt>StreamConnector</tt>
* @param zrtpControl a control which is already created, used to control
* @param srtpControl a control which is already created, used to control
* the ZRTP operations.
*
* @return the newly created <tt>MediaStream</tt>.
*/
public MediaStream createMediaStream(StreamConnector connector,
MediaDevice device,
SrtpControl zrtpControl);
public MediaStream createMediaStream(
StreamConnector connector,
MediaDevice device,
SrtpControl srtpControl);
/**
* Initializes a new <tt>MediaStream</tt> instance which is to exchange
* media of a specific <tt>MediaType</tt> via a specific
* <tt>StreamConnector</tt>. The security of the media exchange is to be
* controlled by a specific <tt>SrtpControl</tt>.
*
* @param connector the <tt>StreamConnector</tt> the stream should use for
* sending and receiving media or <tt>null</tt> if the stream is to not have
* a <tt>StreamConnector</tt> configured at initialization time and a
* <tt>StreamConnector</tt> is to be specified later on
* @param mediaType the <tt>MediaType</tt> of the media to be exchanged by
* the new instance via the specified <tt>connector</tt>
* @param srtpControl the <tt>SrtpControl</tt> to control the security of
* the media exchange
* @return a new <tt>MediaStream</tt> instance which is to exchange media of
* the specified <tt>mediaType</tt> via the specified <tt>connector</tt>
*/
public MediaStream createMediaStream(
StreamConnector connector,
MediaType mediaType,
SrtpControl srtpControl);
/**
* Creates a new <tt>MediaDevice</tt> which uses a specific
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment