Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix broken low-R signing with a password encrypted wallet #7242

Conversation

stejbac
Copy link
Contributor

@stejbac stejbac commented Sep 13, 2024

Fixes #7241

Prevent KeyIsEncryptedException from being thrown when signing with a LowRSigningKey-wrapped, encrypted HD key, due to breakage of the apparent invariant that the keyCrypter field of ECKey should be null whenever the key isn't encrypted.

When signing with a wrapped, encrypted HD key, the original key is decrypted and then re-wrapped as a LowRSigningKey instance. This was blindly copying the keyCrypter property of the decrypted key. But DeterministicKey::getKeyCrypter returns non-null if its parent does, even if the actual field is null, and the decrypted HD key has the same parent as the encrypted original. Thus, blindly copying the property (rather than the field) breaks the above invariant.

--

I've tested the fix on regtest, and proposals, blind votes and vote reveals now appear to publish OK with both encrypted and unencrypted wallets. The unfixed code produced the following exception stack trace on regtest, when making a reimbursement request with a password encrypted wallet:

org.bitcoinj.core.ECKey$KeyIsEncryptedException
	at org.bitcoinj.core.ECKey.sign(ECKey.java:661)
	at org.bitcoinj.core.ECKey.sign(ECKey.java:636)
	at org.bitcoinj.core.ECKey.sign(ECKey.java:662)
	at org.bitcoinj.core.Transaction.calculateWitnessSignature(Transaction.java:1333)
	at org.bitcoinj.core.Transaction.calculateWitnessSignature(Transaction.java:1344)
	at bisq.core.btc.wallet.WalletService.signTransactionInput(WalletService.java:390)
	at bisq.core.btc.wallet.BtcWalletService.signAllBtcInputs(BtcWalletService.java:411)
	at bisq.core.btc.wallet.BtcWalletService.completePreparedProposalTx(BtcWalletService.java:287)
	at bisq.core.btc.wallet.BtcWalletService.completePreparedReimbursementRequestTx(BtcWalletService.java:176)
	at bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalFactory.completeTx(ReimbursementProposalFactory.java:97)
	at bisq.core.dao.governance.proposal.BaseProposalFactory.createTransaction(BaseProposalFactory.java:99)
	at bisq.core.dao.governance.proposal.BaseProposalFactory.createProposalWithTransaction(BaseProposalFactory.java:76)
	at bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalFactory.createProposalWithTransaction(ReimbursementProposalFactory.java:73)
	at bisq.core.dao.DaoFacade.getReimbursementProposalWithTransaction(DaoFacade.java:268)
	at bisq.desktop.main.dao.governance.make.MakeProposalView.getProposalWithTransaction(MakeProposalView.java:417)
	at bisq.desktop.main.dao.governance.make.MakeProposalView.publishMyProposal(MakeProposalView.java:279)
	at bisq.desktop.main.dao.governance.make.MakeProposalView.lambda$setMakeProposalButtonHandler$6(MakeProposalView.java:511)
	at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
	at javafx.event.Event.fireEvent(Event.java:198)
	at javafx.scene.Node.fireEvent(Node.java:8889)
	at javafx.scene.control.Button.fire(Button.java:203)
	at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
	at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
	at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
	at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
	at javafx.event.Event.fireEvent(Event.java:198)
	at javafx.scene.Scene$MouseHandler.process(Scene.java:3856)
	at javafx.scene.Scene.processMouseEvent(Scene.java:1851)
	at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2584)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
	at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
	at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
	at com.sun.glass.ui.View.notifyMouse(View.java:942)
	at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
	at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
	at java.base/java.lang.Thread.run(Thread.java:834)

Prevent 'KeyIsEncryptedException' from being thrown when signing with a
'LowRSigningKey'-wrapped, encrypted HD key, due to breakage of the
apparent invariant that the 'keyCrypter' field of 'ECKey' should be null
whenever the key isn't encrypted.

When signing with a wrapped, encrypted HD key, the original key is
decrypted and then re-wrapped as a 'LowRSigningKey' instance. This was
blindly copying the 'keyCrypter' property of the decrypted key. But
'DeterministicKey::getKeyCrypter' returns non-null if its parent does,
even if the actual field is null, and the decrypted HD key has the same
parent as the encrypted original. Thus, blindly copying the property
(rather than the field) breaks the above invariant.

Fixes issue bisq-network#7241 with blind voting, caused by the earlier PR bisq-network#7238
which introduced low-R nonce grinding.
Copy link
Contributor

@djing-chan djing-chan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

Copy link
Collaborator

@HenrikJannsen HenrikJannsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@HenrikJannsen HenrikJannsen merged commit 66f9648 into bisq-network:master Sep 13, 2024
3 checks passed
@alejandrogarcia83 alejandrogarcia83 added this to the v1.9.18 milestone Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error when trying to make a BSQ transaction (blind vote)
4 participants