-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit cb86dad
Showing
12 changed files
with
913 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.... | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
Oops, something went wrong.