Skip to content

Commit

Permalink
xdr, keypair: Add helpers to create CAP-40 decorated signatures (#4302)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shaptic authored Mar 23, 2022
1 parent af1aaa0 commit f6eca07
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 13 deletions.
4 changes: 4 additions & 0 deletions keypair/from_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func (kp *FromAddress) SignDecorated(input []byte) (xdr.DecoratedSignature, erro
return xdr.DecoratedSignature{}, ErrCannotSign
}

func (kp *FromAddress) SignPayloadDecorated(input []byte) (xdr.DecoratedSignature, error) {
return xdr.DecoratedSignature{}, ErrCannotSign
}

func (kp *FromAddress) Equal(a *FromAddress) bool {
if kp == nil && a == nil {
return true
Expand Down
15 changes: 11 additions & 4 deletions keypair/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,17 @@ func (kp *Full) SignDecorated(input []byte) (xdr.DecoratedSignature, error) {
return xdr.DecoratedSignature{}, err
}

return xdr.DecoratedSignature{
Hint: xdr.SignatureHint(kp.Hint()),
Signature: xdr.Signature(sig),
}, nil
return xdr.NewDecoratedSignature(sig, kp.Hint()), nil
}

// SignPayloadDecorated returns a decorated signature that signs for a signed payload signer where the input is the payload being signed.
func (kp *Full) SignPayloadDecorated(input []byte) (xdr.DecoratedSignature, error) {
sig, err := kp.Sign(input)
if err != nil {
return xdr.DecoratedSignature{}, err
}

return xdr.NewDecoratedSignatureForPayload(sig, kp.Hint(), input), nil
}

func (kp *Full) Equal(f *Full) bool {
Expand Down
8 changes: 8 additions & 0 deletions keypair/full_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,12 @@ var _ = Describe("keypair.Full", func() {
})
})

Describe("SignPayloadDecorated()", func() {
It("returns the correct xdr struct", func() {
sig, err := subject.SignPayloadDecorated(message)
Expect(err).To(BeNil())
Expect(sig.Hint).To(BeEquivalentTo(payloadHint))
Expect(sig.Signature).To(BeEquivalentTo(signature))
})
})
})
1 change: 1 addition & 0 deletions keypair/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type KP interface {
Sign(input []byte) ([]byte, error)
SignBase64(input []byte) (string, error)
SignDecorated(input []byte) (xdr.DecoratedSignature, error)
SignPayloadDecorated(input []byte) (xdr.DecoratedSignature, error)
}

// Random creates a random full keypair
Expand Down
13 changes: 7 additions & 6 deletions keypair/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ func TestBuild(t *testing.T) {
}

var (
address = "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H"
seed = "SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4"
hint = [4]byte{0x56, 0xfc, 0x05, 0xf7}
message = []byte("hello")
signature = []byte{
0x2E, 0x75, 0xcc, 0x20, 0xd5, 0x19, 0x11, 0x1c, 0xaa, 0xaa, 0xdd, 0xdf,
address = "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H"
seed = "SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4"
hint = [4]byte{0x56, 0xfc, 0x05, 0xf7}
payloadHint = [4]byte{0x33, 0x90, 0x69, 0x98}
message = []byte("hello")
signature = []byte{
0x2e, 0x75, 0xcc, 0x20, 0xd5, 0x19, 0x11, 0x1c, 0xaa, 0xaa, 0xdd, 0xdf,
0x46, 0x4b, 0xb6, 0x50, 0xd2, 0xea, 0xf0, 0xa5, 0xd1, 0x8d, 0x74, 0x56,
0x93, 0xa1, 0x61, 0x00, 0xf2, 0xa4, 0x93, 0x7b, 0xc1, 0xdf, 0xfa, 0x8b,
0x0b, 0x1f, 0x61, 0xa2, 0x76, 0x99, 0x6d, 0x7e, 0xe8, 0xde, 0xb2, 0xd0,
Expand Down
38 changes: 38 additions & 0 deletions xdr/decorated_signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package xdr

// NewDecoratedSignature constructs a decorated signature structure directly
// from the given signature and hint. Note that the hint should
// correspond to the signer that created the signature, but this helper cannot
// ensure that.
func NewDecoratedSignature(sig []byte, hint [4]byte) DecoratedSignature {
return DecoratedSignature{
Hint: SignatureHint(hint),
Signature: Signature(sig),
}
}

// NewDecoratedSignatureForPayload creates a decorated signature with a hint
// that uses the key hint, the last four bytes of signature, and the last four
// bytes of the input that got signed. Note that the signature should be the
// signature of the payload via the key being hinted, but this construction
// method cannot ensure that.
func NewDecoratedSignatureForPayload(
sig []byte, keyHint [4]byte, payload []byte,
) DecoratedSignature {
hint := [4]byte{}
// copy the last four bytes of the payload into the hint
if len(payload) >= len(hint) {
copy(hint[:], payload[len(payload)-len(hint):])
} else {
copy(hint[:], payload[:])
}

for i := 0; i < len(keyHint); i++ {
hint[i] ^= keyHint[i]
}

return DecoratedSignature{
Hint: SignatureHint(hint),
Signature: Signature(sig),
}
}
51 changes: 51 additions & 0 deletions xdr/decorated_signature_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package xdr_test

import (
"fmt"
"testing"

"github.com/stellar/go/xdr"
"github.com/stretchr/testify/assert"
)

var (
signature = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}
hint = [4]byte{9, 10, 11, 12}
)

func TestDecoratedSignature(t *testing.T) {
decoSig := xdr.NewDecoratedSignature(signature, hint)
assert.EqualValues(t, hint, decoSig.Hint)
assert.EqualValues(t, signature, decoSig.Signature)
}

func TestDecoratedSignatureForPayload(t *testing.T) {
testCases := []struct {
payload []byte
expectedHint [4]byte
}{
{
[]byte{13, 14, 15, 16, 17, 18, 19, 20, 21},
[4]byte{27, 25, 31, 25},
},
{
[]byte{18, 19, 20},
[4]byte{27, 25, 31, 12},
},
{
[]byte{},
hint,
},
}

for _, testCase := range testCases {
t.Run(
fmt.Sprintf("%d-byte payload", len(testCase.payload)),
func(t *testing.T) {
decoSig := xdr.NewDecoratedSignatureForPayload(
signature, hint, testCase.payload)
assert.EqualValues(t, testCase.expectedHint, decoSig.Hint)
assert.EqualValues(t, signature, decoSig.Signature)
})
}
}
5 changes: 2 additions & 3 deletions xdr/signer_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ func TestSignerKey_SetAddress(t *testing.T) {
Name string
Address string
}{

{
Name: "AccountID",
Address: "GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5",
Expand All @@ -69,8 +68,8 @@ func TestSignerKey_SetAddress(t *testing.T) {
Address: "XBU2RRGLXH3E5CQHTD3ODLDF2BWDCYUSSBLLZ5GNW7JXHDIYKXZWGTOG",
},
{
"SignedPayload",
"PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUAAAAFGBU",
Name: "SignedPayload",
Address: "PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUAAAAFGBU",
},
}

Expand Down

0 comments on commit f6eca07

Please sign in to comment.