shohu / c0ban

c0ban source tree
MIT License
0 stars 0 forks source link

Implement for Replay Attack #50

Closed shohu closed 5 years ago

shohu commented 6 years ago

I realized that it is possible to attack replay attack for c0ban when c0ban is forked. So I try to implement for Replay Attack by reffering to Bitcoin Cash source

Requirements

shohu commented 6 years ago

Create signature with fork sign

  1. SigHashType sigHashType = SigHashType().withForkId();
    SigHashType sigHashType = SigHashType().withForkId();
    :
    :
            ProduceSignature(MutableTransactionSignatureCreator(
                                 &keystore, &mergedTx, i, amount, sigHashType),
                             prevPubKey, sigdata);
  2. ProduceSignature

    bool ProduceSignature(const BaseSignatureCreator &creator,
                      const CScript &fromPubKey, SignatureData &sigdata) {
    CScript script = fromPubKey;
    bool solved = true;
    std::vector<valtype> result;
    txnouttype whichType;
    solved = SignStep(creator, script, result, whichType);
    CScript subscript;
    
    if (solved && whichType == TX_SCRIPTHASH) {
        // Solver returns the subscript that needs to be evaluated; the final
        // scriptSig is the signatures from that and then the serialized
        // subscript:
        script = subscript = CScript(result[0].begin(), result[0].end());
        solved = solved && SignStep(creator, script, result, whichType) &&
                 whichType != TX_SCRIPTHASH;
        result.push_back(
            std::vector<uint8_t>(subscript.begin(), subscript.end()));
    }
    
    sigdata.scriptSig = PushAll(result);
    
    // Test solution
    return solved &&
           VerifyScript(sigdata.scriptSig, fromPubKey,
                        STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
    }
  3. CreateSig

    bool TransactionSignatureCreator::CreateSig(std::vector<uint8_t> &vchSig,
    :
    :
    uint256 hash = SignatureHash(scriptCode, *txTo, nIn, sigHashType, amount);
    if (!key.Sign(hash, vchSig)) {
        return false;
    }
    
    vchSig.push_back(uint8_t(sigHashType.getRawSigHashType()));
    return true;
    }

    Detail SignatureHash.

    uint256 SignatureHash(const CScript &scriptCode, const CTransaction &txTo,
                      unsigned int nIn, SigHashType sigHashType,
                      const Amount amount,
                      const PrecomputedTransactionData *cache, uint32_t flags) {
    if (flags & SCRIPT_ENABLE_REPLAY_PROTECTION) {
        // Legacy chain's value for fork id must be of the form 0xffxxxx.
        // By xoring with 0xdead, we ensure that the value will be different
        // from the original one, even if it already starts with 0xff.
        uint32_t newForkValue = sigHashType.getForkValue() ^ 0xdead;
        sigHashType = sigHashType.withForkValue(0xff0000 | newForkValue);
    }
    
    if (sigHashType.hasForkId() && (flags & SCRIPT_ENABLE_SIGHASH_FORKID)) {
        uint256 hashPrevouts;
        uint256 hashSequence;
        uint256 hashOutputs;
    
        if (!sigHashType.hasAnyoneCanPay()) {
            hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
        }
    
        if (!sigHashType.hasAnyoneCanPay() &&
            (sigHashType.getBaseType() != BaseSigHashType::SINGLE) &&
            (sigHashType.getBaseType() != BaseSigHashType::NONE)) {
            hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo);
        }
    
        if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) &&
            (sigHashType.getBaseType() != BaseSigHashType::NONE)) {
            hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo);
        } else if ((sigHashType.getBaseType() == BaseSigHashType::SINGLE) &&
                   (nIn < txTo.vout.size())) {
            CHashWriter ss(SER_GETHASH, 0);
            ss << txTo.vout[nIn];
            hashOutputs = ss.GetHash();
        }
    
        CHashWriter ss(SER_GETHASH, 0);
        // Version
        ss << txTo.nVersion;
        // Input prevouts/nSequence (none/all, depending on flags)
        ss << hashPrevouts;
        ss << hashSequence;
        // The input being signed (replacing the scriptSig with scriptCode +
        // amount). The prevout may already be contained in hashPrevout, and the
        // nSequence may already be contain in hashSequence.
        ss << txTo.vin[nIn].prevout;
        ss << scriptCode;
        ss << amount.GetSatoshis();
        ss << txTo.vin[nIn].nSequence;
        // Outputs (none/one/all, depending on flags)
        ss << hashOutputs;
        // Locktime
        ss << txTo.nLockTime;
        // Sighash type
        ss << sigHashType;
    
        return ss.GetHash();
    }
    
    static const uint256 one(uint256S(
        "0000000000000000000000000000000000000000000000000000000000000001"));
    if (nIn >= txTo.vin.size()) {
        //  nIn out of range
        return one;
    }
    
    // Check for invalid use of SIGHASH_SINGLE
    if ((sigHashType.getBaseType() == BaseSigHashType::SINGLE) &&
        (nIn >= txTo.vout.size())) {
        //  nOut out of range
        return one;
    }
    
    // Wrapper to serialize only the necessary parts of the transaction being
    // signed
    CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, sigHashType);
    
    // Serialize and hash
    CHashWriter ss(SER_GETHASH, 0);
    ss << txTmp << sigHashType;
    return ss.GetHash();
    }
    ss << txTmp << sigHashType;
    return ss.GetHash();

or

        // Sighash type
        ss << sigHashType;

        return ss.GetHash();

affect fork signature.

shohu commented 6 years ago

Check signature

Over threshold height when fork flag is on by GetBlockScriptFlags.

1. AcceptToMemoryPoolWorker or ConnectBlock use CheckInputs. (following case is ConnectBlock)

  const uint32_t flags = GetBlockScriptFlags(config, pindex->pprev);
  :
  :
            if (!CheckInputs(tx, state, view, fScriptChecks, flags,
                             fCacheResults, fCacheResults,
                             PrecomputedTransactionData(tx), &vChecks)) {
                return error("ConnectBlock(): CheckInputs on %s failed with %s",
                             tx.GetId().ToString(), FormatStateMessage(state));
            }
// Returns the script flags which should be checked for a given block
static uint32_t GetBlockScriptFlags(const Config &config,
                                    const CBlockIndex *pChainTip) {
 :
    // If the UAHF is enabled, we start accepting replay protected txns
    if (IsUAHFenabled(config, pChainTip)) {
        flags |= SCRIPT_VERIFY_STRICTENC;
        flags |= SCRIPT_ENABLE_SIGHASH_FORKID;
    }
:
    return flags;
}
static bool IsUAHFenabled(const Config &config, int nHeight) {
    return nHeight >= config.GetChainParams().GetConsensus().uahfHeight;
}

bool IsUAHFenabled(const Config &config, const CBlockIndex *pindexPrev) {
    if (pindexPrev == nullptr) {
        return false;
    }

    return IsUAHFenabled(config, pindexPrev->nHeight);
}

2. CheckInputs

bool CheckInputs(const CTransaction &tx, CValidationState &state,
                 const CCoinsViewCache &inputs, bool fScriptChecks,
                 const uint32_t flags, bool sigCacheStore,
                 bool scriptCacheStore,
                 const PrecomputedTransactionData &txdata,
                 std::vector<CScriptCheck> *pvChecks) {
 :
 :
        // Verify signature
        CScriptCheck check(scriptPubKey, amount, tx, i, flags, sigCacheStore,
                           txdata);
        if (pvChecks) {
            pvChecks->push_back(std::move(check));
        } else if (!check()) {
 :

3.CScriptCheck::operator()

bool CScriptCheck::operator()() {
    const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
    return VerifyScript(scriptSig, scriptPubKey, nFlags,
                        CachingTransactionSignatureChecker(ptxTo, nIn, amount,
                                                           cacheStore, txdata),
                        &error);
}

4.VerifyScript

bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey,
                  uint32_t flags, const BaseSignatureChecker &checker,
                  ScriptError *serror) {
    set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
  :
  :
    std::vector<valtype> stack, stackCopy;
    if (!EvalScript(stack, scriptSig, flags, checker, serror)) {
        // serror is set
        return false;
    }
  :

5.EvalScript

bool EvalScript(std::vector<valtype> &stack, const CScript &script,
                uint32_t flags, const BaseSignatureChecker &checker,
                ScriptError *serror) {
 :
 :
                    case OP_CHECKSIG:
                    case OP_CHECKSIGVERIFY: {
                        // (sig pubkey -- bool)
                        if (stack.size() < 2) {
                            return set_error(
                                serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                        }
                        valtype &vchSig = stacktop(-2);
                        valtype &vchPubKey = stacktop(-1);

                        if (!CheckSignatureEncoding(vchSig, flags, serror) ||
                            !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
                            // serror is set
                            return false;
                        }
 :
                    case OP_CHECKMULTISIG:
                    case OP_CHECKMULTISIGVERIFY: {
 :
 :
                            if (!CheckSignatureEncoding(vchSig, flags,
                                                        serror) ||
                                !CheckPubKeyEncoding(vchPubKey, flags,
                                                     serror)) {
                                // serror is set
                                return false;
                            }

6. Check forkid sign by CheckSignatureEncoding

bool CheckSignatureEncoding(const std::vector<uint8_t> &vchSig, uint32_t flags,
                            ScriptError *serror) {
    // Empty signature. Not strictly DER encoded, but allowed to provide a
    // compact way to provide an invalid signature for use with CHECK(MULTI)SIG
    if (vchSig.size() == 0) {
        return true;
    }
    if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S |
                  SCRIPT_VERIFY_STRICTENC)) != 0 &&
        !IsValidSignatureEncoding(vchSig)) {
        return set_error(serror, SCRIPT_ERR_SIG_DER);
    }
    if ((flags & SCRIPT_VERIFY_LOW_S) != 0 &&
        !IsLowDERSignature(vchSig, serror)) {
        // serror is set
        return false;
    }
    if ((flags & SCRIPT_VERIFY_STRICTENC) != 0) {
        if (!GetHashType(vchSig).isDefined()) {
            return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE);
        }
        bool usesForkId = GetHashType(vchSig).hasForkId();
        bool forkIdEnabled = flags & SCRIPT_ENABLE_SIGHASH_FORKID;
        if (!forkIdEnabled && usesForkId) {
            return set_error(serror, SCRIPT_ERR_ILLEGAL_FORKID);
        }
        if (forkIdEnabled && !usesForkId) {
            return set_error(serror, SCRIPT_ERR_MUST_USE_FORKID);
        }
    }
    return true;
}