diff --git a/compat/bsm/sign.go b/compat/bsm/sign.go index bd3e151..70a83c7 100644 --- a/compat/bsm/sign.go +++ b/compat/bsm/sign.go @@ -14,7 +14,11 @@ const hBSV = "Bitcoin Signed Message:\n" // SignMessage signs a string with the provided PrivateKey using Bitcoin Signed Message encoding // sigRefCompressedKey bool determines whether the signature will reference a compressed or uncompresed key // Spec: https://github.com/bitcoin/bitcoin/pull/524 -func SignMessage(privateKey *ec.PrivateKey, message []byte, sigRefCompressedKey bool) ([]byte, error) { +func SignMessage(privateKey *ec.PrivateKey, message []byte) ([]byte, error) { + return SignMessageWithCompression(privateKey, message, true) +} + +func SignMessageWithCompression(privateKey *ec.PrivateKey, message []byte, sigRefCompressedKey bool) ([]byte, error) { if privateKey == nil { return nil, errors.New("private key is required") } diff --git a/compat/bsm/sign_test.go b/compat/bsm/sign_test.go index 8e5ce93..93027a5 100644 --- a/compat/bsm/sign_test.go +++ b/compat/bsm/sign_test.go @@ -19,7 +19,7 @@ func TestSigningCompression(t *testing.T) { if err != nil { t.Errorf("Get address err %s", err) } - sig, err := compat.SignMessage(testKey, testData, true) + sig, err := compat.SignMessage(testKey, testData) if err != nil { t.Errorf("Failed to sign compressed %s", err) } @@ -33,9 +33,96 @@ func TestSigningCompression(t *testing.T) { // TestSignMessage will test the method SignMessage() func TestSignMessage(t *testing.T) { - t.Parallel() + var tests = []struct { + inputKey string + inputMessage string + expectedSignature string + expectedError bool + }{ + { + "0499f8239bfe10eb0f5e53d543635a423c96529dd85fa4bad42049a0b435ebdd", + "test message", + "IFxPx8JHsCiivB+DW/RgNpCLT6yG3j436cUNWKekV3ORBrHNChIjeVReyAco7PVmmDtVD3POs9FhDlm/nk5I6O8=", + false, + }, + { + "ef0b8bad0be285099534277fde328f8f19b3be9cadcd4c08e6ac0b5f863745ac", + "This is a test message", + "H+zZagsyz7ioC/ZOa5EwsaKice0vs2BvZ0ljgkFHxD3vGsMlGeD4sXHEcfbI4h8lP29VitSBdf4A+nHXih7svf4=", + false, + }, + { + "0499f8239bfe10eb0f5e53d543635a423c96529dd85fa4bad42049a0b435ebdd", + "This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af. This time I'm writing a new message that is obnixiously long af.", + "HxRcFXQc7LHxFNpK5lzhR+LF5ixIvhB089bxYzTAV02yGHm/3ALxltz/W4lGp77Q5UTxdj+TU+96mdAcJ5b/fGs=", + false, + }, + { + "93596babb564cbbdc84f2370c710b9bcc94333495b60af719b5fcf9ba00ba82c", + "This is a test message", + "IIuDw09ffPgEDuxEw5yHVp1+mi4QpuhAwLyQdpMTfsHCOkMqTKXuP7dSNWMEJqZsiQ8eKMDRvf2wZ4e5bxcu4O0=", + false, + }, + { + "50381cf8f52936faae4a05a073a03d688a9fa206d005e87a39da436c75476d78", + "This is a test message", + "ILBmbjCY2Z7eSXGXZoBI3x2ZRaYUYOGtEaDjXetaY+zNDtMOvagsOGEHnVT3f5kXlEbuvmPydHqLnyvZP3cDOWk=", + false, + }, + { + "c7726663147afd1add392d129086e57c0b05aa66a6ded564433c04bd55741434", + "This is a test message", + "IOI207QUnTLr2Ll+s4kUxNgLgorkc/Z5Pc+XNvUBYLy2TxaU6oHEJ2TTJ1mZVrtUyHm6e315v1tIjeosW3Odfqw=", + false, + }, + { + "c7726663147afd1add392d129086e57c0b05aa66a6ded564433c04bd55741434", + "1", + "IMcRFG1VNN9TDGXpCU+9CqKLNOuhwQiXI5hZpkTOuYHKBDOWayNuAABofYLqUHYTMiMf9mYFQ0sPgFJZz3F7ELQ=", + false, + }, + { + "", + "This is a test message", + "", + true, + }, + { + "0", + "This is a test message", + "", + true, + }, + { + "0000000", + "This is a test message", + "", + true, + }, + { + "c7726663147afd1add392d129086e57c0b", + "This is a test message", + "H6N+iPf23i2YkLsNzF/yyeBm9eSYBoY/HFV1Md1F0ElWBXW5E5mkdRtgjoRuq0yNb1CCFNWWlkn2gZknFJNUFJ8=", + false, + }, + } + + for idx, test := range tests { + testPk, errKey := ec.PrivateKeyFromHex(test.inputKey) + if signature, err := compat.SignMessage(testPk, []byte(test.inputMessage)); err != nil && !test.expectedError { + t.Fatalf("%d %s Failed: [%s] [%s] inputted and error not expected but got: %s", idx, t.Name(), test.inputKey, test.inputMessage, err.Error()) + } else if err == nil && errKey == nil && test.expectedError { + t.Fatalf("%d %s Failed: [%s] [%s] inputted and error was expected", idx, t.Name(), test.inputKey, test.inputMessage) + } else if base64.StdEncoding.EncodeToString(signature) != test.expectedSignature { + t.Fatalf("%d %s Failed: [%s] [%s] inputted [%s] expected but got: %s", idx, t.Name(), test.inputKey, test.inputMessage, test.expectedSignature, signature) + } + + } +} +func TestSignMessageUncompressed(t *testing.T) { + t.Parallel() var tests = []struct { inputKey string inputMessage string @@ -111,36 +198,33 @@ func TestSignMessage(t *testing.T) { } for idx, test := range tests { - testPk, errKey := ec.PrivateKeyFromHex(test.inputKey) - - if signature, err := compat.SignMessage(testPk, []byte(test.inputMessage), false); err != nil && !test.expectedError { + if signature, err := compat.SignMessageWithCompression(testPk, []byte(test.inputMessage), false); err != nil && !test.expectedError { t.Fatalf("%d %s Failed: [%s] [%s] inputted and error not expected but got: %s", idx, t.Name(), test.inputKey, test.inputMessage, err.Error()) } else if err == nil && errKey == nil && test.expectedError { t.Fatalf("%d %s Failed: [%s] [%s] inputted and error was expected", idx, t.Name(), test.inputKey, test.inputMessage) } else if base64.StdEncoding.EncodeToString(signature) != test.expectedSignature { t.Fatalf("%d %s Failed: [%s] [%s] inputted [%s] expected but got: %s", idx, t.Name(), test.inputKey, test.inputMessage, test.expectedSignature, signature) } - } } // ExampleSignMessage example using SignMessage() func ExampleSignMessage() { pk, _ := ec.PrivateKeyFromHex("ef0b8bad0be285099534277fde328f8f19b3be9cadcd4c08e6ac0b5f863745ac") - signature, err := compat.SignMessage(pk, []byte("This is a test message"), false) + signature, err := compat.SignMessage(pk, []byte("This is a test message")) if err != nil { fmt.Printf("error occurred: %s", err.Error()) return } fmt.Printf("signature created: %s", base64.StdEncoding.EncodeToString(signature)) - // Output:signature created: G+zZagsyz7ioC/ZOa5EwsaKice0vs2BvZ0ljgkFHxD3vGsMlGeD4sXHEcfbI4h8lP29VitSBdf4A+nHXih7svf4= + // Output:signature created: H+zZagsyz7ioC/ZOa5EwsaKice0vs2BvZ0ljgkFHxD3vGsMlGeD4sXHEcfbI4h8lP29VitSBdf4A+nHXih7svf4= } // BenchmarkSignMessage benchmarks the method SignMessage() func BenchmarkSignMessage(b *testing.B) { key, _ := ec.NewPrivateKey() for i := 0; i < b.N; i++ { - _, _ = compat.SignMessage(key, []byte("This is a test message"), false) + _, _ = compat.SignMessage(key, []byte("This is a test message")) } } diff --git a/compat/bsm/verify.go b/compat/bsm/verify.go index 28c98de..8718939 100644 --- a/compat/bsm/verify.go +++ b/compat/bsm/verify.go @@ -48,13 +48,9 @@ func VerifyMessage(address string, sig, data []byte) error { return err } - if !wasCompressed { - return fmt.Errorf("only compressed keys and signatures are supported") - } - // Get the address var scriptAddress *script.Address - if scriptAddress, err = script.NewAddressFromPublicKey(publicKey, true); err != nil { + if scriptAddress, err = script.NewAddressFromPublicKeyWithCompression(publicKey, true, wasCompressed); err != nil { return err } diff --git a/script/address.go b/script/address.go index 3abad1a..726b085 100644 --- a/script/address.go +++ b/script/address.go @@ -101,7 +101,16 @@ func NewAddressFromPublicKeyHash(hash []byte, mainnet bool) (*Address, error) { // If mainnet parameter is true it will return a mainnet address (starting with a 1). // Otherwise, (mainnet is false) it will return a testnet address (starting with an m or n). func NewAddressFromPublicKey(pubKey *ec.PublicKey, mainnet bool) (*Address, error) { - hash := crypto.Hash160(pubKey.SerializeCompressed()) + return NewAddressFromPublicKeyWithCompression(pubKey, mainnet, true) +} + +func NewAddressFromPublicKeyWithCompression(pubKey *ec.PublicKey, mainnet bool, isCompressed bool) (*Address, error) { + var hash []byte + if isCompressed { + hash = crypto.Hash160(pubKey.SerializeCompressed()) + } else { + hash = crypto.Hash160(pubKey.SerializeUncompressed()) + } // regtest := 111 // mainnet: 0