diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2fdc5c2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2024 NAME HERE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 3462fb6..96a6f64 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,46 @@ # bloat -> XML serialization version. for ActivityPub see dogberry \ No newline at end of file +> XML serialization version. for ActivityPub see dogberry + + + +- [ ] Read + - [ ] serialize + - [ ] decrypt + - [ ] verify +- [ ] Write + - [ ] sign + - [ ] encrypt + - [ ] deserialize + + + +- notes on deletions: + +- /host.xml + - /group/ + - /index.xml <- + - /indexes/ + - /{peer name}/ + - /passwords.xml <- encrypted to a peer + - /posts.a.xml <- + - /posts.b.xml <- + + +- options: + - encrypt each post to every group member + - pros: + - safe if they haven't downloaded something + - cons: + - removing a member means rewriting each post + - adding a member means rewriting each post + - encrypt a password file next to each post + - pros: + - don't need to upload the content twice + - cons: + - need a password file for each member (n x m storage) + - encrypt an index file + - pros: + - need o(n) storage + - cons: + - if they've downloaded the index they have the passwords to the files \ No newline at end of file diff --git a/decrypt.go b/decrypt.go index 32b62dd..00cd0cc 100644 --- a/decrypt.go +++ b/decrypt.go @@ -1 +1,18 @@ package bloat + +import ( + "bytes" + "io" + + "filippo.io/age" +) + +func (b *Bloat[T]) Decrypt(src []byte) ([]byte, error) { + input := bytes.NewReader(src) + out, err := age.Decrypt(input, &b.encDec) + if err != nil { + return nil, err + } + res, err := io.ReadAll(out) + return res, err +} diff --git a/encrypt.go b/encrypt.go index 32b62dd..1d512a9 100644 --- a/encrypt.go +++ b/encrypt.go @@ -1 +1,19 @@ package bloat + +import ( + "bytes" + "io" + + "filippo.io/age" +) + +func (b *Bloat[T]) Encrypt(plain []byte) ([]byte, error) { + buf := &bytes.Buffer{} + w, err := age.Encrypt(buf, &b.EncPub) // my wrapper doesn't support multiple recipients yet + if err != nil { + return []byte{}, err + } + var r string + _, err = io.WriteString(w, r) + return []byte(r), err +} diff --git a/go.mod b/go.mod index 7f131f4..8fec521 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,13 @@ module git.bivouac.wiki/go/bloat go 1.22.5 + +require ( + aead.dev/minisign v0.3.0 + filippo.io/age v1.2.1 +) + +require ( + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/sys v0.21.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ca971b7 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +aead.dev/minisign v0.3.0 h1:8Xafzy5PEVZqYDNP60yJHARlW1eOQtsKNp/Ph2c0vRA= +aead.dev/minisign v0.3.0/go.mod h1:NLvG3Uoq3skkRMDuc3YHpWUTMTrSExqm+Ij73W13F6Y= +filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o= +filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/marshal.go b/marshal.go index 32b62dd..f7b9291 100644 --- a/marshal.go +++ b/marshal.go @@ -1 +1,8 @@ package bloat + +import "encoding/xml" + +func (b *Bloat[T]) Marshal(body T) ([]byte, error) { + final, err := xml.Marshal(body) + return final, err +} diff --git a/module_test.go b/module_test.go new file mode 100644 index 0000000..5e1d7d8 --- /dev/null +++ b/module_test.go @@ -0,0 +1,47 @@ +package bloat + +import ( + "fmt" + "testing" +) + +func TestItem(t *testing.T) { + type typ struct { + A int + B string + } + data := typ{ + A: 1, + B: "hello", + } + trySender, err := NewBloat[typ]() + if err != nil { + fmt.Println(err) + t.Errorf("error on newbloat") + } + trySender.Obj = typ{} + tryReceiver, err := NewBloat[typ]() + if err != nil { + fmt.Println(err) + t.Errorf("error on newbloat") + } + cyphertext, err := trySender.Write(data, tryReceiver) + if err != nil { + fmt.Println(err) + t.Errorf("error on write") + } + plainobj, err := tryReceiver.Read(cyphertext, trySender) + if err != nil { + fmt.Println(err) + t.Errorf("error on read") + } + if plainobj.A != 1 { + fmt.Println(err) + t.Errorf("int not equal") + } + if plainobj.B != "hello" { + fmt.Println(err) + t.Errorf("string not hello") + } + +} diff --git a/obj.go b/obj.go new file mode 100644 index 0000000..a4f4f72 --- /dev/null +++ b/obj.go @@ -0,0 +1,37 @@ +package bloat + +import ( + "crypto/rand" + + "aead.dev/minisign" + "filippo.io/age" +) + +type Bloat[T any] struct { + SignPub minisign.PublicKey + signPriv minisign.PrivateKey + EncPub age.X25519Recipient + encDec age.X25519Identity // Scrypt is the password one? + Obj T +} + +func NewBloat[T any]() (Bloat[T], error) { + + pub, sign, err := minisign.GenerateKey(rand.Reader) + if err != nil { + return Bloat[T]{}, err + } + enc, err := age.GenerateX25519Identity() + if err != nil { + return Bloat[T]{}, err + } + recip := enc.Recipient() + + res := Bloat[T]{ + SignPub: pub, + signPriv: sign, + EncPub: *recip, + encDec: *enc, + } + return res, nil +} diff --git a/read.go b/read.go index 32b62dd..7836f42 100644 --- a/read.go +++ b/read.go @@ -1 +1,20 @@ package bloat + +import "errors" + +func (b *Bloat[T]) Read(packed []byte, author Bloat[T]) (T, error) { + var final T + decrypted, err := b.Decrypt(packed) // our own key + if err != nil { + return final, err + } + body, verified, err := author.Verify(decrypted) // author's key + if err != nil { + return final, err + } + if !verified { + return final, errors.New("not verified") + } + final, err = b.Unmarshal(body) + return final, err +} diff --git a/sign.go b/sign.go index 32b62dd..2cfd1e8 100644 --- a/sign.go +++ b/sign.go @@ -1 +1,16 @@ package bloat + +import ( + "bytes" + + "aead.dev/minisign" +) + +// sign the payload and append 4 lines +func (b *Bloat[T]) Sign(plaintext []byte) ([]byte, error) { + + signature := minisign.Sign(b.signPriv, plaintext) + parts := [][]byte{plaintext, signature} + final := bytes.Join(parts, []byte("\n")) + return final, nil +} diff --git a/unmarshal.go b/unmarshal.go index 32b62dd..323f97e 100644 --- a/unmarshal.go +++ b/unmarshal.go @@ -1 +1,18 @@ package bloat + +import "encoding/xml" + +/* +types to decode: + .phish (identity) + .bivouac (decisions) + .cicada (indexes/feeds) + .crew (groups) + .mores (rules) + +*/ + +func (b *Bloat[T]) Unmarshal(plain []byte) (T, error) { + err := xml.Unmarshal(plain, b.Obj) + return b.Obj, err +} diff --git a/verify.go b/verify.go index 32b62dd..bc07e1b 100644 --- a/verify.go +++ b/verify.go @@ -1 +1,19 @@ package bloat + +import ( + "bytes" + + "aead.dev/minisign" +) + +// Verify checks the minisign signature and returns the body if valid. +func (b *Bloat[T]) Verify(message []byte) ([]byte, bool, error) { + // signature is last 4 lines: + separated := bytes.Split(message, []byte("\n")) + signature := bytes.Join(separated[len(separated)-4:len(separated)], []byte("\n")) + body := bytes.Join(separated[:len(separated)-4], []byte("\n")) + if !minisign.Verify(b.SignPub, body, signature) { + return []byte(""), false, nil + } + return body, true, nil +} diff --git a/write.go b/write.go index 32b62dd..5ca3a47 100644 --- a/write.go +++ b/write.go @@ -1 +1,14 @@ package bloat + +func (b *Bloat[T]) Write(body T, recip Bloat[T]) ([]byte, error) { + start, err := b.Marshal(body) + if err != nil { + return nil, err + } + signed, err := b.Sign(start) // our key + if err != nil { + return nil, err + } + final, err := recip.Encrypt(signed) // their key + return final, err +}