From 8dd83b679f6b44243fa9771f920a3a542fb4a78c Mon Sep 17 00:00:00 2001
From: Lyubomir Marinov <lyubomir.marinov@jitsi.org>
Date: Fri, 24 Oct 2014 20:43:41 +0300
Subject: [PATCH] Prepares to use SHA-1 from OpenSSL instead of BouncyCastle.

---
 src/native/openssl/Digest.c                   | 102 ++++++
 src/native/openssl/Digest.h                   |  77 +++++
 .../impl/neomedia/transform/srtp/AES.java     |   4 +-
 .../transform/srtp/OpenSSLDigest.java         | 307 ++++++++++++++++++
 .../impl/neomedia/transform/srtp/SHA1.java    |  63 +++-
 5 files changed, 548 insertions(+), 5 deletions(-)
 create mode 100644 src/native/openssl/Digest.c
 create mode 100644 src/native/openssl/Digest.h
 create mode 100644 src/org/jitsi/impl/neomedia/transform/srtp/OpenSSLDigest.java

diff --git a/src/native/openssl/Digest.c b/src/native/openssl/Digest.c
new file mode 100644
index 00000000..f3b1c2e1
--- /dev/null
+++ b/src/native/openssl/Digest.c
@@ -0,0 +1,102 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "Digest.h"
+
+#include <openssl/evp.h>
+#include <stdint.h>
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1DigestFinal_1ex
+    (JNIEnv *env, jclass clazz, jlong ctx, jbyteArray md, jint off)
+{
+    jbyte *md_ = (*env)->GetPrimitiveArrayCritical(env, md, NULL);
+    int i;
+
+    if (md_)
+    {
+        unsigned int s = 0;
+
+        i = EVP_DigestFinal_ex((EVP_MD_CTX *) (intptr_t) ctx, md_ + off, &s);
+        (*env)->ReleasePrimitiveArrayCritical(env, md, md_, 0);
+        i = i ? ((int) s) : -1;
+    }
+    else
+    {
+        i = -1;
+    }
+    return i;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1DigestInit_1ex
+    (JNIEnv *env, jclass clazz, jlong ctx, jlong type, jlong impl)
+{
+    int i
+        = EVP_DigestInit_ex(
+                (EVP_MD_CTX *) (intptr_t) ctx,
+                (const EVP_MD *) (intptr_t) type,
+                (ENGINE *) (intptr_t) impl);
+
+    return i ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1DigestUpdate
+    (JNIEnv *env, jclass clazz, jlong ctx, jbyteArray d, jint off, jint cnt)
+{
+    jbyte *d_ = (*env)->GetPrimitiveArrayCritical(env, d, NULL);
+    jboolean b;
+
+    if (d_)
+    {
+        int i = EVP_DigestUpdate((EVP_MD_CTX *) (intptr_t) ctx, d_ + off, cnt);
+
+        (*env)->ReleasePrimitiveArrayCritical(env, d, d_, JNI_ABORT);
+        b = i ? JNI_TRUE : JNI_FALSE;
+    }
+    else
+    {
+        b = JNI_FALSE;
+    }
+    return b;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1block_1size
+    (JNIEnv *env, jclass clazz, jlong ctx)
+{
+    return EVP_MD_CTX_block_size((const EVP_MD_CTX *) (intptr_t) ctx);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1create
+    (JNIEnv *env, jclass clazz)
+{
+    return (jlong) (intptr_t) EVP_MD_CTX_create();
+}
+
+JNIEXPORT void JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1destroy
+    (JNIEnv *env, jclass clazz, jlong ctx)
+{
+    EVP_MD_CTX_destroy((EVP_MD_CTX *) (intptr_t) ctx);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1size
+    (JNIEnv *env, jclass clazz, jlong ctx)
+{
+    return EVP_MD_CTX_size((const EVP_MD_CTX *) (intptr_t) ctx);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1sha1
+    (JNIEnv *env, jclass clazz)
+{
+    return (jlong) (intptr_t) EVP_sha1();
+}
diff --git a/src/native/openssl/Digest.h b/src/native/openssl/Digest.h
new file mode 100644
index 00000000..f79df6ac
--- /dev/null
+++ b/src/native/openssl/Digest.h
@@ -0,0 +1,77 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest */
+
+#ifndef _Included_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+#define _Included_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_DigestFinal_ex
+ * Signature: (J[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1DigestFinal_1ex
+  (JNIEnv *, jclass, jlong, jbyteArray, jint);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_DigestInit_ex
+ * Signature: (JJJ)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1DigestInit_1ex
+  (JNIEnv *, jclass, jlong, jlong, jlong);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_DigestUpdate
+ * Signature: (J[BII)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1DigestUpdate
+  (JNIEnv *, jclass, jlong, jbyteArray, jint, jint);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_MD_CTX_block_size
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1block_1size
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_MD_CTX_create
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1create
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_MD_CTX_destroy
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1destroy
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_MD_CTX_size
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1MD_1CTX_1size
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest
+ * Method:    EVP_sha1
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_jitsi_impl_neomedia_transform_srtp_OpenSSLDigest_EVP_1sha1
+  (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/org/jitsi/impl/neomedia/transform/srtp/AES.java b/src/org/jitsi/impl/neomedia/transform/srtp/AES.java
index 503d8154..2682bd83 100644
--- a/src/org/jitsi/impl/neomedia/transform/srtp/AES.java
+++ b/src/org/jitsi/impl/neomedia/transform/srtp/AES.java
@@ -109,8 +109,8 @@ else if (t instanceof ThreadDeath)
                 {
                     logger.warn(
                             "Failed to employ a java.security.Provider for an"
-                                + " optimized AES implementation.",
-                            t);
+                                + " optimized AES implementation: "
+                                + t.getLocalizedMessage());
                 }
             }
         }
diff --git a/src/org/jitsi/impl/neomedia/transform/srtp/OpenSSLDigest.java b/src/org/jitsi/impl/neomedia/transform/srtp/OpenSSLDigest.java
new file mode 100644
index 00000000..606c96f7
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/transform/srtp/OpenSSLDigest.java
@@ -0,0 +1,307 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.transform.srtp;
+
+import org.bouncycastle.crypto.*;
+
+/**
+ * Implements the interface <tt>org.bouncycastle.crypto.Digest</tt> using the
+ * OpenSSL Crypto library.
+ *
+ * @author Lyubomir Marinov
+ */
+public class OpenSSLDigest
+    implements ExtendedDigest
+{
+    private static long EVP_sha1;
+
+    /**
+     * The indicator which determines whether
+     * <tt>System.loadLibrary(String)</tt> is to be invoked in order to load the
+     * OpenSSL (Crypto) library.
+     */
+    private static boolean loadLibrary = true;
+
+    /**
+     * The algorithm of the SHA-1 cryptographic hash function/digest.
+     */
+    public static final int SHA1 = 1;
+
+    private static native int EVP_DigestFinal_ex(
+            long ctx,
+            byte[] md, int off);
+
+    private static native boolean EVP_DigestInit_ex(
+            long ctx,
+            long type,
+            long impl);
+
+    private static native boolean EVP_DigestUpdate(
+            long ctx,
+            byte[] d, int off, int cnt);
+
+    private static native int EVP_MD_CTX_block_size(long ctx);
+
+    private static native long EVP_MD_CTX_create();
+
+    private static native void EVP_MD_CTX_destroy(long ctx);
+
+    private static native int EVP_MD_CTX_size(long ctx)
+;
+    private static native long EVP_sha1();
+
+    /**
+     * The name of the algorithm implemented by this instance.
+     */
+    private final String algorithmName;
+
+    /**
+     * The size in bytes of the internal buffer the digest applies its
+     * compression function to.
+     */
+    private int byteLength;
+
+    /**
+     * The digest context of the OpenSSL (Crypto) library through which the
+     * actual algorithm implementation is invoked by this instance.
+     */
+    private long ctx;
+
+    /**
+     * The size in bytes of the digest produced by this message digest.
+     */
+    private int digestSize;
+
+    /**
+     * The OpenSSL Crypto type of the message digest implemented by this
+     * instance.
+     */
+    private final long type;
+
+    /**
+     * Initializes a new <tt>OpenSSLDigest</tt> with a specific algorithm.
+     *
+     * @param algorithm the algorithm with which to initialize the new instance
+     */
+    public OpenSSLDigest(int algorithm)
+    {
+        // Make sure the provided arguments are legal.
+        if (algorithm == SHA1)
+            this.algorithmName = "SHA-1";
+        else
+            throw new IllegalArgumentException("algorithm " + algorithm);
+
+        // Load the OpenSSL (Crypto) library if necessary.
+        synchronized (OpenSSLDigest.class)
+        {
+            if (loadLibrary)
+            {
+                try
+                {
+                    System.loadLibrary("jnopenssl");
+                    EVP_sha1 = EVP_sha1();
+                }
+                finally
+                {
+                    loadLibrary = false;
+                }
+            }
+        }
+
+        long type;
+
+        if (algorithm == SHA1)
+        {
+            long EVP_sha1 = OpenSSLDigest.EVP_sha1;
+
+            if (EVP_sha1 == 0)
+                throw new IllegalStateException("EVP_sha1");
+            else
+                type = EVP_sha1;
+        }
+        else
+        {
+            // It must have been checked prior to loading the OpenSSL (Crypto)
+            // library but the compiler needs it to be convinced that we are not
+            // attempting to use an uninitialized variable.
+            throw new IllegalArgumentException("algorithm " + algorithm);
+        }
+        this.type = type;
+
+        long ctx = EVP_MD_CTX_create();
+
+        if (ctx == 0)
+        {
+            throw new RuntimeException("EVP_MD_CTX_create");
+        }
+        else
+        {
+            boolean ok = false;
+
+            this.ctx = ctx;
+            try
+            {
+                reset();
+                ok = true;
+            }
+            finally
+            {
+                if (!ok)
+                {
+                    if (this.ctx == ctx)
+                        this.ctx = 0;
+                    EVP_MD_CTX_destroy(ctx);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int doFinal(byte[] out, int off)
+    {
+        if (out == null)
+            throw new NullPointerException("out");
+        if ((off < 0) || (out.length <= off))
+            throw new ArrayIndexOutOfBoundsException(off);
+
+        long ctx = this.ctx;
+
+        if (ctx == 0)
+        {
+            throw new IllegalStateException("ctx");
+        }
+        else
+        {
+            int s = EVP_DigestFinal_ex(ctx, out, off);
+
+            if (s < 0)
+            {
+                throw new RuntimeException("EVP_DigestFinal_ex");
+            }
+            else
+            {
+                // As the javadoc on interface method specifies, the doFinal
+                // call leaves this Digest reset.
+                reset();
+                return s;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void finalize()
+        throws Throwable
+    {
+        try
+        {
+            // Well, the destroying in the finalizer should exist as a backup
+            // anyway. There is no way to explicitly invoke the destroying at
+            // the time of this writing but it is a start.
+            long ctx = this.ctx;
+
+            if (ctx != 0)
+            {
+                this.ctx = 0;
+                EVP_MD_CTX_destroy(ctx);
+            }
+        }
+        finally
+        {
+            super.finalize();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getAlgorithmName()
+    {
+        return algorithmName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getByteLength()
+    {
+        return byteLength;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getDigestSize()
+    {
+        return digestSize;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void reset()
+    {
+        long ctx = this.ctx;
+
+        if (ctx == 0)
+        {
+            throw new IllegalStateException("ctx");
+        }
+        else if (EVP_DigestInit_ex(ctx, type, /* impl */ 0))
+        {
+            byteLength = EVP_MD_CTX_block_size(ctx);
+            digestSize = EVP_MD_CTX_size(ctx);
+        }
+        else
+        {
+            throw new RuntimeException(
+                    "EVP_DigestInit_ex(" + getAlgorithmName() + ")");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void update(byte in)
+    {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void update(byte[] in, int off, int len)
+    {
+        if (len != 0)
+        {
+            if (in == null)
+                throw new NullPointerException("in");
+            if ((off < 0) || (in.length <= off))
+                throw new ArrayIndexOutOfBoundsException(off);
+            if ((len < 0) || (in.length < off + len))
+                throw new IllegalArgumentException("len " + len);
+
+            long ctx = this.ctx;
+
+            if (ctx == 0)
+                throw new IllegalStateException("ctx");
+            else if (!EVP_DigestUpdate(ctx, in, off, len))
+                throw new RuntimeException("EVP_DigestUpdate");
+        }
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/transform/srtp/SHA1.java b/src/org/jitsi/impl/neomedia/transform/srtp/SHA1.java
index e4ac56ed..9b6b1121 100644
--- a/src/org/jitsi/impl/neomedia/transform/srtp/SHA1.java
+++ b/src/org/jitsi/impl/neomedia/transform/srtp/SHA1.java
@@ -8,12 +8,69 @@
 
 import org.bouncycastle.crypto.*;
 import org.bouncycastle.crypto.digests.*;
+import org.jitsi.util.*;
 
-class SHA1
+/**
+ * Implements a factory for a SHA-1 <tt>Digest</tt>.
+ *
+ * @author Lyubomir Marinov
+ */
+public class SHA1
 {
-    static Digest createDigest()
+    /**
+     * The <tt>Logger</tt> used by the <tt>SHA1</tt> class to print out debug
+     * information.
+     */
+    private static final Logger logger = Logger.getLogger(SHA1.class);
+
+    /**
+     * The indicator which determines whether the OpenSSL (Crypto) library is to
+     * be used. If <tt>true</tt>, an attempt will be made to initialize an
+     * <tt>OpenSSLDigest</tt> instance. If the attempt fails, <tt>false</tt>
+     * will be assigned in order to not repeatedly attempt the initialization
+     * which is known to have failed.
+     */
+    private static boolean useOpenSSL = true;
+
+    /**
+     * Initializes a new <tt>org.bouncycastle.crypto.Digest</tt> instance which
+     * implements the SHA-1 cryptographic hash function/digest.
+     *
+     * @return a new <tt>org.bouncycastle.crypto.Digest</tt> instance which
+     * implements the SHA-1 cryptographic hash function/digest
+     */
+    public static Digest createDigest()
     {
-        // TODO Auto-generated method stub
+        if (useOpenSSL)
+        {
+            try
+            {
+                return new OpenSSLDigest(OpenSSLDigest.SHA1);
+            }
+            catch (Throwable t)
+            {
+                // If an exception is thrown once, it is very likely to be
+                // thrown multiple times.
+                useOpenSSL = false;
+
+                if (t instanceof InterruptedException)
+                {
+                    Thread.currentThread().interrupt();
+                }
+                else if (t instanceof ThreadDeath)
+                {
+                    throw (ThreadDeath) t;
+                }
+                else
+                {
+                    logger.warn(
+                            "Failed to employ OpenSSL (Crypto) for an optimized"
+                                + " SHA-1 implementation: "
+                                + t.getLocalizedMessage());
+                }
+            }
+        }
+
         return new SHA1Digest();
     }
 }
-- 
GitLab