diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index c91172fc08..0ff5bff334 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -705,6 +705,59 @@ void CSHA256::Midstate(unsigned char hash[OUTPUT_SIZE], uint64_t* len, unsigned } } +std::vector CSHA256::Save() const { + size_t buf_size = bytes % 64; + std::vector result(40 + buf_size); + + WriteBE32(&result[ 0], s[0]); + WriteBE32(&result[ 4], s[1]); + WriteBE32(&result[ 8], s[2]); + WriteBE32(&result[12], s[3]); + WriteBE32(&result[16], s[4]); + WriteBE32(&result[20], s[5]); + WriteBE32(&result[24], s[6]); + WriteBE32(&result[28], s[7]); + + WriteLE64(&result[32], bytes << 3); + + if (buf_size) memcpy(&result[40], buf, buf_size); + + return result; +} + +bool CSHA256::Load(const std::vector& vch) { + if (vch.size() < 40) return false; + + uint64_t bits = ReadLE64(&vch[32]); + size_t buf_size = (bits >> 3) % 64; + + if ((bits & 0x07) != 0 || vch.size() != 40 + buf_size) return false; + + // We want to leave the internal state of the object unchanged if false is returned. + // So no member variables can be modified until now. + + s[0] = ReadBE32(&vch[ 0]); + s[1] = ReadBE32(&vch[ 4]); + s[2] = ReadBE32(&vch[ 8]); + s[3] = ReadBE32(&vch[12]); + s[4] = ReadBE32(&vch[16]); + s[5] = ReadBE32(&vch[20]); + s[6] = ReadBE32(&vch[24]); + s[7] = ReadBE32(&vch[28]); + + bytes = bits >> 3; + if (buf_size) memcpy(buf, &vch[40], buf_size); + + return true; +} + +bool CSHA256::SafeWrite(const unsigned char* data, size_t len) { + const uint64_t SHA256_MAX = 0x1FFFFFFFFFFFFFFF; // SHA256's maximum allowed message length in bytes. + if (SHA256_MAX < bytes || SHA256_MAX - bytes < len) return false; + Write(data, len); + return true; +} + CSHA256& CSHA256::Reset() { bytes = 0; diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index 7cdcde1956..79d5ad8b82 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -8,6 +8,7 @@ #include #include #include +#include /** A hasher class for SHA-256. */ class CSHA256 @@ -26,7 +27,10 @@ public: //TODO: Midstate is a hack'ish speedup that probably should make way for something //akin to the SHA256D64 speedups void Midstate(unsigned char hash[OUTPUT_SIZE], uint64_t* len, unsigned char *buffer); + std::vector Save() const; + bool Load(const std::vector& vch); CSHA256& Reset(); + bool SafeWrite(const unsigned char* data, size_t len); }; /** Autodetect the best available SHA256 implementation. diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a13b796d79..03cbca8954 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1594,7 +1594,7 @@ bool EvalScript(std::vector >& stack, const CScript& return false; } - valtype vchHash(32); + valtype vchHash(CSHA256::OUTPUT_SIZE); CSHA256().Write(vchData.data(), vchData.size()).Finalize(vchHash.data()); uint256 hash(vchHash); @@ -1613,6 +1613,73 @@ bool EvalScript(std::vector >& stack, const CScript& } break; + case OP_SHA256INITIALIZE: // (in -- sha256_ctx) + { + // OP_SHA256INITIALIZE is only available in Tapscript + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + + if (stack.size() < 1) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + CSHA256 ctx; + valtype& vch = stacktop(-1); + if (!ctx.SafeWrite(vch.data(), vch.size())) + return set_error(serror, SCRIPT_ERR_SHA2_CONTEXT_WRITE); + + popstack(stack); + stack.push_back(ctx.Save()); + } + break; + + case OP_SHA256UPDATE: // (sha256_ctx in -- sha256_ctx) + { + // OP_SHA256UPDATE is only available in Tapscript + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + CSHA256 ctx; + valtype& vchCtx = stacktop(-2); + if (!ctx.Load(vchCtx)) + return set_error(serror, SCRIPT_ERR_SHA2_CONTEXT_LOAD); + + valtype& vch = stacktop(-1); + if (!ctx.SafeWrite(vch.data(), vch.size())) + return set_error(serror, SCRIPT_ERR_SHA2_CONTEXT_WRITE); + + popstack(stack); + popstack(stack); + stack.push_back(ctx.Save()); + } + break; + + case OP_SHA256FINALIZE: // (sha256_ctx in -- hash) + { + // OP_SHA256FINALIZE is only available in Tapscript + if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); + + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype& vchCtx = stacktop(-2); + CSHA256 ctx; + if (!ctx.Load(vchCtx)) + return set_error(serror, SCRIPT_ERR_SHA2_CONTEXT_LOAD); + + valtype& vch = stacktop(-1); + if (!ctx.SafeWrite(vch.data(), vch.size())) + return set_error(serror, SCRIPT_ERR_SHA2_CONTEXT_WRITE); + + valtype vchHash(CHash256::OUTPUT_SIZE); + ctx.Finalize(vchHash.data()); + + popstack(stack); + popstack(stack); + stack.push_back(std::move(vchHash)); + } + break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } diff --git a/src/script/script.cpp b/src/script/script.cpp index a6e68e3ed1..82bd5d010e 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -147,6 +147,10 @@ std::string GetOpName(opcodetype opcode) // Opcode added by BIP 342 (Tapscript) case OP_CHECKSIGADD : return "OP_CHECKSIGADD"; + // Elements Tapscript opcodes + case OP_SHA256INITIALIZE : return "OP_SHA256INITIALIZE"; + case OP_SHA256UPDATE : return "OP_SHA256UPDATE"; + case OP_SHA256FINALIZE : return "OP_SHA256FINALIZE"; case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: @@ -415,6 +419,7 @@ bool IsOpSuccess(const opcodetype& opcode) return opcode == 80 || opcode == 98 || (opcode >= 137 && opcode <= 138) || // ELEMENTS: Don't mark OP_INVERT , OP_AND, OP_OR, OP_XOR. OP_LSHIFT, OP_RSHIFT as success (opcode >= 141 && opcode <= 142) || (opcode >= 149 && opcode <= 151) || - // ELEMENTS: Exclude OP_DETERMINISTICRANDOM, OP_CHECKSIGFROMSTACK(VERIFY), OP_SUBSTRLAZY - (opcode >= 187 && opcode <= 191) || (opcode >= 196 && opcode <= 254); + // ELEMENTS: Exclude OP_DETERMINISTICRANDOM(192), OP_CHECKSIGFROMSTACK(VERIFY)(192-193), OP_SUBSTRLAZY(195) + // ELEMENTS: Tapscript extension from OP_SHA256INITIALIZE(196) till OP_TWEAKVERIFY(228) + (opcode >= 187 && opcode <= 191) || (opcode >= 229 && opcode <= 254); } diff --git a/src/script/script.h b/src/script/script.h index 89bcb90f0f..dff1bc527d 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -213,11 +213,16 @@ enum opcodetype // Opcode added by BIP 342 (Tapscript) OP_CHECKSIGADD = 0xba, + // Elements: Tapscript (Streaming sha2 opcodes) + OP_SHA256INITIALIZE = 0xc4, + OP_SHA256UPDATE = 0xc5, + OP_SHA256FINALIZE = 0xc6, + OP_INVALIDOPCODE = 0xff, }; // Maximum value that an opcode can be -static const unsigned int MAX_OPCODE = OP_SUBSTR_LAZY; // 0xc3 +static const unsigned int MAX_OPCODE = OP_SHA256FINALIZE; // 0xc6 std::string GetOpName(opcodetype opcode); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index fadc04262c..158c9796ee 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -115,6 +115,10 @@ std::string ScriptErrorString(const ScriptError serror) return "Using OP_CODESEPARATOR in non-witness script"; case SCRIPT_ERR_SIG_FINDANDDELETE: return "Signature is found in scriptCode"; + case SCRIPT_ERR_SHA2_CONTEXT_LOAD: + return "Invalid Sha256 context object read"; + case SCRIPT_ERR_SHA2_CONTEXT_WRITE: + return "Invalid Sha256 context object write"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 92681205e5..5e6d0f478d 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -86,7 +86,12 @@ typedef enum ScriptError_t // ELEMENTS: SCRIPT_ERR_RANGEPROOF, - SCRIPT_ERR_PEDERSEN_TALLY + SCRIPT_ERR_PEDERSEN_TALLY, + + + /* Elements: New tapscript related errors */ + SCRIPT_ERR_SHA2_CONTEXT_LOAD, + SCRIPT_ERR_SHA2_CONTEXT_WRITE, } ScriptError; #define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 5a6e45ad3d..0155761886 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1470,7 +1470,7 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps) BOOST_CHECK(script.HasValidOps()); script = ScriptFromHex("ff88ac"); // Script with OP_INVALIDOPCODE explicit BOOST_CHECK(!script.HasValidOps()); - script = ScriptFromHex("88acc4"); // Script with undefined opcode: one higher then MAX_OPCODE + script = ScriptFromHex("88ace5"); // Script with undefined opcode: one higher then MAX_OPCODE BOOST_CHECK(!script.HasValidOps()); }