rddl-network / rddl-2-plmnt-service

GNU Affero General Public License v3.0
1 stars 0 forks source link

add signature to mint request #27

Closed LaurentMontBlanc closed 7 months ago

LaurentMontBlanc commented 8 months ago

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.

LaurentMontBlanc commented 8 months ago

update documentation as wel

eckelj commented 8 months ago

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 )

jmastr commented 7 months ago

@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