types, pubkey and signature codec

This commit is contained in:
2025-01-30 18:17:59 -01:06
parent 5d58f61011
commit 282fb7e361
14 changed files with 366 additions and 4 deletions

13
pkg/codec/codec.go Normal file
View File

@@ -0,0 +1,13 @@
package codec
// C is an interface for encoding and decoding that allows embedding encoders
// inside other encoders by the use of append for Marshal and slice for
// Unmarshal.
type C interface {
// Marshal data by appending it to the provided destination, and return the
// resultant slice.
Marshal(dst []byte) (result []byte, err error)
// Unmarshal the next expected data element from the provided slice and return
// the remainder after the expected separator.
Unmarshal(data []byte) (rem []byte, err error)
}

14
pkg/event/event.go Normal file
View File

@@ -0,0 +1,14 @@
package event
import (
"protocol.realy.lol/pkg/event/types"
)
type Event struct {
Type types.T
Pubkey []byte
Timestamp int64
Tags [][]byte
Content []byte
Signature []byte
}

9
pkg/event/log.go Normal file
View File

@@ -0,0 +1,9 @@
package event
import (
"protocol.realy.lol/pkg/lol"
)
var (
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
)

9
pkg/event/types/log.go Normal file
View File

@@ -0,0 +1,9 @@
package types
import (
"protocol.realy.lol/pkg/lol"
)
var (
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
)

44
pkg/event/types/types.go Normal file
View File

@@ -0,0 +1,44 @@
package types
import (
"io"
)
// A T is a type descriptor, that is terminated by a newline.
type T []byte
// Marshal append the T to a slice and appends a terminal newline, and returns
// the result.
func (t *T) Marshal(dst []byte) (result []byte, err error) {
if t == nil {
return
}
result = append(append(dst, []byte(*t)...), '\n')
return
}
// Unmarshal expects an identifier followed by a newline. If the buffer ends
// without a newline an EOF is returned.
func (t *T) Unmarshal(data []byte) (rem []byte, err error) {
rem = data
if t == nil {
err = errorf.E("can't unmarshal into nil types.T")
return
}
if len(rem) < 2 {
err = errorf.E("can't unmarshal nothing")
return
}
for i := range rem {
if rem[i] == '\n' {
// write read data up to the newline and return the remainder after
// the newline.
*t = rem[:i]
rem = rem[i+1:]
return
}
}
// a T must end with a newline or an io.EOF is returned.
err = io.EOF
return
}

View File

@@ -0,0 +1,28 @@
package types
import (
"bytes"
"testing"
)
func TestT_Marshal_Unmarshal(t *testing.T) {
typ := T("note")
var err error
var res []byte
if res, err = typ.Marshal(nil); chk.E(err) {
t.Fatal(err)
}
log.I.S(res)
t2 := new(T)
var rem []byte
if rem, err = t2.Unmarshal(res); chk.E(err) {
t.Fatal(err)
}
if len(rem) > 0 {
log.I.S(rem)
}
log.I.S(t2)
if !bytes.Equal(typ, *t2) {
t.Fatal("types.T did not encode/decode faithfully")
}
}

9
pkg/pubkey/log.go Normal file
View File

@@ -0,0 +1,9 @@
package pubkey
import (
"protocol.realy.lol/pkg/lol"
)
var (
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
)

74
pkg/pubkey/pubkey.go Normal file
View File

@@ -0,0 +1,74 @@
package pubkey
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"io"
)
const Len = 44
type P struct{ ed25519.PublicKey }
func New(pk []byte) (p *P, err error) {
if len(pk) != ed25519.PublicKeySize {
err = errorf.E("invalid public key size: %d; require %d",
len(pk), ed25519.PublicKeySize)
return
}
p = &P{pk}
return
}
func (p *P) Marshal(dst []byte) (result []byte, err error) {
result = dst
if p == nil || p.PublicKey == nil || len(p.PublicKey) == 0 {
err = errorf.E("nil/zero length pubkey")
return
}
if len(p.PublicKey) != ed25519.PublicKeySize {
err = errorf.E("invalid public key length %d; require %d '%0x'",
len(p.PublicKey), ed25519.PublicKeySize, p.PublicKey)
return
}
buf := bytes.NewBuffer(result)
w := base64.NewEncoder(base64.URLEncoding, buf)
if _, err = w.Write(p.PublicKey); chk.E(err) {
return
}
if err = w.Close(); chk.E(err) {
return
}
result = append(buf.Bytes(), '\n')
return
}
func (p *P) Unmarshal(data []byte) (rem []byte, err error) {
rem = data
if p == nil {
err = errorf.E("can't unmarshal into nil types.T")
return
}
if len(rem) < 2 {
err = errorf.E("can't unmarshal nothing")
return
}
for i := range rem {
if rem[i] == '\n' {
if i != Len {
err = errorf.E("invalid encoded pubkey length %d; require %d '%0x'",
i, Len, rem[:i])
return
}
p.PublicKey = make([]byte, ed25519.PublicKeySize)
if _, err = base64.URLEncoding.Decode(p.PublicKey, rem[:i]); chk.E(err) {
return
}
rem = rem[i+1:]
return
}
}
err = io.EOF
return
}

38
pkg/pubkey/pubkey_test.go Normal file
View File

@@ -0,0 +1,38 @@
package pubkey
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"testing"
)
func TestP_Marshal_Unmarshal(t *testing.T) {
pk := make([]byte, ed25519.PublicKeySize)
var err error
if _, err = rand.Read(pk); chk.E(err) {
t.Fatal(err)
}
log.I.S(pk)
var p *P
if p, err = New(pk); chk.E(err) {
t.Fatal(err)
}
var o []byte
if o, err = p.Marshal(nil); chk.E(err) {
t.Fatal(err)
}
log.I.F("%d %s", len(o), o)
p2 := &P{}
var rem []byte
if rem, err = p2.Unmarshal(o); chk.E(err) {
t.Fatal(err)
}
if len(rem) > 0 {
log.I.F("%d %s", len(rem), rem)
}
log.I.S(p2.PublicKey)
if !bytes.Equal(pk, p2.PublicKey) {
t.Fatal("public key did not encode/decode faithfully")
}
}

9
pkg/signature/log.go Normal file
View File

@@ -0,0 +1,9 @@
package signature
import (
"protocol.realy.lol/pkg/lol"
)
var (
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
)

View File

@@ -0,0 +1,74 @@
package signature
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"io"
)
const Len = 88
type S struct{ Signature []byte }
func New(sig []byte) (p *S, err error) {
if len(sig) != ed25519.SignatureSize {
err = errorf.E("invalid signature size: %d; require %d",
len(sig), ed25519.SignatureSize)
return
}
p = &S{sig}
return
}
func (p *S) Marshal(dst []byte) (result []byte, err error) {
result = dst
if p == nil || p.Signature == nil || len(p.Signature) == 0 {
err = errorf.E("nil/zero length signature")
return
}
if len(p.Signature) != ed25519.SignatureSize {
err = errorf.E("invalid signature length %d; require %d '%0x'",
len(p.Signature), ed25519.SignatureSize, p.Signature)
return
}
buf := bytes.NewBuffer(result)
w := base64.NewEncoder(base64.URLEncoding, buf)
if _, err = w.Write(p.Signature); chk.E(err) {
return
}
if err = w.Close(); chk.E(err) {
return
}
result = append(buf.Bytes(), '\n')
return
}
func (p *S) Unmarshal(data []byte) (rem []byte, err error) {
rem = data
if p == nil {
err = errorf.E("can't unmarshal into nil types.T")
return
}
if len(rem) < 2 {
err = errorf.E("can't unmarshal nothing")
return
}
for i := range rem {
if rem[i] == '\n' {
if i != Len {
err = errorf.E("invalid encoded signature length %d; require %d '%0x'",
i, Len, rem[:i])
return
}
p.Signature = make([]byte, ed25519.SignatureSize)
if _, err = base64.URLEncoding.Decode(p.Signature, rem[:i]); chk.E(err) {
return
}
rem = rem[i+1:]
return
}
}
err = io.EOF
return
}

View File

@@ -0,0 +1,38 @@
package signature
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"testing"
)
func TestS_Marshal_Unmarshal(t *testing.T) {
sig := make([]byte, ed25519.SignatureSize)
var err error
if _, err = rand.Read(sig); chk.E(err) {
t.Fatal(err)
}
log.I.S(sig)
var s *S
if s, err = New(sig); chk.E(err) {
t.Fatal(err)
}
var o []byte
if o, err = s.Marshal(nil); chk.E(err) {
t.Fatal(err)
}
log.I.F("%d %s", len(o), o)
p2 := &S{}
var rem []byte
if rem, err = p2.Unmarshal(o); chk.E(err) {
t.Fatal(err)
}
if len(rem) > 0 {
log.I.F("%d %s", len(rem), rem)
}
log.I.S(p2.Signature)
if !bytes.Equal(sig, p2.Signature) {
t.Fatal("signature did not encode/decode faithfully")
}
}