Skip to content

Commit e5110c9

Browse files
committed
Fix key marshalling
1 parent 5cd2125 commit e5110c9

File tree

2 files changed

+298
-0
lines changed

2 files changed

+298
-0
lines changed

client/internal/updatemanager/sign/key.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@ func computeKeyID(pub ed25519.PublicKey) KeyID {
2727
return id
2828
}
2929

30+
// UnmarshalJSON implements json.Unmarshaler for KeyID
31+
func (k *KeyID) UnmarshalJSON(data []byte) error {
32+
var s string
33+
if err := json.Unmarshal(data, &s); err != nil {
34+
return err
35+
}
36+
37+
parsed, err := ParseKeyID(s)
38+
if err != nil {
39+
return err
40+
}
41+
42+
*k = parsed
43+
return nil
44+
}
45+
46+
// MarshalJSON implements json.Marshaler for KeyID
47+
func (k KeyID) MarshalJSON() ([]byte, error) {
48+
return json.Marshal(k.String())
49+
}
50+
3051
// ParseKeyID parses a hex string (16 hex chars = 8 bytes) into a KeyID.
3152
func ParseKeyID(s string) (KeyID, error) {
3253
var id KeyID
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
package sign
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestParseSignature_Valid(t *testing.T) {
13+
timestamp := time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC)
14+
keyID, err := ParseKeyID("0123456789abcdef")
15+
require.NoError(t, err)
16+
17+
signatureData := []byte{0x01, 0x02, 0x03, 0x04}
18+
19+
jsonData, err := json.Marshal(Signature{
20+
Signature: signatureData,
21+
Timestamp: timestamp,
22+
KeyID: keyID,
23+
Algorithm: "ed25519",
24+
HashAlgo: "blake2s",
25+
})
26+
require.NoError(t, err)
27+
28+
sig, err := ParseSignature(jsonData)
29+
require.NoError(t, err)
30+
assert.NotNil(t, sig)
31+
assert.Equal(t, signatureData, sig.Signature)
32+
assert.Equal(t, timestamp.Unix(), sig.Timestamp.Unix())
33+
assert.Equal(t, keyID, sig.KeyID)
34+
assert.Equal(t, "ed25519", sig.Algorithm)
35+
assert.Equal(t, "blake2s", sig.HashAlgo)
36+
}
37+
38+
func TestParseSignature_InvalidJSON(t *testing.T) {
39+
invalidJSON := []byte(`{invalid json}`)
40+
41+
sig, err := ParseSignature(invalidJSON)
42+
assert.Error(t, err)
43+
assert.Nil(t, sig)
44+
}
45+
46+
func TestParseSignature_EmptyData(t *testing.T) {
47+
emptyJSON := []byte(`{}`)
48+
49+
sig, err := ParseSignature(emptyJSON)
50+
require.NoError(t, err)
51+
assert.NotNil(t, sig)
52+
assert.Empty(t, sig.Signature)
53+
assert.True(t, sig.Timestamp.IsZero())
54+
assert.Equal(t, KeyID{}, sig.KeyID)
55+
assert.Empty(t, sig.Algorithm)
56+
assert.Empty(t, sig.HashAlgo)
57+
}
58+
59+
func TestParseSignature_MissingFields(t *testing.T) {
60+
// JSON with only some fields
61+
partialJSON := []byte(`{
62+
"signature": "AQIDBA==",
63+
"algorithm": "ed25519"
64+
}`)
65+
66+
sig, err := ParseSignature(partialJSON)
67+
require.NoError(t, err)
68+
assert.NotNil(t, sig)
69+
assert.NotEmpty(t, sig.Signature)
70+
assert.Equal(t, "ed25519", sig.Algorithm)
71+
assert.True(t, sig.Timestamp.IsZero())
72+
}
73+
74+
func TestSignature_MarshalUnmarshal_Roundtrip(t *testing.T) {
75+
timestamp := time.Date(2024, 6, 20, 14, 45, 30, 0, time.UTC)
76+
keyID, err := ParseKeyID("fedcba9876543210")
77+
require.NoError(t, err)
78+
79+
original := Signature{
80+
Signature: []byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe},
81+
Timestamp: timestamp,
82+
KeyID: keyID,
83+
Algorithm: "ed25519",
84+
HashAlgo: "sha512",
85+
}
86+
87+
// Marshal
88+
jsonData, err := json.Marshal(original)
89+
require.NoError(t, err)
90+
91+
// Unmarshal
92+
parsed, err := ParseSignature(jsonData)
93+
require.NoError(t, err)
94+
95+
// Verify
96+
assert.Equal(t, original.Signature, parsed.Signature)
97+
assert.Equal(t, original.Timestamp.Unix(), parsed.Timestamp.Unix())
98+
assert.Equal(t, original.KeyID, parsed.KeyID)
99+
assert.Equal(t, original.Algorithm, parsed.Algorithm)
100+
assert.Equal(t, original.HashAlgo, parsed.HashAlgo)
101+
}
102+
103+
func TestSignature_NilSignatureBytes(t *testing.T) {
104+
timestamp := time.Now().UTC()
105+
keyID, err := ParseKeyID("0011223344556677")
106+
require.NoError(t, err)
107+
108+
sig := Signature{
109+
Signature: nil,
110+
Timestamp: timestamp,
111+
KeyID: keyID,
112+
Algorithm: "ed25519",
113+
HashAlgo: "blake2s",
114+
}
115+
116+
jsonData, err := json.Marshal(sig)
117+
require.NoError(t, err)
118+
119+
parsed, err := ParseSignature(jsonData)
120+
require.NoError(t, err)
121+
assert.Nil(t, parsed.Signature)
122+
}
123+
124+
func TestSignature_LargeSignature(t *testing.T) {
125+
timestamp := time.Now().UTC()
126+
keyID, err := ParseKeyID("aabbccddeeff0011")
127+
require.NoError(t, err)
128+
129+
// Create a large signature (64 bytes for ed25519)
130+
largeSignature := make([]byte, 64)
131+
for i := range largeSignature {
132+
largeSignature[i] = byte(i)
133+
}
134+
135+
sig := Signature{
136+
Signature: largeSignature,
137+
Timestamp: timestamp,
138+
KeyID: keyID,
139+
Algorithm: "ed25519",
140+
HashAlgo: "blake2s",
141+
}
142+
143+
jsonData, err := json.Marshal(sig)
144+
require.NoError(t, err)
145+
146+
parsed, err := ParseSignature(jsonData)
147+
require.NoError(t, err)
148+
assert.Equal(t, largeSignature, parsed.Signature)
149+
}
150+
151+
func TestSignature_WithDifferentHashAlgorithms(t *testing.T) {
152+
tests := []struct {
153+
name string
154+
hashAlgo string
155+
}{
156+
{"blake2s", "blake2s"},
157+
{"sha512", "sha512"},
158+
{"sha256", "sha256"},
159+
{"empty", ""},
160+
}
161+
162+
keyID, err := ParseKeyID("1122334455667788")
163+
require.NoError(t, err)
164+
165+
for _, tt := range tests {
166+
t.Run(tt.name, func(t *testing.T) {
167+
sig := Signature{
168+
Signature: []byte{0x01, 0x02},
169+
Timestamp: time.Now().UTC(),
170+
KeyID: keyID,
171+
Algorithm: "ed25519",
172+
HashAlgo: tt.hashAlgo,
173+
}
174+
175+
jsonData, err := json.Marshal(sig)
176+
require.NoError(t, err)
177+
178+
parsed, err := ParseSignature(jsonData)
179+
require.NoError(t, err)
180+
assert.Equal(t, tt.hashAlgo, parsed.HashAlgo)
181+
})
182+
}
183+
}
184+
185+
func TestSignature_TimestampPrecision(t *testing.T) {
186+
// Test that timestamp preserves precision through JSON marshaling
187+
timestamp := time.Date(2024, 3, 15, 10, 30, 45, 123456789, time.UTC)
188+
keyID, err := ParseKeyID("8877665544332211")
189+
require.NoError(t, err)
190+
191+
sig := Signature{
192+
Signature: []byte{0xaa, 0xbb},
193+
Timestamp: timestamp,
194+
KeyID: keyID,
195+
Algorithm: "ed25519",
196+
HashAlgo: "blake2s",
197+
}
198+
199+
jsonData, err := json.Marshal(sig)
200+
require.NoError(t, err)
201+
202+
parsed, err := ParseSignature(jsonData)
203+
require.NoError(t, err)
204+
205+
// JSON timestamps typically have second or millisecond precision
206+
// so we check that at least seconds match
207+
assert.Equal(t, timestamp.Unix(), parsed.Timestamp.Unix())
208+
}
209+
210+
func TestParseSignature_MalformedKeyID(t *testing.T) {
211+
// Test with a malformed KeyID field
212+
malformedJSON := []byte(`{
213+
"signature": "AQID",
214+
"timestamp": "2024-01-15T10:30:00Z",
215+
"key_id": "invalid_keyid_format",
216+
"algorithm": "ed25519",
217+
"hash_algo": "blake2s"
218+
}`)
219+
220+
// This should fail since "invalid_keyid_format" is not a valid KeyID
221+
sig, err := ParseSignature(malformedJSON)
222+
assert.Error(t, err)
223+
assert.Nil(t, sig)
224+
}
225+
226+
func TestParseSignature_InvalidTimestamp(t *testing.T) {
227+
// Test with an invalid timestamp format
228+
invalidTimestampJSON := []byte(`{
229+
"signature": "AQID",
230+
"timestamp": "not-a-timestamp",
231+
"key_id": "0123456789abcdef",
232+
"algorithm": "ed25519",
233+
"hash_algo": "blake2s"
234+
}`)
235+
236+
sig, err := ParseSignature(invalidTimestampJSON)
237+
assert.Error(t, err)
238+
assert.Nil(t, sig)
239+
}
240+
241+
func TestSignature_ZeroKeyID(t *testing.T) {
242+
// Test with a zero KeyID
243+
sig := Signature{
244+
Signature: []byte{0x01, 0x02, 0x03},
245+
Timestamp: time.Now().UTC(),
246+
KeyID: KeyID{},
247+
Algorithm: "ed25519",
248+
HashAlgo: "blake2s",
249+
}
250+
251+
jsonData, err := json.Marshal(sig)
252+
require.NoError(t, err)
253+
254+
parsed, err := ParseSignature(jsonData)
255+
require.NoError(t, err)
256+
assert.Equal(t, KeyID{}, parsed.KeyID)
257+
}
258+
259+
func TestParseSignature_ExtraFields(t *testing.T) {
260+
// JSON with extra fields that should be ignored
261+
jsonWithExtra := []byte(`{
262+
"signature": "AQIDBA==",
263+
"timestamp": "2024-01-15T10:30:00Z",
264+
"key_id": "0123456789abcdef",
265+
"algorithm": "ed25519",
266+
"hash_algo": "blake2s",
267+
"extra_field": "should be ignored",
268+
"another_extra": 12345
269+
}`)
270+
271+
sig, err := ParseSignature(jsonWithExtra)
272+
require.NoError(t, err)
273+
assert.NotNil(t, sig)
274+
assert.NotEmpty(t, sig.Signature)
275+
assert.Equal(t, "ed25519", sig.Algorithm)
276+
assert.Equal(t, "blake2s", sig.HashAlgo)
277+
}

0 commit comments

Comments
 (0)