Skip to content

Commit

Permalink
Updated to v0.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ArbitronNL committed Jun 2, 2023
1 parent e20577c commit f502313
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 42 deletions.
58 changes: 58 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Lint

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.18

- name: Caching
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
restore-keys: go-

- name: Dependencies
run: go mod vendor

- name: Checksum
run: go mod verify

- name: Codestyle
uses: golangci/golangci-lint-action@v2
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest

# Optional: working directory, useful for monorepos
working-directory: ./

# Optional: golangci-lint command line arguments.
args: --timeout 200s --build-tags=musl

# optionally use a specific version of Go rather than the latest one
go_version: '1.18'

- name: Golang Vulncheck
uses: Templum/govulncheck-action@v0.10.0
with:
go-version: 1.18
vulncheck-version: latest
fail-on-vuln: true
package: ./...
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Tests

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.18

- name: Caching
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: go-${{ hashFiles('**/go.sum') }}
restore-keys: go-

- name: Dependencies
run: go mod vendor

- name: Checksum
run: go mod verify

- name: Test
run: go test -short -v -failfast ./...
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Folders
/coverage/
/reports/
/.example/vendor

### Go ###
# Binaries for programs and plugins
Expand All @@ -22,3 +23,5 @@
### Go Patch ###
/vendor/
/Godeps/
.idea
.vscode
34 changes: 4 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,7 @@ go get -u github.com/bitonicnl/verify-signed-message

## Usage

```go
package main

import (
"fmt"

"github.com/btcsuite/btcd/chaincfg"

"github.com/bitonicnl/verify-signed-message/pkg"
)

func main() {
// Bitcoin Mainnet
fmt.Println(verifier.Verify(verifier.SignedMessage{
Address: "18J72YSM9pKLvyXX1XAjFXA98zeEvxBYmw",
Message: "Test123",
Signature: "Gzhfsw0ItSrrTCChykFhPujeTyAcvVxiXwywxpHmkwFiKuUR2ETbaoFcocmcSshrtdIjfm8oXlJoTOLosZp3Yc8=",
}))

// Bitcoin Testnet3
fmt.Println(verifier.VerifyWithChain(verifier.SignedMessage{
Address: "tb1qr97cuq4kvq7plfetmxnl6kls46xaka78n2288z",
Message: "The outage comes at a time when bitcoin has been fast approaching new highs not seen since June 26, 2019.",
Signature: "H/bSByRH7BW1YydfZlEx9x/nt4EAx/4A691CFlK1URbPEU5tJnTIu4emuzkgZFwC0ptvKuCnyBThnyLDCqPqT10=",
}, &chaincfg.TestNet3Params))
}
```

In this example it will output `true, <nil>`, since the signature is valid and there are no errors.
For examples, checkout the [example](/.example) folder.

## Support

Expand All @@ -69,10 +41,12 @@ This library tries to support as many signatures as possible.
- Bitcoin Core
- Any wallet that follows [BIP 137](https://github.com/bitcoin/bips/blob/master/bip-0137.mediawiki), example:
- Trezor: P2PKH, P2WPKH and P2WPKH-P2SH
- Taproot (P2TR)
- The verification is using the internal key, so only addresses without a tapscript are allowed.

**Currently not supported:**
- Pay-to-Witness-Script-Hash (P2WSH)
- Taproot ([as there is no consensus](https://github.com/trezor/trezor-firmware/issues/1943))
- BIP-322

## Development

Expand Down
31 changes: 31 additions & 0 deletions internal/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"errors"
"fmt"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
Expand All @@ -21,6 +23,7 @@ func ValidateP2PKH(recoveryFlag int, pubkeyHash []byte, addr btcutil.Address, ne
return false, errors.New("cannot use P2PKH for recovery flag 'BIP137 (Trezor) P2WPKH'")
}

// Generate the address and validate it
if p2pkhAddr, err := btcutil.NewAddressPubKeyHash(pubkeyHash, net); err != nil {
return false, err
} else if addr.String() != p2pkhAddr.String() {
Expand All @@ -39,6 +42,7 @@ func ValidateP2SH(recoveryFlag int, pubkeyHash []byte, addr btcutil.Address, net
return false, errors.New("cannot use P2SH for recovery flag 'BIP137 (Trezor) P2WPKH'")
}

// Generate the address and validate it
if scriptSig, err := txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(pubkeyHash).Script(); err != nil {
return false, err
} else if p2shAddr, err := btcutil.NewAddressScriptHash(scriptSig, net); err != nil {
Expand All @@ -57,6 +61,7 @@ func ValidateP2WPKH(recoveryFlag int, pubkeyHash []byte, addr btcutil.Address, n
return false, errors.New("cannot use P2WPKH for recovery flag 'P2PKH uncompressed'")
}

// Generate the address and validate it
if p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubkeyHash, net); err != nil {
return false, err
} else if addr.String() != p2wkhAddr.String() {
Expand All @@ -65,3 +70,29 @@ func ValidateP2WPKH(recoveryFlag int, pubkeyHash []byte, addr btcutil.Address, n

return true, nil
}

// ValidateP2TR ensures that the passed P2TR address matches the address generated from the public key hash, recovery flag and network.
func ValidateP2TR(recoveryFlag int, pubKey *btcec.PublicKey, addr btcutil.Address, net *chaincfg.Params) (bool, error) {
// Ensure proper address type will be generated
if lo.Contains[int](flags.Compressed(), recoveryFlag) {
return false, errors.New("cannot use P2TR for recovery flag 'compressed'")
} else if lo.Contains[int](flags.TrezorP2WPKHAndP2SH(), recoveryFlag) {
return false, errors.New("cannot use P2TR for recovery flag 'BIP137 (Trezor) P2WPKH-P2SH'")
} else if lo.Contains[int](flags.TrezorP2WPKH(), recoveryFlag) {
return false, errors.New("cannot use P2TR for recovery flag 'BIP137 (Trezor) P2WPKH'")
}

// Ensure proper public key
if _, err := schnorr.ParsePubKey(schnorr.SerializePubKey(pubKey)); err != nil {
return false, err
}

// Generate the address and validate it
if p2trAddr, err := btcutil.NewAddressTaproot(schnorr.SerializePubKey(txscript.ComputeTaprootKeyNoScript(pubKey)), net); err != nil {
return false, err
} else if addr.String() != p2trAddr.String() {
return false, fmt.Errorf("generated address '%s' does not match expected address '%s'", p2trAddr.String(), addr.String())
}

return true, nil
}
64 changes: 62 additions & 2 deletions internal/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"errors"
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

Expand Down Expand Up @@ -110,7 +112,7 @@ func (s *ValidateTestSuite) TestValidateP2SH() {
want: errors.New("generated address '3Nxee1CFDqFRtUrixREpNMhsmH9TBXcY48' does not match expected address 'Invalid'"),
},
{
name: "Invalid address for public key hash",
name: "Valid P2SH",
args: args{recoveryFlag: 35, pubKeyHash: pubKeyHash, addr: &RandomAddress{Address: "3Nxee1CFDqFRtUrixREpNMhsmH9TBXcY48"}},
want: nil,
},
Expand Down Expand Up @@ -151,7 +153,7 @@ func (s *ValidateTestSuite) TestValidateP2WPKH() {
want: errors.New("generated address 'bc1qs4c46q43meu623fz8km84ma93rjhef7z88rg99' does not match expected address 'Invalid'"),
},
{
name: "Invalid address for public key hash",
name: "Valid P2WPKH",
args: args{recoveryFlag: 32, witnessProg: pubKeyHash, addr: &RandomAddress{Address: "bc1qs4c46q43meu623fz8km84ma93rjhef7z88rg99"}},
want: nil,
},
Expand All @@ -165,6 +167,64 @@ func (s *ValidateTestSuite) TestValidateP2WPKH() {
}
}

func (s *ValidateTestSuite) TestValidateP2TR() {
// Generated via https://unisat.io/ which uses https://github.com/bitpay/bitcore
x, y := &btcec.FieldVal{}, &btcec.FieldVal{}
x.SetBytes(&[32]byte{199, 142, 160, 82, 151, 162, 66, 186, 11, 43, 16, 91, 237, 71, 91, 135, 150, 252, 234, 48, 99, 136, 19, 243, 89, 137, 196, 224, 241, 223, 158, 246})
y.SetBytes(&[32]byte{72, 92, 183, 101, 120, 192, 238, 204, 246, 77, 101, 137, 220, 171, 222, 21, 13, 79, 125, 140, 76, 22, 138, 145, 121, 157, 206, 101, 219, 101, 217, 105})

pubKey := btcec.NewPublicKey(x, y)

type args struct {
recoveryFlag int
pubKey *btcec.PublicKey
addr btcutil.Address
}
tests := []struct {
name string
args args
want error
}{
{
name: "Invalid recovery flag - compressed",
args: args{recoveryFlag: 33, pubKey: &btcec.PublicKey{}, addr: &RandomAddress{}},
want: errors.New("cannot use P2TR for recovery flag 'compressed'"),
},
{
name: "Invalid recovery flag - TrezorP2WPKH",
args: args{recoveryFlag: 36, pubKey: &btcec.PublicKey{}, addr: &RandomAddress{}},
want: errors.New("cannot use P2TR for recovery flag 'BIP137 (Trezor) P2WPKH-P2SH'"),
},
{
name: "Invalid recovery flag - TrezorP2WPKH",
args: args{recoveryFlag: 39, pubKey: &btcec.PublicKey{}, addr: &RandomAddress{}},
want: errors.New("cannot use P2TR for recovery flag 'BIP137 (Trezor) P2WPKH'"),
},
{
name: "Invalid public key",
args: args{recoveryFlag: 27, pubKey: btcec.NewPublicKey(&btcec.FieldVal{}, &btcec.FieldVal{}), addr: &RandomAddress{}},
want: secp256k1.Error{Err: secp256k1.ErrPubKeyNotOnCurve, Description: "invalid public key: x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve"},
},
{
name: "Invalid address for public key",
args: args{recoveryFlag: 27, pubKey: pubKey, addr: &RandomAddress{Address: "Invalid"}},
want: errors.New("generated address 'bc1pg48rw0vphy9mght5dr8s5prx92a44wpqmzk67xk8yjf5zlancj9sa3plhc' does not match expected address 'Invalid'"),
},
{
name: "Valid P2TR",
args: args{recoveryFlag: 27, pubKey: pubKey, addr: &RandomAddress{Address: "bc1pg48rw0vphy9mght5dr8s5prx92a44wpqmzk67xk8yjf5zlancj9sa3plhc"}},
want: nil,
},
}

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {
_, err := internal.ValidateP2TR(tt.args.recoveryFlag, tt.args.pubKey, tt.args.addr, &chaincfg.MainNetParams)
require.Equal(t, err, tt.want)
})
}
}

// RandomAddress implements the btcutil.Address interface and serves as a no-op to test these calls.
type RandomAddress struct {
Address string
Expand Down
21 changes: 11 additions & 10 deletions pkg/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func Verify(sig SignedMessage) (bool, error) {
}

// VerifyWithChain will verify a SignedMessage based on the recovery flag on the passed network.
// Supported address types are P2PKH, P2WKH, NP2WKH (P2WPKH), P2TR.
func VerifyWithChain(signedMessage SignedMessage, net *chaincfg.Params) (bool, error) {
// Check if message contains spaces that can be trimmed, if so run the verification with the trimmed message
// This is required because Electrum trims messages before signing
Expand Down Expand Up @@ -96,21 +97,21 @@ func VerifyWithChain(signedMessage SignedMessage, net *chaincfg.Params) (bool, e
// Get the hash from the public key, so we can check that address matches
publicKeyHash := internal.GeneratePublicKeyHash(recoveryFlag, publicKey)

switch address.(type) {
// Validate P2PKH
if _, ok := address.(*btcutil.AddressPubKeyHash); ok {
case *btcutil.AddressPubKeyHash:
return internal.ValidateP2PKH(recoveryFlag, publicKeyHash, address, net)
}

// Validate P2SH
if _, ok := address.(*btcutil.AddressScriptHash); ok {
case *btcutil.AddressScriptHash:
return internal.ValidateP2SH(recoveryFlag, publicKeyHash, address, net)
}

// Validate P2WPKH
if _, ok := address.(*btcutil.AddressWitnessPubKeyHash); ok {
case *btcutil.AddressWitnessPubKeyHash:
return internal.ValidateP2WPKH(recoveryFlag, publicKeyHash, address, net)
// Validate P2TR
case *btcutil.AddressTaproot:
return internal.ValidateP2TR(recoveryFlag, publicKey, address, net)
// Unsupported address
default:
return false, fmt.Errorf("unsupported address type '%s'", reflect.TypeOf(address))
}

// Catch all, should never happen
return false, fmt.Errorf("unexpected address type '%s'", reflect.TypeOf(address))
}
6 changes: 6 additions & 0 deletions pkg/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@ func (s *VerifyTestSuite) TestVerify() {
Message: " Lorem ipsum dolor sit amet, consectetur adipiscing elit. In a turpis dignissim, tincidunt dolor quis, aliquam justo. Sed eleifend eleifend tempus. Sed blandit lectus at ullamcorper blandit. Quisque suscipit ligula lacus, tempor fringilla erat pharetra a. Curabitur pretium varius purus vel luctus. Donec fringilla velit vel risus fermentum, ac aliquam enim sollicitudin. Aliquam elementum, nunc nec malesuada fringilla, sem sem lacinia libero, id tempus nunc velit nec dui. Vestibulum gravida non tortor sit amet accumsan. Nunc semper vehicula vestibulum. Praesent at nibh dapibus, eleifend neque vitae, vehicula justo. Nam ultricies at orci vel laoreet. Morbi metus sapien, pulvinar ut dui ut, malesuada lobortis odio. Curabitur eget diam ligula. Nunc vel nisl consectetur, elementum magna et, elementum erat. Maecenas risus massa, mattis a sapien sed, molestie ullamcorper sapien. ",
Signature: "HHOGSz6AUEEyVGoCUw1GqQ5qy9KvW5uO1FfqWLbwYxkQVsI+sbM0jpBQWkyjr72166yiL/LQEtW3SpVBR1gXdYY=",
},
// Generated via https://unisat.io/ which uses https://github.com/bitpay/bitcore
"p2tr": {
Address: "bc1pg48rw0vphy9mght5dr8s5prx92a44wpqmzk67xk8yjf5zlancj9sa3plhc",
Message: "this is a random message",
Signature: "G5Q4LobfmVKN4+CG/QF8r2mVBWE14nhbczdHWiCHaS8OcqUUzWF8A/chCyQbr95r1aG4TwUi6PZ01hDrtuuypmk=",
},
}

for i := range tests {
Expand Down

0 comments on commit f502313

Please sign in to comment.