Skip to content

Instantly share code, notes, and snippets.

@real-or-random
Created August 12, 2025 07:37
Show Gist options
  • Select an option

  • Save real-or-random/a4aaae5d04eee9f63e7a2e43d25bc2b1 to your computer and use it in GitHub Desktop.

Select an option

Save real-or-random/a4aaae5d04eee9f63e7a2e43d25bc2b1 to your computer and use it in GitHub Desktop.
# MIT License
#
# Copyright (c) Tim Ruffing
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# /// script
# dependencies = [
# "sha256",
# ]
# ///
import sha256
from copy import copy
import hashlib
import sys
import argparse
def char_init(b):
if b == b"":
b = b"\0"
return "{0x" + b.hex("_").replace("_", ", 0x") + "}"
def uint32_t_init(b):
return "{0x" + b.hex("_", 4).replace("_", ", 0x") + "}"
def secp256k1_sha256_def(msg):
hasher = sha256.sha256()
hasher.update(msg)
assert copy(hasher).digest() == hashlib.sha256(msg).digest()
internal_state = hasher.state[0]
unprocessed_msg = msg[-(len(msg) % hashlib.sha256().block_size) or len(msg):]
byte_counter = hasher.state[1]
assert byte_counter == len(msg)
ret = "secp256k1_sha256 midstate = {\n"
ret += f" {uint32_t_init(internal_state)},\n"
ret += f" {char_init(unprocessed_msg)}, {hex(byte_counter)}\n"
ret += "}\n"
return ret
def bip340_prefix(tag):
half = hashlib.sha256(tag).digest()
return 2 * half
def main():
parser = argparse.ArgumentParser(description="Compute SHA256 midstate for a message or BIP340 tag prefix.")
parser.add_argument("message", nargs="?", help="Message to hash")
parser.add_argument("-t", "--tag", help="BIP340 tag to use as prefix")
args = parser.parse_args()
if args.tag:
tag = args.tag.encode()
print(f"/* Midstate after processing BIP340 tag {tag} */")
prefix = bip340_prefix(tag)
print(secp256k1_sha256_def(prefix))
elif args.message:
msg = args.message.encode()
print(f"/* Midstate after processing {msg} */")
print(secp256k1_sha256_def(msg))
else:
print("Usage: python sha256_midstate.py <message> [-t TAG]")
sys.exit(1)
if __name__ == "__main__":
main()
@real-or-random
Copy link
Author

If you have uv installed, you can run this without installing dependencies using uv run sha256_midstate.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment