Skip to content

Conversation

kallewoof
Copy link
Contributor

The simple signature takes a sighash as argument and verifies a signature and pubkey against it.

It is used in signet (#16411) for verifying blocks and in BIP-322 implementation (#16440) for verifying messages.

@kallewoof kallewoof force-pushed the 2019-08-genpursigs branch 2 times, most recently from 2098ca9 to 02a1db1 Compare August 19, 2019 08:04
@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 19, 2019

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@NicolasDorier
Copy link
Contributor

ACK 02a1db1

@laanwj laanwj added the Tests label Aug 19, 2019
@Sjors
Copy link
Member

Sjors commented Aug 19, 2019

Concept ACK. It strictly adds new functions so that seems a pretty safe approach. This PR needs tests though.

@cvengler
Copy link
Contributor

cvengler commented Aug 19, 2019

Concept NACK until it is being used actively.
I don't think unused code is good, it just makes the binary bigger

@kallewoof
Copy link
Contributor Author

@emilengler Did you read the PR description?

@kallewoof
Copy link
Contributor Author

@Sjors Added tests. The multisig ones are based on the multisig_tests.cpp file, but adapted to use the new creators/checkers.

@kallewoof kallewoof force-pushed the 2019-08-genpursigs branch 2 times, most recently from cb50c61 to a2b4370 Compare August 20, 2019 05:52
@cvengler
Copy link
Contributor

@kallewoof I did but until nothing is merged I don't think this should be added.
I prefer a chronological order.

@kallewoof
Copy link
Contributor Author

lol good trolling there

@Sjors
Copy link
Member

Sjors commented Aug 21, 2019

@kallewoof thanks for adding the tests. They pass for me on macOS. I haven't followed the BIP-322 discussion in great detail, so can't really comment on the cryptography itself or how likely that is to change. It would be nice if it remained consistent with signet.

@emilengler when you built the binary, how many bytes were added? Even if this difference is non-trivial, it only matters if for some reason this PR makes it into the the 0.19 release and neither of the two followup PRs make the cut-off. This PR seems well worth the reduced review burden and the improved coordination between both projects.

@NicolasDorier
Copy link
Contributor

On my side I am fine both about either having it in this PR or on the same PR as signet.

@cvengler
Copy link
Contributor

@Sjors Sure thing but why isn't this being added to the original PR? @kallewoof

@sdaftuar
Copy link
Member

This interface, where the caller provides the hash of a message, and then "verifies" that the signature is valid, seems problematic to me at this layer of our code -- my understanding is that without knowing the preimage of the hash (ie the message being signed), this doesn't prove that the signature could only have come from someone with knowledge of the private key.

I know at some layer of our cryptography code we will have verification methods like this, but at this layer it seems inconsistent with all our other signature checkers, which are operating under different assumptions (well, except maybe for the DummySignatureChecker which does not do signature verifications, but hopefully that is clear to any callers). So my initial thought is that this is not a good place to have what is effectively a lower-level API call to a crypto library function.

I'm not very familiar with the broader work this is part of, so if this is in fact necessary or expedient from an implementation perspective, then I'd say at a minimum that we should at least include comments explaining that the assumptions here are different than for the other signature checkers.

@kallewoof
Copy link
Contributor Author

kallewoof commented Aug 22, 2019

@sdaftuar

Maybe I'm misunderstanding, but I'm not sure if it's that inconsistent in the end. The "CreateSig" methods right now

  1. Get the private key from the signing provider.
  2. Generate the sighash based on internal data.
  3. Sign the sighash using the aforementioned private key.

This is consistent across all of these, and in fact, the other implementations could become sub-classes of this one (perform step 2 into internal sighash then have master do the rest; edit: I actually tried to do this, but it turns out a bit messy as the checker has to be re-constructed for every CreateSig call).

To pass the preimage rather than the hash itself would only lock this down to using some specific type of hasher, and would have no practical meaning for the actual implementation. The two (current) uses of these are here (#16411):

bitcoin/src/signet.cpp

Lines 31 to 36 in 55d7c4e

bool CheckBlockSolution(const uint256& signet_hash, const std::vector<uint8_t>& signature, const Consensus::Params& params)
{
SimpleSignatureChecker bsc(signet_hash);
CScript solution = CScript(signature.begin(), signature.end());
return VerifyScript(solution, g_signet_blockscript, nullptr, MANDATORY_SCRIPT_VERIFY_FLAGS, bsc);
}

which gets the hash from

bitcoin/src/signet.cpp

Lines 54 to 58 in 55d7c4e

uint256 GetSignetHash(const CBlock& block)
{
if (block.vtx.size() == 0) return block.GetHash();
return (CHashWriter(SER_DISK, PROTOCOL_VERSION) << block.nVersion << block.hashPrevBlock << BlockSignetMerkleRoot(block) << block.nTime << block.nBits).GetHash();
}

and here (#16440):

bitcoin/src/script/proof.h

Lines 226 to 233 in 203464e

void GenerateSingleProof(const SignMessage& challenge, SigningProvider* sp, const uint256& sighash, const CScript& scriptPubKey, const std::string& message) override {
SimpleSignatureCreator sc(sighash);
SignatureData sigdata;
if (!ProduceSignature(*sp, sc, scriptPubKey, sigdata)) {
throw signing_error("Failed to produce a signature");
}
m_proof.m_items.emplace_back(sigdata);
}

which gets the hash from

CHashWriter hw(SER_DISK, 0);
std::string s = strMessageMagic + message;
hw << m_scriptpubkey << LimitedString<65536>(s);
sighash_out = hw.GetHash();

I made a note that these lack some assumptions that are normally present about the validity of the signature.

Copy link
Member

@sdaftuar sdaftuar left a comment

Choose a reason for hiding this comment

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

@kallewoof Thanks for providing that context, now I understand a bit better why this is being implemented in this way. My only suggestion is that we improve the comments to explain the implications of this for verification.

@@ -162,6 +162,18 @@ class BaseSignatureChecker
virtual ~BaseSignatureChecker() {}
};

/** A general purpose signature checker. Note that this lacks assumptions about the `sighash`, as the preimage is not known to the signer. */
Copy link
Member

Choose a reason for hiding this comment

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

This should emphasize that the issue about not knowing the hash preimage is for the verifier, not the signer. Perhaps:

/** A low level signature checker.  Note that the caller must verify that the hash passed in relates to a known message (unlike for the other signature checkers). */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for delay. Yes, that looks great, thanks. Adopting.

@jtimon
Copy link
Contributor

jtimon commented Oct 23, 2019

utACK 0bd274f

An alternative that perhaps @sdaftuar would like more given his reasonings, is having a templated class that will work for any object implementing a GetHash() method.
Something like:

explicit SimpleSignatureChecker(const T& hashable) : hash(hashable.GetHash()) {}

But that would require a wrapper class for signet and another one for the signed messages, I think.
And if a comment solves the concerns, that's certainly easier. Just thinking out loud.

@kallewoof kallewoof force-pushed the 2019-08-genpursigs branch 2 times, most recently from 7704285 to 7ba973a Compare January 15, 2020 08:08
Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

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

Concept ACK


public:
const uint256& GetHash() const { return hash; }
explicit SimpleSignatureChecker(const uint256& hash_in) : hash(hash_in) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Move constructor one line up, immediately after public (like for the other class SimpleSignatureCreator and for most classes in general I'd say)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's fine as is, and there are plenty of examples of the constructor not being right below public:.

@kallewoof kallewoof force-pushed the 2019-08-genpursigs branch from ffe9624 to cdcb377 Compare April 1, 2020 00:17
@instagibbs
Copy link
Member

concept and approach ACK. It's ok sometimes to have really small tooling to support not-yet-merged uses, provided there are tests

@kallewoof
Copy link
Contributor Author

Closing this as it's no longer certain whether it will be used, now that BIP-322 switched to a tx based version.

@kallewoof kallewoof closed this Jul 31, 2020
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.