# SLIP-0019 : Proof of Ownership ``` Number: SLIP-0019 Title: Proof of Ownership Type: Standard Status: Accepted Authors: Andrew Kozlik Stepan Snigirev Ondrej Vejpustek Pavol Rusnak Created: 2019-04-25 ``` ## Abstract This specification defines the format for a proof of ownership which can be passed to a hierarchical deterministic wallet together with each input of an unsigned transaction. This proof allows the wallet to determine whether it is able to spend the given input or not. It also allows third parties to verify that a user has the ability to spend the input. ## Motivation In certain applications like CoinJoin or opening a dual-funded channel in the Lightning Network, a wallet has to sign transactions containing external inputs. To calculate the actual amount the user is spending, the wallet needs to reliably determine for each input whether it belongs to the wallet or not. Without such a mechanism an attacker can deceive the wallet into displaying incorrect information about the amount being spent, which can result in theft of user funds. This was first recognized in a bitcoin-dev [mailing list discussion](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-August/014843.html). For example, in a CoinJoin transaction an attacker can construct a transaction with inputs `in1` and `in2` of identical value belonging to the user and two outputs of identical value, `user_out` belonging to the user and `attacker_out` belonging to the attacker. If such a transaction is sent to a hardware wallet twice with `in1` marked as external the first time and `in2` marked as external the second time, then the hardware wallet will display two signing requests to the user with a spending amount of `in2 - user_out` and `in1 - user_out`, respectively. The user will think that they are signing two different CoinJoin transactions and spending `in1 + in2 - 2*user_out` for the fees, while in reality they are signing two different inputs to a single transaction and sending half of the amount to the attacker. To mitigate such an attack, the hardware wallet needs to ascertain non-ownership of all inputs which are claimed to be external. In case of hierarchical deterministic wallets it is generally not feasible to ascertain this solely based on the scriptPubKey of the UTXO, because it would require searching through billions of BIP32 derivation paths. A CoinJoin coordinator can also benefit from such a proof to verify that the CoinJoin participant is able and willing to sign the input. This verification helps to mitigate denial-of-service attacks as the attacker has to use a limited UTXO set that they control and in case of misbehavior this UTXO set gets banned. ## Proof of ownership format A proof of ownership consists of a proof body and a signature. The proof body contains one or more ownership identifiers which allow a wallet to efficiently determine whether or not it is able to spend a UTXO having a given scriptPubKey. The proof signature affirms that the proof body can be trusted to have been generated by the true owner of the UTXO. ``` proofOfOwnership = proofBody || proofSignature ``` ### Ownership identifier Let *k* be a secret *ownership identification key* derived from the wallet's master secret using the [SLIP-0021](https://github.com/satoshilabs/slips/blob/master/slip-0021.md) method for hierarchical derivation of symmetric keys as: ``` k = Key(m/"SLIP-0019"/"Ownership identification key") ``` The ownership identifier for a scriptPubKey is computed as: ``` id = HMAC-SHA256(key = k, msg = scriptPubKey) ``` In case of *m*-of-*n* multi-signature scriptPubKeys the proof of ownership SHOULD contain the ownership identifiers of all *n* co-owners of that scriptPubKey. See [Identifier inclusion](#identifier-inclusion) for further details. A wallet MUST NOT produce and reveal an ownership identifier for a scriptPubKey which it does not control. Such a fake ownership identifier can be used to mount a denial-of-service attack. ### Proof body The *proofBody* is a concatenation of the following fields: * *versionMagic* (4 bytes): b"\x53\x4c\x00\x19" (this is "SL" followed by 0019 in compressed numeric form as an abbreviation for "SLIP-0019"). * *flags* (1 byte, bit 0 is the least significant bit): * Bit 0: User confirmation * 0 means the proof was generated without user confirmation. * 1 means the user confirmed the generation of the proof. * Bits 1 to 7: Reserved for future use (all must be 0). * *n* (VarInt): the number of ownership identifiers which follow. The VarInt MUST be encoded in the fewest possible number of bytes. * *id*1 || *id*2 || ... || *id**n* (32 bytes each): concatenation of the ownership identifiers for the given scriptPubKey, one for each co-owner, see [Identifier inclusion](#identifier-inclusion) for further details. ### Proof footer The *proofFooter* is a concatenation of the following fields: * *scriptPubKey* (length-prefixed string). * *commitmentData* (length-prefixed string), any additional data to which the proof should commit, see below. The proof footer is included only in the *sighash* computation. It is not part of the proof of ownership, because the verifier of the proof should obtain these fields externally based on the context in which the proof is provided. Namely the *scriptPubKey* should be obtained by looking up the output being spent and the *commitmentData* is given by the application context. Variable-length fields are encoded the same way as in Bitcoin transactions, as a length-prefixed string, where the length is encoded as a variable-length integer (VarInt). ### Proof signature The concatenation of the *proofBody* and *proofFooter* is signed using the Generic Signed Message Format as defined in the [original BIP-0322](https://github.com/bitcoin/bips/blob/f9e95849f337358cd89c83b948fbede3875481c3/bip-0322.mediawiki) until October 2020, when the BIP-0322 specification was rewritten to use the transaction-based approach. The *proofSignature* is the `SignatureProof` container defined in the original BIP-0322 using the sighash computed as: ``` sighash = SHA-256(proofBody || proofFooter) ``` ### Additional commitment data The content of the *commitmentData* field is application-specific. If an application does not define the content of this field, then a zero-length string should be used by default. In case of CoinJoin transactions the *commitmentData* SHOULD contain a globally unique PSBT identifier (*psbtId*). The purpose of such an identifier is to prevent an attacker from causing denial of service by registering an input into a different CoinJoin transaction than the one for which the input was intended. The user should explicitly confirm the generation of the proof and the *commitmentData* value to affirm their intent to participate in the given CoinJoin transaction. The *psbtId* is not to be confused with TXID, which is the hash of a transaction's data. Since the *psbtId* needs to be known before the transaction is created, it cannot be derived from the transaction data but needs to be generated as a nonce. For example: 1. The concatenation of a globally unique CoinJoin server identifier (192 bits) with a sequential round identifier (64 bits). 2. A random 256 bit value. ## Proof construction ### Single-signature scriptPubKeys When constructing a proof of ownership for a single-signature scriptPubKey the inputs to the wallet are the *flags*, *scriptPubKey*, *commitmentData* and the BIP32 derivation path. The wallet takes the following steps: 1. Ensure that bits 1 through 7 of *flags* are clear. 2. Ensure that the wallet controls the private key to the provided *scriptPubKey*. This is typically done by using the provided BIP32 derivation path. 3. If bit 0 (user confirmation) of *flags* is set, then prompt the user to confirm generation of the ownership proof with the given *commitmentData*. If the user does not confirm, then abort. 4. Compute the ownership identifier for the scriptPubKey. 5. Compile the *proofBody* and *proofFooter*, and generate the *proofSignature*. 6. Return the *proofBody* and *proofSignature*. ### Multi-signature scriptPubKeys The construction of a proof of ownership for a *m*-of-*n* multi-signature scriptPubKey requires a signing coordinator, i.e. a watch-only software wallet. The signing coordinator is assumed to have obtained the ownership identifiers of all *n* co-owners in advance. These ownership identifiers should generally be produced at the time of the creation of the multi-signature address. When constructing a proof of ownership, the signing coordinator prepares the *proofBody* and *proofFooter* and sends these to each signer together with any other required metadata, such as the BIP32 derivation path for the input. Each of the *m* signers then takes the following steps: 1. Parse the *proofBody* and *proofFooter*. If *versionMagic* is not recognized or if any of the bits 1 through 7 of *flags* is set, then abort. 2. Derive the ownership identifier using the *scriptPubKey* provided in the *proofFooter*. 3. If the derived ownership identifier is not listed in the *proofBody*, then abort. 4. If bit 0 (user confirmation) of *flags* is set, then prompt the user to confirm generation of the ownership proof with the given *commitmentData*. If the user does not confirm, then abort. 5. Return the signature for the provided *proofBody* and *proofFooter*. The signing coordinator collects all the signatures and combines them into a `SignatureProof` container to finalize the proof. ## Proof usage ### Verifying non-ownership of transaction inputs When a wallet is requested to sign a transaction, each external input SHOULD be accompanied with a proof of ownership so that the wallet may ascertain non-ownership of such an input in order to correctly inform the user about the amount they are spending in the transaction. For each external input the wallet takes the following steps: 1. By reliable means obtain the scriptPubKey of the UTXO being spent by that input. Prior to SegWit version 1 witness programs this step involves acquiring the full transaction being spent and verifying its hash against that which is given in the outpoint. 2. Parse the *proofBody*. If *versionMagic* is not recognized or if any of the bits 1 through 7 of *flags* is set, then abort. 3. Verify that the *proofSignature* is valid in accordance with BIP-0322 using the obtained scriptPubKey and the sighash as defined in the [Proof signature](#proof-signature) section. 4. Derive the ownership identifier using the wallet's ownership identification key and the obtained scriptPubKey. 5. Verify that the derived ownership identifier is not included in the *proofBody*. ### Verifying ability and intent to sign an input Each input which is registered to take part in a CoinJoin transaction should be accompanied with a proof of ownership which affirms the owner's intent to take part, so as to mitigate denial-of-service attacks. The CoinJoin coordinator takes the following steps before registering an input: 1. By reliable means obtain the scriptPubKey of the UTXO being spent by that input. 2. Parse the *proofBody*. If *versionMagic* is not recognized or if any of the bits 1 through 7 of *flags* is set, then abort. 3. Verify that bit 0 (user confirmation) of *flags* is set. 4. Verify that the *proofSignature* is valid using the obtained scriptPubKey. A proof of ownership commits to a particular scriptPubKey, which means that the proof is replayable for UTXOs with the same address. Nevertheless, freshness of such a proof is guaranteed if a nonce (such as the *psbtId*) is included in the *commitmentData*. ## PSBT extension This section proposes additional fields for [BIP-0174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) PSBTv0 and [BIP-0370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki) PSBTv2 that allow for SLIP-0019 proofs of ownership to be included in a PSBT of any version. The following new global type is defined: Name | `` | `` | `` Description | `` | `` Description | Versions Requiring Inclusion | Versions Requiring Exclusion | Versions Allowing Inclusion -----|-------------|-------------|-------------------------|---------------|---------------------------|------------------------------|------------------------------|---------------------------- Proof-of-ownership commitment data | `PSBT_GLOBAL_OWNERSHIP_COMMITMENT = 0x07` | None | No key data | `` | The value used as the *commitmentData* in each input's proof-of-ownership. | | | 0, 2 The following new per-input type is defined: Name | `` | `` | `` Description | `` | `` Description | Versions Requiring Inclusion | Versions Requiring Exclusion | Versions Allowing Inclusion -----|-------------|-------------|-------------------------|---------------|---------------------------|------------------------------|------------------------------|---------------------------- Proof-of-ownership | `PSBT_IN_OWNERSHIP_PROOF = 0x19` | None | No key data | `` | A *proofOfOwnership* for this input, as defined above, allowing a wallet to determine whether it is able to spend this input or not. | | | 0, 2 ## Implementation considerations ### Script evaluation on hardware wallets Currently most hardware wallets do not support complete Bitcoin script verification, so initial deployment of proofs of ownership can be limited to a set of known scripts. In the future hardware wallets may implement [miniscript](http://bitcoin.sipa.be/miniscript/) verification, that will cover most of the use-cases known today. ### Identifier inclusion When generating a proof of ownership for *m*-of-*n* multi-signature scriptPubKeys the proof body SHOULD contain the ownership identifiers of all *n* co-owners of that scriptPubKey. Failing to include all ownership identifiers opens the door to the following attack. For simplicity consider two equal-valued UTXOs *A* and *B*, both of which have the same 1-of-2 multi-signature scriptPubKey controlled by Users 1 and 2. The attacker requests a proof of ownership *P1* from User 1 containing only User 1's ownership identifier. Similarly the attacker requests a proof of ownership *P2* from User 2 containing only User 2's ownership identifier. The attacker then creates a CoinJoin transaction with inputs *A* and *B* and equal-valued outputs *out_user* and *out_attacker*, the former of which is a multi-signature scriptPubKey controlled by Users 1 and 2. User 1 is given the transaction to sign with proof *P2* for the input spending *B*, and User 2 is given the same transaction to sign with proof *P1* for the input spending *A*. User 1 perceives *B* as foreign, assumes they are transferring *A* to *out_user* and signs the input spending *A*. User 2 perceives *A* as foreign, assumes they are transferring *B* to *out_user* and signs the input spending *B*. As a result half of the amount from *A* and *B* is transferred to the attacker. This attack is extendable to more complex *m*-of-*n* multi-signatures. In some cases there are legitimate reasons not to include the ownership identifier of a co-owner: 1. The excluded co-owner does not support any kind of proof of ownership format and will never take part in a transaction containing external inputs. An example of this would be a cryptocurrency custody service which is included in the multi-signature setup only as a backup in case the key of one of the co-owners is lost. 2. A co-owner is intentionally excluded to avoid signing failures due to input ownership collisions. Consider a user who is participating in a CoinJoin transaction with their UTXO *A*. At the same time this user happens to be a co-owner of another UTXO *B* being spent as an input in the same transaction. The user is not meant to be cosigning *B*, because this input was registered independently by a group of co-owners who did not expect the user to participate. Thus the user's wallet will recognize *B* as an input it co-owns, but it will not be able to sign because it was not given the corresponding BIP32 derivation path. Even if the path were to be provided, the user might not be willing to cosign due to confusion at the unexpected presence of the input amount supplied by *B*. As a result the CoinJoin transaction will fail to complete. Before excluding an ownership identifier on these grounds, the likelihood of this kind of scenario needs to be carefully weighed against the risk of the attack described above. ## Test vectors ### Test vector 1 (P2WPKH) #### Input parameters Parameter | Value -----------------------|------ BIP39 seed | "all all all all all all all all all all all all" Passphrase | "" Ownership ID key (hex) | `0a115a171e30f8a740bae6c4144bec5dc1099ffa79b83dfb8aa3501d094de585` Path | m/84'/0'/0'/1/0 *scriptPubKey* (hex) | `0014b2f771c370ccf219cd3059cda92bdf7f00cf2103` User confirmation | False *commitmentData* | "" *sighash* (hex) | `850dd556283b49d80fa5501035b4775e62f0c80bf36f62d1adf2f2f9f108c884` #### Proof of ownership (hex) `534c00190001a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad57070002483045022100c0dc28bb563fc5fea76cacff75dba9cb4122412faae01937cdebccfb065f9a7002202e980bfbd8a434a7fc4cd2ca49da476ce98ca097437f8159b1a386b41fcdfac50121032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b` Split into components: Name | Value -------------------|------ *versionMagic* | `534c0019` *flags* | `00` *n* | `01` *id* | `a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad5707` *scriptSig* length | `00` *scriptSig* | (empty) *witness* | `02483045022100c0dc28bb563fc5fea76cacff75dba9cb4122412faae01937cd`
`ebccfb065f9a7002202e980bfbd8a434a7fc4cd2ca49da476ce98ca097437f81`
`59b1a386b41fcdfac50121032ef68318c8f6aaa0adec0199c69901f0db7d3485`
`eb38d9ad235221dc3d61154b` ### Test vector 2 (P2WPKH nested in BIP16 P2SH) #### Input parameters Parameter | Value -----------------------|------ BIP39 seed | "all all all all all all all all all all all all" Passphrase | "" Ownership ID key (hex) | `0a115a171e30f8a740bae6c4144bec5dc1099ffa79b83dfb8aa3501d094de585` Path | m/49'/0'/0'/1/0 *scriptPubKey* (hex) | `a914b9ddc52a7d95ad46d474bfc7186d0150e15a499187` User confirmation | True *commitmentData* | "TREZOR" *sighash* (hex) | `709fa3a60709cecefbd7aaaf551ff23421d65d1c046e6a9390abf73cbcd2fc83` #### Proof of ownership (hex) `534c0019010192caf0b8daf78f1d388dbbceaec34bd2dabc31b217e32343663667f6694a3f4617160014e0cffbee1925a411844f44c3b8d81365ab51d0360247304402207f1003c59661ddf564af2e10d19ad8d6a1a47ad30e7052197d95fd65d186a67802205f0a804509980fec1b063554aadd8fb871d7c9fe934087cba2da09cbeff8531c012103a961687895a78da9aef98eed8e1f2a3e91cfb69d2f3cf11cbd0bb1773d951928` Split into components: Name | Value -------------------|------ *versionMagic* | `534c0019` *flags* | `01` *n* | `01` *id* | `92caf0b8daf78f1d388dbbceaec34bd2dabc31b217e32343663667f6694a3f46` *scriptSig* length | `17` *scriptSig* | `160014e0cffbee1925a411844f44c3b8d81365ab51d036` *witness* | `0247304402207f1003c59661ddf564af2e10d19ad8d6a1a47ad30e7052197d95`
`fd65d186a67802205f0a804509980fec1b063554aadd8fb871d7c9fe934087cb`
`a2da09cbeff8531c012103a961687895a78da9aef98eed8e1f2a3e91cfb69d2f`
`3cf11cbd0bb1773d951928` ### Test vector 3 (P2PKH) #### Input parameters Parameter | Value -----------------------|------ BIP39 seed | "all all all all all all all all all all all all" Passphrase | "TREZOR" Ownership ID key (hex) | `2d773852e0959b3c1bac15bd3a8ad410e2c6720befb4f7f428d74bdd5d6e4f1d` Path | m/44'/0'/0'/1/0 *scriptPubKey* (hex) | `76a9145a4deff88ada6705ed70835bc0db56a124b9cdcd88ac` User confirmation | False *commitmentData* | "" *sighash* (hex) | `abf12242bc87f457126373a08775fbeb67ccd5e09c4acbc1d8b310be68a3ac33` #### Proof of ownership (hex) `534c00190001ccc49ac5fede0efc80725fbda8b763d4e62a221c51cc5425076cffa7722c0bda6b483045022100e818002d0a85438a7f2140503a6aa0a6af6002fa956d0101fd3db24e776e546f0220430fd59dc1498bc96ab6e71a4829b60224828cf1fc35edc98e0973db203ca3f0012102f63159e21fbcb54221ec993def967ad2183a9c243c8bff6e7d60f4d5ed3b386500` Split into components: Name | Value -------------------|------ *versionMagic* | `534c0019` *flags* | `00` *n* | `01` *id* | `ccc49ac5fede0efc80725fbda8b763d4e62a221c51cc5425076cffa7722c0bda` *scriptSig* length | `6b` *scriptSig* | `483045022100e818002d0a85438a7f2140503a6aa0a6af6002fa956d0101fd`
`3db24e776e546f0220430fd59dc1498bc96ab6e71a4829b60224828cf1fc35ed`
`c98e0973db203ca3f0012102f63159e21fbcb54221ec993def967ad2183a9c24`
`3c8bff6e7d60f4d5ed3b3865` *witness* | `00` ### Test vector 4 (P2WSH 2-of-3 multisig) #### Input parameters Parameter | Value -------------------------|------ BIP39 seed 1 | "all all all all all all all all all all all all" Passphrase 1 | "" Ownership ID key 1 (hex) | `0a115a171e30f8a740bae6c4144bec5dc1099ffa79b83dfb8aa3501d094de585` BIP39 seed 2 | "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" Passphrase 2 | "" Ownership ID key 2 (hex) | `cd50559c65666fd381e823b82fff04763465062c1ff4c93d3e147a306f884130` BIP39 seed 3 | "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" Passphrase 3 | "" Ownership ID key 3 (hex) | `64b3e4f003fd7dea4168dd19f85410ac3b1844abd1d7f9f3a74254a7852af725` Path | m/84'/0'/0'/1/0 *scriptPubKey* (hex) | `00209149b5bcaae8c876f1997ef6b60ec197475217fd3e736d4c54fcf49fe4f5213a` User confirmation | False *commitmentData* | "TREZOR" *sighash* (hex) | `d2cca14e9ea31a5e4bb36e6e5813adf31f8744bc6da09680e3a0d69e5c8dddb1` #### Proof of ownership (hex) The proof is signed using the first and the third key. `534c00190003309c4ffec5c228cc836b51d572c0a730dbabd39df9f01862502ac9eabcdeb94a46307177b959c48bf2eb516e0463bb651aad388c7f8f597320df7854212fa3443892f9573e08cedff9160b243759520733a980fed45b131a8bba171317ae5d940004004830450221009d8cd2d792633732b3a406ea86072e94c72c0d1ffb5ddde466993ee2142eeef502206fa9c6273ab35400ebf689028ebcf8d2031edb3326106339e92d499652dc43030147304402205fae1218bc4600ad6c28b6093e8f3757603681b024e60f1d92fca579bfce210b022011d6f1c6ef1c7f7601f635ed237dafc774386dd9f4be0aef85e3af3f095d8a9201695221032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b2103025324888e429ab8e3dbaf1f7802648b9cd01e9b418485c5fa4c1b9b5700e1a621033057150eb57e2b21d69866747f3d377e928f864fa88ecc5ddb1c0e501cce3f8153ae` Split into components: Name | Value -------------------|------ *versionMagic* | `534c0019` *flags* | `00` *n* | `03` *id*1 | `309c4ffec5c228cc836b51d572c0a730dbabd39df9f01862502ac9eabcdeb94a` *id*2 | `46307177b959c48bf2eb516e0463bb651aad388c7f8f597320df7854212fa344` *id*3 | `3892f9573e08cedff9160b243759520733a980fed45b131a8bba171317ae5d94` *scriptSig* length | `00` *scriptSig* | (empty) *witness* | `04004830450221009d8cd2d792633732b3a406ea86072e94c72c0d1ffb5ddde4`
`66993ee2142eeef502206fa9c6273ab35400ebf689028ebcf8d2031edb332610`
`6339e92d499652dc43030147304402205fae1218bc4600ad6c28b6093e8f3757`
`603681b024e60f1d92fca579bfce210b022011d6f1c6ef1c7f7601f635ed237d`
`afc774386dd9f4be0aef85e3af3f095d8a9201695221032ef68318c8f6aaa0ad`
`ec0199c69901f0db7d3485eb38d9ad235221dc3d61154b2103025324888e429a`
`b8e3dbaf1f7802648b9cd01e9b418485c5fa4c1b9b5700e1a621033057150eb5`
`7e2b21d69866747f3d377e928f864fa88ecc5ddb1c0e501cce3f8153ae` ### Test vector 5 (P2TR) #### Input parameters Parameter | Value -----------------------|------ BIP39 seed | "all all all all all all all all all all all all" Passphrase | "" Ownership ID key (hex) | `0a115a171e30f8a740bae6c4144bec5dc1099ffa79b83dfb8aa3501d094de585` Path | m/86'/0'/0'/1/0 *scriptPubKey* (hex) | `51204102897557de0cafea0a8401ea5b59668eccb753e4b100aebe6a19609f3cc79f` User confirmation | False *commitmentData* | "" *sighash* (hex) | `331a936e0a94d8ec7a105507dbdd445d6cd6a516d53c0bfd83769bdac1950483` #### Proof of ownership (hex) `534c00190001dc18066224b9e30e306303436dc18ab881c7266c13790350a3fe415e438135ec000140647d6af883107a870417e808abe424882bd28ee04a28ba85a7e99400e1b9485075733695964c2a0fa02d4439ab80830e9566ccbd10f2597f5513eff9f03a0497` Split into components: Name | Value -------------------|------ *versionMagic* | `534c0019` *flags* | `00` *n* | `01` *id* | `dc18066224b9e30e306303436dc18ab881c7266c13790350a3fe415e438135ec` *scriptSig* length | `00` *scriptSig* | (empty) *witness* | `0140647d6af883107a870417e808abe424882bd28ee04a28ba85a7e99400e1b9`
`485075733695964c2a0fa02d4439ab80830e9566ccbd10f2597f5513eff9f03a`
`0497` ## References * [bitcoin-dev](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-August/014843.html): Original mailing list thread * [BIP-0174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki): Partially Signed Bitcoin Transaction Format * [BIP-0322](https://github.com/bitcoin/bips/blob/f9e95849f337358cd89c83b948fbede3875481c3/bip-0322.mediawiki): Generic Signed Message Format from March 25th 2020