2016-06-29 22:44:19 +02:00
|
|
|
package com.dre.brewery.lore;
|
|
|
|
|
|
|
|
import java.io.FilterOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.util.Random;
|
|
|
|
|
2019-10-18 16:30:03 +02:00
|
|
|
/**
|
|
|
|
* A Scramble Stream that uses XOR operations to scramble an outputstream.
|
2019-11-25 22:16:16 +01:00
|
|
|
* <p>a byte generator feeded with the seed is used as xor source
|
|
|
|
* <p>The resulting data can be unscrambled by the XORUnscrambleStream
|
2019-10-18 16:30:03 +02:00
|
|
|
*/
|
2016-06-29 22:44:19 +02:00
|
|
|
public class XORScrambleStream extends FilterOutputStream {
|
|
|
|
|
|
|
|
private final long seed;
|
|
|
|
private SeedInputStream xorStream;
|
|
|
|
private boolean running;
|
|
|
|
|
2019-10-18 16:30:03 +02:00
|
|
|
/**
|
|
|
|
* Create a new instance of an XORScrambler, scrambling the given outputstream
|
|
|
|
*
|
|
|
|
* @param out The Outputstream to be scrambled
|
|
|
|
* @param seed The seed used for scrambling
|
|
|
|
*/
|
2016-06-29 22:44:19 +02:00
|
|
|
public XORScrambleStream(OutputStream out, long seed) {
|
|
|
|
super(out);
|
|
|
|
this.seed = seed;
|
|
|
|
}
|
|
|
|
|
2019-10-18 16:30:03 +02:00
|
|
|
/**
|
|
|
|
* To start the scrambling process this has to be called before writing any data to this stream.
|
2019-11-25 22:16:16 +01:00
|
|
|
* <br>Before starting the scrambler, any data will just be passed through unscrambled to the underlying stream.
|
|
|
|
* <br>The Scrambling can be started and stopped arbitrarily at any point, allowing for parts of unscrambled data in the stream.
|
2019-10-18 16:30:03 +02:00
|
|
|
*
|
|
|
|
* @throws IOException IOException
|
|
|
|
*/
|
2016-06-29 22:44:19 +02:00
|
|
|
public void start() throws IOException {
|
|
|
|
running = true;
|
|
|
|
if (xorStream == null) {
|
|
|
|
short id = 0;
|
|
|
|
while (id == 0) {
|
|
|
|
id = (short) new Random().nextInt();
|
|
|
|
}
|
|
|
|
xorStream = new SeedInputStream(seed ^ id);
|
|
|
|
out.write((byte) (id >> 8));
|
|
|
|
out.write((byte) id);
|
2016-06-30 15:32:10 +02:00
|
|
|
write((int) (seed >> 48) & 0xFF); // parity/sanity
|
2016-06-29 22:44:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 16:30:03 +02:00
|
|
|
/**
|
|
|
|
* Stop the scrambling, any following data will be passed through unscrambled.
|
2019-11-25 22:16:16 +01:00
|
|
|
* <br>The scrambling can be started again at any point after calling this
|
2019-10-18 16:30:03 +02:00
|
|
|
*/
|
2016-06-29 22:44:19 +02:00
|
|
|
public void stop() {
|
|
|
|
running = false;
|
|
|
|
}
|
|
|
|
|
2019-10-18 16:30:03 +02:00
|
|
|
/**
|
2019-11-25 22:16:16 +01:00
|
|
|
* Mark the stream as unscrambled, any effort of unscrambing the data later will automatically read the already unscrambled data.
|
|
|
|
* <p>Useful if a stream may be scrambled or unscrambled, the unscrambler will automatically identify either way.
|
2019-10-18 16:30:03 +02:00
|
|
|
*
|
|
|
|
* @throws IOException IOException
|
|
|
|
* @throws IllegalStateException If the Scrambler was started in normal scrambling mode before
|
|
|
|
*/
|
|
|
|
public void startUnscrambled() throws IOException, IllegalStateException {
|
|
|
|
if (xorStream != null) throw new IllegalStateException("The Scrambler was started in scrambling mode before");
|
|
|
|
short id = 0;
|
|
|
|
out.write((byte) (id >> 8));
|
|
|
|
out.write((byte) id);
|
|
|
|
}
|
|
|
|
|
2016-06-29 22:44:19 +02:00
|
|
|
@Override
|
|
|
|
public void write(int b) throws IOException {
|
|
|
|
if (!running) {
|
|
|
|
out.write(b);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
out.write(b ^ xorStream.read());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void write(byte[] b, int off, int len) throws IOException {
|
|
|
|
if (!running) {
|
|
|
|
out.write(b, off, len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
byte[] xored = new byte[len];
|
|
|
|
xorStream.read(xored);
|
|
|
|
int j = off;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
xored[i] ^= b[j++];
|
|
|
|
}
|
|
|
|
out.write(xored);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() throws IOException {
|
|
|
|
running = false;
|
|
|
|
xorStream = null;
|
|
|
|
super.close();
|
|
|
|
}
|
|
|
|
}
|