本日も深い考察(その2)です。コールドウォレットとハッカーの関係 そこで、取引用の紙片の出入りを要約(ハッシュ)する仕組みに着目です。

その他

暗号通貨系コインでは、取引用の紙片の出入りを要約(ハッシュ)する処理を以下のクラス(class CTransactionSignatureSerializer)になります。
※ 厳密には古い方という解釈になります。
※ ビットコインでは、従来からある1または3(非SegWit)から始まる古くからのアドレスが以下のクラスで処理されます。もともとは別の形だったのですが、このクラスに置き換えられていました。要約(ハッシュ)の論理は変わりません。もし変わってしまったら古い取引のハッシュが合わなくなって同期ができなくなります。

template <class T>
class CTransactionSignatureSerializer
{
private:
    const T& txTo;             //!< reference to the spending transaction (the one being serialized)
    const CScript& scriptCode; //!< output script being consumed
    const unsigned int nIn;    //!< input index of txTo being signed
    const bool fAnyoneCanPay;  //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set
    const bool fHashSingle;    //!< whether the hashtype is SIGHASH_SINGLE
    const bool fHashNone;      //!< whether the hashtype is SIGHASH_NONE

public:
    CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
        txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
        fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
        fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
        fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {}

    /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */
    template<typename S>
    void SerializeScriptCode(S &s) const {
        CScript::const_iterator it = scriptCode.begin();
        CScript::const_iterator itBegin = it;
        opcodetype opcode;
        unsigned int nCodeSeparators = 0;
        while (scriptCode.GetOp(it, opcode)) {
            if (opcode == OP_CODESEPARATOR)
                nCodeSeparators++;
        }
        ::WriteCompactSize(s, scriptCode.size() - nCodeSeparators);
        it = itBegin;
        while (scriptCode.GetOp(it, opcode)) {
            if (opcode == OP_CODESEPARATOR) {
                s.write((char*)&itBegin[0], it-itBegin-1);
                itBegin = it;
            }
        }
        if (itBegin != scriptCode.end())
            s.write((char*)&itBegin[0], it-itBegin);
    }

    /** Serialize an input of txTo */
    template<typename S>
    void SerializeInput(S &s, unsigned int nInput) const {
        // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized
        if (fAnyoneCanPay)
            nInput = nIn;
        // Serialize the prevout
        ::Serialize(s, txTo.vin[nInput].prevout);
        // Serialize the script
        if (nInput != nIn)
            // Blank out other inputs' signatures
            ::Serialize(s, CScript());
        else
            SerializeScriptCode(s);
        // Serialize the nSequence
        if (nInput != nIn && (fHashSingle || fHashNone))
            // let the others update at will
            ::Serialize(s, (int)0);
        else
            ::Serialize(s, txTo.vin[nInput].nSequence);
    }

    /** Serialize an output of txTo */
    template<typename S>
    void SerializeOutput(S &s, unsigned int nOutput) const {
        if (fHashSingle && nOutput != nIn)
            // Do not lock-in the txout payee at other indices as txin
            ::Serialize(s, CTxOut());
        else
            ::Serialize(s, txTo.vout[nOutput]);
    }

    /** Serialize txTo */
    template<typename S>
    void Serialize(S &s) const {
        // Serialize nVersion
        ::Serialize(s, txTo.nVersion);
        // Serialize vin
        unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
        ::WriteCompactSize(s, nInputs);
        for (unsigned int nInput = 0; nInput < nInputs; nInput++)
             SerializeInput(s, nInput);
        // Serialize vout
        unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size());
        ::WriteCompactSize(s, nOutputs);
        for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
             SerializeOutput(s, nOutput);
        // Serialize nLockTime
        ::Serialize(s, txTo.nLockTime);
    }
};

それで……、初期の頃からある仕組みとなりますので、ハッカーはこの部分を研究し尽くした感じがあるのです。それで、忽然と残高が消えてしまう原因、例えば確実なセキュリティ対策を施していたにも関わらず残高が0になってしまう現象の原因が、ここに一つありました。

当然ながら、その発生確率は非常に低いですが、偶然にも凄惨な航空機事故に巻き込まれてしまう位の確率はあります。つまり、天文学的に小さな確率ゆえに無視できるという水準ではありません。その例としてハッシュの衝突は1/2^256なので気にする必要はありません。ここまで低い確率なら発生しないとみなすことができます。

よって特に高額の場合は、一つのアドレスを過信せずに残高を分散させておく必要があります。まず、これが一つ目の対策です。これは確率の問題であって、セキュリティの問題ではありません。万一の場合でも一つの少額なアドレスだけで被害が済むように対策する必要があります。

ところで、少額なら狙われない。その考えも捨ててください。ハッカーは狙えそうな高額な場所をくまなく24時間仕掛けている状況です。ちなみにハッカー同士でも激しい競争になっています。そして、CTransactionSignatureSerializerの副作用により、少額がターゲットに入るのです。それで、その少額がハッキング可能となった瞬間、それが例え1000円であろうとも、ハッカーに引き抜かれます。

タイトルとURLをコピーしました