Closed LaurentMontBlanc closed 7 months ago
update documentation as wel
The current implementation does ensure that a given beneficiary and the tx-hash belong to each other. A tx could be hijacked and abused. Therefore, I propose the following to protect against fraud and abuse:
The payload is to be structured as follows:
{ "conversion" :
{ "beneficiary": "<planetmint address>",
"tx-hash": "<tx id on liquid that represents the value to be converted>"
},
"signature": "< sign( private_key, object("beneficiary") ) of the private key belonging to the public key that signed the tx identified by tx-hash"
}
The verification process will look as follows: verify( payload.tx.public_key, payload.conversion )
@LaurentMontBlanc Here is some PoC code. The code does not check if the public key sent in the descriptor is actually valid, but I think we can build from here:
package main
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"regexp"
btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
)
// Text used to signify that a signed message follows and to prevent
// inadvertently signing a transaction.
const messageSignatureHeader = "Bitcoin Signed Message:\n"
var ErrInvalidDescriptor = errors.New("invalid descriptor: input is malformed")
type Conversion struct {
Beneficiary string `json:"beneficiary"`
LiquidTxHash string `json:"liquid-tx-hash"`
Descriptor string `json:"descriptor"`
}
type ConversionWithSignature struct {
Conversion Conversion `json:"conversion"`
Signature string `json:"signature"`
}
func VerifyMessage(conversionWithSignature ConversionWithSignature) (valid bool, err error) {
re := regexp.MustCompile(`wpkh\(\[.*\](.*)\)#.*`)
match := re.FindStringSubmatch(conversionWithSignature.Conversion.Descriptor)
if len(match) < 2 {
err = ErrInvalidDescriptor
return
}
conversionPK, err := hex.DecodeString(match[1])
if err != nil {
return
}
msg, err := json.Marshal(conversionWithSignature.Conversion)
if err != nil {
return
}
message := string(msg)
sig, err := base64.StdEncoding.DecodeString(conversionWithSignature.Signature)
if err != nil {
return
}
var buf bytes.Buffer
wire.WriteVarString(&buf, 0, messageSignatureHeader)
wire.WriteVarString(&buf, 0, message)
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
pk, wasCompressed, err := btcecdsa.RecoverCompact(sig, expectedMessageHash)
if err != nil {
panic(err)
}
// Reconstruct the pubkey hash.
var serializedPK []byte
if wasCompressed {
serializedPK = pk.SerializeCompressed()
} else {
serializedPK = pk.SerializeUncompressed()
}
valid = bytes.Equal(serializedPK, conversionPK)
return
}
func main() {
// Input created with help of elements-cli getaddressinfo and signmessagewithprivkey and sent to our service
conversionWithSignature := ConversionWithSignature{
Conversion{
"plmnt1w5dww335zhh98pzv783hqre355ck3u4w4hjxcx",
"b356413f906468a3220f403c350d01a5880dbd1417f3ff294a4a2ff62faf0839",
"wpkh([6a00c946/0'/0'/501']02e24c96e967524fb2ad3b3e3c29c275e05934b12f420b7871443143d05ffe11c8)#8ktzldqn",
},
"ICucxAHOsf1kanl9UAjxMXemLmnP0deHWwyqdav68e8XCknJeaNBPFl9t7h52Ny1/XNgiQFu8XzrGLM8qahSy38=",
}
// Verify
valid, err := VerifyMessage(conversionWithSignature)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", valid)
}
Output:
true
We need to secure the funds that are sent to the rddl-2-plmnt escrow. In order to do that we need to bridge between liquid keypair and planetmint address (beneficiary).
Add signature of beneficiary address and liquid tx hash signed by liquid privkey person sending to escrow.