Repository: threefoldtech/zos
Pull request #1685 (2022-05-10 08:36:38)

Polka signing
Add `signature-style` flag on the signature requirements object. this to allow signing directly from the polka-wallet. Notice that the style flag must be part of the challenge hash. 

If set, verification is done against `<Bytes>$hash</Bytes>` instead of `$hash`


Patch:
diff --git a/pkg/gridtypes/deployment.go b/pkg/gridtypes/deployment.go
index 1b53a38f..e4978067 100644
--- a/pkg/gridtypes/deployment.go
+++ b/pkg/gridtypes/deployment.go
@@ -1,6 +1,7 @@
 package gridtypes
 
 import (
+	"bytes"
 	"crypto/ed25519"
 	"crypto/md5"
 	"encoding/hex"
@@ -186,6 +187,21 @@ type Signature struct {
 	SignatureType string `json:"signature_type"`
 }
 
+// SignatureStyle specify the signature style for the signature
+// verification.
+type SignatureStyle string
+
+const (
+	// SignatureStyleDefault default signature style is done by verifying the
+	// signature against the computed ChallengeHash of the deployment. In other
+	// words the signature results from signing the
+	SignatureStyleDefault SignatureStyle = ""
+	// SignatureStylePolka signature by polka-wallet surrounds the ChallengeHash with
+	// <Bytes>$hash</Bytes> tags. If this signature-style is selected validation is done
+	// against the same constructed message.
+	SignatureStylePolka SignatureStyle = "polka-wallet"
+)
+
 // SignatureRequirement struct describes the signatures that are needed to be valid
 // for the node to accept the deployment
 // for example
@@ -226,6 +242,7 @@ type SignatureRequirement struct {
 	Requests       []SignatureRequest `json:"requests"`
 	WeightRequired uint               `json:"weight_required"`
 	Signatures     []Signature        `json:"signatures"`
+	SignatureStyle SignatureStyle     `json:"signature_style"`
 }
 
 // Challenge computes challenge for SignatureRequest
@@ -240,6 +257,10 @@ func (r *SignatureRequirement) Challenge(w io.Writer) error {
 		return err
 	}
 
+	if _, err := fmt.Fprintf(w, "%s", r.SignatureStyle); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -366,6 +387,24 @@ func (d *Deployment) Verify(getter KeyGetter) error {
 	}
 
 	requirements := &d.SignatureRequirement
+
+	// if signature style is `polka-wallet` the hash
+	// is surrounded by <Byte></Byte> tags
+	if requirements.SignatureStyle == SignatureStylePolka {
+		buf := bytes.Buffer{}
+		if _, err := buf.WriteString("<Bytes>"); err != nil {
+			return err
+		}
+		if _, err := buf.Write(message); err != nil {
+			return err
+		}
+		if _, err := buf.WriteString("</Bytes>"); err != nil {
+			return err
+		}
+
+		message = buf.Bytes()
+	}
+
 	get := func(twin uint32) (Signature, bool) {
 		for _, sig := range requirements.Signatures {
 			if sig.TwinID == twin {


Test patch:
diff --git a/pkg/gridtypes/sign_test.go b/pkg/gridtypes/sign_test.go
new file mode 100644
index 00000000..01c3cf70
--- /dev/null
+++ b/pkg/gridtypes/sign_test.go
@@ -0,0 +1,71 @@
+package gridtypes
+
+import (
+	"bytes"
+	"crypto/ed25519"
+	"crypto/rand"
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"github.com/threefoldtech/substrate-client"
+)
+
+type polkaSigner struct {
+	Signer
+}
+
+func (s *polkaSigner) Sign(msg []byte) ([]byte, error) {
+	var buf bytes.Buffer
+	if _, err := buf.WriteString("<Bytes>"); err != nil {
+		return nil, err
+	}
+
+	if _, err := buf.Write(msg); err != nil {
+		return nil, err
+	}
+
+	if _, err := buf.WriteString("</Bytes>"); err != nil {
+		return nil, err
+	}
+
+	return s.Signer.Sign(buf.Bytes())
+}
+
+type keyGetter struct {
+	twin uint32
+	key  []byte
+}
+
+func (k *keyGetter) GetKey(twin uint32) ([]byte, error) {
+	if k.twin != twin {
+		return nil, fmt.Errorf("unknown twin")
+	}
+
+	return k.key, nil
+}
+
+func TestSignVerify(t *testing.T) {
+	pk, sk, err := ed25519.GenerateKey(rand.Reader)
+	require.NoError(t, err)
+
+	id, err := substrate.NewIdentityFromEd25519Key(sk)
+	require.NoError(t, err)
+
+	signer := polkaSigner{id}
+
+	dl := Deployment{
+		TwinID: 1,
+		SignatureRequirement: SignatureRequirement{
+			Requests: []SignatureRequest{
+				{TwinID: 1, Required: true, Weight: 1},
+			},
+			SignatureStyle: SignatureStylePolka,
+		},
+	}
+
+	require.NoError(t, dl.Sign(1, &signer))
+
+	getter := keyGetter{twin: 1, key: pk}
+	require.NoError(t, dl.Verify(&getter))
+}