package bsv
import "encoding/base64"
type Message struct {
MessageBuffer []byte
Error string
}
func NewMessage(message interface{}) (*Message, error) {
switch v := message.(type) {
case string:
return &Message{MessageBuffer: []byte(v)}, nil
case []byte:
return &Message{MessageBuffer: v}, nil
default:
return nil, newError("InvalidArgument", "First argument should be a string or Buffer")
}
}
var MagicBytes = []byte("Bitcoin Signed Message:\n")
func (m *Message) MagicHash() []byte {
prefix1 := varintBufNum(len(MagicBytes))
prefix2 := varintBufNum(len(m.MessageBuffer))
buf := concatBytes(prefix1, MagicBytes, prefix2, m.MessageBuffer)
return (Hash{}).Sha256sha256(buf)
}
func (m *Message) Sign(privateKey *PrivateKey) (string, error) {
hash := m.MagicHash()
sig, err := ECDSASignWithCalcI(hash, privateKey, "")
if err != nil {
return "", err
}
compact, err := sig.ToCompact(sig.I, sig.Compressed)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(compact), nil
}
func (m *Message) Verify(bitcoinAddress interface{}, signatureString string) (bool, error) {
if bitcoinAddress == nil {
return false, newError("InvalidArgument", "bitcoinAddress is required")
}
if signatureString == "" {
return false, newError("InvalidArgument", "signatureString is required")
}
var address *Address
var err error
switch v := bitcoinAddress.(type) {
case *Address:
address = v
case string:
address, err = AddressFromString(v, nil, "")
if err != nil {
return false, err
}
default:
return false, newError("InvalidArgument", "bitcoinAddress must be an Address or string")
}
sigBytes, err := base64.StdEncoding.DecodeString(signatureString)
if err != nil {
return false, err
}
signature, err := SignatureFromCompact(sigBytes)
if err != nil {
return false, err
}
ecdsa := NewECDSA(&ECDSA{Hashbuf: m.MagicHash(), Sig: signature})
pubkey, err := ecdsa.ToPublicKey()
if err != nil {
return false, err
}
signatureAddress, err := AddressFromPublicKey(pubkey, address.Network)
if err != nil {
return false, err
}
if address.ToString() != signatureAddress.ToString() {
m.Error = "The signature did not match the message digest"
return false, nil
}
return m.verifyWithPublicKey(pubkey, signature)
}
func (m *Message) verifyWithPublicKey(pubkey *PublicKey, signature *Signature) (bool, error) {
hash := m.MagicHash()
verified := ECDSAVerify(hash, signature, pubkey, "")
if !verified {
m.Error = "The signature was invalid"
}
return verified, nil
}
func (m *Message) ToString() string { return string(m.MessageBuffer) }
func (m *Message) ToObject() map[string]interface{} {
return map[string]interface{}{"messageHex": toHex(m.MessageBuffer)}
}