Files
status-keycard-java/lib/src/main/java/im/status/keycard/applet/KeyPath.java
T

146 lines
3.8 KiB
Java
Raw Normal View History

2018-12-07 16:44:02 +03:00
package im.status.keycard.applet;
2018-11-15 20:16:18 +03:00
import java.util.StringTokenizer;
/**
2018-12-07 16:44:02 +03:00
* Keypath object to be used with the KeycardCommandSet
2018-11-15 20:16:18 +03:00
*/
public class KeyPath {
private int source;
private byte[] data;
/**
2018-12-07 16:44:02 +03:00
* Parses a keypath into a byte array and source parameter to be used with the KeycardCommandSet object.
2018-11-15 20:16:18 +03:00
*
* A valid string is composed of a minimum of one and a maximum of 11 components separated by "/".
*
* The first component can be either "m", indicating the master key, "..", indicating the parent of the current key,
* or "." indicating the current key. It can also be omitted, in which case it is considered the same as being ".".
*
* All other components are positive integers fitting in 31 bit, eventually suffixed by an apostrophe (') sign,
* which indicates an hardened key.
*
* An example of a valid path is "m/44'/0'/0'/0/0"
*
*
* @param keypath the keypath as a string
*/
public KeyPath(String keypath) {
StringTokenizer tokenizer = new StringTokenizer(keypath, "/");
String sourceOrFirstElement = tokenizer.nextToken();
switch(sourceOrFirstElement) {
case "m":
2018-12-07 16:44:02 +03:00
source = KeycardCommandSet.DERIVE_P1_SOURCE_MASTER;
2018-11-15 20:16:18 +03:00
break;
case "..":
2018-12-07 16:44:02 +03:00
source = KeycardCommandSet.DERIVE_P1_SOURCE_PARENT;
2018-11-15 20:16:18 +03:00
break;
case ".":
2018-12-07 16:44:02 +03:00
source = KeycardCommandSet.DERIVE_P1_SOURCE_CURRENT;
2018-11-15 20:16:18 +03:00
break;
default:
2018-12-07 16:44:02 +03:00
source = KeycardCommandSet.DERIVE_P1_SOURCE_CURRENT;
2018-11-15 20:16:18 +03:00
tokenizer = new StringTokenizer(keypath, "/"); // rewind
break;
}
int componentCount = tokenizer.countTokens();
if (componentCount > 10) {
throw new IllegalArgumentException("Too many components");
}
2018-11-16 11:12:33 +03:00
data = new byte[4 * componentCount];
2018-11-15 20:16:18 +03:00
for (int i = 0; i < componentCount; i++) {
long component = parseComponent(tokenizer.nextToken());
writeComponent(component, i);
}
}
2018-11-16 11:12:33 +03:00
public KeyPath(byte[] data, int source) {
this.data = data;
this.source = source;
}
public KeyPath(byte[] data) {
2018-12-07 16:44:02 +03:00
this(data, KeycardCommandSet.DERIVE_P1_SOURCE_MASTER);
2018-11-16 11:12:33 +03:00
}
2018-11-15 20:16:18 +03:00
private long parseComponent(String num) {
long sign;
if (num.endsWith("'")) {
sign = 0x80000000L;
num = num.substring(0, (num.length() - 1));
} else {
sign = 0L;
}
if (num.startsWith("+") || num.startsWith("-")) {
throw new NumberFormatException("No sign allowed");
}
return (sign | Long.parseLong(num));
}
private void writeComponent(long component, int i) {
int off = (i*4);
data[off] = (byte)((component >> 24) & 0xff);
data[off + 1] = (byte)((component >> 16) & 0xff);
data[off + 2] = (byte)((component >> 8) & 0xff);
data[off + 3] = (byte)(component & 0xff);
}
/**
* The source of the derive command.
*
* @return the source of the derive command
*/
public int getSource() {
return source;
}
/**
* The byte encoded key path.
*
* @return byte encoded key path
*/
public byte[] getData() {
return data;
}
2018-11-16 11:12:33 +03:00
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
switch(source) {
2018-12-07 16:44:02 +03:00
case KeycardCommandSet.DERIVE_P1_SOURCE_MASTER:
2018-11-16 11:12:33 +03:00
sb.append('m');
break;
2018-12-07 16:44:02 +03:00
case KeycardCommandSet.DERIVE_P1_SOURCE_PARENT:
2018-11-16 11:12:33 +03:00
sb.append("..");
break;
2018-12-07 16:44:02 +03:00
case KeycardCommandSet.DERIVE_P1_SOURCE_CURRENT:
2018-11-16 11:12:33 +03:00
sb.append('.');
break;
}
for (int i = 0; i < this.data.length; i += 4) {
sb.append('/');
appendComponent(sb, i);
}
return sb.toString();
}
private void appendComponent(StringBuffer sb, int i) {
int num = ((this.data[i] & 0x7f) << 24) | ((this.data[i+1] & 0xff) << 16) | ((this.data[i+2] & 0xff) << 8) | (this.data[i+3] & 0xff);
sb.append(num);
if ((this.data[i] & 0x80) == 0x80) {
sb.append('\'');
}
}
2018-11-15 20:16:18 +03:00
}