mirror of
https://github.com/sparrowwallet/hummingbird.git
synced 2024-12-26 18:16:45 +00:00
v1.6.2 to fix byte padding bug in short source fingerprint integers
This commit is contained in:
parent
0bdd3ebc83
commit
06af9b8ee2
7 changed files with 72 additions and 5 deletions
|
@ -12,7 +12,7 @@ Hummingbird requires a minimum of Java 8.
|
||||||
Hummingbird is hosted in Maven Central and can be added as a dependency with the following:
|
Hummingbird is hosted in Maven Central and can be added as a dependency with the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
implementation('com.sparrowwallet:hummingbird:1.6.1')
|
implementation('com.sparrowwallet:hummingbird:1.6.2')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
|
@ -16,7 +16,7 @@ apply plugin: 'com.bmuschko.nexus'
|
||||||
|
|
||||||
archivesBaseName = 'hummingbird'
|
archivesBaseName = 'hummingbird'
|
||||||
group 'com.sparrowwallet'
|
group 'com.sparrowwallet'
|
||||||
version '1.6.1'
|
version '1.6.2'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
|
@ -47,12 +47,13 @@ public class CryptoAccount extends RegistryItem {
|
||||||
Map cryptoAccountMap = (Map)cbor;
|
Map cryptoAccountMap = (Map)cbor;
|
||||||
|
|
||||||
UnsignedInteger uintMasterFingerprint = (UnsignedInteger)cryptoAccountMap.get(new UnsignedInteger(MASTER_FINGERPRINT_KEY));
|
UnsignedInteger uintMasterFingerprint = (UnsignedInteger)cryptoAccountMap.get(new UnsignedInteger(MASTER_FINGERPRINT_KEY));
|
||||||
|
byte[] masterFingerprint = bigIntegerToBytes(uintMasterFingerprint.getValue(), 4);
|
||||||
Array outputDescriptors = (Array)cryptoAccountMap.get(new UnsignedInteger(OUTPUT_DESCRIPTORS_KEY));
|
Array outputDescriptors = (Array)cryptoAccountMap.get(new UnsignedInteger(OUTPUT_DESCRIPTORS_KEY));
|
||||||
List<CryptoOutput> cryptoOutputs = new ArrayList<>(outputDescriptors.getDataItems().size());
|
List<CryptoOutput> cryptoOutputs = new ArrayList<>(outputDescriptors.getDataItems().size());
|
||||||
for(DataItem item : outputDescriptors.getDataItems()) {
|
for(DataItem item : outputDescriptors.getDataItems()) {
|
||||||
cryptoOutputs.add(CryptoOutput.fromCbor(item));
|
cryptoOutputs.add(CryptoOutput.fromCbor(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CryptoAccount(uintMasterFingerprint.getValue().toByteArray(), cryptoOutputs);
|
return new CryptoAccount(masterFingerprint, cryptoOutputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ public class CryptoHDKey extends RegistryItem {
|
||||||
} else if(intKey == CHILDREN_KEY) {
|
} else if(intKey == CHILDREN_KEY) {
|
||||||
children = CryptoKeypath.fromCbor(map.get(uintKey));
|
children = CryptoKeypath.fromCbor(map.get(uintKey));
|
||||||
} else if(intKey == PARENT_FINGERPRINT_KEY) {
|
} else if(intKey == PARENT_FINGERPRINT_KEY) {
|
||||||
parentFingerprint = ((UnsignedInteger)map.get(uintKey)).getValue().toByteArray();
|
parentFingerprint = bigIntegerToBytes(((UnsignedInteger)map.get(uintKey)).getValue(), 4);
|
||||||
} else if(intKey == NAME_KEY) {
|
} else if(intKey == NAME_KEY) {
|
||||||
name = ((UnicodeString)map.get(uintKey)).getString();
|
name = ((UnicodeString)map.get(uintKey)).getString();
|
||||||
} else if(intKey == NOTE_KEY) {
|
} else if(intKey == NOTE_KEY) {
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class CryptoKeypath extends RegistryItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(intKey == SOURCE_FINGERPRINT_KEY) {
|
} else if(intKey == SOURCE_FINGERPRINT_KEY) {
|
||||||
sourceFingerprint = ((UnsignedInteger)map.get(key)).getValue().toByteArray();
|
sourceFingerprint = bigIntegerToBytes(((UnsignedInteger)map.get(key)).getValue(), 4);
|
||||||
} else if(intKey == DEPTH_KEY) {
|
} else if(intKey == DEPTH_KEY) {
|
||||||
depth = ((UnsignedInteger)map.get(key)).getValue().intValue();
|
depth = ((UnsignedInteger)map.get(key)).getValue().intValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import co.nstant.in.cbor.CborException;
|
||||||
import com.sparrowwallet.hummingbird.UR;
|
import com.sparrowwallet.hummingbird.UR;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public abstract class RegistryItem implements CborSerializable {
|
public abstract class RegistryItem implements CborSerializable {
|
||||||
public abstract RegistryType getRegistryType();
|
public abstract RegistryType getRegistryType();
|
||||||
|
@ -19,4 +21,34 @@ public abstract class RegistryItem implements CborSerializable {
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The regular {@link BigInteger#toByteArray()} includes the sign bit of the number and
|
||||||
|
* might result in an extra byte addition. This method removes this extra byte.
|
||||||
|
* </p>
|
||||||
|
* @param b the integer to format into a byte array
|
||||||
|
* @param numBytes the desired size of the resulting byte array
|
||||||
|
* @return numBytes byte long array.
|
||||||
|
*/
|
||||||
|
protected static byte[] bigIntegerToBytes(BigInteger b, int numBytes) {
|
||||||
|
if(b.signum() < 0) {
|
||||||
|
throw new IllegalArgumentException("b must be positive or zero");
|
||||||
|
}
|
||||||
|
if(numBytes <= 0) {
|
||||||
|
throw new IllegalArgumentException("numBytes must be positive");
|
||||||
|
}
|
||||||
|
byte[] src = b.toByteArray();
|
||||||
|
byte[] dest = new byte[numBytes];
|
||||||
|
boolean isFirstByteOnlyForSign = src[0] == 0;
|
||||||
|
int length = isFirstByteOnlyForSign ? src.length - 1 : src.length;
|
||||||
|
if(length > numBytes) {
|
||||||
|
throw new IllegalArgumentException("The given number does not fit in " + numBytes);
|
||||||
|
}
|
||||||
|
int srcPos = isFirstByteOnlyForSign ? 1 : 0;
|
||||||
|
int destPos = numBytes - length;
|
||||||
|
System.arraycopy(src, srcPos, dest, destPos, length);
|
||||||
|
Arrays.fill(src, (byte)0);
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,4 +61,38 @@ public class CryptoHDKeyTest {
|
||||||
String ur = "ur:crypto-hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtaaddyoeadlaaocylpgrstlfiewtseje";
|
String ur = "ur:crypto-hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtaaddyoeadlaaocylpgrstlfiewtseje";
|
||||||
Assert.assertEquals(ur, cryptoHDKey.toUR().toString());
|
Assert.assertEquals(ur, cryptoHDKey.toUR().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testZeroMasterFingerprint() throws CborException {
|
||||||
|
String hex = "a303582103ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9045820e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f952006d90130a201800200";
|
||||||
|
byte[] data = TestUtils.hexToBytes(hex);
|
||||||
|
List<DataItem> items = CborDecoder.decode(data);
|
||||||
|
CryptoHDKey cryptoHDKey = CryptoHDKey.fromCbor(items.get(0));
|
||||||
|
Assert.assertFalse(cryptoHDKey.isMaster());
|
||||||
|
Assert.assertFalse(cryptoHDKey.isPrivateKey());
|
||||||
|
Assert.assertEquals("03ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9", TestUtils.bytesToHex(cryptoHDKey.getKey()));
|
||||||
|
Assert.assertEquals("e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f9520", TestUtils.bytesToHex(cryptoHDKey.getChainCode()));
|
||||||
|
Assert.assertNull(cryptoHDKey.getOrigin().getPath());
|
||||||
|
Assert.assertEquals("00000000", TestUtils.bytesToHex(cryptoHDKey.getOrigin().getSourceFingerprint()));
|
||||||
|
Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(cryptoHDKey.toCbor()));
|
||||||
|
String ur = "ur:crypto-hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtaaddyoeadlaaoaebkoxdive";
|
||||||
|
Assert.assertEquals(ur, cryptoHDKey.toUR().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShortMasterFingerprint() throws CborException {
|
||||||
|
String hex = "A303582103AC3DF08EC59F6F1CDC55B3007F90F1A98435EC345C3CAC400EDE1DD533D75FA9045820E8145DB627E79188E14C1FD6C772998509961D358FE8ECF3D7CC43BB1D0F952006D90130A201800218FF";
|
||||||
|
byte[] data = TestUtils.hexToBytes(hex);
|
||||||
|
List<DataItem> items = CborDecoder.decode(data);
|
||||||
|
CryptoHDKey cryptoHDKey = CryptoHDKey.fromCbor(items.get(0));
|
||||||
|
Assert.assertFalse(cryptoHDKey.isMaster());
|
||||||
|
Assert.assertFalse(cryptoHDKey.isPrivateKey());
|
||||||
|
Assert.assertEquals("03ac3df08ec59f6f1cdc55b3007f90f1a98435ec345c3cac400ede1dd533d75fa9", TestUtils.bytesToHex(cryptoHDKey.getKey()));
|
||||||
|
Assert.assertEquals("e8145db627e79188e14c1fd6c772998509961d358fe8ecf3d7cc43bb1d0f9520", TestUtils.bytesToHex(cryptoHDKey.getChainCode()));
|
||||||
|
Assert.assertNull(cryptoHDKey.getOrigin().getPath());
|
||||||
|
Assert.assertEquals("000000ff", TestUtils.bytesToHex(cryptoHDKey.getOrigin().getSourceFingerprint()));
|
||||||
|
Assert.assertEquals(hex.toLowerCase(), TestUtils.encode(cryptoHDKey.toCbor()));
|
||||||
|
String ur = "ur:crypto-hdkey/otaxhdclaxpsfswtmnsknejlceuogoqdaelbmhwnptlrecwpeehhfnpsfzbauecatleotsheptaahdcxvsbbhlrpdivdmelovygscttbstjpnllpasmtcaecmyvswpwftssffxrkcabsmdcxamtaaddyoeadlaaocszmtnkocyct";
|
||||||
|
Assert.assertEquals(ur, cryptoHDKey.toUR().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue