def quick_sign_message(message, wif, hashfn=hashlib.sha256): if not isinstance(message, bytes): message = bytes(message, "utf-8") digest = hashfn(message).digest() priv_key = QuickPrivateKey(wif) if sys.version > '3': p = bytes(priv_key) else: p = bytes(priv_key.__bytes__()) ndata = CCffi.new("const int *ndata") ndata[0] = 0 while True: ndata[0] += 1 signature = CCffi.new('secp256k1_ecdsa_recoverable_signature *') signed = CClib.secp256k1_ecdsa_sign_recoverable( CCGLOBAL_CONTEXT.ctx, signature, digest, p, CCffi.NULL, ndata) if not signed: raise AssertionError() output = CCffi.new('unsigned char[%d]' % 64) recid = CCffi.new('int *') CClib.secp256k1_ecdsa_recoverable_signature_serialize_compact( CCGLOBAL_CONTEXT.ctx, output, recid, signature) output_sig = CCffi.buffer(output, 64) if _is_canonical(output_sig): return bytes(CCutils.int_to_bytes(31 + recid[0]) + output_sig)
def serialize_recoverable(recover_sig, context=GLOBAL_CONTEXT): output = ffi.new('unsigned char[%d]' % CDATA_SIG_LENGTH) recid = ffi.new('int *') lib.secp256k1_ecdsa_recoverable_signature_serialize_compact( context.ctx, output, recid, recover_sig) return bytes(ffi.buffer(output, CDATA_SIG_LENGTH)) + int_to_bytes(recid[0])
def sigrec_decode(sigrec): """ Decodes a pk-recoverable signature in the format used by LN to be input to ``PublicKey.from_signature_and_message``. Args: sigrec(:obj:`bytes`): the signature to be decoded. Returns: :obj:`bytes`: the decoded signature. """ rid, rsig = int_to_bytes(sigrec[0] - 31), sigrec[1:] rsig_rid = rsig + rid return rsig_rid
def sigrec_encode(rsig_rid): """ Encodes a pk-recoverable signature to be used in LN. ``rsig_rid`` can be obtained trough ``PrivateKey.sign_recoverable``. The required format has the recovery id as the last byte, and for signing LN messages we need it as the first. From: https://twitter.com/rusty_twit/status/1182102005914800128 Args: rsig_rid(:obj:`bytes`): the signature to be encoded. Returns: :obj:`bytes`: the encoded signature. """ rsig, rid = rsig_rid[:64], rsig_rid[64] sigrec = int_to_bytes(rid + 31) + rsig return sigrec
def sigrec_decode(sigrec): """ Decodes a pk-recoverable signature in the format used by LN to be input to ``PublicKey.from_signature_and_message``. Args: sigrec(:obj:`bytes`): the signature to be decoded. Returns: :obj:`bytes`: the decoded signature. Raises: :obj:`ValueError`: if the SigRec is not properly encoded (first byte is not 31 + recovery id) """ int_rid, rsig = sigrec[0] - 31, sigrec[1:] if int_rid < 0: raise ValueError("Wrong SigRec") else: rid = int_to_bytes(int_rid) return rsig + rid
def sign_recoverable(self, message: bytes, hasher: Hasher = sha256, custom_nonce: Nonce = DEFAULT_NONCE) -> bytes: """ Create a recoverable ECDSA signature. :param message: The message to sign. :param hasher: The hash function to use, which must return 32 bytes. By default, the `sha256` algorithm is used. If `None`, no hashing occurs. :param custom_nonce: Custom nonce data in the form `(nonce_function, input_data)`. Refer to [secp256k1_recovery.h](https://github.com/bitcoin-core/secp256k1/blob/f8c0b57e6ba202b1ce7c5357688de97c9c067697/include/secp256k1_recovery.h#L78-L79). :return: The recoverable ECDSA signature. :raises ValueError: If the message hash was not 32 bytes long, the nonce generation function failed, or the private key was invalid. """ msg_hash = hasher(message) if hasher is not None else message if len(msg_hash) != 32: raise ValueError('Message hash must be 32 bytes long.') ndata = ffi.new("const int *ndata") ndata[0] = 0 while 1: ndata[0] += 1 signature = ffi.new('secp256k1_ecdsa_recoverable_signature *') signed = lib.secp256k1_ecdsa_sign_recoverable( self.context.ctx, signature, msg_hash, self.secret, ffi.NULL, ndata) if not signed: raise ValueError( 'The nonce generation function failed, or the private key was invalid.' ) #return serialize_recoverable(signature, self.context) i, sig = serialize_recoverable(signature, self.context) if i != -1: return int_to_bytes(i) + sig
def test_bytes_int_conversion(): bytestr = b'\x00' + urandom(31) assert pad_scalar(int_to_bytes(bytes_to_int(bytestr))) == bytestr
def KANGAROOS(): # debug if flag_profile == "custom": #midJsize = (Wsqrt//2)+1 midJsize = int(round(1.0 * Wsqrt / 2)) pow2Jmax = getPow2Jmax(midJsize) sizeJmax = 2**pow2Jmax #sizeJmax = Wsqrt*8 #pow2Jmax = int(round(math.log(sizeJmax,2))) pow2dp = (pow2W // 2) - 2 # DPmodule = 2**pow2dp # settings by Pollard # expected of 2w^(1/2) group operations #elif flag_profile == "standart": else: # mean jumpsize # by Pollard ".. The best choice of m (mean jump size) is w^(1/2)/2 .." #midJsize = (Wsqrt//2)+1 midJsize = int(round(1.0 * Wsqrt / 2)) pow2Jmax = getPow2Jmax(midJsize) sizeJmax = 2**pow2Jmax # discriminator for filter added new distinguished points (ram economy) pow2dp = (pow2W // 2) - 2 # DPmodule = 2**pow2dp if flag_debug > 0: if flag_debug > 0: print('[DPmodule] 2^%s = %s' % (pow2dp, DPmodule)) print('[sizeJmax] 2^%s = %s' % (pow2Jmax, sizeJmax)) # dT/dW - int, sum distance traveled dT = dW = 0 # Tp/Wp - point, sum distance traveled Tp = Wp = False # generate random start points # Tame if 1: #dT = (3<<(pow2bits-2)) + random.randint(1,(2**(pow2bits-1))) # by 57fe dT = M # by Pollard if not (dT % 2): dT += 1 # T odd recommended if (not flag_gmpy2) and flag_coincurve: Tp = Gp.multiply(int_to_bytes(dT)) else: Tp = mul_ka(dT) if flag_debug > 1: print('dT 0x%064x' % (dT)) # Wild if 1: #dW = random.randint(1, (1<<(pow2bits-1))) # by 57fe dW = 1 # by Pollard if (not flag_gmpy2) and flag_coincurve: Wp = PublicKey.combine_keys([W0p, Gp.multiply(int_to_bytes(dW))]) else: Wp = add_a(W0p, mul_ka(dW)) if flag_debug > 1: print('dW 0x%064x' % (dW)) print('[+] T+W kangaroos - ready') # DTp/DWp - points, distinguished of Tp/Wp DTp, DWp = dict(), dict( ) # dict is hashtable of python, provides uniqueness distinguished points t0 = t1 = t2 = t1_info = t2_info = time.time() n_jump = last_jump = 0 prvkey = False # main loop while (1): # Tame if 1: n_jump += 1 # Xcoord Xcoord = getXcoord(Tp) pw = Xcoord % pow2Jmax pw = int(pw) nowjumpsize = 1 << pw # check, is it distinguished point? if Xcoord % DPmodule == 0: # add new distinguished point DTp[Xcoord] = dT if flag_debug > 1: printstr = '\r[tame] T+W=%s+%s=%s' % (len(DTp), len(DWp), len(DTp) + len(DWp)) printstr += '; %064x 0x%x' % (Xcoord, dT) print(printstr) save2file('tame.txt', 'a', '%064x %s\n' % (Xcoord, dT)) # compare distinguished points, Tame & Wild compare = list(set(DTp) & set(DWp)) if len(compare) > 0: dDT = DTp[compare[0]] dDW = DWp[compare[0]] if dDT > dDW: prvkey = dDT - dDW elif dDW > dDT: prvkey = dDW - dDT else: print("\r[error] dDW == dDT !!! (0x%x)" % dDW) exit(-1) if prvkey: break dT += nowjumpsize if (not flag_gmpy2) and flag_coincurve: Tp = PublicKey.combine_keys([Tp, Sp[pw]]) else: Tp = add_a(Tp, Sp[pw]) if prvkey: break # Wild if 1: n_jump += 1 # Xcoord Xcoord = getXcoord(Wp) pw = Xcoord % pow2Jmax pw = int(pw) nowjumpsize = 1 << pw # add new distinguished point if Xcoord % DPmodule == 0: # add new distinguished point DWp[Xcoord] = dW if flag_debug > 1: printstr = '\r[wild] T+W=%s+%s=%s' % (len(DTp), len(DWp), len(DTp) + len(DWp)) printstr += '; %064x 0x%x' % (Xcoord, dW) print(printstr) save2file('wild.txt', 'a', '%064x %s\n' % (Xcoord, dW)) # compare distinguished points, Tame & Wild compare = list(set(DTp) & set(DWp)) if len(compare) > 0: dDT = DTp[compare[0]] dDW = DWp[compare[0]] if dDT > dDW: prvkey = dDT - dDW elif dDW > dDT: prvkey = dDW - dDT else: print("\r[error] dDW == dDT !!! (0x%x)" % dDW) exit(-1) if prvkey: break dW += nowjumpsize if (not flag_gmpy2) and flag_coincurve: Wp = PublicKey.combine_keys([Wp, Sp[pw]]) else: Wp = add_a(Wp, Sp[pw]) if prvkey: break if not (n_jump % 5000): t2 = t2_info = time.time() # info if (flag_debug > 0 and (t2_info - t1_info) > 10) or prvkey: printstr = '\r[i] DP T+W=%s+%s=%s; dp/kgr=%.1f' % ( len(DTp), len(DWp), len(DTp) + len(DWp), 1.0 * (len(DTp) + len(DWp)) / 2) printstr += ' ' * 60 print(printstr) t1_info = t2_info # indicator, progress, time if (t2 - t1) > 1 or prvkey: printstr = '\r' printstr += '[%s ' % (time_format(t2 - t0, (1, 1, 1, 1, 1, 1, 0, 0))) if t2 - t1 != 0: printstr += '; %s j/s' % prefSI( (n_jump - last_jump) / (t2 - t1)) else: printstr += '; %s j/s' % prefSI( (n_jump - last_jump) / 0.001) #printstr += '; %sj of %sj %.1f%%' % ( printstr += '; %sj %.1f%%' % ( n_jump if n_jump < 10**3 else prefSI(n_jump) #, 2*Wsqrt if 2*Wsqrt < 10**3 else prefSI(2*Wsqrt) , (1.0 * n_jump / (2 * Wsqrt)) * 100) if 1 or flag_debug < 1: printstr += '; dp/kgr=%.1f' % (1.0 * (len(DTp) + len(DWp)) / 2) #printstr += 'lost_TIME_left' timeleft = (t2 - t0) * (1 - (1.0 * n_jump / (2 * Wsqrt))) / (1.0 * n_jump / (2 * Wsqrt)) if timeleft > 0: printstr += ';%s ] ' % (time_format( timeleft, (1, 1, 1, 1, 1, 1, 0, 0))) else: printstr += ';%s ] ' % (time_format( 0, (1, 1, 1, 1, 1, 1, 0, 0))) if sys.version_info[0] == 2: print(printstr, end='') sys.stdout.flush() else: print(printstr, end='', flush=True) t1 = t2 last_jump = n_jump return prvkey, n_jump, (time.time() - t0), len(DTp), len(DWp)
print('[range] 2^%s..2^%s ; W = U - L = 0x%x (2^%s)' % (pow2L, pow2U, W, pow2W)) if pow2W < bitMin or pow2W > bitMax: print('[error] W must be 2^%s..2^%s!' % (bitMin, bitMax)) usage() if pow2W > 55: print('[warn!] W = 2^%s too big! long runtime expected' % (pow2W)) print("[~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]") starttime = time.time() Sp = [Gp] for k in xrange(255): if (not flag_gmpy2) and flag_coincurve: Sp.append(Sp[k].multiply(int_to_bytes(2))) else: Sp.append(mul_2a(Sp[k])) print('[+] Sp-table of pow2 points - ready') #print("[~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]") list_runjump, list_runtime, list_dpkgr = list(), list(), list() #timeit for i in xrange(Ntimeit): print("[~~~~~~~~~~~~~~~~~~~~~~[%s/%s]~~~~~~~~~~~~~~~~~~~~~]" % (i + 1, Ntimeit)) if flag_debug > 1: save2file('tame.txt', 'w', '')