1

I have 2 transactions.

0100000001494d40d44b9ea0d652fca9258ab7caa42551eb52975887f96fb50cd632c8b497000000008c493046022100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e022100b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd014104cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6ffffffff0120030000000000001976a91468979da3812f340e577fc379c20a8f8a505f02dd88ac00000000



0100000001494d40d44b9ea0d652fca9258ab7caa42551eb52975887f96fb50cd632c8b427000000008c493046022100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e022100be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045014104cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6ffffffff0120030000000000001976a91468979da3812f340e577fc379c20a8f8a505f02dd88ac00000000

I know that the k values and hence the R values of both these transactions are the same.

So now how do I recover the private key from these two transactions in hex?

A detailed step by step procedure on how to arrive at the answer will be appreciated.

0
void Bitcoin::test ( )
{
  const QByteArray t1 ( QByteArray::fromHex ( "010000000...ac00000000" ) ); // strip
  const QByteArray t2 ( QByteArray::fromHex ( "010000000...ac00000000" ) ); // strip
  Stream stream1 ( t1 );
  Stream stream2 ( t2 );
  const Transaction tx1 ( stream1 );
  const Transaction tx2 ( stream2 );
  _trace ( QString ( "txid1=%1" ).arg ( tx1.getKey ( ).toString ( ) ) );
  _trace ( QString ( "txid2=%1" ).arg ( tx2.getKey ( ).toString ( ) ) );
  const TxInput in1 ( tx1.getInput ( 0 ) );
  const TxInput in2 ( tx2.getInput ( 0 ) );
  const QByteArray script1 ( in1.getScript ( ) );
  const QByteArray script2 ( in2.getScript ( ) );
  EvalScript escr1 ( script1 );
  EvalScript escr2 ( script2 );
  xassert ( escr1.evalInput ( ) == EVAL_I_SIG_PUB  );
  xassert ( escr2.evalInput ( ) == EVAL_I_SIG_PUB  );
  _trace ( QString ( "pub1=%1" ).arg ( escr1.getInPubKey ( ).toHex ( ).constData ( ) ) );
  _trace ( QString ( "pub2=%1" ).arg ( escr2.getInPubKey ( ).toHex ( ).constData ( ) ) );
  xassert ( escr1.getInPubKey ( ) == escr2.getInPubKey ( ) ); // same pubkeys?
  const MyByteArray sig1 ( escr1.getInSignature ( ) );
  const MyByteArray sig2 ( escr2.getInSignature ( ) );
  char buf [64];
  sig1.signatureRS ( buf );
  const MyKey32 R1 ( buf );
  const MyKey32 S1 ( buf + 32 );
  _trace ( QString ( "R1=%1" ).arg ( R1.toStringRev ( ) ) );
  _trace ( QString ( "S1=%1" ).arg ( S1.toStringRev ( ) ) );
  sig2.signatureRS ( buf );
  const MyKey32 R2 ( buf );
  const MyKey32 S2 ( buf + 32 );
  _trace ( QString ( "R2=%1" ).arg ( R2.toStringRev ( ) ) );
  _trace ( QString ( "S2=%1" ).arg ( S2.toStringRev ( ) ) );
  xassert ( R1 == R2 ); // are R-values really equal?
  const QByteArray xsc ( MyByteArray ( )
             .putInt8 ( OP_DUP )
             .putInt8 ( OP_HASH160 )
             .putPush ( MyKey20::calc ( escr1.getInPubKey ( ) ) )
             .putInt8 ( OP_EQUALVERIFY )
             .putInt8 ( OP_CHECKSIG ) );
  const MyKey32 Z1 ( tx1.getRawHash ( 0, xsc ) );
  const MyKey32 Z2 ( tx2.getRawHash ( 0, xsc ) );

  _trace ( QString ( "Z1=%1" ).arg ( Z1.toStringRev ( ) ) );
  _trace ( QString ( "Z2=%1" ).arg ( Z2.toStringRev ( ) ) );

  const MyKey20 address ( MyKey20::calc ( escr1.getInPubKey ( ) ) );
  const MyKey32 priv ( MyKey32::getPrivateKey ( R1, S1, Z1, S2, Z2, address ) );
  if ( priv.getAddressHashClassic ( ) == address )
    _trace ( QString ( "classic priv=%1" ).arg ( priv.toStringWif ( false ) ) );
  if ( priv.getAddressHashCompressed ( ) == address )
    _trace ( QString ( "compressed priv=%1" ).arg ( priv.toStringWif ( true ) ) );
}

and the output is:

"txid1=8c1d0dbe2fb84ec4b14ee146e3e899623b3e5c30aef4c8238cf9fe2f8406246c"
"txid2=40e334f8addc00a67209b2818ceb32e1ebd5c28133479f3e408d99f28267828d"
"pub1=04cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6"
"pub2=04cb5035b2d8fc1019146a7e4fba751a9a9bfafa2d1e35528a9397051c6e630513694ab925adc4083dcafc86c92796a2b6ae977adfc0bb47df9106a6a9263d35f6"
"R1=bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e"
"S1=b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd"
"R2=bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e"
"S2=be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045"
"Z1=187509d80dacdd9b35f626dedd2856c7a318455e2d37bcacfe006406b60cf2fe"
"Z2=41d91692534416f8e8d6f3480bb1eae488ce4e24e3afb2c830534411ea5efb1c"
"classic priv=5K1V3pAHQ5DgALeiCC2NfgKESKEaygmCLtBRPFbw9eu1uJrDHW1"

... aaaaaaand the private key is 5K1V3pAHQ5DgALeiCC2NfgKESKEaygmCLtBRPFbw9eu1uJrDHW1

2

Hopefully most web wallets and wallet services are not re-using R values to sign transactions since this has been a known vulnerability for a while now. On a very low level you are turning an equation like x + y = 10 into x + 2 = 10 making it easy to find x.

First find the reused R values, in your case it's: 2100bdc4132fbd2d9334b54531babdd1f8ecc0a37e75b04c9521684490da25e5938e

Then find the signature coordinates in the two transactions:

tx1 coordinate(S1):
00b973d6c776f1a8db2b7bc654c7e55384422677f41928ec4c30643f383d587bfd
tx2 coordinate(S2):
00be1c50365d8ed4e269cb7350dee3b5fcc9f81856726a590dcdd3f538dafcd045

Now the tricky part, you can use the bitcoin secp256k1 curve to reverse calculate the private key, but you'll need also need to use a bitcoin transaction library to use op_checksig() for the missing sig hashes to find SOP1 and SOP2. I'll let one of the more advanced users explain finding the above two values, as I don't fully understand it myself. Link

p = parameter for secp256k1
Limit the secp256k1 field:
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
K = GF(p)

In the end it's:

K = (sop1*s2 - sop2*s1)/(r*(s1-s2))

This will output an integer, will need to be hexcoded, and then converted to WIF.

Source:
https://bitcointalk.org/index.php?topic=977070.msg10669517#msg10669517
https://bitcointalk.org/index.php?topic=977070.msg11161149#msg11161149

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.