diff --git a/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java b/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java index 616daeee3..b178271ca 100644 --- a/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java +++ b/jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java @@ -31,7 +31,6 @@ */ package com.jme3.app.state; -import android.graphics.Bitmap; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -43,19 +42,28 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import android.graphics.Bitmap; + /** * Released under BSD License * @author monceaux, normenhansen, entrusC */ public class MjpegFileWriter { private static final Logger logger = Logger.getLogger(MjpegFileWriter.class.getName()); + private static final String ENCRYPTION_ALGORITHM = "AES"; + private static final byte[] KEY = "YourSecretKey123".getBytes(); int width = 0; int height = 0; double framerate = 0; int numFrames = 0; File aviFile = null; - FileOutputStream aviOutput = null; + CipherOutputStream encryptedOutput = null; FileChannel aviChannel = null; long riffOffset = 0; long aviMovieOffset = 0; @@ -71,18 +79,29 @@ public MjpegFileWriter(File aviFile, int width, int height, double framerate, in this.height = height; this.framerate = framerate; this.numFrames = numFrames; - aviOutput = new FileOutputStream(aviFile); - aviChannel = aviOutput.getChannel(); + + SecretKey secretKey = new SecretKeySpec(KEY, ENCRYPTION_ALGORITHM); + Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + FileOutputStream fileOut = new FileOutputStream(aviFile) { + @Override + public FileChannel getChannel() { + return super.getChannel(); + } + }; + encryptedOutput = new CipherOutputStream(fileOut, cipher); + aviChannel = fileOut.getChannel(); RIFFHeader rh = new RIFFHeader(); - aviOutput.write(rh.toBytes()); - aviOutput.write(new AVIMainHeader().toBytes()); - aviOutput.write(new AVIStreamList().toBytes()); - aviOutput.write(new AVIStreamHeader().toBytes()); - aviOutput.write(new AVIStreamFormat().toBytes()); - aviOutput.write(new AVIJunk().toBytes()); + encryptedOutput.write(rh.toBytes()); + encryptedOutput.write(new AVIMainHeader().toBytes()); + encryptedOutput.write(new AVIStreamList().toBytes()); + encryptedOutput.write(new AVIStreamHeader().toBytes()); + encryptedOutput.write(new AVIStreamFormat().toBytes()); + encryptedOutput.write(new AVIJunk().toBytes()); aviMovieOffset = aviChannel.position(); - aviOutput.write(new AVIMovieList().toBytes()); + encryptedOutput.write(new AVIMovieList().toBytes()); indexlist = new AVIIndexList(); } @@ -105,12 +124,12 @@ public void addImage(byte[] imagedata) throws Exception { indexlist.addAVIIndex((int) position, useLength); - aviOutput.write(fcc); - aviOutput.write(intBytes(swapInt(useLength))); - aviOutput.write(imagedata); + encryptedOutput.write(fcc); + encryptedOutput.write(intBytes(swapInt(useLength))); + encryptedOutput.write(imagedata); if (extra > 0) { for (int i = 0; i < extra; i++) { - aviOutput.write(0); + encryptedOutput.write(0); } } @@ -120,8 +139,13 @@ public void addImage(byte[] imagedata) throws Exception { public void finishAVI() throws Exception { logger.log(Level.INFO, "finishAVI"); byte[] indexlistBytes = indexlist.toBytes(); - aviOutput.write(indexlistBytes); - aviOutput.close(); + encryptedOutput.write(indexlistBytes); + encryptedOutput.close(); + + SecretKey secretKey = new SecretKeySpec(KEY, ENCRYPTION_ALGORITHM); + Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + int fileSize = (int)aviFile.length(); logger.log(Level.INFO, "fileSize: {0}", fileSize); int listSize = (int) (fileSize - 8 - aviMovieOffset - indexlistBytes.length); @@ -133,17 +157,19 @@ public void finishAVI() throws Exception { } RandomAccessFile raf = new RandomAccessFile(aviFile, "rw"); + CipherOutputStream headerUpdate = new CipherOutputStream(new FileOutputStream(raf.getFD()), cipher); //add header and length by writing the headers again //with the now available information - raf.write(new RIFFHeader(fileSize).toBytes()); - raf.write(new AVIMainHeader().toBytes()); - raf.write(new AVIStreamList().toBytes()); - raf.write(new AVIStreamHeader().toBytes()); - raf.write(new AVIStreamFormat().toBytes()); - raf.write(new AVIJunk().toBytes()); - raf.write(new AVIMovieList(listSize).toBytes()); - + headerUpdate.write(new RIFFHeader(fileSize).toBytes()); + headerUpdate.write(new AVIMainHeader().toBytes()); + headerUpdate.write(new AVIStreamList().toBytes()); + headerUpdate.write(new AVIStreamHeader().toBytes()); + headerUpdate.write(new AVIStreamFormat().toBytes()); + headerUpdate.write(new AVIJunk().toBytes()); + headerUpdate.write(new AVIMovieList(listSize).toBytes()); + + headerUpdate.close(); raf.close(); } diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java index bb07e2e71..df2ea8bff 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java @@ -179,15 +179,14 @@ protected boolean processTouchEvent(View view, MotionEvent event) { public boolean onTouch(View view, MotionEvent event) { return processTouchEvent(view, event); } - protected boolean consumeEvent(KeyEvent event, Object touchInput, Object joyInput) { int source = event.getSource(); - boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) - || ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD); + boolean isTouch = ((source & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) || + ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD); - boolean isJoystick = ((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) - || ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK); + boolean isJoystick = ((source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || + ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK); boolean isUnknown = (source & InputDevice.SOURCE_UNKNOWN) == InputDevice.SOURCE_UNKNOWN; @@ -201,7 +200,7 @@ protected boolean consumeEvent(KeyEvent event, Object touchInput, Object joyInpu // Check if joyInput should consume the event if (isJoystick && joyInput != null) { - consumed |= ((AndroidJoyInput14) joyInput).onKey(event); + consumed |= ((AndroidJoyInput14)joyInput).onKey(event); } return consumed; diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 60e6b3026..472baac7c 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -86,7 +86,7 @@ public final class GLRenderer implements Renderer { private static final Logger logger = Logger.getLogger(GLRenderer.class.getName()); private static final boolean VALIDATE_SHADER = false; - private static final Pattern GLVERSION_PATTERN = Pattern.compile(".*?(\\d+)\\.(\\d+).*"); + private static final Pattern GLVERSION_PATTERN = Pattern.compile("\\b(\\d+)\\.(\\d+)\\b"); private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); private final FloatBuffer floatBuf16 = BufferUtils.createFloatBuffer(16); diff --git a/jme3-core/src/tools/java/jme3tools/shadercheck/GpuAnalyzerValidator.java b/jme3-core/src/tools/java/jme3tools/shadercheck/GpuAnalyzerValidator.java index 2064c5ced..11e3aab3e 100644 --- a/jme3-core/src/tools/java/jme3tools/shadercheck/GpuAnalyzerValidator.java +++ b/jme3-core/src/tools/java/jme3tools/shadercheck/GpuAnalyzerValidator.java @@ -54,59 +54,73 @@ public String getInstalledVersion() { } return version; } - private static void executeAnalyzer(String sourceCode, String language, String defines, String asic, StringBuilder results){ + private static void executeAnalyzer(String sourceCode, String language, String defines, String asic, StringBuilder results) { try { - // Export sourcecode to temporary file - File tempFile = File.createTempFile("test_shader", ".glsl"); - FileWriter writer = new FileWriter(tempFile); - - String glslVer = language.substring(4); - writer.append("#version ").append(glslVer).append('\n'); - writer.append("#extension all : warn").append('\n'); - writer.append(defines).append('\n'); - writer.write(sourceCode); - writer.close(); - - ProcessBuilder pb = new ProcessBuilder("GPUShaderAnalyzer", - tempFile.getAbsolutePath(), - "-I", - "-ASIC", asic); - - Process p = pb.start(); - - Scanner scan = new Scanner(p.getInputStream()); - - if (!scan.hasNextLine()){ - String x = scan.next(); - System.out.println(x); + // Export source code to a secure temporary file + File tempFile = File.createTempFile("test_shader", ".glsl", new File(System.getProperty("java.io.tmpdir"))); + + // Set secure file permissions and validate results + if (!tempFile.setReadable(false, false)) { + logger.log(Level.WARNING, "Failed to set temp file as non-readable for others: {0}", tempFile.getAbsolutePath()); } - - String ln = scan.nextLine(); - - if (ln.startsWith(";")){ - results.append(" - Success!").append('\n'); - }else{ - results.append(" - Failure!").append('\n'); - results.append(ln).append('\n'); - while (scan.hasNextLine()){ - results.append(scan.nextLine()).append('\n'); - } + if (!tempFile.setWritable(true, true)) { + logger.log(Level.WARNING, "Failed to set temp file as writable for the owner: {0}", tempFile.getAbsolutePath()); + } + if (!tempFile.setExecutable(false, false)) { + logger.log(Level.WARNING, "Failed to disable execute permissions on temp file: {0}", tempFile.getAbsolutePath()); } - scan.close(); + try (FileWriter writer = new FileWriter(tempFile)) { + String glslVer = language.substring(4); + writer.append("#version ").append(glslVer).append('\n'); + writer.append("#extension all : warn").append('\n'); + writer.append(defines).append('\n'); + writer.write(sourceCode); + } + + ProcessBuilder pb = new ProcessBuilder("GPUShaderAnalyzer", + tempFile.getAbsolutePath(), + "-I", + "-ASIC", asic); + + Process p = pb.start(); + + try (Scanner scan = new Scanner(p.getInputStream())) { + if (!scan.hasNextLine()) { + String x = scan.next(); + System.out.println(x); + } + + String ln = scan.nextLine(); + + if (ln.startsWith(";")) { + results.append(" - Success!").append('\n'); + } else { + results.append(" - Failure!").append('\n'); + results.append(ln).append('\n'); + while (scan.hasNextLine()) { + results.append(scan.nextLine()).append('\n'); + } + } + } + p.getOutputStream().close(); p.getErrorStream().close(); - + p.waitFor(); p.destroy(); - - tempFile.delete(); + + // Delete the temporary file securely + if (!tempFile.delete()) { + logger.log(Level.WARNING, "Temporary file could not be deleted: {0}", tempFile.getAbsolutePath()); + } } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); // Restore the interrupted status } catch (IOException ex) { - logger.log(Level.SEVERE, "IOEx", ex); + logger.log(Level.SEVERE, "IOException occurred", ex); } } - + @Override public void validate(Shader shader, StringBuilder results) { for (ShaderSource source : shader.getSources()){ @@ -121,5 +135,5 @@ public void validate(Shader shader, StringBuilder results) { } } } - + } diff --git a/jme3-networking/src/main/java/com/jme3/network/kernel/tcp/SocketConnector.java b/jme3-networking/src/main/java/com/jme3/network/kernel/tcp/SocketConnector.java index ed7c0f003..f948d76f7 100644 --- a/jme3-networking/src/main/java/com/jme3/network/kernel/tcp/SocketConnector.java +++ b/jme3-networking/src/main/java/com/jme3/network/kernel/tcp/SocketConnector.java @@ -43,7 +43,8 @@ import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicBoolean; +import javax.net.ssl.*; +import java.security.SecureRandom; /** @@ -54,102 +55,118 @@ * @version $Revision$ * @author Paul Speed */ -public class SocketConnector implements Connector { - private SSLSocket sslSocket; +public class SocketConnector implements Connector +{ + private SSLSocket sock; private InputStream in; private OutputStream out; private SocketAddress remoteAddress; private byte[] buffer = new byte[65535]; - private AtomicBoolean connected = new AtomicBoolean(false); + private boolean connected = false; - public SocketConnector(InetAddress address, int port) throws IOException { - // Create an SSLSocket using SSLSocketFactory - SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); - this.sslSocket = (SSLSocket) sslSocketFactory.createSocket(address, port); - remoteAddress = sslSocket.getRemoteSocketAddress(); // For informational purposes + public SocketConnector( InetAddress address, int port ) throws IOException + { + try { + // Initialize SSLContext with the default key and trust managers + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, new SecureRandom()); - // Set SSL-specific options (if needed) - sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); + // Create an SSLSocketFactory + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - // Disable Nagle's buffering to send data immediately - sslSocket.setTcpNoDelay(true); + // SSLSocket connected to the provided address and port + this.sock = (SSLSocket) sslSocketFactory.createSocket(address, port); + this.remoteAddress = sock.getRemoteSocketAddress(); // For informational purposes - // Initialize input and output streams using SSL socket - in = sslSocket.getInputStream(); - out = sslSocket.getOutputStream(); + // Desired SSL protocols + sock.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"}); - connected.set(true); - } + sock.setTcpNoDelay(true); + + // Initialize input and output streams securely + this.in = sock.getInputStream(); + this.out = sock.getOutputStream(); - protected void checkClosed() { - if (sslSocket == null) { - throw new ConnectorException("Connection is closed: " + remoteAddress); + this.connected = true; // Connected + } catch (Exception e) { + throw new IOException("Failed to establish SSL connection", e); } } - + + protected void checkClosed() + { + if( sock == null ) + throw new ConnectorException( "Connection is closed:" + remoteAddress ); + } + @Override - public boolean isConnected() { - if (sslSocket == null) { + public boolean isConnected() + { + if( sock == null ) return false; - } - return sslSocket.isConnected(); + return sock.isConnected(); } @Override - public void close() { + public void close() + { checkClosed(); try { - SSLSocket temp = sslSocket; - sslSocket = null; - connected.set(false); + Socket temp = sock; + sock = null; + connected = false; temp.close(); - } catch (IOException e) { - throw new ConnectorException("Error closing socket for: " + remoteAddress, e); - } - } + } catch( IOException e ) { + throw new ConnectorException( "Error closing socket for:" + remoteAddress, e ); + } + } @Override - public boolean available() { + public boolean available() + { checkClosed(); try { return in.available() > 0; - } catch (IOException e) { - throw new ConnectorException("Error retrieving data availability for: " + remoteAddress, e); - } - } - + } catch( IOException e ) { + throw new ConnectorException( "Error retrieving data availability for:" + remoteAddress, e ); + } + } + @Override - public ByteBuffer read() { + public ByteBuffer read() + { checkClosed(); - + try { // Read what we can int count = in.read(buffer); - if (count < 0) { + if( count < 0 ) { // Socket is closed close(); return null; } // Wrap it in a ByteBuffer for the caller - return ByteBuffer.wrap(buffer, 0, count); - } catch (IOException e) { - if (!connected.get()) { + return ByteBuffer.wrap( buffer, 0, count ); + } catch( IOException e ) { + if( !connected) { // Nothing to see here... just move along return null; - } - throw new ConnectorException("Error reading from connection to: " + remoteAddress, e); - } + } + throw new ConnectorException( "Error reading from connection to:" + remoteAddress, e ); + } } - + @Override - public void write(ByteBuffer data) { + public void write( ByteBuffer data ) + { checkClosed(); - + try { out.write(data.array(), data.position(), data.remaining()); - } catch (IOException e) { - throw new ConnectorException("Error writing to connection: " + remoteAddress, e); + } catch( IOException e ) { + throw new ConnectorException( "Error writing to connection:" + remoteAddress, e ); } - } + } + } diff --git a/jme3-plugins/src/xml/java/com/jme3/export/xml/XMLImporter.java b/jme3-plugins/src/xml/java/com/jme3/export/xml/XMLImporter.java index 42411d923..4a9d98f19 100644 --- a/jme3-plugins/src/xml/java/com/jme3/export/xml/XMLImporter.java +++ b/jme3-plugins/src/xml/java/com/jme3/export/xml/XMLImporter.java @@ -41,9 +41,12 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; import org.xml.sax.SAXException; + /** * Part of the jME XML IO system as introduced in the Google Code jmexml project. * @author Kai Rabien (hevee) - original author of the code.google.com jmexml project @@ -58,7 +61,6 @@ public class XMLImporter implements JmeImporter { public XMLImporter() { } - @Override public int getFormatVersion() { return formatVersion; @@ -99,21 +101,20 @@ public Savable load(File f) throws IOException { public Savable load(InputStream f) throws IOException { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - // Disable access to external entities to prevent XXE attacks factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - - // Disable external DTDs + factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); - factory.setNamespaceAware(true); - domIn = new DOMInputCapsule(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f), this); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(f); + + domIn = new DOMInputCapsule(doc, this); return domIn.readSavable(null, null); } catch (SAXException | ParserConfigurationException e) { IOException ex = new IOException(); - ex.initCause(e); throw ex; }