def __init__(self, u=None, priv=None, P=None, P2=None, s=None, e=None, used=False): #This class allows storing of utxo in format "txid:n" only for #convenience of storage/access; it doesn't check or use the data. #Arguments must be provided in hex. self.u = u if not priv: if P: #Construct a pubkey from raw hex self.P = secp256k1.PublicKey(safe_from_hex(P), raw=True, ctx=ctx) else: self.P = None else: if P: raise PoDLEError("Pubkey should not be provided with privkey") #any other formatting abnormality will just throw in PrivateKey if len(priv) == 66 and priv[-2:] == '01': priv = priv[:-2] self.priv = secp256k1.PrivateKey(safe_from_hex(priv), ctx=ctx) self.P = self.priv.pubkey if P2: self.P2 = secp256k1.PublicKey(safe_from_hex(P2), raw=True, ctx=ctx) else: self.P2 = None #These sig values should be passed in hex. if s: self.s = safe_from_hex(s) if e: self.e = safe_from_hex(e) #Optionally maintain usage state (boolean) self.used = used #the H(P2) value self.commitment = None
def verify_message(message, signature, hashfn=hashlib.sha256): if not isinstance(message, bytes_types): message = py23_bytes(message, "utf-8") if not isinstance(signature, bytes_types): signature = py23_bytes(signature, "utf-8") if not isinstance(message, bytes_types): raise AssertionError() if not isinstance(signature, bytes_types): raise AssertionError() digest = hashfn(message).digest() sig = signature[1:] recoverParameter = bytearray( signature)[0] - 4 - 27 # recover parameter only if SECP256K1_MODULE == "secp256k1": ALL_FLAGS = secp256k1.lib.SECP256K1_CONTEXT_VERIFY | secp256k1.lib.SECP256K1_CONTEXT_SIGN # Placeholder pub = secp256k1.PublicKey(flags=ALL_FLAGS) # Recover raw signature sig = pub.ecdsa_recoverable_deserialize(sig, recoverParameter) # Recover PublicKey verifyPub = secp256k1.PublicKey(pub.ecdsa_recover(message, sig)) # Convert recoverable sig to normal sig normalSig = verifyPub.ecdsa_recoverable_convert(sig) # Verify verifyPub.ecdsa_verify(message, normalSig) phex = verifyPub.serialize(compressed=True) elif SECP256K1_MODULE == "cryptography": p = recover_public_key(digest, sig, recoverParameter, message) order = ecdsa.SECP256k1.order r, s = ecdsa.util.sigdecode_string(sig, order) sigder = encode_dss_signature(r, s) p.verify(sigder, message, ec.ECDSA(hashes.SHA256())) phex = compressedPubkey(p) else: p = recover_public_key(digest, sig, recoverParameter) # Will throw an exception of not valid p.verify_digest(sig, digest, sigdecode=ecdsa.util.sigdecode_string) phex = compressedPubkey(p) return phex
def _public_key_bytes_to_object(public_key): """ Public key bytes to object. Args: public_key (bytes): secp256k1 public key in bytes Returns: public key object """ return Secp256k1PublicKey( secp256k1.PublicKey(public_key, raw=True, ctx=__CTX__))
def __init__(self, pubkey=None, raw=False, flags=None, ctx=None): if USE_SECP: if flags == None: flags = secp256k1.FLAG_VERIFY self.obj = secp256k1.PublicKey(pubkey, raw, flags, ctx) else: if not raw: raise Exception("Non raw init unsupported") pubkey = pubkey[1:] x = int.from_bytes(pubkey[0:32], 'big') y = int.from_bytes(pubkey[32:], 'big') self.obj = ECPublicKey(Point(x, y, CURVE_SECP256K1))
def convert_azure_secp256k1_signature_to_vrs(pub_key_bytes, msg_hash_bytes, sig_bytes, chain_id=0): sig_bytes = bytes(make_canonical(sig_bytes)) # Check the signature is still valid ecdsa_pubkey = secp256k1.PublicKey(pubkey=pub_key_bytes, raw=True) if len(sig_bytes) < 64: return 0, 0, 0, False sig_ser = ecdsa_pubkey.ecdsa_deserialize_compact(sig_bytes) verified_ecdsa = ecdsa_pubkey.ecdsa_verify(msg_hash_bytes, sig_ser, raw=True) v = -1 unrelated = MyECDSA() for i in range(0, 2): recsig = unrelated.ecdsa_recoverable_deserialize(sig_bytes, i) pubkey_recovered = unrelated.ecdsa_recover(msg_hash_bytes, recsig, raw=True) pubser = secp256k1.PublicKey(pubkey_recovered).serialize( compressed=False) if pubser == pub_key_bytes: v = i break assert v == 0 or v == 1 v += 27 if chain_id > 0: v += chain_id * 2 + 8 r = sig_bytes[0:32] s = sig_bytes[32:64] v = v return v, int_from_bytes(r), int_from_bytes(s), True
def verify_deprecated(self, message): pub = secp256k1.PublicKey(self.pub_key, raw=True) message = VarInt(len(message)).encode() + message print("message", message) # LOGGER.debug("Comparing with %r" % (MESSAGE_TEMPLATE.format(message).encode())) try: sig_raw = pub.ecdsa_deserialize(self.sig_ser) good = pub.ecdsa_verify( MESSAGE_TEMPLATE.format(message).encode(), sig_raw) except Exception: LOGGER.exception("Verification failed") good = False return good
def multiply(s, pub, usehex, rawpub=True): '''Input binary compressed pubkey P(33 bytes) and scalar s(32 bytes), return s*P. The return value is a binary compressed public key. Note that the called function does the type checking of the scalar s. ('raw' options passed in) ''' newpub = secp256k1.PublicKey(pub, raw=rawpub, ctx=ctx) #see note to "tweak_mul" function in podle.py res = secp256k1._tweak_public(newpub, secp256k1.lib.secp256k1_ec_pubkey_tweak_mul, s) return res.serialize()
def verify(data, signature, pk): """ Verify if the signature of data matches the public key Args: data(bytes): data for signature signature(bytes): signature of the data pk(bytes): public key Returns: boolean """ pk_object = secp256k1.PublicKey(pubkey=pk, raw=True) result = pk_object.ecdsa_verify(data, signature) return result
def recover_pubkey_parameter(self, digest, signature, pubkey): for i in range(0, 4): if USE_SECP256K1: sig = pubkey.ecdsa_recoverable_deserialize(signature, i) p = secp256k1.PublicKey(pubkey.ecdsa_recover( self.message, sig)) if p.serialize() == pubkey.serialize(): return i else: p = self.recover_public_key(digest, signature, i) if (p.to_string() == pubkey.to_string() or self.compressed_pubkey(p) == pubkey.to_string()): return i return None
def hw_sign(keypath, msg_b): ledger_sig, recid = ledger.call_sign_mnb(keypath, msg_b, recoverable=True) pubkey = secp256k1.PublicKey(raw=True, flags=secp256k1.ALL_FLAGS) compact_sig = pubkey.ecdsa_serialize_compact( pubkey.ecdsa_deserialize(ledger_sig)) is_compressed = True recoverable_sig = bytes([27 + recid + (4 if is_compressed else 0)]) + compact_sig if not verify_sig(msg_b, recoverable_sig, raw=False): raise RuntimeError('could not verify signature') return recoverable_sig
def get_public_key_from_wif(cls, buf): if len(buf) <= 3: raise CipherException("The length of buffer should be " "larger than 3") if buf[:3] != b"COS": raise CipherException("The prefix of buffer should be \'COS\'") buf = base58.b58decode(buf[3:]) if len(buf) <= 4: raise CipherException("The length of decoded buffer should be " "larger than 4") data, vinfo = buf[:-4], buf[-4:] data_hash = SHA256.new(data=SHA256.new(data=data).digest()) if vinfo != data_hash.digest()[:4]: raise CipherException("Couldn't verify public key") return secp256k1.PublicKey(pubkey=data, raw=True)
def recoverPubkeyParameter(self, digest, signature, pubkey): """Use to derive a number that allows to easily recover the public key from the signature.""" for i in range(0, 4): if USE_SECP256K1: sig = pubkey.ecdsa_recoverable_deserialize(signature, i) p = secp256k1.PublicKey(pubkey.ecdsa_recover( self.message, sig)) if p.serialize() == pubkey.serialize(): return i else: p = self.recover_public_key(digest, signature, i) if p.to_string() == pubkey.to_string( ) or self.compressedPubkey(p) == pubkey.to_string(): return i return None
def set_public_key(self, public_key_string: str) -> None: """Sets the public key (and clears the private key) for this instantiation of the class Note: This must be called AFTER self.encryption (set_encryption) is already set Args: public_key_string: base58 encoded public key string (public dragonchain id) Raises: NotImplementedError when invalid encryption_type """ decoded_key = base58.b58decode(public_key_string) # Modify this statement as support for other encryption algorithms change if self.encryption == crypto.SupportedEncryption.secp256k1: self.pub = secp256k1.PublicKey(pubkey=decoded_key, raw=True) else: raise NotImplementedError("Encryption algorithm not implemented") self.priv = None
def verify(self, signature: Union[str, bytes], message: Union[str, bytes]) -> None: """Verify signature, raise exception if it is not valid. :param message: sequance of bytes, raw format or hexadecimal notation :param signature: a signature in base58 encoding :raises: ValueError if signature is not valid """ encoded_signature = scrub_input(signature) encoded_message = scrub_input(message) if not self.public_point: raise ValueError("Cannot verify without a public key") if encoded_signature[:3] != b'sig': # not generic if self.curve != encoded_signature[:2]: # "sp", "p2" "ed" raise ValueError("Signature and public key curves mismatch.") decoded_signature = base58_decode(encoded_signature) # Ed25519 if self.curve == b"ed": digest = pysodium.crypto_generichash(encoded_message) try: pysodium.crypto_sign_verify_detached(decoded_signature, digest, self.public_point) except ValueError as exc: raise ValueError('Signature is invalid.') from exc # Secp256k1 elif self.curve == b"sp": pk = secp256k1.PublicKey(self.public_point, raw=True) sig = pk.ecdsa_deserialize_compact(decoded_signature) if not pk.ecdsa_verify(encoded_message, sig, digest=blake2b_32): raise ValueError('Signature is invalid.') # P256 elif self.curve == b"p2": pk = fastecdsa.encoding.sec1.SEC1Encoder.decode_public_key( self.public_point, curve=fastecdsa.curve.P256) r, s = bytes_to_int(decoded_signature[:32]), bytes_to_int( decoded_signature[32:]) if not fastecdsa.ecdsa.verify( sig=(r, s), msg=encoded_message, Q=pk, hashfunc=blake2b_32): raise ValueError('Signature is invalid.') else: raise Exception( f'Unknown elliptic curve {self.curve}') # type: ignore
def test_schnorr_partial(): if not secp256k1.HAS_SCHNORR: pytest.skip('secp256k1_schnorr not enabled, skipping') return signer1 = secp256k1.PrivateKey() pubnonce1, privnonce1 = signer1.schnorr_generate_nonce_pair(b'hello') signer2 = secp256k1.PrivateKey() pubnonce2, privnonce2 = signer2.schnorr_generate_nonce_pair(b'hello') partial1 = signer1.schnorr_partial_sign(b'hello', privnonce1, pubnonce2) blank = secp256k1.PublicKey(flags=secp256k1.NO_FLAGS) with pytest.raises(TypeError): blank.schnorr_partial_combine([partial1, secp256k1.ffi.NULL]) with pytest.raises(Exception): blank.schnorr_partial_combine([partial1, b''])
def ecdsa_raw_verify(msg, pub, sig, usehex, rawmsg=False): '''Take the binary message msg and binary signature sig, and verify it against the pubkey pub. If rawmsg is True, no sha256 hash is applied to msg before verifying. In this case, msg must be a precalculated hash (256 bit). If rawmsg is False, the secp256k1 lib will hash the message as part of the ECDSA-SHA256 verification algo. Return value: True if the signature is valid for this pubkey, False otherwise. Since the arguments may come from external messages their content is not guaranteed, so return False on any parsing exception. ''' try: if rawmsg: assert len(msg) == 32 newpub = secp256k1.PublicKey(pubkey=pub, raw=True, ctx=ctx) sigobj = newpub.ecdsa_deserialize(sig) retval = newpub.ecdsa_verify(msg, sigobj, raw=rawmsg) except: return False return retval
def verify(self, signature, message): """ Verify signature, raise exception if it is not valid :param message: sequance of bytes, raw format or hexadecimal notation :param signature: a signature in base58 encoding """ signature = scrub_input(signature) message = scrub_input(message) if not self.public_point: raise ValueError("Cannot verify without a public key") if signature[:3] != b'sig': # not generic if self.curve != signature[:2]: # "sp", "p2" "ed" raise ValueError("Signature and public key curves mismatch.") signature = base58_decode(signature) # Ed25519 if self.curve == b"ed": digest = pysodium.crypto_generichash(message) try: pysodium.crypto_sign_verify_detached(signature, digest, self.public_point) except ValueError: raise ValueError('Signature is invalid.') # Secp256k1 elif self.curve == b"sp": pk = secp256k1.PublicKey(self.public_point, raw=True) sig = pk.ecdsa_deserialize_compact(signature) if not pk.ecdsa_verify(message, sig, digest=blake2b_32): raise ValueError('Signature is invalid.') # P256 elif self.curve == b"p2": pk = SEC1Encoder.decode_public_key(self.public_point, curve=P256) r, s = bytes_to_int(signature[:32]), bytes_to_int(signature[32:]) if not verify(sig=(r, s), msg=message, Q=pk, hashfunc=blake2b_32): raise ValueError('Signature is invalid.') else: assert False
def test_publickey(): with pytest.raises(Exception): # Must be bytes. # In Python 2 this will not raise a TypeError # since bytes is an alias to str, instead it will fail # during deserialization. secp256k1.PublicKey('abc', raw=True) with pytest.raises(Exception): secp256k1.PublicKey([], raw=True) with pytest.raises(Exception): # Invalid size. secp256k1.PublicKey(b'abc', raw=True) with pytest.raises(Exception): # Invalid public key. secp256k1.PublicKey(b'a' * 33, raw=True) # Invalid usage: passing a raw public key but not specifying raw=True. with pytest.raises(TypeError): secp256k1.PublicKey(b'a' * 33) # No public key. assert secp256k1.PublicKey() pub1 = secp256k1.PrivateKey().pubkey.public_key new = secp256k1.PublicKey() with pytest.raises(AssertionError): # Trying to combine with something that is not a public key. new.combine([pub1, secp256k1.ffi.NULL]) new = secp256k1.PublicKey() with pytest.raises(AssertionError): # Nothing to combine. new.combine([])
def verify(self): try: pub_key = secp256k1.PublicKey() pub_key.deserialize(self.get_public_key()) assert pub_key.public_key, "No public key defined" if pub_key.flags & lib.SECP256K1_CONTEXT_VERIFY != \ lib.SECP256K1_CONTEXT_VERIFY: raise Exception("instance not configured for sig verification") msg_digest = self.get_hashing_algorithm()( self._entity_hash).digest() raw_sig = pub_key.ecdsa_deserialize_compact( self._entity_hash_signature) verified = lib.secp256k1_ecdsa_verify( pub_key.ctx, raw_sig, msg_digest, pub_key.public_key) except Exception as e: LOGGER.exception(e) return False else: return bool(verified)
def ecrecover_to_pub(rawhash, v, r, s): if secp256k1: # Legendre symbol check; the secp256k1 library does not seem to do this pk = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS) xc = r * r * r + 7 assert pow(xc, (SECP256K1P - 1) // 2, SECP256K1P) == 1 try: pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32), v - 27 ), raw=True ) pub = pk.serialize(compressed=False)[1:] except: pub = b"\x00" * 64 else: recovered_addr = ecdsa_raw_recover(rawhash, (v, r, s)) pub = encode_pubkey(recovered_addr, 'bin_electrum') assert len(pub) == 64 return pub
def _main(): if len(sys.argv) != 4: print("usage: {} public data signature".format(sys.argv[0]), file=sys.stderr) sys.exit(1) data = Path(sys.argv[2]).read_bytes() pubkey_compressed = bytes( int(x, 16) for x in Path(sys.argv[1]).read_text().strip().split(",") if x) signature_raw = bytes( int(x, 16) for x in Path(sys.argv[3]).read_text().strip().split(",") if x) pubkey = secp256k1.PublicKey(pubkey_compressed, raw=True) signature = pubkey.ecdsa_deserialize_compact(signature_raw) was_normalized, normalized = pubkey.ecdsa_signature_normalize(signature) if was_normalized: print("Signature was normalized") #if not pubkey.ecdsa_verify(data, signature): if not pubkey.ecdsa_verify(data, normalized): print("verification failed", file=sys.stderr) sys.exit(1)
def getNUMS(index=0): """Taking secp256k1's G as a seed, either in compressed or uncompressed form, append "index" as a byte, and append a second byte "counter" try to create a new NUMS base point from the sha256 of that bytestring. Loop counter and alternate compressed/uncompressed until finding a valid curve point. The first such point is considered as "the" NUMS base point alternative for this index value. The search process is of course deterministic/repeatable, so it's fine to just store a list of all the correct values for each index, but for transparency left in code for initialization by any user. The NUMS generator generated is returned as a secp256k1.PublicKey. """ assert index in range(256) nums_point = None for G in [getG(True), getG(False)]: seed = G + chr(index) for counter in range(256): seed_c = seed + chr(counter) hashed_seed = hashlib.sha256(seed_c).digest() #Every x-coord on the curve has two y-values, encoded #in compressed form with 02/03 parity byte. We just #choose the former. claimed_point = "\x02" + hashed_seed try: nums_point = secp256k1.PublicKey(claimed_point, raw=True, ctx=ctx) return nums_point except: continue assert False, "It seems inconceivable, doesn't it?" # pragma: no cover
def decompress_raw(comp_type, raw, chomp=False): if comp_type == 0 or comp_type == 1: l = 20 elif comp_type >= 2 and comp_type <= 5: l = 32 else: l = comp_type - 6 data = raw[:l] raw = raw[l:] if not chomp: assert len(raw) == 0 if comp_type == 0: script = OP_DUP + OP_HASH160 + chr(20) + data + \ OP_EQUALVERIFY + OP_CHECKSIG elif comp_type == 1: script = OP_HASH160 + chr(20) + data + OP_EQUAL elif comp_type == 2 or comp_type == 3: script = chr(33) + chr(comp_type) + data + OP_CHECKSIG elif comp_type == 4 or comp_type == 5: comp_pubkey = chr(comp_type - 2) + data pubkey = secp256k1.PublicKey(comp_pubkey, raw=True).serialize(compressed=False) script = chr(65) + pubkey + OP_CHECKSIG else: script = data return script, raw
import warnings from sawtooth_signing.core import SigningError from sawtooth_signing.core import ParseError from sawtooth_signing.core import PrivateKey from sawtooth_signing.core import PublicKey from sawtooth_signing.core import Context import secp256k1 import bitcoin as pybitcointools __CONTEXTBASE__ = secp256k1.Base(ctx=None, flags=secp256k1.ALL_FLAGS) __CTX__ = __CONTEXTBASE__.ctx __PK__ = secp256k1.PublicKey(ctx=__CTX__) # Cache object to use as factory class Secp256k1PrivateKey(PrivateKey): def __init__(self, secp256k1_private_key): self._private_key = secp256k1_private_key def get_algorithm_name(self): return "secp256k1" def as_hex(self): return binascii.hexlify(self.as_bytes()).decode() def as_bytes(self): return bytes(self._private_key.private_key)
def lndecode(options): hrp, data = bech32_decode(options.lnaddress) if not hrp: sys.exit("Bad bech32 checksum") if not hrp.startswith('ln'): sys.exit("Does not start with ln") # Final signature takes 104 bytes (65 bytes base32 encoded) if len(data) < 103: sys.exit("Too short to contain signature") sigdecoded = convertbits(data[-104:], 5, 8, False) data = data[:-104] pubkey = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS) sig = pubkey.ecdsa_recoverable_deserialize(sigdecoded[0:64], sigdecoded[64]) pubkey.public_key = pubkey.ecdsa_recover( bytearray([ord(c) for c in hrp] + data), sig) print("Signed with public key: {}".format( bytearray(pubkey.serialize()).hex())) m = re.search("[^\d]+", hrp[2:]) currency = m.group(0) print("Currency: {}".format(currency)) amountstr = hrp[2 + m.end():] if amountstr != '': # Postfix? if amountstr.endswith('p'): mul = 1 amountstr = amountstr[:-1] elif amountstr.endswith('n'): mul = 10**3 amountstr = amountstr[:-1] elif amountstr.endswith('u'): mul = 10**6 amountstr = amountstr[:-1] elif amountstr.endswith('m'): mul = 10**9 amountstr = amountstr[:-1] picobtc = int(amountstr) * mul print("Amount: {}".format(picobtc / 10**12)) if options.rate: print("(Conversion: {})".format(picobtc / 10**12 * float(options.rate))) if len(data) < 7: sys.exit("Not long enough to contain timestamp") tstamp = from_u35(data[:7]) data = data[7:] print("Timestamp: {} ({})".format(tstamp, time.ctime(tstamp))) while len(data) > 0: tag, tagdata, data = pull_tagged(data) if tag == 'r': tagdata = convertbits(tagdata, 5, 8, False) if len(tagdata) != 33 + 8 + 4 + 4: sys.exit('Unexpected r tag length {}'.format(len(tagdata))) print("Route: {}/{}/{}/{}".format( bytearray(tagdata[0:33]).hex(), bytearray(tagdata[33:41]).hex(), from_u32list(tagdata[41:45]), from_u32list(tagdata[45:49]))) elif tag == 'f': if currency == 'bc' or currency == 'tb': wver = tagdata[0] if wver == 17: addr = base58.b58encode_check( bytes([base58_prefix_map[currency][0]] + convertbits(tagdata[1:], 5, 8, False))) elif wver == 18: addr = base58.b58encode_check( bytes([base58_prefix_map[currency][1]] + convertbits(tagdata[1:], 5, 8, False))) elif wver <= 16: addr = bech32_encode(currency, tagdata) else: sys.exit('Invalid witness version {}'.format(wver)) # Other currencies here... else: addr = bytearray(tagdata).hex() print("Fallback: {}".format(addr)) elif tag == 'd': tagdata = convertbits(tagdata, 5, 8, False) print("Description: {}".format(''.join(chr(c) for c in tagdata))) elif tag == 'h': tagdata = convertbits(tagdata, 5, 8, False) print("Description hash: {}".format(bytearray(tagdata).hex())) elif tag == 'x': print("Expiry (seconds): {}".format(from_5bit(tagdata))) elif tag == 'p': tagdata = convertbits(tagdata, 5, 8, False) assert len(tagdata) == 32 print("Payment hash: {}".format(bytearray(tagdata).hex())) else: tagdata = convertbits(tagdata, 5, 8, False) print("UNKNOWN TAG {}: {}".format(tag, bytearray(tagdata).hex()))
def from_bytes(byte_str): public_key = __PK__.deserialize(byte_str) return Secp256k1PublicKey(secp256k1.PublicKey(public_key, ctx=__CTX__))
def lndecode(a): hrp, data = bech32_decode(a) if not hrp: raise ValueError("Bad bech32 checksum") # BOLT #11: # # A reader MUST fail if it does not understand the `prefix`. if not hrp.startswith('ln'): raise ValueError("Does not start with ln") data = u5_to_bitarray(data) # Final signature 65 bytes, split it off. if len(data) < 65 * 8: raise ValueError("Too short to contain signature") sigdecoded = data[-65 * 8:].tobytes() data = bitstring.ConstBitStream(data[:-65 * 8]) addr = LnAddr() addr.pubkey = None m = re.search("[^\d]+", hrp[2:]) if m: addr.currency = m.group(0) amountstr = hrp[2 + m.end():] # BOLT #11: # # A reader SHOULD indicate if amount is unspecified, otherwise it MUST # multiply `amount` by the `multiplier` value (if any) to derive the # amount required for payment. if amountstr != '': addr.amount = unshorten_amount(amountstr) addr.date = data.read(35).uint while data.pos != data.len: tag, tagdata, data = pull_tagged(data) # BOLT #11: # # A reader MUST skip over unknown fields, an `f` field with unknown # `version`, or a `p`, `h`, `n` or `r` field which does not have # `data_length` 52, 52, 53 or 82 respectively. data_length = len(tagdata) / 5 if tag == 'r': if data_length != 82: addr.unknown_tags.append((tag, tagdata)) continue tagbytes = trim_to_bytes(tagdata) addr.tags.append(('r', (tagbytes[0:33], tagbytes[33:41], tagdata[41 * 8:49 * 8].intbe, tagdata[49 * 8:51 * 8].intbe))) elif tag == 'f': fallback = parse_fallback(tagdata, addr.currency) if fallback: addr.tags.append(('f', fallback)) else: # Incorrect version. addr.unknown_tags.append((tag, tagdata)) continue elif tag == 'd': addr.tags.append(('d', trim_to_bytes(tagdata).decode('utf-8'))) elif tag == 'h': if data_length != 52: addr.unknown_tags.append((tag, tagdata)) continue addr.tags.append(('h', trim_to_bytes(tagdata))) elif tag == 'x': addr.tags.append(('x', tagdata.uint)) elif tag == 'p': if data_length != 52: addr.unknown_tags.append((tag, tagdata)) continue addr.paymenthash = trim_to_bytes(tagdata) elif tag == 'n': if data_length != 53: addr.unknown_tags.append((tag, tagdata)) continue addr.pubkey = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS) addr.pubkey.deserialize(trim_to_bytes(tagdata)) elif tag == 'c': addr.min_final_cltv_expiry = tagdata.uint else: addr.unknown_tags.append((tag, tagdata)) # BOLT #11: # # A reader MUST check that the `signature` is valid (see the `n` tagged # field specified below). if addr.pubkey: # Specified by `n` # BOLT #11: # # A reader MUST use the `n` field to validate the signature instead of # performing signature recovery if a valid `n` field is provided. addr.signature = addr.pubkey.ecdsa_deserialize_compact( sigdecoded[0:64]) if not addr.pubkey.ecdsa_verify( bytearray([ord(c) for c in hrp]) + data.tobytes(), addr.signature): raise ValueError('Invalid signature') else: # Recover pubkey from signature. addr.pubkey = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS) addr.signature = addr.pubkey.ecdsa_recoverable_deserialize( sigdecoded[0:64], sigdecoded[64]) addr.pubkey.public_key = addr.pubkey.ecdsa_recover( bytearray([ord(c) for c in hrp]) + data.tobytes(), addr.signature) return addr
def podle_PublicKey(P): """Returns a PublicKey object from a binary string """ return secp256k1.PublicKey(P, raw=True, ctx=ctx)
#!/usr/bin/python from __future__ import print_function import binascii import hashlib import re import sys import base64 import secp256k1 #Required only for PoDLE calculation: N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 #Global context for secp256k1 operations (helps with performance) ctx = secp256k1.lib.secp256k1_context_create(secp256k1.ALL_FLAGS) #required for point addition dummy_pub = secp256k1.PublicKey(ctx=ctx) #Standard prefix for Bitcoin message signing. BITCOIN_MESSAGE_MAGIC = '\x18' + 'Bitcoin Signed Message:\n' if sys.version_info.major == 2: string_types = (str, unicode) string_or_bytes_types = string_types int_types = (int, float, long) # Base switching code_strings = { 2: '01', 10: '0123456789', 16: '0123456789abcdef', 32: 'abcdefghijklmnopqrstuvwxyz234567', 58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
def lndecode(a, verbose=False): hrp, data = bech32_decode(a) if not hrp: raise ValueError("Bad bech32 checksum") # BOLT #11: # # A reader MUST fail if it does not understand the `prefix`. if not hrp.startswith("ln"): raise ValueError("Does not start with ln") data = u5_to_bitarray(data) # Final signature 65 bytes, split it off. if len(data) < 65 * 8: raise ValueError("Too short to contain signature") sigdecoded = data[-65 * 8:].tobytes() data = bitstring.ConstBitStream(data[:-65 * 8]) addr = LnAddr() addr.pubkey = None m = re.search("[^\d]+", hrp[2:]) if m: addr.currency = m.group(0) amountstr = hrp[2 + m.end():] # BOLT #11: # # A reader SHOULD indicate if amount is unspecified, otherwise it MUST # multiply `amount` by the `multiplier` value (if any) to derive the # amount required for payment. if amountstr != "": addr.amount = unshorten_amount(amountstr) addr.date = data.read(35).uint while data.pos != data.len: tag, tagdata, data = pull_tagged(data) # BOLT #11: # # A reader MUST skip over unknown fields, an `f` field with unknown # `version`, or a `p`, `h`, or `n` field which does not have # `data_length` 52, 52, or 53 respectively. data_length = len(tagdata) / 5 if tag == "r": # BOLT #11: # # * `r` (3): `data_length` variable. One or more entries # containing extra routing information for a private route; # there may be more than one `r` field, too. # * `pubkey` (264 bits) # * `short_channel_id` (64 bits) # * `feebase` (32 bits, big-endian) # * `feerate` (32 bits, big-endian) # * `cltv_expiry_delta` (16 bits, big-endian) route = [] s = bitstring.ConstBitStream(tagdata) while s.pos + 264 + 64 + 32 + 32 + 16 < s.len: route.append(( s.read(264).tobytes(), s.read(64).tobytes(), s.read(32).intbe, s.read(32).intbe, s.read(16).intbe, )) addr.tags.append(("r", route)) elif tag == "f": fallback = parse_fallback(tagdata, addr.currency) if fallback: addr.tags.append(("f", fallback)) else: # Incorrect version. addr.unknown_tags.append((tag, tagdata)) continue elif tag == "d": addr.tags.append(("d", trim_to_bytes(tagdata).decode("utf-8"))) elif tag == "h": if data_length != 52: addr.unknown_tags.append((tag, tagdata)) continue addr.tags.append(("h", trim_to_bytes(tagdata))) elif tag == "x": addr.tags.append(("x", tagdata.uint)) elif tag == "p": if data_length != 52: addr.unknown_tags.append((tag, tagdata)) continue addr.paymenthash = trim_to_bytes(tagdata) elif tag == "n": if data_length != 53: addr.unknown_tags.append((tag, tagdata)) continue addr.pubkey = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS) addr.pubkey.deserialize(trim_to_bytes(tagdata)) else: addr.unknown_tags.append((tag, tagdata)) if verbose: print("hex of signature data (32 byte r, 32 byte s): {}".format( hexlify(sigdecoded[0:64]))) print("recovery flag: {}".format(sigdecoded[64])) print("hex of data for signing: {}".format( hexlify(bytearray([ord(c) for c in hrp]) + data.tobytes()))) print("SHA256 of above: {}".format( hashlib.sha256(bytearray([ord(c) for c in hrp]) + data.tobytes()).hexdigest())) # BOLT #11: # # A reader MUST check that the `signature` is valid (see the `n` tagged # field specified below). if addr.pubkey: # Specified by `n` # BOLT #11: # # A reader MUST use the `n` field to validate the signature instead of # performing signature recovery if a valid `n` field is provided. addr.signature = addr.pubkey.ecdsa_deserialize_compact( sigdecoded[0:64]) if not addr.pubkey.ecdsa_verify( bytearray([ord(c) for c in hrp]) + data.tobytes(), addr.signature): raise ValueError("Invalid signature") else: # Recover pubkey from signature. addr.pubkey = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS) addr.signature = addr.pubkey.ecdsa_recoverable_deserialize( sigdecoded[0:64], sigdecoded[64]) addr.pubkey.public_key = addr.pubkey.ecdsa_recover( bytearray([ord(c) for c in hrp]) + data.tobytes(), addr.signature) return addr