Bitcoin Forum
May 04, 2024, 07:36:29 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Signature Generation/Verification - WTF?  (Read 1425 times)
etotheipi (OP)
Legendary
*
expert
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
July 13, 2011, 05:13:35 AM
 #1

Of course, the devil is in the details... I've been pouring over the bitcoin specification protocol, finally figuring most of what I need, in terms of pulling public keys and signature components out of a transaction on block explorer.  Now that I have the public key and signature from a particular transaction, I want to verify the signature... but I can't figure out what block of data to verify! 

By the way block explorer is organized, it looks like it's signing the OutPoint structure, but that doesn't make sense, because the recipient address would have to be in the signed data (or else the node receiving the transaction could just replace the recipient addr with their own).  So what is it?

theSignature = privateKey.sign( sha256(sha256( WHAT?!? ) ) )

I've been pounding my head over it, source diving, searching the internet and reading specs.  Unfortunately, the C++ code is too abstract for me to follow.  I hope someone can just tell me!

Thanks,
Eto

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
1714851389
Hero Member
*
Offline Offline

Posts: 1714851389

View Profile Personal Message (Offline)

Ignore
1714851389
Reply with quote  #2

1714851389
Report to moderator
1714851389
Hero Member
*
Offline Offline

Posts: 1714851389

View Profile Personal Message (Offline)

Ignore
1714851389
Reply with quote  #2

1714851389
Report to moderator
1714851389
Hero Member
*
Offline Offline

Posts: 1714851389

View Profile Personal Message (Offline)

Ignore
1714851389
Reply with quote  #2

1714851389
Report to moderator
Unlike traditional banking where clients have only a few account numbers, with Bitcoin people can create an unlimited number of accounts (addresses). This can be used to easily track payments, and it improves anonymity.
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
1714851389
Hero Member
*
Offline Offline

Posts: 1714851389

View Profile Personal Message (Offline)

Ignore
1714851389
Reply with quote  #2

1714851389
Report to moderator
1714851389
Hero Member
*
Offline Offline

Posts: 1714851389

View Profile Personal Message (Offline)

Ignore
1714851389
Reply with quote  #2

1714851389
Report to moderator
Stefan Thomas
Full Member
***
Offline Offline

Activity: 234
Merit: 100


AKA: Justmoon


View Profile WWW
July 13, 2011, 05:23:01 AM
 #2

This is one of the most complicated parts of Bitcoin imho. The data that is signed is a double-SHA256 hash of a specially serialized version of the transaction.

First, the transaction is copied. The input being signed is replaced with the scriptPubKey of the corresponding txout.

With the default hashType SIGHASH_ALL, we're done now. But for the other hashTypes SIGHASH_NONE and SIGHASH_SINGLE as well as the SIGHASH_ANYONECANPAY flag, some more steps are needed. See the function SignatureHash in script.cpp for details.

After that, the transaction is serialized and the hashType is appended as a single byte, followed by three zero bytes, e.g. 01000000.

Then Bitcoin calculates the double SHA256 of that and signs the resulting hash.

I implemented this twice, once in pure JavaScript, once in Node.js. Both times it was a royal pain in the ***.

Twitter: @justmoon
PGP: D16E 7B04 42B9 F02E 0660  C094 C947 3700 A4B0 8BF3
etotheipi (OP)
Legendary
*
expert
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
July 13, 2011, 05:41:29 AM
 #3

Wow, I guess I can't feel bad about this.  I wasn't even close to figuring it out!  Why is this not documented anywhere!?! 

So I am trying to submit a new transaction (TxNew) which will have multiple OutPoints from multiple old transactions in other blocks (TxOld1, TxOld2, ...)

Which transaction is being copied? 

TxNew = [version,  numIn,  {TxIn1, TxIn2, TxIn3},  numOut,  {TxOut1, TxOut2},  locktime]

Each TxIn object has a script in it   [ TxOldHash, OutIndex, numScriptBytes, SCRIPT, Sequence ]
Each TxOut object has a script in it [ Amt, numScriptBytes, PK_SCRIPT]

First of all... I have broken apart ONE of these scripts, but I can't even remember which one.  It is [DerSignature, PublicKey] ... is this SCRIPT?  or PK_SCRIPT?  Or is that only part of one of those scripts?
Second, which scripts and which parts of them am I moving around?   And which transaction is serialized?
Third, if my transaction has multiple inputs, won't I need multiple signatures?

Wow, this is so terribly confusing.  And I'm relatively fluent in C++, but I'm having a terrible time tracing through the code...

-Eto

P.S. -- BTW, what should the locktime be?  What is the sequence value?   What is your BTC address so I can send you a small donation for answering all these questions?  Smiley

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
Stefan Thomas
Full Member
***
Offline Offline

Activity: 234
Merit: 100


AKA: Justmoon


View Profile WWW
July 13, 2011, 05:54:57 AM
 #4

Which transaction is being copied?

The one you're signing, TxNew.


It is [DerSignature, PublicKey] ... is this SCRIPT?  or PK_SCRIPT?  Or is that only part of one of those scripts?

This is scriptPubKey, that's what you replace the input that is being signed with.


Second, which scripts and which parts of them am I moving around?

See my original post for the hashType == 1 case. It sounds like you're trying to sign transactions you create yourself, so you probably don't have to worry about other hashTypes.


And which transaction is serialized?

You serialize the copy you created in the first step.


Third, if my transaction has multiple inputs, won't I need multiple signatures?

Yes, you repeat the procedure for each input.


Wow, this is so terribly confusing.  And I'm relatively fluent in C++, but I'm having a terrible time tracing through the code...

I recommend again the function SignatureHash(). Read it, sleep on it, read it again, you'll get it.


P.S. -- BTW, what should the locktime be?

Locktime should be 0 unless you want to do some fancy stuff. Smiley


What is the sequence value?

Sequence should be the maximum value: 0xffffffff (which is the same as -1)


What is your BTC address so I can send you a small donation for answering all these questions?  Smiley

Not necessary. But if you can't restrain yourself: 1DEjwkdqcpteKsxXTxtMfgrpS6LjqW7k4K

Twitter: @justmoon
PGP: D16E 7B04 42B9 F02E 0660  C094 C947 3700 A4B0 8BF3
Mike Hearn
Legendary
*
expert
Offline Offline

Activity: 1526
Merit: 1129


View Profile
July 13, 2011, 10:18:22 AM
 #5

If you can't read C++ you may find the Java version easier:

   http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Transaction.java#311

This stuff is documented, on the wiki page for the CHECKSIG  opcode:

https://en.bitcoin.it/wiki/OP_CHECKSIG
vector76
Member
**
Offline Offline

Activity: 70
Merit: 18


View Profile
July 13, 2011, 11:03:47 AM
 #6

The entire transaction (serialized and hashed) is what is signed, minus the scriptSig fields, because it is impossible for a signature to sign itself.

When generating the signature, the scriptSig fields are left blank.  Then after the signatures have been generated, they are filled in.  When verifying the signature, the scriptSig fields are wiped out, and the signature is verified.

Now, what I've written above is almost but not exactly what happens, because in fact all the scriptSig fields are blank except for the one being signed/verified.  For the one being signed/verified, the scriptSig field is populated with the public key and only the signature is absent.

I don't really understand why the public key is inserted rather than the simpler method of leaving them entirely blank, but I am guessing someone smarter than me found an important reason for it.
etotheipi (OP)
Legendary
*
expert
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
July 15, 2011, 04:23:08 AM
 #7

As soon as I think I understand it, I don't understand it anymore!  Step three in the thread above talks about "blanking" the signature field in the subscript.  But this is on the TxOut script, which doesn't have a sig/pubkey... I thought...?

The way I understand it is NewTx contains a TxIn object referencing a TxOut on an OldTx object. 

Execute 1:  NewTx.TxIn.script is only [<signature> <pubkey>]
Execute 2:  OldTx.TxOut.script is  ["OP_DUP OP_HASH160 <recipient addr hash> OP_EQUALVERIFY OP_CHECKSIG"]

So the first script is run first to put the sig and key onto the stack, then the second script is run with the stack from the first script, which will verify the public key matches the addr and verify the signature.  But I thought that the OldTx.TxOut.script doesn't contain a signature/pubkey.  But only TxOut.script contains OP_CHECKSIG which is what would be OP'ing the verification.  So, what am I missing?

-Eto

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
casascius
Mike Caldwell
VIP
Legendary
*
Offline Offline

Activity: 1386
Merit: 1136


The Casascius 1oz 10BTC Silver Round (w/ Gold B)


View Profile WWW
July 15, 2011, 04:29:52 AM
 #8

As soon as I think I understand it, I don't understand it anymore!  Step three in the thread above talks about "blanking" the signature field in the subscript.  But this is on the TxOut script, which doesn't have a sig/pubkey... I thought...?

The way I understand it is NewTx contains a TxIn object referencing a TxOut on an OldTx object.  

Execute 1:  NewTx.TxIn.script is only [<signature> <pubkey>]
Execute 2:  OldTx.TxOut.script is  ["OP_DUP OP_HASH160 <recipient addr hash> OP_EQUALVERIFY OP_CHECKSIG"]

So the first script is run first to put the sig and key onto the stack, then the second script is run with the stack from the first script, which will verify the public key matches the addr and verify the signature.  But I thought that the OldTx.TxOut.script doesn't contain a signature/pubkey.  But only TxOut.script contains OP_CHECKSIG which is what would be OP'ing the verification.  So, what am I missing?

-Eto
The two scripts are combined together to create one script, which is executed.  If the script is executed and leaves TRUE on the stack, the coins are unlocked and can be spent.
Step 1, signature is pushed on to the stack.
Step 2, pubkey is pushed onto the stack.  Stack contains from top: pubkey, signature
Step 3, OP_DUP duplicates the top item.  Stack contains from top: pubkey, pubkey, signature.  (it is duplicated because two copies are needed: one copy will be consumed by OP_HASH160, the other by OP_CHECKSIG)
Step 4, OP_HASH160 converts pubkey into addrhash.  Stack contains from top: addrhash, pubkey, signature.
Step 5, the recipient address hash is pushed to the stack, so (assuming they match) the hash appears twice.  Stack from top: addrhashR, addrhash, pubkey, signature
Step 6, OP_EQUALVERIFY confirms that addrhashR equals addrhash, consuming both.  Transaction fails if they're not equal.  Stack now: pubkey, signature
Step 7, OP_CHECKSIG makes sure that signature is a valid transaction signature with respect to pubkey.  Transaction fails if not valid, leaves behind TRUE if valid.

My guess as to what "blanking" means - the signature isn't known at the time the hash is being created, because the hash is needed for the signature.  So I would guess that blanking means that a placeholder is hashed in place of the signature before its final value is known.  When you are creating a transaction, you are signing that transaction (the new one not the old one).

Companies claiming they got hacked and lost your coins sounds like fraud so perfect it could be called fashionable.  I never believe them.  If I ever experience the misfortune of a real intrusion, I declare I have been honest about the way I have managed the keys in Casascius Coins.  I maintain no ability to recover or reproduce the keys, not even under limitless duress or total intrusion.  Remember that trusting strangers with your coins without any recourse is, as a matter of principle, not a best practice.  Don't keep coins online. Use paper or hardware wallets instead.
etotheipi (OP)
Legendary
*
expert
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
July 15, 2011, 04:41:27 AM
 #9

I would've loved this description 12 hours ago before I spent all day studying understanding scripting, and then coded it all up in python.  Of course, the one OP code I'm struggling with is OP_CHECKSIG...

Your first line is what confuses me.  You say they are combined to create one script, but they don't appear to be, in that example on the other thread:

     subscript = tx1.txout[0].script   
     subscript = subscript.replace(chr(len(signature)) + signature, "")

New.TxIn contains the [sig, pubkey], Old.TxOut contains the script to be run afterwards.  But that script does not contain a signature/pubkey, so when I am running that script and told to blank it out... why am I doing it?

I need a really good graphical solution... maybe when I finally understand this, I will make one.


Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
vector76
Member
**
Offline Offline

Activity: 70
Merit: 18


View Profile
July 15, 2011, 11:02:55 AM
 #10

New.TxIn script pushes the pubkey and signature onto the stack, which effectively become inputs to the Old.TxOut script.

Old.TxOut script verifies that the pubkey and signature (provided by New.TxIn) satisfy the requirement:  The pubkey must hash to the specified value, and the pubkey must verify the signature of the transaction.

OldTx.TxOut.script does reference a pubkey, because it contains the hash of the pubkey.  You cannot supply any old pubkey.  And then the private key corresponding to the pubkey is required to claim the funds, because the transaction must be signed by the private key corresponding to the pubkey.  Only then will the signature of the transaction verify against the pubkey and unlock the funds.

When the transaction signature is being generated or verified is the only time when the scripts are temporarily blanked, because the signatures themselves cannot be included in the data being signed.


Suppose you have a hash function
hash = H(data)
And you also have a signature function:
signature = S(privkey, data)
And a corresponding signature verification function
true/false = V(pubkey, signature, data)
And you also have a 'simplification' function which strips out the input scripts and/or signatures of a transaction
T' = Q(T)

The OldTx.TxOut.script effectively says:
To claim the funds in this output, place on the stack a pubkey K and a signature G in a transaction T such that H(K) matches the specified value, and V(K, G, Q(T)) verifies to true.

Only someone with the private key can produce G.  Malicious nodes cannot modify T and pass it along, because doing so would cause the signature verification V(K, G, Q(T)) to fail. 

In case it wasn't already clear, V(K, G, Q(T)) is what OP_CHECKSIG does, using K and G from the stack.  It gets T through other means (not an input from the stack, but an implicit input to OP_CHECKSIG).
etotheipi (OP)
Legendary
*
expert
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
July 15, 2011, 03:46:24 PM
 #11

Gah, I really appreciate your guys' explanation, but you are explaining everything else I have already spent a dozen hours figuring out.  I completely understand scripting, OP codes, script stacks, signing, hashing and cryptography in general (I have programmed EC cryptography modules before).   What I don't get is Q(T) which is step 3 identified in https://en.bitcoin.it/wiki/OP_CHECKSIG.  I didn't understand why step 3 says to blank out the signature in "subscript" when subscript doesn't contain a signature.  I thought maybe I didn't understand, and maybe it does actually contain one...

However, I have been looking at this thread:  http://forum.bitcoin.org/index.php?topic=18051.0 and realized I could actually execute the python code there... if I assume that that code is correct, then my question is further justified.  I printed out the state of "subscript" before and after step 3:

Before:
76 a9 1402bf4b2889c6ada8190c252e70bde1a1909f9617 88 ac 30

After:
76 a9 1402bf4b2889c6ada8190c252e70bde1a1909f9617 88 ac 30

Both are the same:
(OP_DUP) (OP_HASH160)  <AddrHash>  (OP_EQUALVERIFY) (OP_CHECKSIG) \x30


So indeed, there is no sig/key in the txout script, step 3 does nothing. 
So let me ask more directly:  If this script is correct, what is the point of step 3?  Only for non-standard scripts?  If it is not correct, what is wrong with it?

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
vector76
Member
**
Offline Offline

Activity: 70
Merit: 18


View Profile
July 15, 2011, 04:06:51 PM
 #12

Oh, you're right, that is weird.  I thought it was stripping the signature out of NewTx.TxIn, but it is instead stripping the signature from OldTx.TxOut (which will not normally contain the signature anyway) and it inserts the stripped OldTx.TxOut into the transaction's input script slot before verifying the signature.

I don't see a reason for it in the usual transaction, and I can't see how it would ever be useful.  Someone with a better idea of some of the strange transaction structures will have to describe how it could be useful.
etotheipi (OP)
Legendary
*
expert
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
July 16, 2011, 08:11:00 PM
 #13

FYI, I have created the mother of all illustrative diagrams and posted it in it's own thread.  I think this should finally clear up everything (since it describes exactly what I did to finally get my code working!).

http://forum.bitcoin.org/index.php?topic=29416.msg370321#msg370321

Please let me know if you see any errors in the diagram so I can fix them!

-Eto

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!