Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
DiniFarb committed Jun 4, 2023
0 parents commit cb86dad
Show file tree
Hide file tree
Showing 12 changed files with 913 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work

# queuic
*.qic
*.queuic
*.bin
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Queuic Message Broker

## Introduction
This is a very small qeueue message broker that I wrote for learning. It is not meant to be used in production so far.

## Features
- [x] Simple massage queueing
- [x] Message persistence
- [x] Own protocol
- [ ] Server implementation
- [x] Encryption without certs

## Protocol

0 1 2 3 4
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Command | Queue Name |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Item UUID | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| Item.... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+

45 changes: 45 additions & 0 deletions cmd/queueic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"fmt"
"os"
"strings"

"github.com/dinifarb/mlog"
"github.com/dinifarb/queuic/pkg/server"
)

func main() {
fmt.Println(`
___ _
/ _ \ _ _ ___ _ _(_) ___
| | | | | | |/ _ \ | | | |/ __|
| |_| | |_| | __/ |_| | | (__
\__\_\\__,_|\___|\__,_|_|\___|
`)
mlog.SetAppName("QUEUEIC")
logLevel := os.Getenv("LOG_LEVEL")
switch strings.ToLower(logLevel) {
case "debug":
mlog.SetLevel(mlog.Ldebug)
case "info":
mlog.SetLevel(mlog.Linfo)
case "warn":
mlog.SetLevel(mlog.Lwarn)
case "error":
mlog.SetLevel(mlog.Lerror)
default:
mlog.Warn("LOG_LEVEL env variable is not set use default level")
mlog.SetLevel(mlog.Linfo)
}
keyString := os.Getenv("QUEUEIC_KEY_STRING")
if keyString == "" {
mlog.Warn("QUEUEIC_KEY_STRING env variable is not set use default key")
keyString = "QUEUEIC"
}
svr := server.NewQueuicServer(keyString)
if err := svr.Serve(); err != nil {
mlog.Error("server error: %v", err)
os.Exit(1)
}
}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/dinifarb/queuic

go 1.19

require (
github.com/dinifarb/mlog v1.0.0
github.com/google/uuid v1.3.0
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/dinifarb/mlog v1.0.0 h1:9juET199uDF1NbVtlLanyc1kGW0eThXTcbx3sGctpD0=
github.com/dinifarb/mlog v1.0.0/go.mod h1:QEOWY+no8+AH92MS0iZGH2ERdpvIKIkJODByMaQyYM8=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
125 changes: 125 additions & 0 deletions pkg/proto/proto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package proto

import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"

"github.com/google/uuid"
)

type Command uint8
type QueueName [16]byte

const (
ENQUEUE Command = iota
ENQUEUE_ACK
PEEK
PEEK_ACK
ACCEPT
ACCEPT_ACK
RELEASE
RELEASE_ACK
SIZE
SIZE_ACK
)

const (
// MAX_PACKET_LENGTH = 4096
MIN_PACKET_LENGTH = 17
)

type Queuic struct {
Command Command
QueueName QueueName
QueuicItem
}

type QueuicItem struct {
Id uuid.UUID
Item []byte
}

func (q *QueueName) String() string {
b := q[:]
b = bytes.Trim(b, "\x00")
return string(b[:])
}

func Encode(q *Queuic) ([]byte, error) {
length := MIN_PACKET_LENGTH
if q.QueuicItem.Item != nil {
length += len(q.QueuicItem.Id)
length += len(q.QueuicItem.Item)
}
b := make([]byte, length)
b[0] = byte(q.Command)
copy(b[1:17], q.QueueName[:])
if q.QueuicItem.Item == nil {
return b, nil
} else {
copy(b[17:33], q.QueuicItem.Id[:])
copy(b[33:], q.QueuicItem.Item[:])
return b, nil
}
}

func Decode(data []byte) (*Queuic, error) {
if len(data) < MIN_PACKET_LENGTH {
return nil, fmt.Errorf("packet is too short")
}
/* if len(data) > MAX_PACKET_LENGTH {
return nil, fmt.Errorf("packet is too long")
} */
var q Queuic
q.Command = Command(data[0])
copy(q.QueueName[:], data[1:17])
if len(data) > MIN_PACKET_LENGTH {
itemId, err := uuid.FromBytes(data[17:33])
if err != nil {
return nil, fmt.Errorf("failed to decode uuid: %v", err)
}
q.QueuicItem.Id = itemId
q.QueuicItem.Item = data[33:]
}
return &q, nil
}

func Encrypt(key []byte, message []byte) ([]byte, error) {
if len(key) != 32 {
return nil, fmt.Errorf("key must be 32 bytes long")
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create new cipher: %v", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create new gcm: %v", err)
}
nonce := make([]byte, gcm.NonceSize())
ciphertext := gcm.Seal(nonce, nonce, message, nil)
return ciphertext, nil
}

func Decrypt(key []byte, encryptedMessage []byte) ([]byte, error) {
if len(key) != 32 {
return nil, fmt.Errorf("key must be 32 bytes long")
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create new cipher: %v", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create new gcm: %v", err)
}
nonceSize := gcm.NonceSize()
nonce, ciphertext := encryptedMessage[:nonceSize], encryptedMessage[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("failed to decrypt message: %v", err)
}
return plaintext, nil
}
79 changes: 79 additions & 0 deletions pkg/proto/proto_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package proto_test

import (
"crypto/sha256"
"testing"

"github.com/dinifarb/queuic/pkg/proto"
"github.com/google/uuid"
)

func TestEnDecode(t *testing.T) {
queueName := proto.QueueName{}
copy(queueName[:], []byte("test"))
q := proto.Queuic{
Command: proto.ENQUEUE,
QueueName: queueName,
QueuicItem: proto.QueuicItem{
Id: uuid.New(),
Item: []byte("test message"),
},
}
b, err := proto.Encode(&q)
if err != nil {
t.Errorf("failed to encode request: %v", err)
}
q2, err := proto.Decode(b)
if err != nil {
t.Errorf("failed to decode response: %v", err)
}
if q2.Command != proto.ENQUEUE {
t.Errorf("unexpected response command: %v", q2.Command)
}
if q2.QueueName != queueName {
t.Errorf("unexpected queue name: %v", q2.QueueName)
}
if string(q2.QueuicItem.Item) != "test message" {
t.Errorf("unexpected value: %v", q2.QueuicItem.Item)
}
}

func TestEnDecodeWithCrypto(t *testing.T) {
queueName := proto.QueueName{}
copy(queueName[:], []byte("test"))
q := proto.Queuic{
Command: proto.ENQUEUE,
QueueName: queueName,
QueuicItem: proto.QueuicItem{
Id: uuid.New(),
Item: []byte("test message"),
},
}
b, err := proto.Encode(&q)
if err != nil {
t.Errorf("failed to encode request: %v", err)
}
enk := sha256.Sum256([]byte("test"))
encrypted, err := proto.Encrypt(enk[:], b)
if err != nil {
t.Errorf("failed to encrypt request: %v", err)
}
dek := sha256.Sum256([]byte("test"))
decrypted, err := proto.Decrypt(dek[:], encrypted)
if err != nil {
t.Errorf("failed to decrypt request: %v", err)
}
q2, err := proto.Decode(decrypted)
if err != nil {
t.Errorf("failed to decode response: %v", err)
}
if q2.Command != proto.ENQUEUE {
t.Errorf("unexpected response command: %v", q2.Command)
}
if q2.QueueName != queueName {
t.Errorf("unexpected queue name: %v", q2.QueueName)
}
if string(q2.QueuicItem.Item) != "test message" {
t.Errorf("unexpected value: %v", q2.QueuicItem.Item)
}
}
Loading

0 comments on commit cb86dad

Please sign in to comment.