示例#1
0
def op_checksig(stack, message_hash):
    """
    해제 스크립트가 올바른지 검증하는 부분
    - 올바른 경우, 스택에 1추가
    - 올바르지 않은 경우, 스택에 0추가
    :param stack: 명령어 및 원소 스택
    :param message_hash: 메시지 해시
    :return: 실행 결과 boolean
    """
    # 필요한 원소는 2개
    # : 서명(der 직렬화)과 공개키(sec 직렬화)
    if len(stack) < 2:
        return False

    pubkey_sec = stack.pop()
    signature_der = stack.pop()[:-1]

    try:
        pubkey = S256Point.parse(pubkey_sec)
        signature = Signature.parse(signature_der)
    except (ValueError, SyntaxError) as e:
        return False

    # 서명 검증
    if pubkey.verify(z=message_hash, signature=signature):
        stack.append(encode_num(1))
    else:
        stack.append(encode_num(0))

    return True
def exercise4():
    from io import BytesIO
    from ecc.secp256k1 import S256Point, Signature
    from ecc.utils import encode_varint, hash256, int_to_little_endian
    from ecc.transaction import Tx, SIGHASH_ALL

    # 문제 : 주어진 p2sh 다중 서명 스크립트와 서명이 주어질 때, 해당 서명 검증
    hex_tx = '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c568700000000'
    hex_sec = '03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb71'
    hex_der = '3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e754022'
    hex_redeem_script = '475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152ae'
    sec = bytes.fromhex(hex_sec)
    der = bytes.fromhex(hex_der)

    # 해답

    # sec 공개키와 der 서명을 통해 해당 객체 생성
    point = S256Point.parse(sec)
    signature = Signature.parse(der)

    redeem_script = Script.parse(BytesIO(bytes.fromhex(hex_redeem_script)))
    stream = BytesIO(bytes.fromhex(hex_tx))

    tx = Tx.parse(stream)

    # 두번째 서명을 만들기 위한 트랜잭션의 해시 계산
    sig = int_to_little_endian(tx.version, 4)

    # 이전 트랜잭션 정보 : 이전 트랜잭션 개수 및 이전 트랜잭션들의 정보
    sig += encode_varint(len(tx.tx_inputs))

    # 서명 : 트랜잭션 입력의 해제 스크립트를 리딤 스크립트로 대체
    cur_input = tx.tx_inputs[0]
    tx_in_copy = TxIn(prev_tx=cur_input.prev_tx,
                      prev_index=cur_input.prev_index,
                      script_sig=redeem_script,
                      sequence=cur_input.sequence)
    sig += tx_in_copy.serialize()

    # 트랜잭션 출력 : 출력 개수 및 출력들의 정보
    sig += encode_varint(len(tx.tx_outputs))
    for tx_output in tx.tx_outputs:
        sig += tx_output.serialize()
    # 록타임 : 4바이트의 리틀엔디언
    sig += int_to_little_endian(tx.locktime, 4)

    # 해시 유형 덧붙임 : SIGHASH_ALL 사용함
    sig += int_to_little_endian(SIGHASH_ALL, 4)

    # 서명 해시 생성
    result256 = hash256(sig)
    sig_message_hash = int.from_bytes(result256, "big")

    print(point.verify(sig_message_hash, signature))
示例#3
0
def example2():
    print("Example 2 : 서명 검증")
    from ecc.secp256k1 import S256Point, Signature
    sec = bytes.fromhex(
        '0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a')
    der = bytes.fromhex(
        '3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed'
    )
    z = 0x27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6
    point = S256Point.parse(sec)
    signature = Signature.parse(der)
    print(point.verify(z, signature))
def example2():
    from ecc.secp256k1 import S256Point, Signature
    from ecc.utils import hash256

    modified_tx = bytes.fromhex(
        '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c56870000000001000000'
    )
    h256 = hash256(modified_tx)

    z = int.from_bytes(h256, 'big')

    # sec 공개키는 리딤 스크립트에서 구할 수 있음
    sec = bytes.fromhex(
        '022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb70')
    point = S256Point.parse(sec)

    # der 서명은 해제 스크립트에서 구할 수 있음
    der = bytes.fromhex(
        '3045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a89937'
    )
    sig = Signature.parse(der)

    print(point.verify(z, sig))
示例#5
0
def op_checkmultisig(stack, message_hash):
    """
    m-of-n 다중 서명을 검증하는 명령어
    - m-of-n 다중 서명
        - n개의 공개키가 잠금 스크립트에 들어 있음
        - 해당 잠금 스크립트를 해제하기 위해서는 최소 m개의 비밀키를 활용해야 하는 서명
        - n개의 공개키 중에서 서로 다른 m개의 비밀키로 m개의 서명을 검증
    - 스택의 구성 (top에서부터 시작)
        - 공개키 정보
            - 공개키의 개수 n
            - n개의 공개키
        - 서명 정보
            - 비밀키의 개수 m
            - m개의 서명
        => (1+n) + (1+m) = m+n+2 개의 원소 필요
    - Off-by-One 버그
        - 해당 명령어는 m+n+3개의 원소를 가져옴
        - 추가로 가져오는 1개의 원소는 아무런 동작도 하지 않음
        - 현재 스택의 크기가 m+n+2개인 경우에 검증에 실패하는 버그
            => 원소가 모자르지 않도록 원소 1개를 미리 추가한 stack이 매개변수로 들어옴
    :param stack: 명령어 및 원소 스택
    :param message_hash: 메시지 해시
    :return: 실행 결과 boolean
    """

    if len(stack) < 1:
        return False

    # 공개키 개수
    n = decode_num(stack.pop())

    # 공개키가 모두 저장되어있는지 확인
    if len(stack) <= n:
        return False

    # 공개키 저장
    sec_pubkeys = []
    for _ in range(n):
        sec_pubkeys.append(stack.pop())

    # 비밀키 개수
    m = decode_num(stack.pop())

    # 비밀키가 모두 저장되어있는지 확인
    if len(stack) <= m:
        return False

    # 비밀키에 해당하는 서명 저장
    der_signatures = []
    for _ in range(m):
        # 모든 der 서명은 SIGHASH_ALL로 서명된 것으로 간주
        der_signatures.append(stack.pop()[:-1])

    # Off-by-One 버그 => 미리 추가된 의미없는 원소를 삭제
    stack.pop()

    try:
        # 공개키 정보를 통해 공개키 객체(S256Point) 리스트 생성
        public_points = [
            S256Point.parse(sec_pubkey) for sec_pubkey in sec_pubkeys
        ]

        # 비밀키에 대한 서명 정보를 통해 서명 객체(Signature) 리스트 생성
        signatures = [
            Signature.parse(der_signature) for der_signature in der_signatures
        ]

        # 비밀키에 대한 서명 정보를 이용하여 공개키 검증
        for sig in signatures:
            # 검증을 해야하는 공개키가 없는 경우에는 검증 불가
            if len(public_points) == 0:
                return False

            # 검증을 해야하는 공개키들에 대해서 검증
            while public_points:
                public_point = public_points.pop(0)
                # 검증이 된다면 참
                if public_point.verify(message_hash, sig):
                    break

        # 검증이 되면 1을 추가한다
        stack.append(encode_num(1))
    except (ValueError, SyntaxError):
        return False

    return True