Example #1
0
def test_der_size() -> None:

    sig8 = 1, 1
    sig72 = ec.n - 1, ec.n - 1
    sig71 = 2**255 - 1, ec.n - 1
    sig70 = 2**255 - 1, 2**255 - 1
    sig70b = 2**255 - 1, 2**248 - 1
    sig69 = 2**255 - 1, 2**247 - 1
    sig68 = 2**247 - 1, 2**247 - 1
    sigs = [sig8, sig72, sig71, sig70, sig70b, sig69, sig68]
    lenghts = [8, 72, 71, 70, 70, 69, 68]  # not including SIGHASHES

    for length, sig in zip(lenghts, sigs):
        for sighash in SIGHASHES:
            der_sig = serialize(*sig, sighash)
            r, s, sighash2 = deserialize(der_sig)
            assert sig == (r, s)
            assert sighash == sighash2
            assert len(der_sig) == length + 1

    # with the last one only...
    assert (r, s, sighash) == deserialize((r, s, sighash))
Example #2
0
    def test_pubkey_recovery(self):
        ec = secp112r2
        q = 0x10
        Q = mult(q, ec.G, ec)
        msg = 'Satoshi Nakamoto'
        k = sighash = None
        sig = dsa.sign(msg, q, k, ec)
        self.assertTrue(dsa.verify(msg, Q, sig, ec))
        dersig = der.serialize(*sig, sighash, ec)
        self.assertTrue(dsa.verify(msg, Q, dersig, ec))
        r, s, _ = der.deserialize(dersig)
        self.assertEqual((r, s), sig)

        keys = dsa.recover_pubkeys(msg, dersig, ec)
        self.assertEqual(len(keys), 4)
        self.assertIn(Q, keys)
        for Q in keys:
            self.assertTrue(dsa.verify(msg, Q, sig, ec))
Example #3
0
def test_der_deserialize() -> None:

    err_msg = "non-hexadecimal number found "
    with pytest.raises(ValueError, match=err_msg):
        deserialize("not a sig")

    sig = 2**255 - 1, 2**247 - 1
    for sighash in SIGHASHES:
        der_sig = serialize(*sig, sighash)
        r_size = der_sig[3]

        bad_der_sig = b"\x00" * 74
        err_msg = "invalid DER size: "
        with pytest.raises(ValueError, match=err_msg):
            deserialize(bad_der_sig)

        bad_der_sig = b"\x31" + der_sig[1:]
        err_msg = "DER type must be 0x30 "
        with pytest.raises(ValueError, match=err_msg):
            deserialize(bad_der_sig)

        bad_der_sig = der_sig[:1] + b"\x41" + der_sig[2:]
        err_msg = "Declared size incompatible with actual size: "
        with pytest.raises(ValueError, match=err_msg):
            deserialize(bad_der_sig)

        bad_der_sig = der_sig + b"\x01"
        err_msg = "Declared size incompatible with actual size: "
        with pytest.raises(ValueError, match=err_msg):
            deserialize(bad_der_sig)

        bad_der_sig = der_sig[:-1] + b"\x00"
        err_msg = "invalid sighash: 0x"
        with pytest.raises(ValueError, match=err_msg):
            deserialize(bad_der_sig)

        # r and s scalars
        for offset in (4, 6 + r_size):
            bad_der_sig = der_sig[:offset - 2] + b"\x00" + der_sig[offset - 1:]
            err_msg = "scalar must be an integer"
            with pytest.raises(ValueError, match=err_msg):
                deserialize(bad_der_sig)

            bad_der_sig = der_sig[:offset - 1] + b"\x00" + der_sig[offset:]
            err_msg = "scalar has size zero"
            with pytest.raises(ValueError, match=err_msg):
                deserialize(bad_der_sig)

            bad_der_sig = der_sig[:offset - 1] + b"\x80" + der_sig[offset:]
            err_msg = "Size of scalar is too large: "
            with pytest.raises(ValueError, match=err_msg):
                deserialize(bad_der_sig)

            bad_der_sig = der_sig[:offset] + b"\x80" + der_sig[offset + 1:]
            err_msg = "Negative number not allowed for scalar"
            with pytest.raises(ValueError, match=err_msg):
                deserialize(bad_der_sig)

            bad_der_sig = der_sig[:offset] + b"\x00\x7f" + der_sig[offset + 2:]
            err_msg = "invalid null bytes at the start of scalar"
            with pytest.raises(ValueError, match=err_msg):
                deserialize(bad_der_sig)

        data_size = der_sig[1]
        malleated_size = (data_size + 1).to_bytes(1, byteorder="big")
        bad_der_sig = der_sig[:1] + malleated_size + der_sig[2:] + b"\x01"
        err_msg = "Too big DER size for "
        with pytest.raises(ValueError, match=err_msg):
            deserialize(bad_der_sig)
Example #4
0
) == "04ae3d0d5c669ed364636e79e72abc012a33be63e537babddf56bfd393256acf6dba0fac21da6386513674573a2d7baff4375c9b6d2498383853c52f0565f97f1a"
app_wif = base58wif.wif_from_xprv(app_xprv)
app_address = slip32.address_from_xpub(app_xpub)
assert app_address == b'1J6674MtZBpfHytdNofLUX6sLHAUaG33uK'

msg = "hello world"
h = sha256(msg.encode())
h256 = h.digest()
assert h256.hex().upper(
) == "B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9", h256.hex(
).upper()

# hex-string
ledger_sig = bytes.fromhex(
    "3044022044487c80833b7025739f450751c1d6624118e32e5f922b5a40a407efb48382e202200f2b6e53448f8e219ee1c2f109fa5b0a2b8bae482a4a81cf8c54f8c168260886"
)

# from ledger signature style to (base64-encoded) compact signature standard
r, s, _ = der.deserialize(ledger_sig)
rec_flag = 27 + ledger_sig[0] - 44
print(rec_flag)
rec_flag = 27 + 4 + (ledger_sig[0] & 0x01)
print(rec_flag)
b64sig = btcmsg.serialize(rec_flag + 1, r, s)
btcmsg._verify(msg, app_address, b64sig)

# from (tuple) compact signature standard to ledger signature style
rf, r, s = btcmsg.sign(msg, app_wif, app_address)
btcmsg._verify(msg, app_address, (rf + 1, r, s))
ledger_sig_equivalent = der.serialize(r, s, None)
Example #5
0
    def test_ledger(self):
        """Hybrid ECDSA Bitcoin message signature generated by Ledger"""

        mnemonic = "barely sun snack this snack relief pipe attack disease boss enlist lawsuit"

        #### non-standard leading 31 in DER serialization
        derivation_path = "m/1"
        msg = b"\xfb\xa3\x1f\x8cd\x85\xe29#K\xb3{\xfd\xa7<?\x95oL\xee\x19\xb2'oh\xa7]\xd9A\xfeU\xd8"
        dersig = '3144022012ec0c174936c2a46dc657252340b2e6e6dd8c31dd059b6f9f33a90c21af2fba022030e6305b3ccf88009d419bf7651afcfcc0a30898b93ae9de9aa6ac03cf8ec56b'

        # pubkey derivation
        rprv = bip32.rootxprv_from_bip39mnemonic(mnemonic)
        xprv = bip32.derive(rprv, derivation_path)
        xpub = bip32.xpub_from_xprv(xprv)

        # the actual message being signed
        magic_msg = btcmsg._magic_hash(msg)

        # save key_id and patch dersig
        dersig = bytes.fromhex(dersig)
        key_id = dersig[0]
        dersig = b'\x30' + dersig[1:]

        r, s, sighash = der.deserialize(dersig)
        self.assertIsNone(sighash)

        # ECDSA signature verification of the patched dersig
        dsa._verify(magic_msg, xpub, dersig, ec, hf)
        self.assertTrue(dsa.verify(magic_msg, xpub, dersig))

        # compressed address
        addr = p2pkh(xpub)

        # equivalent Bitcoin Message Signature (non-serialized)
        rec_flag = 27 + 4 + (key_id & 0x01)
        btcmsgsig = (rec_flag, r, s)

        # Bitcoin Message Signature verification
        btcmsg._verify(msg, addr, btcmsgsig)
        self.assertTrue(btcmsg.verify(msg, addr, btcmsgsig))
        self.assertFalse(btcmsg.verify(magic_msg, addr, btcmsgsig))

        btcmsg.sign(msg, xprv)

        #### standard leading 30 in DER serialization
        derivation_path = 'm/0/0'
        msg = "hello world"
        dersig = "3045022100967dac3262b4686e89638c8219c5761017f05cd87a855edf034f4a3ec6b59d3d0220108a4ef9682b71a45979d8c75c393382d9ccb8eb561d73b8c5fc0b87a47e7d27"

        # pubkey derivation
        rprv = bip32.rootxprv_from_bip39mnemonic(mnemonic)
        xprv = bip32.derive(rprv, derivation_path)
        xpub = bip32.xpub_from_xprv(xprv)

        # the actual message being signed
        magic_msg = btcmsg._magic_hash(msg)

        # save key_id and patch dersig
        dersig = bytes.fromhex(dersig)
        key_id = dersig[0]
        dersig = b'\x30' + dersig[1:]

        r, s, sighash = der.deserialize(dersig)
        self.assertIsNone(sighash)

        # ECDSA signature verification of the patched dersig
        dsa._verify(magic_msg, xpub, dersig, ec, hf)
        self.assertTrue(dsa.verify(magic_msg, xpub, dersig))

        # compressed address
        addr = p2pkh(xpub)

        # equivalent Bitcoin Message Signature (non-serialized)
        rec_flag = 27 + 4 + (key_id & 0x01)
        btcmsgsig = (rec_flag, r, s)

        # Bitcoin Message Signature verification
        btcmsg._verify(msg, addr, btcmsgsig)
        self.assertTrue(btcmsg.verify(msg, addr, btcmsgsig))
        self.assertFalse(btcmsg.verify(magic_msg, addr, btcmsgsig))
Example #6
0
    def test_der(self):

        sighash_all = b'\x01'

        sig73  =   ec.n - 1,   ec.n - 1
        sig72  = 2**255 - 1,   ec.n - 1
        sig71  = 2**255 - 1, 2**255 - 1
        sig71b = 2**255 - 1, 2**248 - 1
        sig70  = 2**255 - 1, 2**247 - 1
        sig69  = 2**247 - 1, 2**247 - 1
        sig9   =          1,          1
        sigs = [sig73, sig72, sig71, sig71b, sig70, sig69]
        lenghts = [73, 72, 71, 71, 70, 69, 9]

        for lenght, sig in zip(lenghts, sigs):
            dersig = serialize(*sig, sighash_all)
            r, s, sighash_all2 = deserialize(dersig)
            self.assertEqual(sig, (r, s))
            self.assertEqual(sighash_all, sighash_all2)
            self.assertEqual(len(dersig), lenght)
            # without sighash
            r, s, sighash_all2 = deserialize(dersig[:-1])
            self.assertEqual(sig, (r, s))
            self.assertIsNone(sighash_all2)

        # with the last one

        # DER signature size should be in [9, 73]
        dersig2 = dersig + b'\x00' * 70
        self.assertRaises(ValueError, deserialize, dersig2)

        # DER signature must be of type 0x30 (compound)
        dersig2 = b'\x00' + dersig[1:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Declared signature size does not match with size
        dersig2 = dersig[:1] + b'\x00' + dersig[2:]
        self.assertRaises(ValueError, deserialize, dersig2)

        Rsize = dersig[3]
        # Zero-size integers are not allowed for r
        dersig2 = dersig[:3] + b'\x00' + dersig[4:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Length of the s scalar must be inside the signature
        dersig2 = dersig[:3] + b'\x80' + dersig[4:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Zero-size integers are not allowed for s
        dersig2 = dersig[:Rsize+5] + b'\x00' + dersig[Rsize+6:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Signature size does not match with scalars
        dersig2 = dersig[:Rsize+5] + b'\x4f' + dersig[Rsize+6:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # r scalar must be an integer
        dersig2 = dersig[:2] + b'\x00' + dersig[3:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Negative numbers are not allowed for r
        dersig2 = dersig[:4] + b'\x80' + dersig[5:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Invalid null bytes at the start of r
        dersig2 = dersig[:4] + b'\x00\x00' + dersig[6:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # s scalar must be an integer
        dersig2 = dersig[:Rsize+4] + b'\x00' + dersig[Rsize+5:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Negative numbers are not allowed for s
        dersig2 = dersig[:Rsize+6] + b'\x80' + dersig[Rsize+7:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # Invalid null bytes at the start of s
        dersig2 = dersig[:Rsize+6] + b'\x00\x00' + dersig[Rsize+8:]
        self.assertRaises(ValueError, deserialize, dersig2)

        # sighash size > 1
        self.assertRaises(ValueError, serialize, *sig, sighash_all + b'\x01')

        # negative signature scalar
        sig2 = -1, sig[1]
        self.assertRaises(ValueError, serialize, *sig2, sighash_all)

        # Invalid sighash type b'\x00'
        self.assertRaises(ValueError, deserialize, dersig[:-1] + b'\x00')