Move to an entirely JSON encoded file for the database

This commit is contained in:
Alexander Bakker 2018-02-13 22:06:24 +01:00
parent 7eaffc933e
commit 587835ad38
14 changed files with 187 additions and 297 deletions

View file

@ -13,11 +13,7 @@ public class Database {
private List<DatabaseEntry> _entries = new ArrayList<>();
private long _counter = 0;
public byte[] serialize() throws Exception {
return serialize(false);
}
public byte[] serialize(boolean pretty) throws Exception {
public JSONObject serialize() throws Exception {
JSONArray array = new JSONArray();
for (DatabaseEntry e : _entries) {
array.put(e.serialize());
@ -26,18 +22,14 @@ public class Database {
JSONObject obj = new JSONObject();
obj.put("version", VERSION);
obj.put("entries", array);
String string = pretty ? obj.toString(4) : obj.toString();
return string.getBytes("UTF-8");
return obj;
}
public void deserialize(byte[] data) throws Exception {
deserialize(data, true);
public void deserialize(JSONObject obj) throws Exception {
deserialize(obj, true);
}
public void deserialize(byte[] data, boolean incCount) throws Exception {
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
public void deserialize(JSONObject obj, boolean incCount) throws Exception {
// TODO: support different VERSION deserialization providers
int ver = obj.getInt("version");
if (ver != VERSION) {

View file

@ -1,163 +1,116 @@
package me.impy.aegis.db;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import android.util.Base64;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import me.impy.aegis.crypto.CryptParameters;
import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.util.LittleByteBuffer;
import me.impy.aegis.encoding.Hex;
public class DatabaseFile {
private static final byte SECTION_ENCRYPTION_PARAMETERS = 0x00;
private static final byte SECTION_SLOTS = 0x01;
private static final byte SECTION_END = (byte) 0xFF;
private static final byte VERSION = 1;
public static final byte VERSION = 1;
private final byte[] HEADER;
private byte[] _content;
private Object _content;
private CryptParameters _cryptParameters;
private SlotCollection _slots;
public DatabaseFile() {
try {
HEADER = "AEGIS".getBytes("US_ASCII");
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
_slots = new SlotCollection();
}
public byte[] serialize() throws IOException {
byte[] content = getContent();
CryptParameters cryptParams = getCryptParameters();
// this is dumb, java doesn't provide an endianness-aware data stream
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream stream = new DataOutputStream(byteStream);
stream.write(HEADER);
stream.write(VERSION);
if (cryptParams != null) {
LittleByteBuffer paramBuffer = LittleByteBuffer.allocate(CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
paramBuffer.put(cryptParams.Nonce);
paramBuffer.put(cryptParams.Tag);
writeSection(stream, SECTION_ENCRYPTION_PARAMETERS, paramBuffer.array());
public byte[] serialize() throws JSONException, UnsupportedEncodingException {
JSONObject cryptObj = null;
if (_cryptParameters != null) {
cryptObj = new JSONObject();
cryptObj.put("nonce", Hex.toString(_cryptParameters.Nonce));
cryptObj.put("tag", Hex.toString(_cryptParameters.Tag));
}
if (!_slots.isEmpty()) {
byte[] bytes = SlotCollection.serialize(_slots);
writeSection(stream, SECTION_SLOTS, bytes);
}
JSONObject headerObj = new JSONObject();
headerObj.put("slots", _slots.isEmpty() ? JSONObject.NULL : SlotCollection.serialize(_slots));
headerObj.put("params", cryptObj != null ? cryptObj : JSONObject.NULL);
writeSection(stream, SECTION_END, null);
stream.write(content);
return byteStream.toByteArray();
JSONObject obj = new JSONObject();
obj.put("version", VERSION);
obj.put("header", headerObj);
obj.put("db", _content);
String string = obj.toString(4);
return string.getBytes("UTF-8");
}
public void deserialize(byte[] data) throws Exception {
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
byte[] header = new byte[HEADER.length];
buffer.get(header);
if (!Arrays.equals(header, HEADER)) {
throw new Exception("Bad header");
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
JSONObject headerObj = obj.getJSONObject("header");
if (obj.getInt("version") > VERSION) {
throw new Exception("unsupported version");
}
// TODO: support different version deserialization providers
byte version = buffer.get();
if (version != VERSION) {
throw new Exception("Unsupported version");
JSONObject slotObj = headerObj.optJSONObject("slots");
if (slotObj != null) {
_slots = SlotCollection.deserialize(slotObj);
}
CryptParameters cryptParams = null;
SlotCollection slots = new SlotCollection();
for (section s = readSection(buffer); s.ID != SECTION_END; s = readSection(buffer)) {
LittleByteBuffer sBuff = LittleByteBuffer.wrap(s.Data);
switch (s.ID) {
case SECTION_ENCRYPTION_PARAMETERS:
assertLength(s.Data, CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
byte[] nonce = new byte[CryptoUtils.CRYPTO_NONCE_SIZE];
byte[] tag = new byte[CryptoUtils.CRYPTO_TAG_SIZE];
sBuff.get(nonce);
sBuff.get(tag);
cryptParams = new CryptParameters() {{
Nonce = nonce;
Tag = tag;
JSONObject cryptObj = headerObj.optJSONObject("params");
if (cryptObj != null) {
_cryptParameters = new CryptParameters() {{
Nonce = Hex.toBytes(cryptObj.getString("nonce"));
Tag = Hex.toBytes(cryptObj.getString("tag"));
}};
break;
case SECTION_SLOTS:
slots = SlotCollection.deserialize(s.Data);
break;
}
}
setCryptParameters(cryptParams);
setSlots(slots);
byte[] content = new byte[buffer.remaining()];
buffer.get(content);
setContent(content);
if (cryptObj == null || slotObj == null) {
_content = obj.getJSONObject("db");
} else {
_content = obj.getString("db");
}
}
public boolean isEncrypted() {
return !_slots.isEmpty() && _cryptParameters != null;
}
private static void writeSection(DataOutputStream stream, byte id, byte[] data) throws IOException {
stream.write(id);
LittleByteBuffer buffer = LittleByteBuffer.allocate(/* sizeof uint32_t */ 4);
if (data == null) {
buffer.putInt(0);
} else {
buffer.putInt(data.length);
}
stream.write(buffer.array());
if (data != null) {
stream.write(data);
}
public JSONObject getContent() {
return (JSONObject) _content;
}
private static section readSection(LittleByteBuffer buffer) {
section s = new section();
s.ID = buffer.get();
int len = buffer.getInt();
s.Data = new byte[len];
buffer.get(s.Data);
return s;
public JSONObject getContent(MasterKey key)
throws NoSuchPaddingException, InvalidKeyException,
NoSuchAlgorithmException, IllegalBlockSizeException,
BadPaddingException, InvalidAlgorithmParameterException, IOException, JSONException {
byte[] bytes = Base64.decode((String) _content, Base64.NO_WRAP);
CryptResult result = key.decrypt(bytes, _cryptParameters);
return new JSONObject(new String(result.Data, "UTF-8"));
}
private static void assertLength(byte[] bytes, int length) throws Exception {
if (bytes.length != length) {
throw new Exception("Bad length");
}
public void setContent(JSONObject dbObj) {
_content = dbObj;
}
public byte[] getContent() {
return _content;
}
public void setContent(JSONObject dbObj, MasterKey key)
throws JSONException, UnsupportedEncodingException,
NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException,
IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
String string = dbObj.toString(4);
byte[] dbBytes = string.getBytes("UTF-8");
public void setContent(byte[] content) {
_content = content;
}
public CryptParameters getCryptParameters() {
return _cryptParameters;
}
public void setCryptParameters(CryptParameters parameters) {
_cryptParameters = parameters;
CryptResult result = key.encrypt(dbBytes);
_content = new String(Base64.encode(result.Data, Base64.NO_WRAP), "UTF-8");
_cryptParameters = result.Parameters;
}
public SlotCollection getSlots() {
@ -167,9 +120,4 @@ public class DatabaseFile {
public void setSlots(SlotCollection slots) {
_slots = slots;
}
private static class section {
byte ID;
byte[] Data;
}
}

View file

@ -3,6 +3,9 @@ package me.impy.aegis.db;
import android.content.Context;
import android.os.Environment;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
@ -16,9 +19,9 @@ import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.MasterKey;
public class DatabaseManager {
private static final String FILENAME = "aegis.db";
private static final String FILENAME_EXPORT = "aegis_export.db";
private static final String FILENAME_EXPORT_PLAIN = "aegis_export.json";
private static final String FILENAME = "aegis.json";
private static final String FILENAME_EXPORT = "aegis_export.json";
private static final String FILENAME_EXPORT_PLAIN = "aegis_export_plain.json";
private MasterKey _key;
private DatabaseFile _file;
@ -58,9 +61,9 @@ public class DatabaseManager {
_file.deserialize(fileBytes);
if (!_file.isEncrypted()) {
byte[] contentBytes = _file.getContent();
JSONObject obj = _file.getContent();
_db = new Database();
_db.deserialize(contentBytes);
_db.deserialize(obj);
}
}
@ -73,15 +76,13 @@ public class DatabaseManager {
public void unlock(MasterKey key) throws Exception {
assertState(true, true);
byte[] encrypted = _file.getContent();
CryptParameters params = _file.getCryptParameters();
CryptResult result = key.decrypt(encrypted, params);
JSONObject obj = _file.getContent(key);
_db = new Database();
_db.deserialize(result.Data);
_db.deserialize(obj);
_key = key;
}
public static void save(Context context, DatabaseFile file) throws IOException {
public static void save(Context context, DatabaseFile file) throws IOException, JSONException {
byte[] bytes = file.serialize();
FileOutputStream stream = null;
@ -98,26 +99,21 @@ public class DatabaseManager {
public void save() throws Exception {
assertState(false, true);
byte[] dbBytes = _db.serialize();
if (!_file.isEncrypted()) {
_file.setContent(dbBytes);
JSONObject obj = _db.serialize();
if (_file.isEncrypted()) {
_file.setContent(obj, _key);
} else {
CryptResult result = _key.encrypt(dbBytes);
_file.setContent(result.Data);
_file.setCryptParameters(result.Parameters);
_file.setContent(obj);
}
save(_context, _file);
}
public String export(boolean encrypt) throws Exception {
assertState(false, true);
byte[] bytes = _db.serialize(!encrypt);
encrypt = encrypt && getFile().isEncrypted();
if (encrypt) {
CryptResult result = _key.encrypt(bytes);
_file.setContent(result.Data);
_file.setCryptParameters(result.Parameters);
bytes = _file.serialize();
if (encrypt && getFile().isEncrypted()) {
_file.setContent(_db.serialize(), _key);
} else {
_file.setContent(_db.serialize());
}
File file;
@ -129,6 +125,7 @@ public class DatabaseManager {
throw new IOException("error creating external storage directory");
}
byte[] bytes = _file.serialize();
file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN);
stream = new FileOutputStream(file);
stream.write(bytes);

View file

@ -1,6 +1,11 @@
package me.impy.aegis.db.slots;
public class FingerprintSlot extends RawSlot {
public FingerprintSlot() {
super();
}
@Override
public byte getType() {
return TYPE_FINGERPRINT;

View file

@ -1,12 +1,15 @@
package me.impy.aegis.db.slots;
import org.json.JSONException;
import org.json.JSONObject;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.util.LittleByteBuffer;
import me.impy.aegis.encoding.Hex;
public class PasswordSlot extends RawSlot {
private int _n;
@ -19,27 +22,22 @@ public class PasswordSlot extends RawSlot {
}
@Override
public byte[] serialize() {
byte[] bytes = super.serialize();
LittleByteBuffer buffer = LittleByteBuffer.wrap(bytes);
buffer.position(super.getSize());
buffer.putInt(_n);
buffer.putInt(_r);
buffer.putInt(_p);
buffer.put(_salt);
return buffer.array();
public JSONObject serialize() throws JSONException {
JSONObject obj = super.serialize();
obj.put("n", _n);
obj.put("r", _r);
obj.put("p", _p);
obj.put("salt", Hex.toString(_salt));
return obj;
}
@Override
public void deserialize(byte[] data) throws Exception {
super.deserialize(data);
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
buffer.position(super.getSize());
_n = buffer.getInt();
_r = buffer.getInt();
_p = buffer.getInt();
_salt = new byte[CryptoUtils.CRYPTO_SALT_SIZE];
buffer.get(_salt);
public void deserialize(JSONObject obj) throws Exception {
super.deserialize(obj);
_n = obj.getInt("n");
_r = obj.getInt("r");
_p = obj.getInt("p");
_salt = Hex.toBytes(obj.getString("salt"));
}
public SecretKey deriveKey(char[] password, byte[] salt, int n, int r, int p) throws InvalidKeySpecException, NoSuchAlgorithmException {
@ -55,11 +53,6 @@ public class PasswordSlot extends RawSlot {
return CryptoUtils.deriveKey(password, _salt, _n, _r, _p);
}
@Override
public int getSize() {
return super.getSize() + /* _n, _r, _p */ 4 + 4 + 4 + CryptoUtils.CRYPTO_SALT_SIZE;
}
@Override
public byte getType() {
return TYPE_DERIVED;

View file

@ -1,40 +1,11 @@
package me.impy.aegis.db.slots;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.util.LittleByteBuffer;
public class RawSlot extends Slot {
public RawSlot() {
super();
}
@Override
public byte[] serialize() {
LittleByteBuffer buffer = LittleByteBuffer.allocate(getSize());
buffer.put(getType());
buffer.put(_id);
buffer.put(_encryptedMasterKey);
return buffer.array();
}
@Override
public void deserialize(byte[] data) throws Exception {
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
if (buffer.get() != getType()) {
throw new Exception("slot type mismatch");
}
_id = new byte[ID_SIZE];
buffer.get(_id);
_encryptedMasterKey = new byte[CryptoUtils.CRYPTO_KEY_SIZE];
buffer.get(_encryptedMasterKey);
}
@Override
public int getSize() {
return 1 + ID_SIZE + CryptoUtils.CRYPTO_KEY_SIZE;
}
@Override
public byte getType() {
return TYPE_RAW;

View file

@ -2,6 +2,9 @@ package me.impy.aegis.db.slots;
import android.annotation.SuppressLint;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@ -51,14 +54,24 @@ public abstract class Slot implements Serializable {
return cipher;
}
public JSONObject serialize() throws JSONException {
JSONObject obj = new JSONObject();
obj.put("type", getType());
obj.put("id", Hex.toString(_id));
obj.put("key", Hex.toString(_encryptedMasterKey));
return obj;
}
public void deserialize(JSONObject obj) throws Exception {
if (obj.getInt("type") != getType()) {
throw new Exception("slot type mismatch");
}
_id = Hex.toBytes(obj.getString("id"));
_encryptedMasterKey = Hex.toBytes(obj.getString("key"));
}
public abstract byte getType();
public String getID() {
return Hex.toString(_id);
}
public abstract int getSize();
public abstract byte getType();
// a slot has a binary representation
public abstract byte[] serialize();
public abstract void deserialize(byte[] data) throws Exception;
}

View file

@ -1,5 +1,9 @@
package me.impy.aegis.db.slots;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
@ -9,44 +13,38 @@ import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.util.LittleByteBuffer;
import me.impy.aegis.encoding.Hex;
public class SlotCollection implements Iterable<Slot>, Serializable {
private List<Slot> _slots = new ArrayList<>();
private byte[] _masterHash;
public static byte[] serialize(SlotCollection slots) {
// yep, no streams at this api level
int size = 0;
public static JSONObject serialize(SlotCollection slots) throws JSONException {
JSONObject obj = new JSONObject();
obj.put("hash", Hex.toString(slots.getMasterHash()));
JSONArray entries = new JSONArray();
for (Slot slot : slots) {
size += slot.getSize();
}
size += CryptoUtils.CRYPTO_HASH_SIZE;
LittleByteBuffer buffer = LittleByteBuffer.allocate(size);
buffer.put(slots.getMasterHash());
for (Slot slot : slots) {
byte[] bytes = slot.serialize();
buffer.put(bytes);
}
return buffer.array();
entries.put(slot.serialize());
}
public static SlotCollection deserialize(byte[] data) throws Exception {
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
byte[] masterHash = new byte[CryptoUtils.CRYPTO_HASH_SIZE];
buffer.get(masterHash);
obj.put("entries", entries);
return obj;
}
public static SlotCollection deserialize(JSONObject obj) throws Exception {
SlotCollection slots = new SlotCollection();
byte[] masterHash = Hex.toBytes(obj.getString("hash"));
slots.setMasterHash(masterHash);
while (buffer.remaining() > 0) {
JSONArray entries = obj.getJSONArray("entries");
for (int i = 0; i < entries.length(); i++) {
Slot slot;
JSONObject slotObj = entries.getJSONObject(i);
switch (buffer.peek()) {
switch (slotObj.getInt("type")) {
case Slot.TYPE_RAW:
slot = new RawSlot();
break;
@ -60,10 +58,7 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
throw new Exception("unrecognized slot type");
}
byte[] bytes = new byte[slot.getSize()];
buffer.get(bytes);
slot.deserialize(bytes);
slot.deserialize(slotObj);
slots.add(slot);
}

View file

@ -15,11 +15,11 @@ public class Hex {
private static final char[] hexCode = "0123456789abcdef".toCharArray();
public static byte[] toBytes(String s) {
public static byte[] toBytes(String s) throws HexException {
final int len = s.length();
if (len % 2 != 0)
throw new IllegalArgumentException("hexBinary needs to be even-length: " + s);
throw new HexException("hexBinary needs to be even-length: " + s);
byte[] out = new byte[len / 2];
@ -27,7 +27,7 @@ public class Hex {
int h = hexToBin(s.charAt(i));
int l = hexToBin(s.charAt(i + 1));
if (h == -1 || l == -1)
throw new IllegalArgumentException("contains illegal character for hexBinary: " + s);
throw new HexException("contains illegal character for hexBinary: " + s);
out[i / 2] = (byte) (h * 16 + l);
}

View file

@ -0,0 +1,7 @@
package me.impy.aegis.encoding;
public class HexException extends Exception {
public HexException(String message) {
super(message);
}
}

View file

@ -4,6 +4,7 @@ import java.util.List;
import me.impy.aegis.db.Database;
import me.impy.aegis.db.DatabaseEntry;
import me.impy.aegis.db.DatabaseFile;
import me.impy.aegis.util.ByteInputStream;
public class AegisImporter extends DatabaseImporter {
@ -15,8 +16,10 @@ public class AegisImporter extends DatabaseImporter {
@Override
public List<DatabaseEntry> convert() throws Exception {
byte[] bytes = _stream.getBytes();
DatabaseFile file = new DatabaseFile();
file.deserialize(bytes);
Database db = new Database();
db.deserialize(bytes, false);
db.deserialize(file.getContent());
return db.getKeys();
}

View file

@ -9,6 +9,8 @@ import com.github.paolorotolo.appintro.AppIntro;
import com.github.paolorotolo.appintro.AppIntroFragment;
import com.github.paolorotolo.appintro.model.SliderPage;
import org.json.JSONObject;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
@ -164,7 +166,7 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
try {
// encrypt the master key with the fingerprint key
// and add it to the list of slots
FingerprintSlot slot = new FingerprintSlot();
FingerprintSlot slot = _authenticatedSlide.getFingerSlot();
Cipher cipher = _authenticatedSlide.getFingerCipher();
slots.encrypt(slot, masterKey, cipher);
slots.add(slot);
@ -176,13 +178,11 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
// finally, save the database
try {
byte[] bytes = _database.serialize();
JSONObject obj = _database.serialize();
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
_databaseFile.setContent(bytes);
_databaseFile.setContent(obj);
} else {
CryptResult result = masterKey.encrypt(bytes);
_databaseFile.setContent(result.Data);
_databaseFile.setCryptParameters(result.Parameters);
_databaseFile.setContent(obj, masterKey);
}
DatabaseManager.save(getApplicationContext(), _databaseFile);
} catch (Exception e) {

View file

@ -41,7 +41,7 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
private TextView _textFingerprint;
private FingerprintUiHelper _fingerHelper;
private KeyStoreHandle _storeHandle;
private FingerprintSlot _slot;
private FingerprintSlot _fingerSlot;
private Cipher _fingerCipher;
private boolean _fingerAuthenticated;
@ -75,6 +75,10 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
return _fingerCipher;
}
public FingerprintSlot getFingerSlot() {
return _fingerSlot;
}
public void setBgColor(int color) {
_bgColor = color;
}
@ -95,9 +99,9 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
try {
if (_storeHandle == null) {
_storeHandle = new KeyStoreHandle();
_slot = new FingerprintSlot();
_fingerSlot = new FingerprintSlot();
}
key = _storeHandle.generateKey(_slot.getID());
key = _storeHandle.generateKey(_fingerSlot.getID());
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}

View file

@ -1,38 +0,0 @@
package me.impy.aegis.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
// LittleByteBuffer wraps a ByteBuffer to extend its API a little.
// Its byte order is set to little endian by default.
// All this boilerplate just to change the default byte order and add a peek method... Is it worth it? Probably not.
public class LittleByteBuffer {
private ByteBuffer _buffer;
private LittleByteBuffer(ByteBuffer buffer) {
_buffer = buffer;
_buffer.order(ByteOrder.LITTLE_ENDIAN);
}
public byte peek() {
_buffer.mark();
byte b = _buffer.get();
_buffer.reset();
return b;
}
public byte get() { return _buffer.get(); }
public LittleByteBuffer get(byte[] dst) {_buffer.get(dst); return this; }
public LittleByteBuffer put(byte b) { _buffer.put(b); return this; }
public LittleByteBuffer put(byte[] bytes) { _buffer.put(bytes); return this; }
public int remaining() { return _buffer.remaining(); }
public byte[] array() { return _buffer.array(); }
public LittleByteBuffer putInt(int i) { _buffer.putInt(i); return this; }
public LittleByteBuffer putLong(long l) { _buffer.putLong(l); return this; }
public int getInt() { return _buffer.getInt(); }
public long getLong() { return _buffer.getLong(); }
public int position() { return _buffer.position(); }
public LittleByteBuffer position(int i) { _buffer.position(i); return this; }
public static LittleByteBuffer allocate(int size) { return new LittleByteBuffer(ByteBuffer.allocate(size)); }
public static LittleByteBuffer wrap(byte[] bytes) { return new LittleByteBuffer(ByteBuffer.wrap(bytes)); }
}