signmessage.cpp raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #include <common/signmessage.h>
   7  #include <core_io.h>
   8  #include <hash.h>
   9  #include <key.h>
  10  #include <key_io.h>
  11  #include <outputtype.h>
  12  #include <pubkey.h>
  13  #include <script/interpreter.h>
  14  #include <streams.h>
  15  #include <uint256.h>
  16  #include <util/strencodings.h>
  17  
  18  #include <cassert>
  19  #include <optional>
  20  #include <string>
  21  #include <variant>
  22  #include <vector>
  23  
  24  /**
  25   * Text used to signify that a signed message follows and to prevent
  26   * inadvertently signing a transaction.
  27   */
  28  const std::string MESSAGE_MAGIC = "Bitcoin Signed Message:\n";
  29  
  30  /**
  31   * BIP-322 tagged hash
  32   */
  33  static const HashWriter HASHER_BIP322{TaggedHash("BIP0322-signed-message")};
  34  
  35  static constexpr unsigned int BIP322_REQUIRED_FLAGS =
  36      SCRIPT_VERIFY_CONST_SCRIPTCODE // disallows OP_CODESEPARATOR and FindAndDelete
  37  |   SCRIPT_VERIFY_LOW_S
  38  |   SCRIPT_VERIFY_STRICTENC
  39  |   SCRIPT_VERIFY_NULLFAIL
  40  |   SCRIPT_VERIFY_MINIMALDATA
  41  |   SCRIPT_VERIFY_CLEANSTACK
  42  |   SCRIPT_VERIFY_P2SH
  43  |   SCRIPT_VERIFY_WITNESS
  44  |   SCRIPT_VERIFY_TAPROOT
  45  |   SCRIPT_VERIFY_MINIMALIF;
  46  
  47  static constexpr unsigned int BIP322_INCONCLUSIVE_FLAGS =
  48      SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS
  49  |   SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS
  50  |   SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE
  51  |   SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION
  52  |   SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
  53  
  54  MessageVerificationResult MessageVerifyBIP322(
  55      CTxDestination& destination,
  56      std::vector<unsigned char>& signature,
  57      const std::string& message,
  58      MessageVerificationResult legacyError)
  59  {
  60      auto txs = BIP322Txs::Create(destination, message, legacyError, signature);
  61      if (!txs) return legacyError;
  62  
  63      const CTransaction& to_sign = txs->m_to_sign;
  64      const CTransaction& to_spend = txs->m_to_spend;
  65  
  66      const CScript scriptSig = to_sign.vin[0].scriptSig;
  67      const CScriptWitness& witness = to_sign.vin[0].scriptWitness;
  68  
  69      PrecomputedTransactionData txdata;
  70      txdata.Init(to_sign, {to_spend.vout[0]});
  71      TransactionSignatureChecker sigcheck(&to_sign, /* nInIn= */ 0, /* amountIn= */ to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
  72      sigcheck.m_require_sighash_all = true;
  73  
  74      if (!VerifyScript(scriptSig, to_spend.vout[0].scriptPubKey, &witness, BIP322_REQUIRED_FLAGS, sigcheck)) {
  75          return MessageVerificationResult::ERR_INVALID;
  76      }
  77  
  78      // inconclusive checks
  79  
  80      if (to_sign.version != 0 && to_sign.version != 2) {
  81          return MessageVerificationResult::INCONCLUSIVE;
  82      }
  83  
  84      if (!VerifyScript(scriptSig, to_spend.vout[0].scriptPubKey, &witness, BIP322_INCONCLUSIVE_FLAGS, sigcheck)) {
  85          return MessageVerificationResult::INCONCLUSIVE;
  86      }
  87  
  88      return MessageVerificationResult::OK;
  89  }
  90  
  91  MessageVerificationResult MessageVerify(
  92      const std::string& address,
  93      const std::string& signature,
  94      const std::string& message)
  95  {
  96      auto signature_bytes = DecodeBase64(signature);
  97      if ((!signature_bytes) || signature_bytes->empty()) {
  98          return MessageVerificationResult::ERR_MALFORMED_SIGNATURE;
  99      }
 100  
 101      CTxDestination destination = DecodeDestination(address);
 102      if (!IsValidDestination(destination)) {
 103          return MessageVerificationResult::ERR_INVALID_ADDRESS;
 104      }
 105  
 106      OutputType signed_for_outputtype;
 107      if (std::holds_alternative<PKHash>(destination)) {
 108          signed_for_outputtype = OutputType::LEGACY;
 109      } else if (std::holds_alternative<ScriptHash>(destination)) {
 110          signed_for_outputtype = OutputType::P2SH_SEGWIT;
 111      } else if (std::holds_alternative<WitnessV0KeyHash>(destination)) {
 112          signed_for_outputtype = OutputType::BECH32;
 113      } else {
 114          return MessageVerifyBIP322(destination, *signature_bytes, message, MessageVerificationResult::ERR_ADDRESS_NO_KEY);
 115      }
 116  
 117      uint8_t sigtype{(*signature_bytes)[0]};
 118      if (sigtype < 27 || sigtype > 42) {
 119          return MessageVerifyBIP322(destination, *signature_bytes, message, MessageVerificationResult::ERR_MALFORMED_SIGNATURE);
 120      }
 121      sigtype = (sigtype - 27) >> 2;
 122      if (sigtype == 3) {
 123          (*signature_bytes)[0] -= 8;
 124          signed_for_outputtype = OutputType::BECH32;
 125      } else if (sigtype == 2) {
 126          (*signature_bytes)[0] -= 4;
 127          signed_for_outputtype = OutputType::P2SH_SEGWIT;
 128      }
 129  
 130      CPubKey pubkey;
 131      if (!pubkey.RecoverCompact(MessageHash(message, MessageSignatureFormat::LEGACY), *signature_bytes)) {
 132          return MessageVerifyBIP322(destination, *signature_bytes, message, MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED);
 133      }
 134  
 135      CTxDestination recovered_dest = GetDestinationForKey(pubkey, signed_for_outputtype);
 136  
 137      if (!(recovered_dest == destination)) {
 138          return MessageVerifyBIP322(destination, *signature_bytes, message, MessageVerificationResult::ERR_NOT_SIGNED);
 139      }
 140  
 141      return MessageVerificationResult::OK;
 142  }
 143  
 144  bool MessageSign(
 145      const CKey& privkey,
 146      const std::string& message,
 147      std::string& signature)
 148  {
 149      std::vector<unsigned char> signature_bytes;
 150  
 151      if (!privkey.SignCompact(MessageHash(message, MessageSignatureFormat::LEGACY), signature_bytes)) {
 152          return false;
 153      }
 154  
 155      signature = EncodeBase64(signature_bytes);
 156  
 157      return true;
 158  }
 159  
 160  uint256 MessageHash(const std::string& message, MessageSignatureFormat format)
 161  {
 162      switch (format) {
 163      case MessageSignatureFormat::LEGACY:
 164          {
 165      HashWriter hasher{};
 166      hasher << MESSAGE_MAGIC << message;
 167  
 168      return hasher.GetHash();
 169          }
 170  
 171      case MessageSignatureFormat::SIMPLE:
 172      case MessageSignatureFormat::FULL:
 173          {
 174              HashWriter hasher{HASHER_BIP322};
 175              if (!message.empty()) {
 176                  hasher.write(AsBytes(Span{message.data(), message.size() * sizeof(char)}));
 177              }
 178              return hasher.GetSHA256();
 179          }
 180      }
 181      assert(false);
 182  }
 183  
 184  std::string SigningResultString(const SigningResult res)
 185  {
 186      switch (res) {
 187          case SigningResult::OK:
 188              return "No error";
 189          case SigningResult::PRIVATE_KEY_NOT_AVAILABLE:
 190              return "Private key not available";
 191          case SigningResult::SIGNING_FAILED:
 192              return "Sign failed";
 193          // no default case, so the compiler can warn about missing cases
 194      }
 195      assert(false);
 196  }
 197  
 198  std::optional<BIP322Txs> BIP322Txs::Create(const CTxDestination& destination, const std::string& message, MessageVerificationResult& result, std::optional<const std::vector<unsigned char>> signature)
 199  {
 200      // attempt to get script pub key for destination
 201      CScript message_challenge = GetScriptForDestination(destination);
 202      if (message_challenge.size() == 0) {
 203          // NoDestination; failure
 204          // (use legacy result)
 205          return std::nullopt;
 206      }
 207  
 208      // prepare message hash
 209      uint256 message_hash = MessageHash(message, MessageSignatureFormat::SIMPLE);
 210      std::vector<unsigned char> message_hash_vec(message_hash.begin(), message_hash.end());
 211  
 212      // generate to_spend transaction
 213      CMutableTransaction to_spend;
 214      to_spend.version = 0;
 215      to_spend.nLockTime = 0;
 216      to_spend.vin.emplace_back(COutPoint(Txid::FromUint256(uint256::ZERO), 0xFFFFFFFF), (CScript() << OP_0 << message_hash_vec), 0);
 217      to_spend.vout.emplace_back(0, message_challenge);
 218  
 219      CMutableTransaction to_sign;
 220      if (signature.has_value() && DecodeTx(to_sign, signature.value(), /* try_no_witness= */ true, /* try_witness= */ true)) {
 221          // validate decoded transaction
 222          // multiple inputs (proof of funds) are not supported as we do not have UTXO set access
 223          if (to_sign.vin.size() > 1) {
 224              result = MessageVerificationResult::ERR_POF;
 225              return std::nullopt;
 226          }
 227          if ((to_sign.vin.size() == 0 || to_sign.vin[0].prevout.hash != to_spend.GetHash()) ||
 228              (to_sign.vin[0].prevout.n != 0) ||
 229              (to_sign.vout.size() != 1) ||
 230              (to_sign.vout[0].nValue != 0) ||
 231              (to_sign.vout[0].scriptPubKey != (CScript() << OP_RETURN))) {
 232              result = MessageVerificationResult::ERR_INVALID;
 233              return std::nullopt;
 234          }
 235      } else {
 236          // signature is missing, or a witness stack only
 237          to_sign.version = 0;
 238          to_sign.nLockTime = 0;
 239          to_sign.vin.emplace_back(COutPoint(to_spend.GetHash(), 0), CScript(), 0);
 240          if (signature.has_value()) {
 241              try {
 242                  DataStream ds(signature.value());
 243                  ds >> to_sign.vin[0].scriptWitness.stack;
 244                  if (!ds.empty()) {
 245                      result = MessageVerificationResult::ERR_INVALID;
 246                      return std::nullopt;
 247                  }
 248              } catch (...) {
 249                  // not a script witness either; fall back to legacy error
 250                  // (use legacy result)
 251                  return std::nullopt;
 252              }
 253          }
 254          to_sign.vout.emplace_back(0, CScript() << OP_RETURN);
 255      }
 256  
 257      return BIP322Txs{to_spend, to_sign};
 258  }
 259