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