From 8eb9bb3cee284831b35fed507f0b7e4abdadf193 Mon Sep 17 00:00:00 2001 From: greysoh Date: Sat, 19 Oct 2024 18:06:49 -0400 Subject: [PATCH] chore: Adds lots of documentation. --- .gitignore | 1 + README.md | 21 +++++++++++++++++++++ client/README.md | 3 +++ client/client.go | 10 +++++++++- commons/README.md | 4 ++++ commons/conn.go | 35 ++++++++++++++++++++++++++--------- commons/conv.go | 4 ++++ commons/enum.go | 15 +++++++++++++++ server/README.md | 3 +++ server/server.go | 16 ++++++++++++---- tests/README.md | 3 +++ 11 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 .gitignore create mode 100644 client/README.md create mode 100644 commons/README.md create mode 100644 server/README.md create mode 100644 tests/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad601fc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bismuthd diff --git a/README.md b/README.md index 0421a51..6eeccc4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ # Bismuth Protocol The Bismuth protocol is a thin wrapper for any protocol that adds TLS-like features, without being TLS on its own. + +## Application +### Building + +Git clone this repository and check out a release (except for development, as you probably don't wanna develop on a release version): +```bash +git clone https://git.greysoh.dev/imterah/bismuthd +git checkout v0.1.0 +``` +Then, build the code: +```bash +go build . +``` + +### Usage +To get started, you'll need an exported armored public and private key pair. + +After that, for usage help, run the help command: +``` +./bismuthd help +``` diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..6f929b6 --- /dev/null +++ b/client/README.md @@ -0,0 +1,3 @@ +# Bismuth Client + +This is a wrapper around connections which lets you speak the Bismuth protocol. diff --git a/client/client.go b/client/client.go index 5a210f0..7f49ba6 100644 --- a/client/client.go +++ b/client/client.go @@ -41,13 +41,18 @@ func (bismuth BismuthClient) decryptMessage(aead cipher.AEAD, encMsg []byte) ([] return decryptedData, nil } +// Bismuth Client type BismuthClient struct { - PublicKey *crypto.Key + // GOpenPGP public key + PublicKey *crypto.Key + // GOpenPGP private key PrivateKey *crypto.Key pgp *crypto.PGPHandle } +// Connects to a Bismuth server. This wraps an existing net.Conn interface. +// The returned net.Conn is the server, but over bismuth. func (bismuth BismuthClient) Conn(conn net.Conn) (net.Conn, error) { // Yes, I'm aware defer exists. It won't work if I use it in this context. I'll shank anyone that complains // Exchange our public keys first @@ -183,6 +188,9 @@ func (bismuth BismuthClient) Conn(conn net.Conn) (net.Conn, error) { }, nil } +// Creates a new BismuthClient. +// +// Both `pubKey` and `privKey` are armored PGP public and private keys respectively. func New(pubKey string, privKey string) (*BismuthClient, error) { publicKey, err := crypto.NewKeyFromArmored(pubKey) diff --git a/commons/README.md b/commons/README.md new file mode 100644 index 0000000..ae36053 --- /dev/null +++ b/commons/README.md @@ -0,0 +1,4 @@ +# Commons between Bismuth Client and Server + +This houses common internal libraries and constants between the bismuth client and server. +For most of the time, you're probably going to want the connection handler, which is in `conn.go`. diff --git a/commons/conn.go b/commons/conn.go index 6c2db99..7d7e364 100644 --- a/commons/conn.go +++ b/commons/conn.go @@ -1,3 +1,5 @@ +// Shared connection handler after both the client and server handshake successfully + package commons import ( @@ -9,11 +11,18 @@ import ( "time" ) -// Max size for a TCP packet +// Maximum size for a TCP packet var ConnStandardMaxBufSize = 65535 -var CryptHeader = 43 +var cryptHeaderSize = 43 -// Wild +// Connection used after both the Bismuth client and server negotiate and start +// transmitting data. +// +// Note that using this has the same API as the net.Conn, but it isn't conformant to +// the interface due to using pointers rather than copies to access the struct. +// +// If you need the same interface, wrap this in WrappedBismuthConn. +// Wrapping BismuthConn is done automatically by the client and server. type BismuthConn struct { Aead cipher.AEAD PassedConn net.Conn @@ -26,10 +35,11 @@ type BismuthConn struct { contentBufPos int contentBufSize int - MaxBufSize int + // Maximum buffer size to be used to internally buffer packets + MaxBufSize int + // If true, it enables using the content buffer maximum size + // instead of the TCP packet maximum size AllowNonstandardPacketSizeLimit bool - - net.Conn } func (bmConn *BismuthConn) DoInitSteps() { @@ -68,6 +78,7 @@ func (bmConn *BismuthConn) decryptMessage(encMsg []byte) ([]byte, error) { return decryptedData, nil } +// After you update the property `bmConn.MaxBufSize`, call this function to resize the content buffer func (bmConn *BismuthConn) ResizeContentBuf() error { if !bmConn.initDone { return fmt.Errorf("bmConn not initialized") @@ -95,6 +106,7 @@ func (bmConn *BismuthConn) ResizeContentBuf() error { return nil } +// Reads specifically from the buffer only. If nothing is in the buffer, nothing is returned. func (bmConn *BismuthConn) ReadFromBuffer(b []byte) (n int, err error) { bmConn.lock.Lock() defer bmConn.lock.Unlock() @@ -126,6 +138,7 @@ func (bmConn *BismuthConn) ReadFromBuffer(b []byte) (n int, err error) { return 0, nil } +// Reads specifically from the network. Be careful as using only this may overflow the buffer. func (bmConn *BismuthConn) ReadFromNetwork(b []byte) (n int, err error) { bmConn.lock.Lock() defer bmConn.lock.Unlock() @@ -146,7 +159,7 @@ func (bmConn *BismuthConn) ReadFromNetwork(b []byte) (n int, err error) { // - the max buffer size if 'AllowNonstandardPacketSizeLimit' is set // We check AFTER we read to make sure that we don't corrupt any future packets, because if we don't read the packet, // it will think that the actual packet will be the start of the packet, and that would cause loads of problems. - if !bmConn.AllowNonstandardPacketSizeLimit && encryptedContentLength > uint32(65535+CryptHeader) { + if !bmConn.AllowNonstandardPacketSizeLimit && encryptedContentLength > uint32(65535+cryptHeaderSize) { return 0, fmt.Errorf("packet too large") } else if bmConn.AllowNonstandardPacketSizeLimit && encryptedContentLength > uint32(bmConn.MaxBufSize) { return 0, fmt.Errorf("packet too large") @@ -187,6 +200,7 @@ func (bmConn *BismuthConn) ReadFromNetwork(b []byte) (n int, err error) { return calcSize, nil } +// Reads from the Bismuth connection, using both the buffered and network methods func (bmConn *BismuthConn) Read(b []byte) (n int, err error) { if !bmConn.initDone { return 0, fmt.Errorf("bmConn not initialized") @@ -211,6 +225,7 @@ func (bmConn *BismuthConn) Read(b []byte) (n int, err error) { return bufferReadSize + networkReadSize, nil } +// Encrypts and sends off a message func (bmConn *BismuthConn) Write(b []byte) (n int, err error) { encryptedMessage, err := bmConn.encryptMessage(b) @@ -251,8 +266,10 @@ func (bmConn *BismuthConn) SetWriteDeadline(time time.Time) error { return bmConn.PassedConn.SetWriteDeadline(time) } -// TODO: remove this ugly hack if possible! There's probably a better way around this... - +// Wrapped BismuthConn struct. This is conformant to net.Conn, unlike above. +// To get the raw Bismuth struct, just get the Bismuth property: +// +// `bmConn.Bismuth` -> `BismuthConn` type BismuthConnWrapped struct { Bismuth *BismuthConn } diff --git a/commons/conv.go b/commons/conv.go index bc81bf0..cc44091 100644 --- a/commons/conv.go +++ b/commons/conv.go @@ -1,9 +1,13 @@ +// Conversion libraries for 24 bit numbering instead of 32 bit numbering + package commons +// Converts a 24 bit unsigned integer stored in a big-endian byte array to a 32 bit unsigned integer. func Int24ToInt32(b []byte) uint32 { return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16 } +// Converts a 32 bit unsigned integer to a 24 bit unsigned integer in a byte array using big-endian ordering. func Int32ToInt24(b []byte, int uint32) { b[0] = uint8((int >> 16) & 0xff) b[1] = uint8((int >> 8) & 0xff) diff --git a/commons/enum.go b/commons/enum.go index 5cd9ded..be1848f 100644 --- a/commons/enum.go +++ b/commons/enum.go @@ -1,18 +1,33 @@ +// Enums used internally + package commons +// Commands const ( + // SendPublicKey: Sending public key back and forth SendPublicKey = iota + // SwitchToSymmetricKey: Sent by the client along with the symmetric key that is going to be used SwitchToSymmetricKey + // ClientSendHost: Currently unimplemented. + // Client sends what host they are connecting to. ClientSendHost + // GetSigningServers: Currently unimplemented. + // Gets the signing servers trusting/signing the current server. GetSigningServers + // GetTrustedDomains: Currently unimplemented. + // Gets the domains that are supported by this certificate (should be cross-checked) GetTrustedDomains + // InitiateForwarding: Starts forwarding traffic over this protocol. InitiateForwarding ) +// Encryption algorithms const ( + // Default and only encryption algorithm XChaCha20Poly1305 = iota ) +// Unsigned integer limits const ( BitLimit24 = 16_777_215 BitLimit16 = 65535 diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..c562f7f --- /dev/null +++ b/server/README.md @@ -0,0 +1,3 @@ +# Bismuth Server + +This is the Bismuth server, which lets you speak the Bismuth protocol. diff --git a/server/server.go b/server/server.go index 2357e4d..4e1c183 100644 --- a/server/server.go +++ b/server/server.go @@ -13,17 +13,22 @@ import ( "golang.org/x/crypto/chacha20poly1305" ) +// Bismuth Server type BismuthServer struct { - PublicKey *crypto.Key + // Public key to use for transmission + PublicKey *crypto.Key + // Private key to use for transmission PrivateKey *crypto.Key pgp *crypto.PGPHandle + // Algorithm to use for encryption (currently XChaCha20Poly1305 is the only option) SymmetricEncryptionAlgorithm int - + // Servers that are signing this server. If none, this server becomes self-signed + // in the clients eyes SigningServers []string - // This is what's called after a successful handshake & connection. + // Called after a successful handshake & connection. HandleConnection func(conn net.Conn) error } @@ -56,7 +61,7 @@ func (bismuth BismuthServer) decryptMessage(aead cipher.AEAD, encMsg []byte) ([] return decryptedData, nil } -// This is what's called to handle a connnection for Bismuth. +// Called to handle a connnection for Bismuth. The conn argument is the client you'd like to handle func (bismuth BismuthServer) HandleProxy(conn net.Conn) error { serverState := "keyHandshake" @@ -225,6 +230,9 @@ func (bismuth BismuthServer) HandleProxy(conn net.Conn) error { } } +// Initializes a Bismuth server. +// +// Both `pubKey` and `privKey` are armored PGP public and private keys respectively. func NewBismuthServer(pubKey string, privKey string, signServers []string, encryptionAlgo int, connHandler func(conn net.Conn) error) (*BismuthServer, error) { publicKey, err := crypto.NewKeyFromArmored(pubKey) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..9844b40 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,3 @@ +# Test Suites + +These are either test suites or support code for test suites for Bismuth's client and server code.