require_relative 'spec_helper' describe BTC::Key do def verify_rfc6979_nonce(keyhex, msg, khex) keybin = BTC.from_hex(keyhex) hash = BTC.sha256(msg) k = BTC::OpenSSL.rfc6979_ecdsa_nonce(hash, keybin) k.to_hex.must_equal khex end def verify_rfc6979_signature(keyhex, msg, sighex) key = BTC::Key.new(private_key: keyhex.from_hex) hash = BTC.sha256(msg) sig = key.ecdsa_signature(hash) sig.to_hex.must_equal sighex end it "should use deterministic ECDSA nonce according to RFC6979" do verify_rfc6979_nonce("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", "sample", "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3") verify_rfc6979_nonce("0000000000000000000000000000000000000000000000000000000000000001", "Satoshi Nakamoto", "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15") verify_rfc6979_nonce("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "Satoshi Nakamoto", "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90") verify_rfc6979_nonce("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", "Alan Turing", "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1") verify_rfc6979_nonce("0000000000000000000000000000000000000000000000000000000000000001", "All those moments will be lost in time, like tears in rain. Time to die...", "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3") verify_rfc6979_nonce("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d") end it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979" do verify_rfc6979_signature("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", "sample", "3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124") verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001", "Satoshi Nakamoto", "3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5") verify_rfc6979_signature("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "Satoshi Nakamoto", "3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5") verify_rfc6979_signature("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", "Alan Turing", "304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea") verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001", "All those moments will be lost in time, like tears in rain. Time to die...", "30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21") verify_rfc6979_signature("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", "3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6") end it "should perform Diffie-Hellman multiplication" do alice = BTC::Key.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true) bob = BTC::Key.new(private_key: "2db963f0fe106f483d9afa73bd4e39a8ac4bbcb1fbec99d65bf59d85c8cb62ee".from_hex, public_key_compressed: true) dh_pubkey1 = alice.diffie_hellman(bob) dh_pubkey1.compressed_public_key.to_hex.must_equal "03735932754bc16e10febe40ee0280906d29459d477442f1838dcf27de3b5d9699" dh_pubkey2 = bob.diffie_hellman(alice) dh_pubkey2.compressed_public_key.to_hex.must_equal "03735932754bc16e10febe40ee0280906d29459d477442f1838dcf27de3b5d9699" end it "should support compressed public keys" do k = BTC::Key.new(private_key: "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a".from_hex, public_key_compressed: true) k.private_key.to_hex.must_equal "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a" k.public_key.to_hex.must_equal "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71" k.address.to_s.must_equal "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8" k.public_key_compressed.must_equal true k.compressed_public_key.to_hex.must_equal "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71" k.uncompressed_public_key.to_hex.must_equal "0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455" (k.compressed_key == k).must_equal true (k.uncompressed_key == k).must_equal false k.compressed_key.address.to_s.must_equal "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8" k.uncompressed_key.address.to_s.must_equal "1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T" end it "should support uncompressed public keys" do k = BTC::Key.new(private_key: BTC.from_hex("c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a"), public_key_compressed: false) k.private_key.to_hex.must_equal "c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a" k.public_key.to_hex.must_equal "0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455" k.address.to_s.must_equal "1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T" k.public_key_compressed.must_equal false k.compressed_public_key.to_hex.must_equal "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71" k.uncompressed_public_key.to_hex.must_equal "0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455" (k.compressed_key == k).must_equal false (k.uncompressed_key == k).must_equal true k.compressed_key.address.to_s.must_equal "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8" k.uncompressed_key.address.to_s.must_equal "1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T" end it "should sign and verify data using ECDSA signatures" do 300.times do |n| message = "Test message #{n}" private_key = "Key #{n}".sha256 key = BTC::Key.new(private_key: private_key) signature = key.ecdsa_signature(message.sha256) signature.bytesize.must_be :>=, 60 signature.bytesize.must_be :<=, 72 key2 = BTC::Key.new(public_key: key.public_key) result = key2.verify_ecdsa_signature(signature, message.sha256) result.must_equal true end end it "should test canonicality of the signature" do # Stress-test canonicality checks key = BTC::Key.new(private_key: BTC.sha256("some key")) 2560.times do |i| hash = BTC.sha256("tx#{i}") sig = key.ecdsa_signature(hash) + "\x01".b canonical = BTC::Key.validate_script_signature(sig) if !canonical puts BTC::Diagnostics.current.last_message end canonical.must_equal true end sig = "3045022100e81a33ac22d0ef25d359a5353977f0f953608b2733141239ec02363237ab6781022045c71237e95b56079e9fa88591060e4c1a4bb02c0cad1ebeb092749d4aa9754701".from_hex canonical = BTC::Key.validate_script_signature(sig) if !canonical puts BTC::Diagnostics.current.last_message end canonical.must_equal true end it "should test normalization of non-canonical signatures" do # # Generate non-canonical signatures # key = BTC::Key.new(private_key: BTC.sha256("some other key")) # 2560.times do |i| # hash = BTC.sha256("tx#{i}") # sig = key.ecdsa_signature(hash, normalized: false) + "\x01".b # if !BTC::Key.validate_script_signature(sig) # puts sig[0, sig.size - 1].to_hex # end # end %w[ 30460221009bf6f0f7480d0562f5f56bc53bc90d564d7ce15ecbd116716c583da7311098b8022100880fd3e1c788c144aeede40072e814bead8b470a5a7f978a3804363b4920dc37 304502206700757f4609501016944a7b3144fee587848ad108c79dd4eb7664ac374d646c022100b762e1010f4300fb3b15cde575c80ad405314472812051e34823a5051bfc13fe 3046022100e71c70039b4f3c3894a3e5d20d1e19f95fc7b216a3ad2f540bd817470c07530e022100ebacde4827011195bd789edb6e3bca4dd0d5b59cae6892ee88465ebb205674b3 3045022000ef0203c4df709ab28f701a6c340ecce0c30d60b717096c7a360d4a801371fe022100cd83e86d6d5f0304d8ef428ed928a5af3fcfec652fb334bbfb9e2750c8ebb51a 3046022100cda613be5da7afc13aa6e04399557603b10317ca09e53a8d24c4ea0d892a0a9702210081a3012d810d6228c532fc8225dc32babda8f7b7f749c656b84af14e41a50ef2 3045022063ec43b0a6515abe17636058aac61b11b2e11becbb49439b711cb868788fe52e022100ed350ebafa430efc8e55285aa94ee8365345238596412ac085f9fc0898bb0003 3046022100d4890791c4b9f9fc7a9e853da404df26ab2302c5046527faaf9ae9fd8c99213a022100daec31bda7aa8ce0d9459e14d9fe86feda806ff90b6dd9701a6d94fad04a2860 304502203ee680db8f247e005e84bb0db8e0a39bf47d32282c730b0f1b003c1f32891730022100d69b240494d0562de75b3916acc023711cc37c30d70760ab1fc35e8931738410 304502206f08b37b53fd27096161015c53e20e05e01af59a32333e2bb79fb629479bd3800221009b125d988cd210c2438e6a206f5b9ddd7b6eae67accb0b914fde6b2a80c39b21 3046022100bf94dadbff4a67716955fcc4e22c3c4c88852ef31c5a0c1145ad3d433c97705d022100f2c339e6a1cc6ce75fda29b9d47dc2e7f6fb1df4a2f5e8502a75faf63f2cc87b 3046022100cf268bfa6c9b0a10747979654e386fb64d18bd2ae69e549a0545dc2fe154a6cb022100f651c4389233482f8b99f39f2e2d1fd29c08795f0dad50de47860bca29a4300d 304502203a969e1650b758a6597c01beb050ae737838c24376fcf0cf8fb66b2bb1b291a7022100b59ba04b19a00becb4e41e964cec1c29fd5d080be53d844b020d613a71ae7f65 304602210085234fe98f3bb43e665b2d138de16a265751b83851b1fc50478359c731b9a185022100db2a37784987554344071e60df361d847e1ee2c18844321a7a1edb9a0b53cd92 3046022100b07a37a66c5db8c718147d16b74fbba048e632911be0db380821c399e4823702022100ff67919de148346fdb4d25676d4c4e42a5002002c9c7e20176783ceb46e08a02 3046022100a962f13335383fa0a2ad2c38568fe87301dcc7b8e78cafc05dc3e805c0ecb02b022100c15a38847cc259b0f616cb477ecde0ea0fe8767f0aac1e630e314fcde4c65487 3046022100a8ba33f415f456c7f8daac01824a17612927c4c2b35876caf26b63e33fa17b99022100b4e89ea2021afd554f5f048cc5b9208bba55e727133ca184b51de121654efb33 3045022079b58a6574851d07ab8779a6a6597c326f9181852f88156546ce2331f95bc5b2022100dea5a3b8f454a781e444854416b2a42bacc02d1d21ad385022f50b439c98c076 304502201ff1e2b23342bc5d2960822247084fb86e4be9a37bb0a6a9aad512b6627c2e0a0221008624ceebb150e1de1103b51e1ac533f43e8e7322a37c925baae0dcc90e272ea2 304502205ee578855612719d0f4a7bcaf149d5f283a12176e99a27042d9d8f4a9074f2dd022100f4d7b7b923de4d63e646bb6890ef3634bc317c6dd04e55f2ffa686e32574ccb7 3046022100c2e4f75fd6c294c03dd684608212ec65240bd3319abe20663c7a24676ad8ef600221008743b32fd6051c2eaf326f2e824d3a8ca0ebdc1aa25175ee6b61c1f694b9cfbd 30450220450d1e68977e2ad2776925725e444eb9a58743459a4fd15188552cfc171f8798022100edac0b9210f02f0d8d4201729c8d62c7c302cb7c1a6f9f2f7741c7f99a3518a2 3045022018cddb05aa87011d7fe3ba56d6657bd813eeab9c43e2422af2f44cdd3bd8bc3a022100c53eb96ba81e12e59da73846223310ea6cb325a42ab2a081a101d99c5e3bfeec 304502200be9a279cca265b4e273c165d877474ab854b14ce2b17dff958dc932eae3d9e8022100e45c9087e78ff0120ae2f911299f64c995c61d6750a5913eaac36f096b560685 3045022062d08bff9580238c8cd62d9d64e9c1d518932886651f46a445bff773993500f00221008029263cc9ec64589bbbe140995096c257a1fbacf5f9f5ee69a69b96c626cc7a ].each do |hex_sig| sig = hex_sig.from_hex canonical = BTC::Key.validate_script_signature(sig + "\x01".b) canonical.must_equal false sig2 = BTC::Key.normalized_signature(sig) sig2.wont_equal nil sig2.wont_equal sig canonical = BTC::Key.validate_script_signature(sig2 + "\x01".b) if !canonical puts BTC::Diagnostics.current.last_message end canonical.must_equal true # Non-canonical signature must be normalized sig3 = BTC::Key.validate_and_normalize_script_signature(sig + "\x01".b) sig3.must_equal sig2 + "\x01".b # Canonical signature should stay the same sig4 = BTC::Key.validate_and_normalize_script_signature(sig2 + "\x01".b) sig4.must_equal sig2 + "\x01".b end end end