add metadata parser/encoder
This commit is contained in:
parent
a39924aba3
commit
022b9c4f28
|
@ -0,0 +1,106 @@
|
|||
package im.status.keycard.applet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class Metadata {
|
||||
private String cardName;
|
||||
private SortedSet<Long> wallets;
|
||||
|
||||
public static Metadata fromData(byte[] data) {
|
||||
int version = (data[0] & 0xe0) >> 5;
|
||||
|
||||
if (version != 1) {
|
||||
throw new RuntimeException("Invalid version");
|
||||
}
|
||||
|
||||
int namelen = (data[0] & 0x1f);
|
||||
int off = 1;
|
||||
|
||||
String cardName = new String(data, off, namelen, Charset.forName("US-ASCII"));
|
||||
off += namelen;
|
||||
|
||||
SortedSet<Long> set = new TreeSet<>();
|
||||
|
||||
while(off < data.length) {
|
||||
int[] start = TinyBERTLV.readNum(data, off);
|
||||
int[] count = TinyBERTLV.readNum(data, start[1]);
|
||||
off = count[1];
|
||||
long s = start[0] & 0xffffffffl;
|
||||
buildRange(set, s, (s + count[0]));
|
||||
}
|
||||
|
||||
return new Metadata(cardName, set);
|
||||
}
|
||||
|
||||
private static void buildRange(SortedSet<Long> set, long start, long end) {
|
||||
for (long i = start; i <= end; i++) {
|
||||
set.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
Metadata(String cardName, SortedSet<Long> wallets) {
|
||||
this.cardName = cardName;
|
||||
this.wallets = wallets;
|
||||
}
|
||||
|
||||
public Metadata(String cardName) {
|
||||
this(cardName, new TreeSet<>());
|
||||
}
|
||||
|
||||
public String getCardName() {
|
||||
return cardName;
|
||||
}
|
||||
|
||||
public void setCardName(String cardName) {
|
||||
if (cardName.length() > 20) {
|
||||
throw new IllegalArgumentException("card name too long");
|
||||
}
|
||||
|
||||
this.cardName = cardName;
|
||||
}
|
||||
|
||||
public SortedSet<Long> getWallets() {
|
||||
return wallets;
|
||||
}
|
||||
|
||||
public void addWallet(long w) {
|
||||
this.wallets.add(w);
|
||||
}
|
||||
|
||||
public void removeWallet(long w) {
|
||||
this.wallets.remove(w);
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
byte[] name = this.cardName.getBytes(Charset.forName("US-ASCII"));
|
||||
os.write(0x20 | name.length);
|
||||
os.write(name, 0, name.length);
|
||||
|
||||
if (wallets.isEmpty()) {
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
long start = wallets.first();
|
||||
int len = 0;
|
||||
|
||||
for (Long w : wallets.tailSet(start + 1)) {
|
||||
if (w == (start + len + 1)) {
|
||||
len++;
|
||||
} else {
|
||||
TinyBERTLV.writeNum(os, (int) start);
|
||||
TinyBERTLV.writeNum(os, len);
|
||||
len = 0;
|
||||
start = w;
|
||||
}
|
||||
}
|
||||
|
||||
TinyBERTLV.writeNum(os, (int) start);
|
||||
TinyBERTLV.writeNum(os, len);
|
||||
|
||||
return os.toByteArray();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package im.status.keycard.applet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
|
@ -14,6 +15,57 @@ public class TinyBERTLV {
|
|||
private byte[] buffer;
|
||||
private int pos;
|
||||
|
||||
public static int[] readNum(byte[] buf, int off) {
|
||||
int len = buf[off++] & 0xff;
|
||||
int lenlen = 0;
|
||||
|
||||
if ((len & 0x80) == 0x80) {
|
||||
lenlen = len & 0x7f;
|
||||
len = readVal(buf, off, lenlen);
|
||||
}
|
||||
|
||||
return new int[] {len, off + lenlen};
|
||||
}
|
||||
|
||||
public static int readVal(byte[] val, int off, int len) {
|
||||
switch (len) {
|
||||
case 1:
|
||||
return val[off] & 0xff;
|
||||
case 2:
|
||||
return ((val[off] & 0xff) << 8) | (val[off+1] & 0xff);
|
||||
case 3:
|
||||
return ((val[off] & 0xff) << 16) | ((val[off+1] & 0xff) << 8) | (val[off+2] & 0xff);
|
||||
case 4:
|
||||
return ((val[off] & 0xff) << 24) | ((val[off+1] & 0xff) << 16) | ((val[off+2] & 0xff) << 8) | (val[off+3] & 0xff);
|
||||
default:
|
||||
throw new IllegalArgumentException("Integers of length " + len + " are unsupported");
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeNum(ByteArrayOutputStream os, int len) {
|
||||
if ((len & 0xff000000) != 0) {
|
||||
os.write(0x84);
|
||||
os.write((len & 0xff000000) >> 24);
|
||||
os.write((len & 0x00ff0000) >> 16);
|
||||
os.write((len & 0x0000ff00) >> 8);
|
||||
os.write(len & 0x000000ff);
|
||||
} else if ((len & 0x00ff0000) != 0) {
|
||||
os.write(0x83);
|
||||
os.write((len & 0x00ff0000) >> 16);
|
||||
os.write((len & 0x0000ff00) >> 8);
|
||||
os.write(len & 0x000000ff);
|
||||
} else if ((len & 0x0000ff00) != 0) {
|
||||
os.write(0x82);
|
||||
os.write((len & 0x0000ff00) >> 8);
|
||||
os.write(len & 0x000000ff);
|
||||
} else if ((len & 0x00000080) != 0) {
|
||||
os.write(0x81);
|
||||
os.write(len & 0x000000ff);
|
||||
} else {
|
||||
os.write(len);
|
||||
}
|
||||
}
|
||||
|
||||
public TinyBERTLV(byte[] buffer) {
|
||||
this.buffer = buffer;
|
||||
this.pos = 0;
|
||||
|
@ -64,19 +116,7 @@ public class TinyBERTLV {
|
|||
*/
|
||||
public int readInt() throws IllegalArgumentException {
|
||||
byte[] val = readPrimitive(TLV_INT);
|
||||
|
||||
switch (val.length) {
|
||||
case 1:
|
||||
return val[0] & 0xff;
|
||||
case 2:
|
||||
return ((val[0] & 0xff) << 8) | (val[1] & 0xff);
|
||||
case 3:
|
||||
return ((val[0] & 0xff) << 16) | ((val[1] & 0xff) << 8) | (val[2] & 0xff);
|
||||
case 4:
|
||||
return ((val[0] & 0xff) << 24) | ((val[1] & 0xff) << 16) | ((val[2] & 0xff) << 8) | (val[3] & 0xff);
|
||||
default:
|
||||
throw new IllegalArgumentException("Integers of length " + val.length + " are unsupported");
|
||||
}
|
||||
return TinyBERTLV.readVal(val, 0, val.length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,13 +144,9 @@ public class TinyBERTLV {
|
|||
* @return the tag
|
||||
*/
|
||||
public int readLength() {
|
||||
int len = buffer[pos++] & 0xff;
|
||||
|
||||
if (len == 0x81) {
|
||||
len = buffer[pos++] & 0xff;
|
||||
}
|
||||
|
||||
return len;
|
||||
int[] len = TinyBERTLV.readNum(buffer, pos);
|
||||
pos = len[1];
|
||||
return len[0];
|
||||
}
|
||||
|
||||
private void checkTag(int expected, int actual) throws IllegalArgumentException {
|
||||
|
|
Loading…
Reference in New Issue