Exemple #1
0
 def test_sign_schnorr(self):
     pk = PrivateKey(randint(1, N))
     msg = int_to_big_endian(randint(1, N), 32)
     sig = pk.sign_schnorr(msg, aux=b"\x00" * 32)
     self.assertTrue(pk.point.verify_schnorr(msg, sig))
     # tweak
     tweak = randint(1, N)
     tweak_point = pk.tweaked(tweak).point
     k = randint(1, N)
     r = k * G
     if r.parity:
         k = N - k
         r = k * G
     message = r.bip340() + tweak_point.bip340() + msg
     challenge = big_endian_to_int(hash_challenge(message)) % N
     if pk.point.parity == tweak_point.parity:
         secret = pk.secret
     else:
         secret = -pk.secret
     s = (k + challenge * secret) % N
     if tweak_point.parity:
         s = (s - challenge * tweak) % N
     else:
         s = (s + challenge * tweak) % N
     sig = SchnorrSignature.parse(r.bip340() + int_to_big_endian(s, 32))
     self.assertTrue(tweak_point.verify_schnorr(msg, sig))
Exemple #2
0
 def sec(self, compressed=True):
     # returns the binary version of the sec format, NOT hex
     # if compressed, starts with b'\x02' if self.y.num is even, b'\x03' if self.y is odd
     # then self.x.num
     # remember, you have to convert self.x.num/self.y.num to binary using int_to_big_endian
     x = int_to_big_endian(self.x.num, 32)
     if compressed:
         if self.parity:
             return b"\x03" + x
         else:
             return b"\x02" + x
     else:
         # if non-compressed, starts with b'\x04' followod by self.x and then self.y
         y = int_to_big_endian(self.y.num, 32)
         return b"\x04" + x + y
Exemple #3
0
def decode_bech32(s):
    """Returns network, segwit version and the hash from the bech32 address"""
    regtest_prefix = PREFIX["regtest"]
    if s.startswith(regtest_prefix):
        hrp, raw_data = regtest_prefix, s[5:]
    else:
        hrp, raw_data = s.split("1")

    network = NET_FOR_PREFIX.get(hrp)
    if not network:
        raise ValueError(f"unknown human readable part: {hrp}")

    data = [BECH32_ALPHABET.index(c) for c in raw_data]
    version = data[0]
    verify_fnc = bech32_verify_checksum if version == 0 else bech32m_verify_checksum
    if not verify_fnc(hrp, data):
        raise ValueError(f"bad address: {s}")
    number = 0
    for digit in data[1:-6]:
        number = (number << 5) + digit
    num_bytes = (len(data) - 7) * 5 // 8
    bits_to_ignore = (len(data) - 7) * 5 % 8
    number >>= bits_to_ignore
    hash = int_to_big_endian(number, num_bytes)
    if num_bytes < 2 or num_bytes > 40:
        raise ValueError(f"bytes out of range: {num_bytes}")
    return [network, version, hash]
def secure_mnemonic(num_bits=256, extra_entropy=0):
    """
    Generates a mnemonic phrase using num_bits of entropy
    extra_entropy is optional and should not be saved as it is NOT SUFFICIENT to recover your mnemonic.
    extra_entropy exists only to prevent 100% reliance on your random number generator.
    """
    if num_bits not in (128, 160, 192, 224, 256):
        raise ValueError(f"Invalid num_bits: {num_bits}")
    if type(extra_entropy) is not int:
        raise TypeError(f"extra_entropy must be an int: {extra_entropy}")
    if extra_entropy < 0:
        raise ValueError(f"extra_entropy cannot be negative: {extra_entropy}")

    # if we have more bits than needed, mask so we get what we need
    if len(bin(extra_entropy)) > num_bits + 2:
        extra_entropy &= (1 << num_bits) - 1

    # For added paranoia, xor current epoch to extra_entropy
    # Would use time.time_ns() but that requires python3.7
    extra_entropy ^= int(time() * 1_000_000)

    # xor some random bits with the extra_entropy that was passed in
    preseed = randbits(num_bits) ^ extra_entropy
    # convert the number to big-endian
    s = int_to_big_endian(preseed, num_bits // 8)
    # convert to mnemonic
    mnemonic = bytes_to_mnemonic(s, num_bits)
    # sanity check
    if mnemonic_to_bytes(mnemonic) != s:
        raise RuntimeError("Generated mnemonic does not correspond to random bits")
    return mnemonic
Exemple #5
0
 def sign(self, z):
     # per libsecp256k1 documentation, this helps against side-channel attacks
     if not lib.secp256k1_context_randomize(
             GLOBAL_CTX,
             secrets.token_bytes(32),
     ):
         raise RuntimeError("libsecp256k1 context randomization error")
     secret = int_to_big_endian(self.secret, 32)
     msg = int_to_big_endian(z, 32)
     csig = ffi.new("secp256k1_ecdsa_signature *")
     if not lib.secp256k1_ecdsa_sign(GLOBAL_CTX, csig, msg, secret,
                                     ffi.NULL, ffi.NULL):
         raise RuntimeError("libsecp256k1 ecdsa signing problem")
     sig = Signature(c=csig)
     if not self.point.verify(z, sig):
         raise RuntimeError("generated signature doesn't verify")
     return sig
Exemple #6
0
 def deterministic_k(self, z):
     k = b"\x00" * 32
     v = b"\x01" * 32
     if z > N:
         z -= N
     z_bytes = int_to_big_endian(z, 32)
     secret_bytes = int_to_big_endian(self.secret, 32)
     s256 = hashlib.sha256
     k = hmac.new(k, v + b"\x00" + secret_bytes + z_bytes, s256).digest()
     v = hmac.new(k, v, s256).digest()
     k = hmac.new(k, v + b"\x01" + secret_bytes + z_bytes, s256).digest()
     v = hmac.new(k, v, s256).digest()
     while True:
         v = hmac.new(k, v, s256).digest()
         candidate = big_endian_to_int(v)
         if candidate >= 1 and candidate < N:
             return candidate
         k = hmac.new(k, v + b"\x00", s256).digest()
         v = hmac.new(k, v, s256).digest()
Exemple #7
0
 def der(self):
     # convert the r part to bytes
     rbin = int_to_big_endian(self.r, 32)
     # if rbin has a high bit, add a 00
     if rbin[0] >= 128:
         rbin = b"\x00" + rbin
     while rbin[0] == 0:
         if rbin[1] >= 128:
             break
         else:
             rbin = rbin[1:]
     result = bytes([2, len(rbin)]) + rbin
     sbin = int_to_big_endian(self.s, 32)
     # if sbin has a high bit, add a 00
     if sbin[0] >= 128:
         sbin = b"\x00" + sbin
     while sbin[0] == 0:
         if sbin[1] >= 128:
             break
         else:
             sbin = sbin[1:]
     result += bytes([2, len(sbin)]) + sbin
     return bytes([0x30, len(result)]) + result
Exemple #8
0
 def wif(self, compressed=True):
     # convert the secret from integer to a 32-bytes in big endian using int_to_big_endian(x, 32)
     secret_bytes = int_to_big_endian(self.secret, 32)
     # prepend b'\xef' on testnet/signet, b'\x80' on mainnet
     if self.network == "mainnet":
         prefix = b"\x80"
     else:
         prefix = b"\xef"
     # append b'\x01' if compressed
     if compressed:
         suffix = b"\x01"
     else:
         suffix = b""
     # encode_base58_checksum the whole thing
     return encode_base58_checksum(prefix + secret_bytes + suffix)
Exemple #9
0
 def __rmul__(self, coefficient):
     coef = coefficient % N
     new_key = ffi.new("secp256k1_pubkey *")
     s = self.sec(compressed=False)
     if not lib.secp256k1_ec_pubkey_parse(GLOBAL_CTX, new_key, s, len(s)):
         raise RuntimeError("libsecp256k1 parse error")
     if not lib.secp256k1_ec_pubkey_tweak_mul(GLOBAL_CTX, new_key,
                                              int_to_big_endian(coef, 32)):
         raise RuntimeError("libsecp256k1 multiplication error")
     serialized = ffi.new("unsigned char [65]")
     output_len = ffi.new("size_t *", 65)
     if not lib.secp256k1_ec_pubkey_serialize(
             GLOBAL_CTX, serialized, output_len, new_key,
             lib.SECP256K1_EC_UNCOMPRESSED):
         raise RuntimeError("libsecp256k1 serialization error")
     return self.__class__(usec=bytes(serialized))
Exemple #10
0
 def get_signature(self, s_sum, r, sig_hash, tweak=0):
     tweak_point = self.get_tweak_point(tweak)
     if tweak:
         msg = r.bip340() + tweak_point.bip340() + sig_hash
         challenge = big_endian_to_int(hash_challenge(msg)) % N
         if tweak_point.parity:
             s = (-s_sum - challenge * tweak) % N
         else:
             s = (s_sum + challenge * tweak) % N
     else:
         s = s_sum % N
     s_raw = int_to_big_endian(s, 32)
     sig = r.bip340() + s_raw
     schnorrsig = SchnorrSignature.parse(sig)
     if not tweak_point.verify_schnorr(sig_hash, schnorrsig):
         raise ValueError("Invalid signature")
     return schnorrsig
Exemple #11
0
 def __add__(self, scalar):
     """Multiplies scalar by generator, adds result to current point"""
     coef = scalar % N
     new_key = ffi.new("secp256k1_pubkey *")
     s = self.sec(compressed=False)
     if not lib.secp256k1_ec_pubkey_parse(GLOBAL_CTX, new_key, s, len(s)):
         raise RuntimeError("libsecp256k1 parse error")
     if not lib.secp256k1_ec_pubkey_tweak_add(GLOBAL_CTX, new_key,
                                              int_to_big_endian(coef, 32)):
         raise RuntimeError("libsecp256k1 add error")
     serialized = ffi.new("unsigned char [65]")
     output_len = ffi.new("size_t *", 65)
     if not lib.secp256k1_ec_pubkey_serialize(
             GLOBAL_CTX, serialized, output_len, new_key,
             lib.SECP256K1_EC_UNCOMPRESSED):
         raise RuntimeError("libsecp256k1 serialize error")
     return self.__class__(usec=bytes(serialized))
Exemple #12
0
 def _crypt(cls, payload, id, exponent, passphrase, indices):
     if len(payload) % 2:
         raise ValueError("payload should be an even number of bytes")
     else:
         half = len(payload) // 2
     left = payload[:half]
     right = payload[half:]
     salt = b"shamir" + int_to_big_endian(id, 2)
     for i in indices:
         f = pbkdf2_hmac(
             "sha256",
             i + passphrase,
             salt + right,
             2500 << exponent,
             dklen=half,
         )
         left, right = right, bytes(x ^ y for x, y in zip(left, f))
     return right + left
Exemple #13
0
 def __init__(
     self,
     share_bit_length,
     id,
     exponent,
     group_index,
     group_threshold,
     group_count,
     member_index,
     member_threshold,
     value,
 ):
     self.share_bit_length = share_bit_length
     self.id = id
     self.exponent = exponent
     self.group_index = group_index
     if group_index < 0 or group_index > 15:
         raise ValueError(
             f"Group index should be between 0 and 15 inclusive {group_index}"
         )
     self.group_threshold = group_threshold
     if group_threshold < 1 or group_threshold > group_count:
         raise ValueError(
             f"Group threshold should be between 1 and {group_count} inclusive {group_threshold}"
         )
     self.group_count = group_count
     if group_count < 1 or group_count > 16:
         raise ValueError(
             f"Group count should be between 1 and 16 inclusive {group_count}"
         )
     self.member_index = member_index
     if member_index < 0 or member_index > 15:
         raise ValueError(
             f"Member index should be between 0 and 15 inclusive {member_index}"
         )
     self.member_threshold = member_threshold
     if member_threshold < 1 or member_threshold > 16:
         raise ValueError(
             f"Member threshold should be between 1 and 16 inclusive {member_threshold}"
         )
     self.value = value
     self.bytes = int_to_big_endian(value, share_bit_length // 8)
Exemple #14
0
 def sign_schnorr(self, msg, aux):
     if len(msg) != 32:
         raise ValueError("msg needs to be 32 bytes")
     if len(aux) != 32:
         raise ValueError("aux needs to be 32 bytes")
     # per libsecp256k1 documentation, this helps against side-channel attacks
     if not lib.secp256k1_context_randomize(
             GLOBAL_CTX,
             secrets.token_bytes(32),
     ):
         raise RuntimeError("libsecp256k1 context randomization error")
     keypair = ffi.new("secp256k1_keypair *")
     if not lib.secp256k1_keypair_create(
             GLOBAL_CTX, keypair, int_to_big_endian(self.secret, 32)):
         raise RuntimeError("libsecp256k1 keypair creation problem")
     raw_sig = ffi.new("unsigned char [64]")
     if not lib.secp256k1_schnorrsig_sign(GLOBAL_CTX, raw_sig, msg, keypair,
                                          aux):
         raise RuntimeError("libsecp256k1 schnorr signing problem")
     return SchnorrSignature(bytes(ffi.buffer(raw_sig, 64)))
Exemple #15
0
 def sign_schnorr(self, msg, aux):
     if self.point.parity:
         d = N - self.secret
     else:
         d = self.secret
     if len(msg) != 32:
         raise ValueError("msg needs to be 32 bytes")
     if len(aux) != 32:
         raise ValueError("aux needs to be 32 bytes")
     t = xor_bytes(int_to_big_endian(d, 32), hash_aux(aux))
     k = big_endian_to_int(hash_nonce(t + self.point.bip340() + msg)) % N
     r = k * G
     if r.parity:
         k = N - k
         r = k * G
     message = r.bip340() + self.point.bip340() + msg
     e = big_endian_to_int(hash_challenge(message)) % N
     s = (k + e * d) % N
     sig = SchnorrSignature(r, s)
     if not self.point.verify_schnorr(msg, sig):
         raise RuntimeError("Bad Signature")
     return sig
def mnemonic_to_bytes(mnemonic):
    """returns a byte representation of the mnemonic"""
    all_bits = 0
    words = mnemonic.split()
    # check that there are 12, 15, 18, 21 or 24 words
    # if not, raise a ValueError
    if len(words) not in (12, 15, 18, 21, 24):
        raise InvalidBIP39Length(
            f"{len(words)} words (you need 12, 15, 18, 21, or 24 words)"
        )
    num_words = len(words)
    for word in words:
        all_bits <<= 11
        all_bits += BIP39[word]
    num_checksum_bits = num_words // 3
    checksum = all_bits & ((1 << num_checksum_bits) - 1)
    all_bits >>= num_checksum_bits
    num_bytes = (num_words * 11 - num_checksum_bits) // 8
    s = int_to_big_endian(all_bits, num_bytes)
    computed_checksum = sha256(s)[0] >> (8 - num_checksum_bits)
    if checksum != computed_checksum:
        raise InvalidChecksumWordsError("Checksum is wrong")
    return s
Exemple #17
0
 def __init__(self, shares):
     self.shares = shares
     if len(shares) > 1:
         # check that the identifiers are the same
         ids = {s.id for s in shares}
         if len(ids) != 1:
             raise TypeError("Shares are from different secrets")
         # check that the exponents are the same
         exponents = {s.exponent for s in shares}
         if len(exponents) != 1:
             raise TypeError(
                 f"Shares should have the same exponent {exponents} {shares}"
             )
         # check that the k-of-n is the same
         k = {s.group_threshold for s in shares}
         if len(k) != 1:
             raise ValueError(f"K of K-of-N should be the same {k}")
         n = {s.group_count for s in shares}
         if len(n) != 1:
             raise ValueError(f"N of K-of-N should be the same {n}")
         if k.pop() > n.pop():
             raise ValueError("K > N in K-of-N")
         # check that the share lengths are the same
         lengths = {s.share_bit_length for s in shares}
         if len(lengths) != 1:
             raise ValueError(
                 f"all shares should have the same length {lengths}")
         # check that the x coordinates are unique
         xs = {(s.group_index, s.member_index) for s in shares}
         if len(xs) != len(shares):
             raise ValueError(f"Share indices should be unique {xs}")
     self.id = shares[0].id
     self.salt = b"shamir" + int_to_big_endian(self.id, 2)
     self.exponent = shares[0].exponent
     self.group_threshold = shares[0].group_threshold
     self.group_count = shares[0].group_count
     self.share_bit_length = shares[0].share_bit_length
Exemple #18
0
 def bip340(self):
     # returns the binary version of BIP340 pubkey
     if self.x is None:
         return int_to_big_endian(0, 32)
     return int_to_big_endian(self.x.num, 32)
Exemple #19
0
 def verify(self, z, sig):
     msg = int_to_big_endian(z, 32)
     sig_data = sig.cdata()
     return lib.secp256k1_ecdsa_verify(GLOBAL_CTX, sig_data, msg, self.c)
Exemple #20
0
 def serialize(self):
     return self.r.bip340() + int_to_big_endian(self.s, 32)