in3rsha / bitcoin-utxo-dump

Get a list of UTXOs (unspent transaction outputs) from your Bitcoin Core client.
MIT License
241 stars 103 forks source link

pubkey point compression/decompression #19

Closed paulh69 closed 4 years ago

paulh69 commented 4 years ago

struggling to add working pubkey point decompression script. I've seen python examples but unable to get the math to workout quite the same in golang at the moment

e.g. https://bitcoin.stackexchange.com/questions/86234/how-to-uncompress-a-public-key

! /usr/bin/env python3

import binascii

p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F

def decompress_pubkey(pk): x = int.from_bytes(pk[1:33], byteorder='big') y_sq = (pow(x, 3, p) + 7) % p y = pow(y_sq, (p + 1) // 4, p) if y % 2 != pk[0] % 2: y = p - y y = y.to_bytes(32, byteorder='big') return b'\x04' + pk[1:33] + y

print(binascii.hexlify(decompress_pubkey(binascii.unhexlify('0229b3e0919adc41a316aad4f41444d9bf3a9b639550f2aa735676ffff25ba3898'))).decode()) print(binascii.hexlify(decompress_pubkey(binascii.unhexlify('02f15446771c5c585dd25d8d62df5195b77799aa8eac2f2196c54b73ca05f72f27'))).decode())

in3rsha commented 4 years ago

What does you Golang code look like so far?

paulh69 commented 4 years ago

plenty of non-working examples:

// https://play.golang.org/p/2Wyt0tG0MS - pubkey points decompression - non-working

package main

import ( "crypto/elliptic" "encoding/hex" "fmt" "math/big" )

func main() {

// Points are decompressed by solving for y in the equation used for secp256k1's elliptic curve where x is the last 32 bytes of your public key. 
// The equation is y^2 = x^3 + 7. You will get 2 possible y values, one even and one odd. The correct one is indicated by the prefix byte of your
// public key which indicates whether y is even or odd.
// Note that all operations must be modulo p which is defined by secp256k1's spec as being 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F.
//

// Example value, generated from Ruby OpenSSL bindings

// compressed_hex := "02A8825EBBF4054E83560EC27A0A9143D272586A0193F1299F6B00A0A62B6A9FF8" // expected output y=76522963597422956337536269116481171608116861681996601930470996322015033166962 // compressed_hex := "05cf85155acd39922c881b61861b6ca9114628ff9f7f5c490a3a652d2125d09e12" // expected output for x=04cf85155acd39922c881b61861b6ca9114628ff9f7f5c490a3a652d2125d09e12 // y=d95e98b2e1b9fd9eb26d1e57639ad8f9a92e9bc453a828686364668a2d912885 compressed_hex := "047a488354d9d5414de09b7121b80b973c991b76998ad68756d8cf4560c0ddcbe2" // expected output (047a488354d9d5414de09b7121b80b973c991b76998ad68756d8cf4560c0ddcbe24ae72a77cca86f6d1d11b1b796e3f14caa5852e6d4c60e9bebedc71673b58ec0) // y=4ae72a77cca86f6d1d11b1b796e3f14caa5852e6d4c60e9bebedc71673b58ec0 // check with https://iancoleman.io/bitcoin-key-compression/

compressed_bytes, _ := hex.DecodeString(compressed_hex)

// Split the sign byte from the rest
sign_byte := uint(compressed_bytes[0])
x_bytes := compressed_bytes[1:]

// Convert to big Int.
x := new(big.Int).SetBytes(x_bytes)

// We use 3 a couple of times
three := big.NewInt(3)

// The params for P256
c := elliptic.P256().Params()

// The equation is y^2 = x^3 - 3x + b
// x^3, mod P
x_cubed := new(big.Int).Exp(x, three, c.P)

// 3x, mod P
three_X := new(big.Int).Mul(x, three)
three_X.Mod(three_X, c.P)

// x^3 - 3x
y_squared := new(big.Int).Sub(x_cubed, three_X)

// ... + b mod P
y_squared.Add(y_squared, c.B)
y_squared.Mod(y_squared, c.P)

// Now we need to find the square root mod P.
// This is where Go's big int library redeems itself.
y := new(big.Int).ModSqrt(y_squared, c.P)
if y == nil {
    // If this happens then you're dealing with an invalid point.
    // Panic, return an error, whatever you want.
    fmt.Println("Invalid point")
    return
}

// Finally, check if you have the correct root. If not you want
// -y mod P
if y.Bit(0) != sign_byte & 1 {
    y.Neg(y)
    y.Mod(y, c.P)
}

fmt.Printf("y coord is %v\n", y)

}

paulh69 commented 4 years ago

another example would be https://github.com/btcsuite/btcd/blob/master/btcec/pubkey.go

in3rsha commented 4 years ago

You're using the wrong curve parameters in your code: https://www.johndcook.com/blog/2018/08/21/a-tale-of-two-elliptic-curves/

Also, I cannot help you any further than this. Based on the repositories you have forked and the questions you are are asking, it doesn't appear to me that you're embarking upon an entirely noble pursuit.