aboutsummaryrefslogtreecommitdiffstats
path: root/encoding.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-03-08 22:07:14 -0700
committerbndw <ben@bdw.to>2026-03-08 22:07:14 -0700
commit3ff2bc0530bb98da139a5f68202c8e119f9d4775 (patch)
treebcca197dee7f13823ac17d2b5ec1b62b94b897a8 /encoding.go
parent53b10eab74d83522dd90af697773e32279469b30 (diff)
feat: implement Phase 1 Axon protocol core package
Adds the foundational Go package implementing the full Axon protocol signing and crypto spec per PROTOCOL.md: - Event/Tag structs and all kind constants (KindProfile through KindJobFeedback) - Byte-exact canonical_payload construction per the PROTOCOL.md layout table - Tag sorting and canonical_tags SHA256 hash (duplicate detection included) - Ed25519 sign/verify, challenge sign/verify - X25519 key conversion from Ed25519 keypair (RFC 8032 §5.1.5 clamping + birational Edwards→Montgomery map for pubkeys) - ChaCha20-Poly1305 encrypt/decrypt for DMs (nonce prepended) - MessagePack encode/decode for events and wire messages Test vectors written first in testdata/vectors.json covering canonical_tags, canonical_payload, event_id, and signature verification — all deterministic known-input → known-output pairs for cross-language validation in Phase 4. 13 tests, all passing.
Diffstat (limited to 'encoding.go')
-rw-r--r--encoding.go54
1 files changed, 54 insertions, 0 deletions
diff --git a/encoding.go b/encoding.go
new file mode 100644
index 0000000..4ed9da6
--- /dev/null
+++ b/encoding.go
@@ -0,0 +1,54 @@
1package axon
2
3import (
4 "fmt"
5
6 "github.com/vmihailenco/msgpack/v5"
7)
8
9// MarshalEvent encodes an Event to MessagePack. Fields are encoded in the
10// canonical struct order using the msgpack struct tags. Binary fields (id,
11// pubkey, sig, content) are encoded as msgpack bin type ([]byte).
12func MarshalEvent(e *Event) ([]byte, error) {
13 b, err := msgpack.Marshal(e)
14 if err != nil {
15 return nil, fmt.Errorf("axon: marshal event: %w", err)
16 }
17 return b, nil
18}
19
20// UnmarshalEvent decodes a MessagePack blob into an Event.
21func UnmarshalEvent(data []byte) (*Event, error) {
22 var e Event
23 if err := msgpack.Unmarshal(data, &e); err != nil {
24 return nil, fmt.Errorf("axon: unmarshal event: %w", err)
25 }
26 return &e, nil
27}
28
29// MarshalMessage encodes a wire message as a msgpack array: [type, payload].
30// messageType is a uint16; payload is any msgpack-serializable value.
31func MarshalMessage(messageType uint16, payload interface{}) ([]byte, error) {
32 b, err := msgpack.Marshal([]interface{}{messageType, payload})
33 if err != nil {
34 return nil, fmt.Errorf("axon: marshal message: %w", err)
35 }
36 return b, nil
37}
38
39// UnmarshalMessageType reads only the first element of a [type, payload]
40// msgpack array, returning the message type without decoding the payload.
41func UnmarshalMessageType(data []byte) (uint16, error) {
42 var arr []msgpack.RawMessage
43 if err := msgpack.Unmarshal(data, &arr); err != nil {
44 return 0, fmt.Errorf("axon: unmarshal message: %w", err)
45 }
46 if len(arr) < 1 {
47 return 0, fmt.Errorf("axon: message array is empty")
48 }
49 var t uint16
50 if err := msgpack.Unmarshal(arr[0], &t); err != nil {
51 return 0, fmt.Errorf("axon: unmarshal message type: %w", err)
52 }
53 return t, nil
54}