Skip to content

Commit 85671ef

Browse files
authored
Merge pull request bitcoin#808 from kallewoof/2019-07-bip322-fixes
BIP-322 updates
2 parents 213fb62 + e24e86b commit 85671ef

File tree

1 file changed

+85
-30
lines changed

1 file changed

+85
-30
lines changed

bip-0322.mediawiki

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The current message signing standard only works for P2PKH (1...) addresses. By e
2323

2424
A new structure <code>SignatureProof</code> is added, which is a simple serializable scriptSig & witness container.
2525

26-
Two actions "Sign" and "Verify" are defined along with two *purposes* "SignMessage" and "ProveFunds".
26+
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.
2727

2828
=== SignatureProof container ===
2929

@@ -36,11 +36,7 @@ Two actions "Sign" and "Verify" are defined along with two *purposes* "SignMessa
3636
|-
3737
|Uint32||4||flags||standard flags (1-to-1 with standard flags in Bitcoin Core)
3838
|-
39-
|VarInt||1-8||msglen||Number of bytes in message string, excluding NUL termination
40-
|-
41-
|Char*||[msglen]||msg||The message being signed for all subjects, excluding NUL termination
42-
|-
43-
|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>
39+
|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>
4440
|}
4541

4642
The above is followed by [entries] number of signature entries:
@@ -56,9 +52,9 @@ The above is followed by [entries] number of signature entries:
5652
|-
5753
|Uint8*||[scriptsiglen]||scriptsig||ScriptSig data
5854
|-
59-
|VarInt||1-8||witlen||Number of bytes in witness data
55+
|VarInt||1-8||witlen||Number of entries in witness stack
6056
|-
61-
|Uint8*||[witlen]||wit||Witness
57+
|Uint8[]*||[witlen]||wit||Witness stack, as [witlen] uint8* vectors, each one prepended with a varint of its size
6258
|}
6359

6460
In some cases, the scriptsig or wit may be empty. If both are empty, the proof is incomplete.
@@ -80,36 +76,24 @@ A verification call will return a result code according to the table below.
8076
|-
8177
|INVALID||One or more of the given proofs were invalid
8278
|-
83-
|SPENT||One or more of the claimed UTXO:s has been spent
84-
|-
8579
|ERROR||An error was encountered
8680
|}
8781

8882
== Signing and Verifying ==
8983

90-
Let there be an empty set `inputs` which is populated and tested at each call to one of the actions below.
84+
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.
85+
86+
Let there be an empty set <code>inputs</code> which is populated and tested at each call to one of the actions below.
9187

9288
=== Purpose: SignMessage ===
9389

9490
The "SignMessage" purpose generates a sighash based on a scriptPubKey and a message. It emits a VALID verification result code unless otherwise stated.
9591

96-
# 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>
92+
# 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>
9793
# Define the message pre-image as the sequence "Bitcoin Message:" concatenated with the message, encoded in UTF-8 using Normalization Form Compatibility Decomposition (NFKD)
9894
# Let sighash = sha256(sha256(scriptPubKey || pre-image))
9995
100-
=== Purpose: ProveFunds ===
101-
102-
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.
103-
104-
# Let txid be the transaction ID of the transaction, and vout be the output index corresponding to the index of the output being spent
105-
# Return INVALID if the txid:vout pair already exists in `inputs` set, otherwise insert it
106-
# 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>
107-
# Extract scriptPubKey from transaction output
108-
# 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>
109-
#* the string "POF:"
110-
#* 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)
111-
#* all transactions being proven for, as binary txid (little endian uint256) followed by index (little endian uint32), each separated by a single `0x00` byte
112-
# Let sighash = sha256(sha256(scriptPubKey || pre-image))
96+
A private key may be used directly to sign a message. In this case, its P2WPKH bech32 address shall be derived, and used as the input.
11397

11498
=== Action: Sign ===
11599

@@ -119,6 +103,8 @@ The "Sign" action takes as input a purpose. It returns a signature or fails.
119103
# Derive the private key privkey for the scriptPubKey; FAIL if not VALID
120104
# Generate and return a signature sig with privkey=privkey, sighash=sighash
121105
106+
The resulting signature proof should be encoded using base64 encoding.
107+
122108
=== Action: Verify ===
123109

124110
The "Verify" action takes as input a standard flags value, a script sig, an optional witness, and a purpose.
@@ -139,31 +125,100 @@ Note that the order of the entries in the proof must match the order of the entr
139125
* If a verification call returns ERROR or INVALID, return ERROR or INVALID immediately, ignoring as yet unverified entries
140126
* After all verifications complete,
141127
** return INCONCLUSIVE if any verification call returned INCONCLUSIVE
142-
** return SPENT if any verification call returned SPENT
143128
** return INCOMPLETE if the INCOMPLETE flag is set
144129
** return VALID
145130
131+
== Legacy format ==
132+
133+
The legacy format is restricted to the legacy P2PK(H) address format, and restricted to one single challenge (address).
134+
135+
Any other input (e.g. multiple addresses, or non-P2PK(H) address format(s)) must be signed using the new format described above.
136+
137+
=== Signing ===
138+
139+
Given the P2PK(H) address <code>a</code> and the message <code>m</code>:
140+
# let <code>p</code> be the pubkey-hash contained in <code>a</code>
141+
# let <code>x</code> be the private key associated with <code>p</code>
142+
# let <code>digest</code> be <code>SHA56d("Bitcoin Signed Message:\n"||m)</code>
143+
# create a compact signature <code>sig</code> (aka "recoverable ECDSA signature") using <code>x</code> on <code>digest</code>
144+
145+
The resulting proof is <code>sig</code>, serialized using the base64 encoding.
146+
147+
=== Verifying ===
148+
149+
Given the P2PK(H) address <code>a</code>, the message <code>m</code>, and the compact signature <code>sig</code>:
150+
151+
# let <code>p</code> be the pubkey-hash contained in <code>a</code>
152+
# let <code>digest</code> be <code>SHA56d("Bitcoin Signed Message:\n"||m)</code>
153+
# attempt pubkey recovery for <code>digest</code> using the signature <code>sig</code> and store the resulting pubkey into <code>Q</code>
154+
## fail verification if pubkey recovery above fails
155+
# let <code>q</code> be the pubkey-hash of <code>Q</code>
156+
# if <code>p == q</code>, the proof is valid, otherwise it is invalid
157+
146158
== Compatibility ==
147159

148-
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.
160+
This specification is backwards compatible with the legacy signmessage/verifymessage specification through the special case as described above.
149161

150162
== Rationale ==
151163

152164
<references/>
153165

154166
== Reference implementation ==
155167

156-
To do.
168+
# Pull request to Bitcoin Core: https://github.com/bitcoin/bitcoin/pull/16440
157169
158170
== Acknowledgements ==
159171

160-
TODO
172+
Thanks to David Harding, Jim Posen, Kalle Rosenbaum, Pieter Wuille, and many others for their feedback on the specification.
161173

162174
== References ==
163175

164176
# Original mailing list thread: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-March/015818.html
165-
# Pull request, with comments: https://github.com/bitcoin/bips/pull/725
166177
167178
== Copyright ==
168179

169180
This document is licensed under the Creative Commons CC0 1.0 Universal license.
181+
182+
== Test vectors ==
183+
184+
* <code>STANDARD_SCRIPT_VERIFY_FLAGS = 01ffdf (131039)</code>
185+
* <code>address = 2MsnqGxX7Abtn4b379MEpkDaD3VbNKQosd8</code>
186+
* <code>message = "hello world"</code>
187+
* <code>sighash = 7b66a1861b4e179e1dbab4702e26bcefeabf1cada7cccc97b6ebaec89a035d84</code> (<code>sha256d("Bitcoin Message:hello world")</code>)
188+
189+
A possible proof is:
190+
191+
* HEX: <code>dfff01000117160014689bbb5d76774321c652832ea209958fa1770b330247304402204368b119399d33b9bc9beef06d713becefd3ac508dc95ff62d1859d4912960c7022063d88ddc648faed710b3f870b7a839fdc1b3bfc3c3bd065df51bbbd8c386c81c012102b4e4c6d5021576a5c0bc4535890c3f17e1ff23a94eac87beb0a5e8747c42d920</code>
192+
* Base64: <code>3/8BAAEXFgAUaJu7XXZ3QyHGUoMuogmVj6F3CzMCRzBEAiBDaLEZOZ0zubyb7vBtcTvs79OsUI3JX/YtGFnUkSlgxwIgY9iN3GSPrtcQs/hwt6g5/cGzv8PDvQZd9Ru72MOGyBwBIQK05MbVAhV2pcC8RTWJDD8X4f8jqU6sh76wpeh0fELZIA==</code>
193+
194+
Split into components:
195+
196+
{|class="wikitable" style="text-align: center;"
197+
|-
198+
!Type
199+
!Length
200+
!Name
201+
!Value
202+
!Comment
203+
|-
204+
|Uint32||4||flags||<code>dfff0100</code>||standard flags used in signing
205+
|-
206+
|Uint8||1||entries||<code>01</code>||1 entry
207+
|-
208+
|VarInt||1-8||scriptsiglen||<code>17</code>||23 byte scriptsig
209+
|-
210+
|Uint8[32]||32||scriptsig||<code>160014689bbb5d76774321c652832ea209958fa1770b33</code>||ScriptSig data
211+
|-
212+
|VarInt||1-8||witlen||<code>02</code>||2 entries in witness stack
213+
|-
214+
|VarInt||1-8||entry1len||<code>47</code>||71 byte entry
215+
|-
216+
|Uint8[71]||71||entry1||<code>304402204368b119399d33b9bc9beef06d713becefd3ac50
217+
8dc95ff62d1859d4912960c7022063d88ddc648faed710b3
218+
f870b7a839fdc1b3bfc3c3bd065df51bbbd8c386c81c01</code>||Witness stack item 1
219+
|-
220+
|VarInt||1-8||entry2len||<code>21</code>||33 byte entry
221+
|-
222+
|Uint8[33]||33||entry2||<code>02b4e4c6d5021576a5c0bc4535890c3f17e1ff23a94eac87
223+
beb0a5e8747c42d920</code>||Witness stack item 2
224+
|}

0 commit comments

Comments
 (0)