Konvertieren von RUBY-Skript in PYTHON (Wiederherstellen des privaten Schlüssels, wenn jemand dasselbe k zweimal verwendet)

require 'ecdsa'

public_key_hex = '02a50eb66887d03fe186b608f477d99bc7631c56e64bb3af7dc97e71b917c5b364'
msghash1_hex = '01b125d18422cdfa7b153f5bcf5b01927cf59791d1d9810009c70cd37b14f4e6'
msghash2_hex = '339ff7b1ced3a45c988b3e4e239ea745db3b2b3fda6208134691bd2e4a37d6e1'
sig1_hex = '304402200861cce1da15fc2dd79f1164c4f7b3e6c1526e7e8d85716578689ca9a5dc349d02206cf26e2776f7c94cafcee05cc810471ddca16fa864d13d57bee1c06ce39a3188'
sig2_hex = '304402200861cce1da15fc2dd79f1164c4f7b3e6c1526e7e8d85716578689ca9a5dc349d02204ba75bdda43b3aab84b895cfd9ef13a477182657faaf286a7b0d25f0cb9a7de2'

group = ECDSA::Group::Secp256k1

def hex_to_binary(str)
str.scan(/../).map(&:hex).pack('C*')
end

public_key_str = hex_to_binary(public_key_hex)
public_key = ECDSA::Format::PointOctetString.decode(public_key_str, group)

puts 'public key x: %#x' % public_key.x
puts 'public key y: %#x' % public_key.y

msghash1 = hex_to_binary(msghash1_hex)
msghash2 = hex_to_binary(msghash2_hex)
sig1 = ECDSA::Format::SignatureDerString.decode(hex_to_binary(sig1_hex))
sig2 = ECDSA::Format::SignatureDerString.decode(hex_to_binary(sig2_hex))

raise 'R values are not the same' if sig1.r != sig2.r

r = sig1.r
puts 'sig r: %#x' % r
puts 'sig1 s: %#x' % sig1.s
puts 'sig2 s: %#x' % sig2.s

sig1_valid = ECDSA.valid_signature?(public_key, msghash1, sig1)
sig2_valid = ECDSA.valid_signature?(public_key, msghash2, sig2)
puts "sig1 valid: #{sig1_valid}"
puts "sig2 valid: #{sig2_valid}"

# Step 1: k = (z1 - z2)/(s1 - s2)
field = ECDSA::PrimeField.new(group.order)
z1 = ECDSA::Format::IntegerOctetString.decode(msghash1)
z2 = ECDSA::Format::IntegerOctetString.decode(msghash2)

k_candidates = [
field.mod((z1 - z2) * field.inverse(sig1.s - sig2.s)),
field.mod((z1 - z2) * field.inverse(sig1.s + sig2.s)),
field.mod((z1 - z2) * field.inverse(-sig1.s - sig2.s)),
field.mod((z1 - z2) * field.inverse(-sig1.s + sig2.s)),
]

private_key = nil
k_candidates.each do |k|
 next unless group.new_point(k).x == r
 private_key_maybe = field.mod(field.mod(sig1.s * k - z1) * field.inverse(r))
 if public_key == group.new_point(private_key_maybe)
  private_key = private_key_maybe
 end
end

puts 'private key: %#x' % private_key

Jemand konvertiert bitte den obigen Code in Python, bitte von Wiederherstellen des privaten Schlüssels, wenn jemand dasselbe k zweimal in ECDSA-Signaturen verwendet

Ich denke, Sie sollten die Einrückung des ursprünglichen Skripts beibehalten, damit es für die Leute einfacher zu lesen ist.
Warum diese Abfragen -1 bekommen, brauche ich das Ruby-Skript in Python, was ist dann falsch daran, -1 ist sehr schlecht

Antworten (1)

Folgendes habe ich kürzlich in Python gemacht. Dies ist keine sehr vollständige Lösung (es validiert seine Eingabe nicht, es erfordert, dass Sie die Signatur bereits in r & s decodiert haben, es leitet keinen öffentlichen Schlüssel oder keine Adresse aus dem privaten Schlüssel ab, es tut es nicht Um Probleme mit der Formbarkeit von Signaturen zu lösen, funktioniert es nur mit bestimmten Kurventypen wie Bitcoins secp256k1), sollte aber in den meisten Fällen ausreichend sein.

n  = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141  # order of base point G
r  = 0xd47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1
s1 = 0x78c9d47ef31caf0102f9ae2489d7c78ab51692ddd898b6eb20b16a0d25b01c78
z1 = 0x4435b0704795962ac9efe71b841a5366434f552d8b5beca04a48426c15fd9ad7
s2 = 0x240bcda3967d66c71c92ffc4c4486d99968183f198c5fe1612a5cc99a05ba99a
z2 = 0x6b8bb3201a7ce4c7ed72eddc46d9b6d7350bc2eb8c28df9763518de8d66b0b52

def modinv(x, n=n): return pow(x, n-2, n)  # modular multiplicative inverse (requires that n is prime)

k = (z1 - z2) * modinv(s1 - s2) % n ; print('k = {:x}'.format(k))
print('privkey = {:x}'.format( (s1 * k - z1) * modinv(r) % n ))  # these two should
print('privkey = {:x}'.format( (s2 * k - z2) * modinv(r) % n ))  # be the same

Aktualisierter Code

Hier ist eine vollständigere (aber auch schwieriger zu lesende) Version, die (a) verschiedene Möglichkeiten zum Ausgleich negierter s-Werte anzeigt (wie von David Grayson in dieser Antwort erwähnt ) und (b) den privaten Schlüssel anhand der Signatur verifiziert. abgeleitete öffentliche Schlüssel, wenn Sie Pycoin installiert haben.

# order of base point G of secp256k1
n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141

# modular multiplicative inverse (requires that n is prime)
def modinv(x, n=n):
    return pow(x, n-2, n)

# the two k candidates which aren't just negations of themselves
def k_candidates(s1, z1, s2, z2, n=n):
    z1_z2 = z1 - z2
    yield z1_z2 * modinv(s1 - s2, n) % n
    yield z1_z2 * modinv(s1 + s2, n) % n

# generates two tuples, each with (privkey, k_possibility_1, k_possibility_2)
def privkey_k_candidates(r, s1, z1, s2, z2, n=n):
    modinv_r = modinv(r, n)
    for k in k_candidates(s1, z1, s2, z2, n):
        yield (s1 * k - z1) * modinv_r % n,  k,  -k % n


r  = 0xd47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1
s1 = 0x78c9d47ef31caf0102f9ae2489d7c78ab51692ddd898b6eb20b16a0d25b01c78
z1 = 0x4435b0704795962ac9efe71b841a5366434f552d8b5beca04a48426c15fd9ad7
s2 = 0x240bcda3967d66c71c92ffc4c4486d99968183f198c5fe1612a5cc99a05ba99a
z2 = 0x6b8bb3201a7ce4c7ed72eddc46d9b6d7350bc2eb8c28df9763518de8d66b0b52

try:
    from pycoin.ecdsa import *
    pubkeys = possible_public_pairs_for_signature(generator_secp256k1, z1, (r, s1))
    for privkey, k1, k2 in privkey_k_candidates(r, s1, z1, s2, z2):
        if public_pair_for_secret_exponent(generator_secp256k1, privkey) in pubkeys:
            print('k       = {:x}'.format(k1))
            print('or k    = {:x}'.format(k2))
            print('privkey = {:x}'.format(privkey))
            break
    else:
        print('privkey not found')

except ImportError:
    for privkey, k1, k2 in privkey_k_candidates(r, s1, z1, s2, z2):
        print('possible k       = {:x}'  .format(k1))
        print('possible k       = {:x}'  .format(k2))
        print('possible privkey = {:x}\n'.format(privkey))
Im Gegensatz zum Skript im ursprünglichen Beitrag (den ich geschrieben habe) berücksichtigt das Skript in dieser Antwort nicht die Tatsache, dass die Zeichen eingeschaltet s1und s2möglicherweise umgedreht wurden, sodass Sie 4 verschiedene mögliche Werte für ausprobieren müssen k. Ich denke, Sie sind sich dessen bewusst, weil Sie "Signatur-Formbarkeitsprobleme" erwähnt haben.
@DavidGrayson Sie haben Recht, es wird nicht berücksichtigt, dass s1 und s2 möglicherweise von einem Dritten negiert wurden. Ich war ein bisschen faul, als ich das gepostet habe; Ich werde es in Kürze aktualisieren.
Was hat es mit all diesen Beiträgen auf sich? Ich nehme an, das Debakel von BCI random.org?
@WizardOfOzzie Vielleicht, aber die TXs, auf die OP als Beispiele verwiesen hat, stammen aus dem Jahr 2012, lange vor den beiden (seufz ...) jüngsten BCI-Ausfällen.
@ChristopherGurnee war nur neugierig, da es heute die fünfte Erwähnung des r-Werts ist (für mich, nicht nur hier). BEARBEITEN, wow, 2? Die Java-Hüpfburgausgabe vom August 2013 ist die andere?
@WizardOfOzzie BCI verwendet kein Bouncy Castle (BCI ist JavaScript, Bouncy Castle ist Java). Ich bezog mich auf dieses Problem , bei dem BCI ihren RNG durcheinander gebracht hat, und ohne die heldenhaften Bemühungen von Benutzer johoe hätten sie mehr als 260 BTC ihrer Benutzer verloren. Sie hatten auch Probleme mit SSL-Downgrade-Angriffen, HSTS-Problemen und wahrscheinlich anderen ....
@ChristopherGurnee oh wow, ich habe diesen BCI-Fehler verpasst. Ich erinnerte mich an das RNG-Problem vom August 2013, von dem BCI-Wallets betroffen waren, also dachte ich, es sei ein Java-Problem? Ich bin jedoch nicht so versiert mit Java oder JavaScript, daher kann es sein, dass ich Lücken habe
@WizardOfOzzie Eigentlich wette ich, dass Sie Recht haben, ich habe vergessen, dass BCI eine Android-App hat, die möglicherweise betroffen ist, ich dachte nur an die Weboberfläche.