1

At the moment I'm playing around with ECDSA. I know that I can retrieve the private key if the same K is used to sign two messages.

I tried to use this script: Ruby Script to crack the private key. But I'm always getting an error:

signature_der_string.rb:14:in `decode': null is wrong length (OpenSSL::ASN1::ASN1Error)

My signatures start with a leading 0. If I leave out the 0 I get a different error

`decode': too long (OpenSSL::ASN1::ASN1Error)

Can you help me what I'm doing wrong?

msghash1_hex = '4992c90022d12b85555493d3dcca55671b4047c1'

msghash2_hex = 'fced62bb70b5af004e8720342d036da02009e5e8'

sig1_hex = '436c79af2252b161af0b74e3eeb4064af334c483e8708fe709c46b1aa480fa49b017fb020fbc9717c6bf50ada23a820'

sig2_hex = '436c79af2252b161af0b74e3eeb4064af334c483e8708fe734065fe380b95c601f118c047976b3007831690806e10f4'

Peter234
  • 113
  • 1
  • 3

1 Answers1

3

I have modified this Ruby script to not require public keys and to allow direct (r,s) input. Funnily enough, I stumbled on this thread (created two hours ago as of writing) on Google searching for the exact same problem. Note that I am using Secp192r1 (due to a CTF challenge), so feel free to modify that to your likings:

require 'ecdsa'

msghash1_hex = '000000000000000000000000000000000000000000000000'
msghash2_hex = '000000000000000000000000000000000000000000000000'

sig1 = ECDSA::Signature.new(0x000000000000000000000000000000000000000000000000, 0x222222222222222222222222222222222222222222222222)
sig2 = ECDSA::Signature.new(0x000000000000000000000000000000000000000000000000, 0x333333333333333333333333333333333333333333333333)

group = ECDSA::Group::Secp192r1

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

msghash1 = hex_to_binary(msghash1_hex)
msghash2 = hex_to_binary(msghash2_hex)

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

# 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|
  private_key_maybe = field.mod(field.mod(sig1.s * k - z1) * field.inverse(r))
  puts 'Private key maybe: %#x' % private_key_maybe
  next
end

Signature.new(r,s) are the parameters above. msghash_hex is basically the hex version of your HASH(message) without 0x and with quotation marks around it to make it into a string to be processed later.

KelvZhan
  • 46
  • 3