org.bouncycastle.crypto.NativeLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-lts8on Show documentation
Show all versions of bcprov-lts8on Show documentation
The Long Term Stable (LTS) Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains the JCA/JCE provider and low-level API for the BC LTS version 2.73.7 for Java 8 and later.
package org.bouncycastle.crypto;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.io.Streams;
class NativeLoader
{
private static final Logger LOG = Logger.getLogger(NativeLoader.class.getName());
public static final String BCLTS_LIB_CPU_VARIANT = "org.bouncycastle.native.cpu_variant";
private static boolean nativeLibsAvailableForSystem = false;
private static boolean nativeInstalled = false;
private static boolean nativeEnabled = true;
private static String nativeStatusMessage = "Driver load not attempted";
private static String selectedVariant = null;
private static boolean javaSupportOnly = true;
private static final NativeServices nativeServices = new DefaultNativeServices();
static synchronized boolean isJavaSupportOnly()
{
return javaSupportOnly;
}
/**
* Native has been installed.
*
* @return true if the native lib has been installed.
*/
static synchronized boolean isNativeInstalled()
{
return nativeInstalled;
}
/**
* Native is available.
*
* @return true if native libs have been installed and are NOT disabled.
*/
static synchronized boolean isNativeAvailable()
{
return nativeLibsAvailableForSystem && nativeInstalled && nativeEnabled;
}
/**
* Disable native library even if loaded.
*
* @param enabled when false will disable the use of native extensions.
*/
static synchronized void setNativeEnabled(boolean enabled)
{
nativeEnabled = enabled;
}
static synchronized String getNativeStatusMessage()
{
return nativeStatusMessage;
}
static synchronized String getSelectedVariant()
{
return selectedVariant;
}
static String getFile(String path)
{
String value;
try
{
InputStream in = NativeLoader.class.getResourceAsStream(path);
value = Strings.fromByteArray(Streams.readAll(in));
in.close();
}
catch (Exception ex)
{
return null;
}
return value;
}
static List loadVariantsDeps(String depFile, String libName)
{
String data = getFile(depFile);
if (data == null)
{
return Collections.emptyList();
}
ArrayList out = new ArrayList();
for (String line : data.split("\n"))
{
line = line.trim();
String[] parts = line.split(":");
if (parts[0].trim().equals(libName))
{
out.add(parts[1].trim());
}
}
return Collections.unmodifiableList(out);
}
static File installLib(String name, String libPathSegment, String jarPath, File bcLibPath,
Set filesInInstallLocation)
throws Exception
{
//
// Copy nominated dep for library into bcLibPath
//
String libLocalName = System.mapLibraryName(name);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("attempting to install: " + libLocalName);
}
List deps = loadVariantsDeps(jarPath + "/deps.list", libLocalName);
for (String dep : deps)
{
filesInInstallLocation.remove(copyFromJar(jarPath + "/" + dep, bcLibPath, dep));
}
File libToLoad = copyFromJar(libPathSegment + "/" + libLocalName, bcLibPath, libLocalName);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("installed " + libToLoad);
}
filesInInstallLocation.remove(libToLoad);
return libToLoad;
}
static boolean isLE()
{
return ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN);
}
static synchronized void loadDriver()
{
LOG.log(Level.FINE, "native loader start");
String forcedVariant = Properties.getPropertyValue(BCLTS_LIB_CPU_VARIANT);
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("forced variant is: " + (forcedVariant != null ? forcedVariant : " not defined"));
}
// No variants defined at all, or a
// single variant defined that is java only.
//
if ("java".equals(forcedVariant))
{
javaSupportOnly = true;
nativeInstalled = false;
nativeStatusMessage = "java support only";
LOG.fine("exited with " + nativeStatusMessage);
return;
}
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "examining properties to determine platform and architecture");
}
String arch_ = Strings.toLowerCase(Properties.getPropertyValue("os.arch", ""));
String os_ = Strings.toLowerCase(Properties.getPropertyValue("os.name", ""));
String platform = null;
String arch = null;
boolean isARM = false;
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "host ARCH: " + arch_);
LOG.log(Level.FINE, "host OS: " + os_);
}
if (os_.contains("linux"))
{
platform = "linux";
}
else if (os_.contains("mac") || os_.contains("darwin"))
{
platform = "darwin";
}
if (platform == null)
{
nativeStatusMessage = "OS '" + os_ + "' is not supported.";
LOG.fine("exited with " + nativeStatusMessage);
return;
}
if ((arch_.contains("x86") || (arch_.contains("amd")) && arch_.contains("64")))
{
arch = "x86_64";
}
else if (arch_.contains("aarch64"))
{
arch = "arm64";
isARM = true;
}
if (arch == null)
{
nativeStatusMessage = "architecture '" + arch_ + "' is not supported";
LOG.fine("exited with " + nativeStatusMessage);
return;
}
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "derived native platform: " + platform);
LOG.log(Level.FINE, "derived native architecture: " + arch);
}
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "begin determining path to install native libraries");
}
File bcLtsLibPath = AccessController.doPrivileged(new PrivilegedAction()
{
@Override
public File run()
{
File ioTmpDir = new File(Properties.getPropertyValue("java.io.tmpdir"));
if (!ioTmpDir.exists())
{
nativeInstalled = false;
nativeStatusMessage = ioTmpDir + " did not exist";
LOG.fine("exited with " + nativeStatusMessage);
return null;
}
try
{
//
// Create a temporary file, we cannot use the inbuilt method as it will attempt to start
// an entropy source and the provider is not in a ready state.
//
File dir = null;
long time = System.nanoTime();
for (int t = 0; t < 1000; t++)
{
dir = new File(ioTmpDir, "bc-lts-jni" + Long.toString(time + t, 32) + "-libs");
if (dir.mkdirs())
{
break;
}
dir = null;
Thread.sleep(time % 97);
}
if (dir == null)
{
nativeInstalled = false;
nativeStatusMessage = "unable to create directory in " + ioTmpDir + " after 1000 unique " +
"attempts";
LOG.fine("exited with " + nativeStatusMessage);
return null;
}
//
// Create a directory using that file as a stem
//
if (!dir.exists())
{
nativeInstalled = false;
nativeStatusMessage = "unable to create temp directory for jni libs: " + dir;
LOG.fine("exited with " + nativeStatusMessage);
return null;
}
final File tmpDir = dir;
//
// Shutdown hook clean up installed libraries.
//
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable()
{
@Override
public void run()
{
if (LOG.isLoggable(Level.FINE))
{
LOG.fine("cleanup shutdown hook started");
}
if (!tmpDir.exists())
{
return;
}
boolean isDeleted = true;
if (tmpDir.isDirectory())
{
for (File f : tmpDir.listFiles())
{
isDeleted &= f.delete();
}
}
isDeleted &= tmpDir.delete();
if (!isDeleted)
{
LOG.fine(" failed to delete: " + tmpDir.getAbsolutePath());
}
else
{
LOG.fine("successfully cleaned up: " + tmpDir.getAbsolutePath());
}
}
}));
return tmpDir;
}
catch (Exception ex)
{
nativeInstalled = false;
nativeStatusMessage = "failed because it was not able to create a temporary file in 'java.io" +
".tmpdir' " + ex.getMessage();
LOG.fine("exited with " + nativeStatusMessage);
return null;
}
}
});
if (bcLtsLibPath == null)
{
return;
}
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "native library install location: " + bcLtsLibPath.getAbsolutePath());
}
//
// We track all the existing files in the installation location.
// During installation, we remove them from this set if they have been replaced.
// if any files are remaining in the set then there were unaccounted for files in
// the installation location, and we cannot start the module.
//
Set filesInInstallLocation = new HashSet();
for (File f : bcLtsLibPath.listFiles())
{
filesInInstallLocation.add(f);
}
if (LOG.isLoggable(Level.FINE))
{
if (!filesInInstallLocation.isEmpty())
{
for (File file : filesInInstallLocation)
{
LOG.log(Level.FINE, "found in install location: " + file.getAbsolutePath());
}
}
}
//
// Point to the directory in the jar where the native libs are located.
//
String jarDir = String.format("/native/%s/%s", platform, arch);
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "library path within LTS jar: " + jarDir);
}
//
// Look for a probe library, it matches the platform and architecture.
// It needs to exist regardless of any forced variant, if it does not exist
// any forced variant is not going to function anyway.
//
String probeLibInJarPath = String.format("/native/%s/%s/probe", platform, arch);
if (LOG.isLoggable(Level.FINE))
{
LOG.log(Level.FINE, "begin install probe library from: " + probeLibInJarPath);
}
String probeLibPrefix;
if (isARM)
{
if (isLE())
{
probeLibPrefix = "bc-probe-le";
}
else
{
nativeStatusMessage = String.format("ARM big-endian is not supported", platform,
arch);
nativeInstalled = false;
LOG.fine("exited with " + nativeStatusMessage);
return;
}
}
else
{
probeLibPrefix = "bc-probe";
}
InputStream tmpIn = NativeLoader.class.getResourceAsStream(probeLibInJarPath + "/" + System.mapLibraryName(
probeLibPrefix));
if (tmpIn == null)
{
nativeStatusMessage = String.format("platform '%s' and architecture '%s' are not supported", platform,
arch);
nativeInstalled = false;
LOG.fine("exited with " + nativeStatusMessage);
return;
}
try
{
tmpIn.close();
}
catch (IOException ignored)
{
}
//
// Endeavour ot install the probe library, if this does not install then
// forced variants will be usable.
//
try
{
// Install probe lib
final File lib = installLib(probeLibPrefix, probeLibInJarPath, jarDir, bcLtsLibPath,
filesInInstallLocation);
AccessController.doPrivileged(
new PrivilegedAction
© 2015 - 2024 Weber Informatics LLC | Privacy Policy