mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 05:27:17 +00:00
ECKey: Factor out findRecoveryId() from signMessage().
This commit is contained in:
committed by
Andreas Schildbach
parent
a5aac0f4b7
commit
a6fd71b767
@@ -865,17 +865,7 @@ public class ECKey implements EncryptableItem {
|
|||||||
byte[] data = formatMessageForSigning(message);
|
byte[] data = formatMessageForSigning(message);
|
||||||
Sha256Hash hash = Sha256Hash.twiceOf(data);
|
Sha256Hash hash = Sha256Hash.twiceOf(data);
|
||||||
ECDSASignature sig = sign(hash, aesKey);
|
ECDSASignature sig = sign(hash, aesKey);
|
||||||
// Now we have to work backwards to figure out the recId needed to recover the signature.
|
byte recId = findRecoveryId(hash, sig);
|
||||||
int recId = -1;
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
ECKey k = ECKey.recoverFromSignature(i, sig, hash, isCompressed());
|
|
||||||
if (k != null && k.pub.equals(pub)) {
|
|
||||||
recId = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (recId == -1)
|
|
||||||
throw new RuntimeException("Could not construct a recoverable key. This should never happen.");
|
|
||||||
int headerByte = recId + 27 + (isCompressed() ? 4 : 0);
|
int headerByte = recId + 27 + (isCompressed() ? 4 : 0);
|
||||||
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
||||||
sigData[0] = (byte)headerByte;
|
sigData[0] = (byte)headerByte;
|
||||||
@@ -940,6 +930,26 @@ public class ECKey implements EncryptableItem {
|
|||||||
throw new SignatureException("Signature did not match for message");
|
throw new SignatureException("Signature did not match for message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the recovery ID, a byte with value between 0 and 3, inclusive, that specifies which of 4 possible
|
||||||
|
* curve points was used to sign a message. This value is also referred to as "v".
|
||||||
|
*
|
||||||
|
* @throws RuntimeException if no recovery ID can be found.
|
||||||
|
*/
|
||||||
|
public byte findRecoveryId(Sha256Hash hash, ECDSASignature sig) {
|
||||||
|
byte recId = -1;
|
||||||
|
for (byte i = 0; i < 4; i++) {
|
||||||
|
ECKey k = ECKey.recoverFromSignature(i, sig, hash, isCompressed());
|
||||||
|
if (k != null && k.pub.equals(pub)) {
|
||||||
|
recId = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (recId == -1)
|
||||||
|
throw new RuntimeException("Could not construct a recoverable key. This should never happen.");
|
||||||
|
return recId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Given the components of a signature and a selector value, recover and return the public key
|
* <p>Given the components of a signature and a selector value, recover and return the public key
|
||||||
* that generated the signature according to the algorithm in SEC1v2 section 4.1.6.</p>
|
* that generated the signature according to the algorithm in SEC1v2 section 4.1.6.</p>
|
||||||
|
|||||||
@@ -239,6 +239,52 @@ public class ECKeyTest {
|
|||||||
assertEquals(expectedAddress, gotAddress);
|
assertEquals(expectedAddress, gotAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findRecoveryId() {
|
||||||
|
ECKey key = new ECKey();
|
||||||
|
String message = "Hello World!";
|
||||||
|
Sha256Hash hash = Sha256Hash.of(message.getBytes());
|
||||||
|
ECKey.ECDSASignature sig = key.sign(hash);
|
||||||
|
key = ECKey.fromPublicOnly(key.getPubKeyPoint());
|
||||||
|
|
||||||
|
List<Byte> possibleRecIds = Lists.newArrayList((byte) 0, (byte) 1, (byte) 2, (byte) 3);
|
||||||
|
byte recId = key.findRecoveryId(hash, sig);
|
||||||
|
assertTrue(possibleRecIds.contains(recId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keyRecoveryTestVector() {
|
||||||
|
// a test that exercises key recovery with findRecoveryId() on a test vector
|
||||||
|
// test vector from https://crypto.stackexchange.com/a/41339
|
||||||
|
ECKey key = ECKey.fromPrivate(
|
||||||
|
new BigInteger("ebb2c082fd7727890a28ac82f6bdf97bad8de9f5d7c9028692de1a255cad3e0f", 16));
|
||||||
|
String message = "Maarten Bodewes generated this test vector on 2016-11-08";
|
||||||
|
Sha256Hash hash = Sha256Hash.of(message.getBytes());
|
||||||
|
ECKey.ECDSASignature sig = key.sign(hash);
|
||||||
|
key = ECKey.fromPublicOnly(key.getPubKeyPoint());
|
||||||
|
|
||||||
|
byte recId = key.findRecoveryId(hash, sig);
|
||||||
|
byte expectedRecId = 0;
|
||||||
|
assertEquals(recId, expectedRecId);
|
||||||
|
|
||||||
|
ECKey pubKey = ECKey.fromPublicOnly(key.getPubKeyPoint());
|
||||||
|
ECKey recoveredKey = ECKey.recoverFromSignature(recId, sig, hash, true);
|
||||||
|
assertEquals(recoveredKey, pubKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keyRecoveryWithFindRecoveryId() throws Exception {
|
||||||
|
ECKey key = new ECKey();
|
||||||
|
String message = "Hello World!";
|
||||||
|
Sha256Hash hash = Sha256Hash.of(message.getBytes());
|
||||||
|
ECKey.ECDSASignature sig = key.sign(hash);
|
||||||
|
|
||||||
|
byte recId = key.findRecoveryId(hash, sig);
|
||||||
|
ECKey pubKey = ECKey.fromPublicOnly(key.getPubKeyPoint());
|
||||||
|
ECKey recoveredKey = ECKey.recoverFromSignature(recId, sig, hash, true);
|
||||||
|
assertEquals(recoveredKey, pubKey);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void keyRecovery() throws Exception {
|
public void keyRecovery() throws Exception {
|
||||||
ECKey key = new ECKey();
|
ECKey key = new ECKey();
|
||||||
|
|||||||
Reference in New Issue
Block a user