-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
…ibility) and to fix the serialization format
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ The current message signing standard only works for P2PKH (1...) addresses. By e | |
|
||
A new structure <code>SignatureProof</code> is added, which is a simple serializable scriptSig & witness container. | ||
|
||
Two actions "Sign" and "Verify" are defined along with two *purposes* "SignMessage" and "ProveFunds". | ||
Two actions "Sign" and "Verify" are defined along with one ''purpose'', "SignMessage", with the ability to expand in the future to add a potential "ProveFunds" purpose. | ||
|
||
=== SignatureProof container === | ||
|
||
|
@@ -36,11 +36,7 @@ Two actions "Sign" and "Verify" are defined along with two *purposes* "SignMessa | |
|- | ||
|Uint32||4||flags||standard flags (1-to-1 with standard flags in Bitcoin Core) | ||
|- | ||
|VarInt||1-8||msglen||Number of bytes in message string, excluding NUL termination | ||
|- | ||
|Char*||[msglen]||msg||The message being signed for all subjects, excluding NUL termination | ||
|- | ||
|Uint8||1||entries||Number of proof entries<ref><strong>Why support multiple proofs?</strong> In particular with proof of funds, it is non-trivial to check a large number of individual proofs (one per UTXO) for duplicates. Software could be written to do so, but it seems more efficient to build this check into the specification itself.</ref> | ||
|Uint8||1||entries||number of proof entries<ref><strong>Why support multiple proofs?</strong> It is non-trivial to check a large number of individual proofs for duplicates. Software could be written to do so, but it seems more efficient to build this check into the specification itself.</ref> | ||
|} | ||
|
||
The above is followed by [entries] number of signature entries: | ||
|
@@ -56,9 +52,9 @@ The above is followed by [entries] number of signature entries: | |
|- | ||
|Uint8*||[scriptsiglen]||scriptsig||ScriptSig data | ||
|- | ||
|VarInt||1-8||witlen||Number of bytes in witness data | ||
|VarInt||1-8||witlen||Number of entries in witness stack | ||
|- | ||
|Uint8*||[witlen]||wit||Witness | ||
|Uint8[]*||[witlen]||wit||Witness stack, as [witlen] uint8* vectors, each one prepended with a varint of its size | ||
|} | ||
|
||
In some cases, the scriptsig or wit may be empty. If both are empty, the proof is incomplete. | ||
|
@@ -80,37 +76,23 @@ A verification call will return a result code according to the table below. | |
|- | ||
|INVALID||One or more of the given proofs were invalid | ||
|- | ||
|SPENT||One or more of the claimed UTXO:s has been spent | ||
|- | ||
|ERROR||An error was encountered | ||
|} | ||
|
||
== Signing and Verifying == | ||
|
||
Let there be an empty set `inputs` which is populated and tested at each call to one of the actions below. | ||
If the challenge consists of a single address and the address is in the P2PK(H) (legacy) format, sign using the legacy format (further information below). Otherwise continue as stated below. | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
Let there be an empty set <code>inputs</code> which is populated and tested at each call to one of the actions below. | ||
|
||
=== Purpose: SignMessage === | ||
|
||
The "SignMessage" purpose generates a sighash based on a scriptPubKey and a message. It emits a VALID verification result code unless otherwise stated. | ||
|
||
# Return INVALID if scriptPubKey already exists in `inputs` set, otherwise insert it<ref><strong>Why track duplicates?</strong> Because a 3-entry proof is not proving 3 inputs unless they are all distinct</ref> | ||
# Return INVALID if scriptPubKey already exists in <code>inputs</code> set, otherwise insert it<ref><strong>Why track duplicates?</strong> Because a 3-entry proof is not proving 3 entries unless they are all distinct</ref> | ||
# Define the message pre-image as the sequence "Bitcoin Message:" concatenated with the message, encoded in UTF-8 using Normalization Form Compatibility Decomposition (NFKD) | ||
# Let sighash = sha256(sha256(scriptPubKey || pre-image)) | ||
=== Purpose: ProveFunds === | ||
|
||
The "ProveFunds" purpose generates a sighash and a scriptPubKey from a transaction, an output index, and a message. For multiple simultaneous proofs, it also requires access to the ordered list of proofs. It emits a VALID verification result code unless otherwise stated. | ||
|
||
# Let txid be the transaction ID of the transaction, and vout be the output index corresponding to the index of the output being spent | ||
# Return INVALID if the txid:vout pair already exists in `inputs` set, otherwise insert it | ||
# Return SPENT if the txid/vout is not a valid UTXO according to a Bitcoin node<ref><strong>Synced up or not?</strong> A normal verifier would use a synced up node. An auditor checking records from a client that were submitted in the past want to use a node that is synced up to the block corresponding to the proof, or the proof will fail, even if it may have been valid at the time of creation.</ref> | ||
# Extract scriptPubKey from transaction output | ||
# Define the message pre-image as the concatenation of the following components:<ref><strong>Why not just the UTXO data?</strong> We want the verifier to be able to challenge the prover with a custom message to sign, or anyone can reuse the POF proof for a set of UTXO:s once they have seen it, and the funds have not yet been spent</ref> | ||
#* the string "POF:" | ||
#* the message, encoded in UTF-8 using Normalization Form Compatibility Decomposition (NFKD), including the null terminating character (i.e. write strlen(message) + 1 bytes, for a C string) | ||
#* all transactions being proven for, as binary txid (little endian uint256) followed by index (little endian uint32), each separated by a single `0x00` byte | ||
# Let sighash = sha256(sha256(scriptPubKey || pre-image)) | ||
=== Action: Sign === | ||
|
||
The "Sign" action takes as input a purpose. It returns a signature or fails. | ||
|
@@ -119,6 +101,8 @@ The "Sign" action takes as input a purpose. It returns a signature or fails. | |
# Derive the private key privkey for the scriptPubKey; FAIL if not VALID | ||
# Generate and return a signature sig with privkey=privkey, sighash=sighash | ||
The resulting signature proof should be encoded using base64 encoding. | ||
|
||
=== Action: Verify === | ||
|
||
The "Verify" action takes as input a standard flags value, a script sig, an optional witness, and a purpose. | ||
|
@@ -139,30 +123,51 @@ Note that the order of the entries in the proof must match the order of the entr | |
* If a verification call returns ERROR or INVALID, return ERROR or INVALID immediately, ignoring as yet unverified entries | ||
* After all verifications complete, | ||
** return INCONCLUSIVE if any verification call returned INCONCLUSIVE | ||
** return SPENT if any verification call returned SPENT | ||
** return INCOMPLETE if the INCOMPLETE flag is set | ||
** return VALID | ||
== Legacy format == | ||
|
||
The legacy format is restricted to the legacy P2PK(H) address format, and restricted to one single challenge (address). | ||
|
||
Any other input (e.g. multiple addresses, or non-P2PK(H) address format(s)) must be signed using the new format described above. | ||
|
||
=== Signing === | ||
|
||
Given the P2PK(H) address <code>a</code> and the message <code>m</code>: | ||
# let <code>p</code> be the pubkey-hash contained in <code>a</code> | ||
This comment has been minimized.
Sorry, something went wrong.
harding
Contributor
|
||
# let <code>x</code> be the private key associated with <code>p</code> | ||
# let <code>digest</code> be <code>SHA56d("Bitcoin Signed Message:\n"||m)</code> | ||
# create a compact signature <code>sig</code> (aka "recoverable ECDSA signature") using <code>x</code> on <code>digest</code> | ||
The resulting proof is <code>sig</code>, serialized using the base64 encoding. | ||
|
||
=== Verifying === | ||
|
||
Given the P2PK(H) address <code>a</code>, the message <code>m</code>, and the compact signature <code>sig</code>: | ||
|
||
# let <code>p</code> be the pubkey-hash contained in <code>a</code> | ||
# let <code>digest</code> be <code>SHA56d("Bitcoin Signed Message:\n"||m)</code> | ||
# attempt pubkey recovery for <code>digest</code> using the signature <code>sig</code> and store the resulting pubkey into <code>Q</code> | ||
## fail verification if pubkey recovery above fails | ||
# let <code>q</code> be the pubkey-hash of <code>Q</code> | ||
# if <code>p == q</code>, the proof is valid, otherwise it is invalid | ||
== Compatibility == | ||
|
||
This specification is not backwards compatible with the legacy signmessage/verifymessage specification. However, legacy addresses (1...) may be used in this implementation without any problems. | ||
This specification is backwards compatible with the legacy signmessage/verifymessage specification through the special case as described above. | ||
|
||
== Rationale == | ||
|
||
<references/> | ||
|
||
== Reference implementation == | ||
|
||
To do. | ||
|
||
== Acknowledgements == | ||
|
||
TODO | ||
# Pull request to Bitcoin Core: https://github.com/bitcoin/bitcoin/pull/16440 | ||
== References == | ||
|
||
# Original mailing list thread: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-March/015818.html | ||
# Pull request, with comments: https://github.com/bitcoin/bips/pull/725 | ||
== Copyright == | ||
|
||
|
Strictly speaking, there is no address for P2PK. If you wanted to sign for a P2PK output, you derived the P2PKH address and used that. I think this could just say
P2PKH
instead ofP2PK(H)
.