Added XOR Scrambler for Brew data

Very fast obfuscator for brew data so that a client cant easily read it
This commit is contained in:
Sn0wStorm 2016-06-29 22:44:19 +02:00
parent cdb36b1736
commit 9bf3268fb4
6 changed files with 430 additions and 11 deletions

View File

@ -1,10 +1,7 @@
package com.dre.brewery;
import org.bukkit.Color;
import com.dre.brewery.lore.Base91DecoderStream;
import com.dre.brewery.lore.Base91EncoderStream;
import com.dre.brewery.lore.LoreLoadStream;
import com.dre.brewery.lore.LoreSaveStream;
import com.dre.brewery.lore.*;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.BrewerInventory;
@ -20,6 +17,7 @@ import org.bukkit.potion.PotionType;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -28,7 +26,7 @@ import java.util.Map;
public class Brew {
// represents the liquid in the brewed Potions
private static long saveSeed;
public static Map<Integer, Brew> legacyPotions = new HashMap<>();
public static long installTime = System.currentTimeMillis(); // plugin install time in millis after epoch
public static Boolean colorInBarrels; // color the Lore while in Barrels
@ -732,18 +730,26 @@ public class Brew {
} catch (IllegalArgumentException ignored) {
return null;
}
try (DataInputStream in = new DataInputStream(new Base91DecoderStream(loreStream))) {
XORUnscrambleStream unscrambler = new XORUnscrambleStream(new Base91DecoderStream(loreStream), saveSeed);
try (DataInputStream in = new DataInputStream(unscrambler)) {
boolean parityFailed = false;
if (in.readByte() != 86) {
P.p.errorLog("parity check failed on Brew while loading, trying to load anyways!");
P.p.errorLog("Parity check failed on Brew while loading, trying to load anyways!");
parityFailed = true;
}
Brew brew = new Brew();
byte ver = in.readByte();
switch (ver) {
case 1:
unscrambler.start();
brew.loadFromStream(in);
break;
default:
P.p.errorLog("Brew has data stored in v" + ver + " this version supports up to v1");
if (parityFailed) {
P.p.errorLog("Failed to load Brew. Maybe something corrupted the Lore of the Item?");
} else {
P.p.errorLog("Brew has data stored in v" + ver + " this Plugin version supports up to v1");
}
return null;
}
return brew;
@ -779,7 +785,11 @@ public class Brew {
// Save brew data into meta/lore
public void save(ItemMeta meta) {
try (DataOutputStream out = new DataOutputStream(new Base91EncoderStream(new LoreSaveStream(meta, 0)))) {
XORScrambleStream scrambler = new XORScrambleStream(new Base91EncoderStream(new LoreSaveStream(meta, 0)), saveSeed);
try (DataOutputStream out = new DataOutputStream(scrambler)) {
out.writeByte(86); // Parity/sanity
out.writeByte(1); // Version
scrambler.start();
saveToStream(out);
} catch (IOException e) {
P.p.errorLog("IO Error while saving Brew");
@ -801,8 +811,6 @@ public class Brew {
}
public void saveToStream(DataOutputStream out) throws IOException {
out.writeByte(86); // Parity/sanity
out.writeByte(1); // Version
if (quality > 10) {
quality = 10;
}
@ -831,6 +839,20 @@ public class Brew {
ingredients.save(out);
}
public static void writeSeed(ConfigurationSection section) {
section.set("seed", saveSeed);
}
public static void loadSeed(ConfigurationSection section) {
if (section.contains("seed")) {
saveSeed = section.getLong("seed");
} else {
while (saveSeed == 0) {
saveSeed = new SecureRandom().nextLong();
}
}
}
// Load potion data from data file for backwards compatibility
public static void loadLegacy(BIngredients ingredients, int uid, int quality, byte distillRuns, float ageTime, float wood, String recipe, boolean unlabeled, boolean persistent, boolean stat) {
Brew brew = new Brew(ingredients, quality, distillRuns, ageTime, wood, recipe, unlabeled, stat);

View File

@ -86,6 +86,136 @@ public class P extends JavaPlugin {
//P.p.log("§" + (use1_9 ? "a":"c") + "1.9 " + "§" + (use1_11 ? "a":"c") + "1.11 " + "§" + (use1_13 ? "a":"c") + "1.13 " + "§" + (use1_14 ? "a":"c") + "1.14");
/*long master = new SecureRandom().nextLong();
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
XORScrambleStream scramble = new XORScrambleStream(new Base91EncoderStream(byteStream), master);
DataOutputStream data = new DataOutputStream(scramble);
DataInputStream dataIn = null;
try {
scramble.start();
data.writeLong(12345L);
scramble.stop();
data.writeInt(1);
data.writeInt(1);
scramble.start();
data.writeDouble(0.55555D);
data.writeInt(234323);
//data.writeUTF("Hallo Peter");
data.writeLong(5419L); // Skip
data.writeDouble(0.55555D);
data.close();
XORUnscrambleStream unscramble = new XORUnscrambleStream(new Base91DecoderStream(new ByteArrayInputStream(byteStream.toByteArray())), master);
dataIn = new DataInputStream(unscramble);
unscramble.start();
P.p.log(dataIn.readLong() + "");
unscramble.stop();
P.p.log(dataIn.readInt() + "");
P.p.log(dataIn.readInt() + "");
unscramble.start();
P.p.log(dataIn.readDouble() + "");
dataIn.mark(1000);
P.p.log(dataIn.readInt() + "");
//P.p.log(dataIn.readUTF());
dataIn.skip(8);
P.p.log(dataIn.readDouble() + "");
P.p.log("reset");
dataIn.reset();
P.p.log(dataIn.readInt() + "");
//P.p.log(dataIn.readUTF());
dataIn.skip(8);
P.p.log(dataIn.readDouble() + "");
dataIn.close();
*//*for (int i = 0; i < 10; i++) {
byteStream = new ByteArrayOutputStream();
scramble = new XORScrambleStream(new Base91EncoderStream(byteStream));
data = new DataOutputStream(scramble);
data.writeInt(i);
scramble.start();
data.writeLong(12345L);
data.writeLong(12345L);
scramble.stop();
data.writeInt(1);
data.writeInt(1);
scramble.start();
data.writeInt(234323);
data.writeDouble(0.55555D);
P.p.log(byteStream.toString());
data.close();
}*//*
long time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
unscramble = new XORUnscrambleStream(new Base91DecoderStream(new ByteArrayInputStream(byteStream.toByteArray())), master);
dataIn = new DataInputStream(unscramble);
unscramble.start();
dataIn.readLong();
unscramble.stop();
dataIn.readInt();
dataIn.readInt();
unscramble.start();
dataIn.readDouble();
dataIn.mark(1000);
dataIn.readInt();
//dataIn.readUTF();
dataIn.skip(8);
dataIn.readDouble();
dataIn.reset();
dataIn.readInt();
//dataIn.readUTF();
dataIn.skip(8);
dataIn.readDouble();
dataIn.close();
}
long time2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
unscramble = new XORUnscrambleStream(new ByteArrayInputStream(byteStream.toByteArray()), master);
dataIn = new DataInputStream(unscramble);
unscramble.start();
dataIn.skip(2);
dataIn.readLong();
unscramble.stop();
dataIn.readInt();
dataIn.readInt();
unscramble.start();
dataIn.readDouble();
dataIn.mark(1000);
dataIn.readInt();
//dataIn.readUTF();
dataIn.skip(8);
dataIn.readDouble();
dataIn.reset();
dataIn.readInt();
//dataIn.readUTF();
dataIn.skip(8);
dataIn.readDouble();
dataIn.close();
}
long time3 = System.currentTimeMillis();
P.p.log("Time with base91: " + (time2 - time));
P.p.log("Time without base91: " + (time3 - time2));
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
data.close();
if (dataIn != null) {
dataIn.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}*/
/*try {
ItemMeta meta = new ItemStack(Material.POTION).getItemMeta();
@ -577,8 +707,11 @@ public class P extends JavaPlugin {
FileConfiguration data = YamlConfiguration.loadConfiguration(file);
Brew.installTime = data.getLong("installTime", System.currentTimeMillis());
MCBarrel.mcBarrelTime = data.getLong("MCBarrelTime", 0);
Brew.loadSeed(data);
// Check if data is the newest version
String version = data.getString("Version", null);
if (version != null) {

View File

@ -62,8 +62,11 @@ public class DataSave extends BukkitRunnable {
FileConfiguration configFile = new YamlConfiguration();
configFile.set("installTime", Brew.installTime);
configFile.set("MCBarrelTime", MCBarrel.mcBarrelTime);
Brew.writeSeed(configFile);
if (!Brew.legacyPotions.isEmpty()) {
Brew.save(configFile.createSection("Brew"));
}

View File

@ -0,0 +1,94 @@
package com.dre.brewery.lore;
import java.io.InputStream;
import java.util.Arrays;
public class SeedInputStream extends InputStream {
// From java.util.Random
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
private long seed;
private byte[] buf = new byte[4];
private byte reader = 4;
private long markSeed;
private byte[] markbuf;
public SeedInputStream(long seed) {
this.seed = (seed ^ multiplier) & mask;
}
private void calcSeed() {
seed = (seed * multiplier + addend) & mask;
}
private void genNext() {
calcSeed();
int next = (int)(seed >>> 16);
buf[0] = (byte) (next >> 24);
buf[1] = (byte) (next >> 16);
buf[2] = (byte) (next >> 8);
buf[3] = (byte) next;
reader = 0;
}
@Override
public int read(byte[] b, int off, int len) {
for (int i = off; i < len; i++) {
if (reader >= 4) {
genNext();
}
b[i] = buf[reader++];
}
return len;
}
@Override
public int read() {
if (reader == 4) {
genNext();
}
return buf[reader++];
}
@Override
public long skip(long toSkip) {
long n = toSkip;
while (n > 0) {
if (reader < 4) {
reader++;
n--;
} else if (n >= 4) {
calcSeed();
n -= 4;
} else {
genNext();
}
}
return toSkip;
}
@Override
public void close() {
buf = null;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void mark(int readlimit) {
markbuf = new byte[] {buf[0], buf[1], buf[2], buf[3], reader};
markSeed = seed;
}
@Override
public synchronized void reset() {
seed = markSeed;
buf = Arrays.copyOfRange(markbuf, 0, 4);
reader = markbuf[4];
}
}

View File

@ -0,0 +1,67 @@
package com.dre.brewery.lore;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
public class XORScrambleStream extends FilterOutputStream {
private final long seed;
private SeedInputStream xorStream;
private boolean running;
public XORScrambleStream(OutputStream out, long seed) {
super(out);
this.seed = seed;
}
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);
}
}
public void stop() {
running = false;
}
@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();
}
}

View File

@ -0,0 +1,100 @@
package com.dre.brewery.lore;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class XORUnscrambleStream extends FilterInputStream {
private final long seed;
private SeedInputStream xorStream;
private boolean running;
private boolean markRunning;
private boolean markxor;
public XORUnscrambleStream(InputStream in, long seed) {
super(in);
this.seed = seed;
}
public void start() throws IOException {
running = true;
if (xorStream == null) {
short id = (short) (in.read() << 8 | in.read());
if (id == 0) {
running = false;
return;
}
xorStream = new SeedInputStream(seed ^ id);
}
}
public void stop() {
running = false;
}
@Override
public int read() throws IOException {
if (!running) {
return in.read();
}
return (in.read() ^ xorStream.read()) & 0xFF;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (!running) {
return in.read(b, off, len);
}
len = in.read(b, off, len);
for (int i = off; i < len + off; i++) {
b[i] ^= xorStream.read();
}
return len;
}
@Override
public long skip(long n) throws IOException {
long skipped = in.skip(n);
if (running && skipped > 0) {
xorStream.skip(skipped);
}
return skipped;
}
@Override
public void close() throws IOException {
if (xorStream != null) {
xorStream.close();
xorStream = null;
}
running = false;
super.close();
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public synchronized void reset() throws IOException {
in.reset();
if (markxor) {
xorStream.reset();
} else {
xorStream = null;
}
running = markRunning;
}
@Override
public synchronized void mark(int readlimit) {
in.mark(readlimit);
if (xorStream != null) {
xorStream.mark(readlimit);
markxor = true;
}
markRunning = running;
}
}