add script type checks to bip322 implementation

This commit is contained in:
Craig Raw 2023-07-03 13:15:10 +02:00
parent c0555c3fb0
commit e965a9ddd7
2 changed files with 69 additions and 17 deletions

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.drongo.crypto;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.*;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTInput;
@ -15,7 +16,9 @@ import java.util.*;
import static com.sparrowwallet.drongo.protocol.ScriptType.P2TR;
public class Bip322 {
public static String signMessageBip322(Address address, String message, PSBTInputSigner psbtInputSigner) {
public static String signMessageBip322(ScriptType scriptType, Address address, String message, PSBTInputSigner psbtInputSigner) {
checkScriptType(scriptType);
Transaction toSpend = getBip322ToSpend(address, message);
Transaction toSign = getBip322ToSign(toSpend);
@ -31,12 +34,14 @@ public class Bip322 {
TransactionSignature signature = psbtInput.isTaproot() ? psbtInput.getTapKeyPathSignature() : psbtInput.getPartialSignature(pubKey);
Transaction finalizeTransaction = new Transaction();
TransactionInput finalizedTxInput = address.getScriptType().addSpendingInput(finalizeTransaction, utxoOutput, pubKey, signature);
TransactionInput finalizedTxInput = scriptType.addSpendingInput(finalizeTransaction, utxoOutput, pubKey, signature);
return Base64.getEncoder().encodeToString(finalizedTxInput.getWitness().toByteArray());
}
public static void verifyMessageBip322(Address address, String message, String signatureBase64) throws SignatureException {
public static void verifyMessageBip322(ScriptType scriptType, Address address, String message, String signatureBase64) throws SignatureException {
checkScriptType(scriptType);
byte[] signatureEncoded;
try {
signatureEncoded = Base64.getDecoder().decode(signatureBase64);
@ -52,14 +57,14 @@ public class Bip322 {
signature = witness.getSignatures().get(0);
pubKey = ECKey.fromPublicOnly(witness.getPushes().get(1));
if(!address.equals(address.getScriptType().getAddress(pubKey))) {
if(!address.equals(scriptType.getAddress(pubKey))) {
throw new SignatureException("Provided address does not match pubkey in signature");
}
} else if(address.getScriptType() == ScriptType.P2TR) {
} else if(scriptType == ScriptType.P2TR) {
signature = witness.getSignatures().get(0);
pubKey = P2TR.getPublicKeyFromScript(address.getOutputScript());
} else {
throw new IllegalArgumentException(address.getScriptType() + " addresses are not supported");
throw new IllegalArgumentException(scriptType + " addresses are not supported");
}
Transaction toSpend = getBip322ToSpend(address, message);
@ -70,10 +75,10 @@ public class Bip322 {
psbtInput.setWitnessUtxo(toSpend.getOutputs().get(0));
psbtInput.setSigHash(SigHash.ALL);
if(address.getScriptType() == ScriptType.P2WPKH) {
psbtInput.getPartialSignatures().put(pubKey, signature);
} else if(address.getScriptType() == ScriptType.P2TR) {
if(scriptType == ScriptType.P2TR) {
psbtInput.setTapKeyPathSignature(signature);
} else {
psbtInput.getPartialSignatures().put(pubKey, signature);
}
try {
@ -83,6 +88,20 @@ public class Bip322 {
}
}
private static void checkScriptType(ScriptType scriptType) {
if(!scriptType.isAllowed(PolicyType.SINGLE)) {
throw new UnsupportedOperationException("Only singlesig addresses are currently supported");
}
if(!Arrays.asList(ScriptType.WITNESS_TYPES).contains(scriptType)) {
throw new UnsupportedOperationException("Legacy addresses are not supported for BIP322 simple signatures");
}
if(scriptType == ScriptType.P2SH_P2WPKH) {
throw new UnsupportedOperationException("The P2SH-P2WPKH script type is not currently supported");
}
}
public static Transaction getBip322ToSpend(Address address, String message) {
Transaction toSpend = new Transaction();
toSpend.setVersion(0);

View file

@ -29,7 +29,7 @@ public class Bip322Test {
Address address = ScriptType.P2WPKH.getAddress(privKey);
Assert.assertEquals("bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l", address.toString());
String signature = Bip322.signMessageBip322(address, "", new PSBTInputSigner() {
String signature = Bip322.signMessageBip322(ScriptType.P2WPKH, address, "", new PSBTInputSigner() {
@Override
public TransactionSignature sign(Sha256Hash hash, SigHash sigHash, TransactionSignature.Type signatureType) {
return privKey.sign(hash, sigHash, signatureType);
@ -43,7 +43,7 @@ public class Bip322Test {
Assert.assertEquals("AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=", signature);
String signature2 = Bip322.signMessageBip322(address, "Hello World", new PSBTInputSigner() {
String signature2 = Bip322.signMessageBip322(ScriptType.P2WPKH, address, "Hello World", new PSBTInputSigner() {
@Override
public TransactionSignature sign(Sha256Hash hash, SigHash sigHash, TransactionSignature.Type signatureType) {
return privKey.sign(hash, sigHash, signatureType);
@ -64,7 +64,7 @@ public class Bip322Test {
String message1 = "";
String signature2 = "AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=";
Bip322.verifyMessageBip322(address, message1, signature2);
Bip322.verifyMessageBip322(ScriptType.P2WPKH, address, message1, signature2);
}
@Test
@ -73,14 +73,14 @@ public class Bip322Test {
String message1 = "";
String signature1 = "AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=";
Bip322.verifyMessageBip322(address, message1, signature1);
Bip322.verifyMessageBip322(ScriptType.P2WPKH, address, message1, signature1);
String message2 = "Hello World";
String signature2 = "AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=";
Bip322.verifyMessageBip322(address, message2, signature2);
Bip322.verifyMessageBip322(ScriptType.P2WPKH, address, message2, signature2);
String signature3 = "AkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy";
Bip322.verifyMessageBip322(address, message2, signature3);
Bip322.verifyMessageBip322(ScriptType.P2WPKH, address, message2, signature3);
}
@Test
@ -89,7 +89,7 @@ public class Bip322Test {
Address address = ScriptType.P2TR.getAddress(privKey);
Assert.assertEquals("bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3", address.toString());
String signature = Bip322.signMessageBip322(address, "Hello World", new PSBTInputSigner() {
String signature = Bip322.signMessageBip322(ScriptType.P2TR, address, "Hello World", new PSBTInputSigner() {
@Override
public TransactionSignature sign(Sha256Hash hash, SigHash sigHash, TransactionSignature.Type signatureType) {
return address.getScriptType().getOutputKey(privKey).sign(hash, sigHash, signatureType);
@ -113,6 +113,39 @@ public class Bip322Test {
String message1 = "Hello World";
String signature1 = "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==";
Bip322.verifyMessageBip322(address, message1, signature1);
Bip322.verifyMessageBip322(ScriptType.P2TR, address, message1, signature1);
}
@Test(expected = UnsupportedOperationException.class)
public void signMessageBip322NestedSegwit() {
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
Address address = ScriptType.P2SH_P2WPKH.getAddress(privKey);
Assert.assertEquals("37qyp7jQAzqb2rCBpMvVtLDuuzKAUCVnJb", address.toString());
String signature = Bip322.signMessageBip322(ScriptType.P2SH_P2WPKH, address, "Hello World", new PSBTInputSigner() {
@Override
public TransactionSignature sign(Sha256Hash hash, SigHash sigHash, TransactionSignature.Type signatureType) {
return address.getScriptType().getOutputKey(privKey).sign(hash, sigHash, signatureType);
}
@Override
public ECKey getPubKey() {
return ECKey.fromPublicOnly(privKey);
}
});
Assert.assertEquals("AkcwRAIgHx821fcP3D4R6RsXHF8kXza4d/SqpKGaGu++AEQjJz0CIH9cN5XGDkgkqqF9OMTbYvhgI7Yp9NoHXEgLstjqDOqDASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=", signature);
}
@Test(expected = UnsupportedOperationException.class)
public void verifyMessageBip322NestedSegwit() throws SignatureException {
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
Address address = ScriptType.P2SH_P2WPKH.getAddress(privKey);
Assert.assertEquals("37qyp7jQAzqb2rCBpMvVtLDuuzKAUCVnJb", address.toString());
String message1 = "Hello World";
String signature1 = "AkcwRAIgHx821fcP3D4R6RsXHF8kXza4d/SqpKGaGu++AEQjJz0CIH9cN5XGDkgkqqF9OMTbYvhgI7Yp9NoHXEgLstjqDOqDASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=";
Bip322.verifyMessageBip322(ScriptType.P2SH_P2WPKH, address, message1, signature1);
}
}