Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xdr, keypair: Add helpers to create CAP-40 decorated signatures #4302

Merged
merged 8 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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