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 1 commit
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
Next Next commit
Add decorated signature methods for signed payloads
  • Loading branch information
Shaptic committed Mar 22, 2022
commit fd6a1c2a0d9043b631642664d522ae2e9afe5b3a
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) SignDecoratedPayload(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
16 changes: 12 additions & 4 deletions keypair/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,18 @@ 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
}

// SignDecoratedPayload returns a decorated signature using this key's hint and
// the last four bytes of both the input and the signature on that input.
func (kp *Full) SignDecoratedPayload(input []byte) (xdr.DecoratedSignature, error) {
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
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("SignDecoratedForPayload()", func() {
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
It("returns the correct xdr struct", func() {
sig, err := subject.SignDecoratedPayload(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)
SignDecoratedPayload(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
44 changes: 44 additions & 0 deletions xdr/decorated_signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package xdr

// NewDecoratedSignature constructs a decorated signature structure directly
// from the given signature and key hint. Note that the key hint should
// correspond to the key that created the signature, but this helper cannot
// ensure that.
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
func NewDecoratedSignature(sig []byte, keyHint [4]byte) DecoratedSignature {
return DecoratedSignature{
Hint: SignatureHint(keyHint),
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
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{}
j := iMax(0, len(payload)-4)

for i := 0; i < len(keyHint); i++ {
hint[i] = keyHint[i]
if j < len(payload) {
hint[i] ^= payload[j]
j++
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
}
}

return DecoratedSignature{
Hint: SignatureHint(hint),
Signature: Signature(sig),
}
}

func iMax(a, b int) int {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: add some generics to our monorepo 😭

if a > b {
return a
}
return b
}
41 changes: 41 additions & 0 deletions xdr/decorated_signature_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package xdr_test

import (
"fmt"
"testing"

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

func TestDecoratedSignatures(t *testing.T) {
signature := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}
keyHint := [4]byte{9, 10, 11, 12}

testCases := []struct {
payload []byte
expectedHint [4]byte
}{
{
payload: []byte{13, 14, 15, 16, 17, 18, 19, 20, 21},
expectedHint: [4]byte{27, 25, 31, 25},
},
{
payload: []byte{18, 19, 20},
expectedHint: [4]byte{27, 25, 31, 12},
},
}

for _, testCase := range testCases {
t.Run(fmt.Sprintf("%d-byte", len(testCase.payload)), func(t *testing.T) {
decoSig := xdr.NewDecoratedSignature(signature, keyHint)
assert.EqualValues(t, keyHint, decoSig.Hint)
assert.EqualValues(t, signature, decoSig.Signature)

decoSig = xdr.NewDecoratedSignatureForPayload(
signature, keyHint, testCase.payload)
assert.EqualValues(t, testCase.expectedHint, decoSig.Hint)
assert.EqualValues(t, signature, decoSig.Signature)
})
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
}
}
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