chore: Adds lots of documentation.

This commit is contained in:
greysoh 2024-10-19 18:06:49 -04:00
parent 514ccee264
commit 8eb9bb3cee
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
11 changed files with 101 additions and 14 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
bismuthd

View file

@ -1,3 +1,24 @@
# Bismuth Protocol # Bismuth Protocol
The Bismuth protocol is a thin wrapper for any protocol that adds TLS-like features, without being TLS on its own. 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
```

3
client/README.md Normal file
View file

@ -0,0 +1,3 @@
# Bismuth Client
This is a wrapper around connections which lets you speak the Bismuth protocol.

View file

@ -41,13 +41,18 @@ func (bismuth BismuthClient) decryptMessage(aead cipher.AEAD, encMsg []byte) ([]
return decryptedData, nil return decryptedData, nil
} }
// Bismuth Client
type BismuthClient struct { type BismuthClient struct {
PublicKey *crypto.Key // GOpenPGP public key
PublicKey *crypto.Key
// GOpenPGP private key
PrivateKey *crypto.Key PrivateKey *crypto.Key
pgp *crypto.PGPHandle 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) { 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 // 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 // Exchange our public keys first
@ -183,6 +188,9 @@ func (bismuth BismuthClient) Conn(conn net.Conn) (net.Conn, error) {
}, nil }, 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) { func New(pubKey string, privKey string) (*BismuthClient, error) {
publicKey, err := crypto.NewKeyFromArmored(pubKey) publicKey, err := crypto.NewKeyFromArmored(pubKey)

4
commons/README.md Normal file
View file

@ -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`.

View file

@ -1,3 +1,5 @@
// Shared connection handler after both the client and server handshake successfully
package commons package commons
import ( import (
@ -9,11 +11,18 @@ import (
"time" "time"
) )
// Max size for a TCP packet // Maximum size for a TCP packet
var ConnStandardMaxBufSize = 65535 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 { type BismuthConn struct {
Aead cipher.AEAD Aead cipher.AEAD
PassedConn net.Conn PassedConn net.Conn
@ -26,10 +35,11 @@ type BismuthConn struct {
contentBufPos int contentBufPos int
contentBufSize 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 AllowNonstandardPacketSizeLimit bool
net.Conn
} }
func (bmConn *BismuthConn) DoInitSteps() { func (bmConn *BismuthConn) DoInitSteps() {
@ -68,6 +78,7 @@ func (bmConn *BismuthConn) decryptMessage(encMsg []byte) ([]byte, error) {
return decryptedData, nil return decryptedData, nil
} }
// After you update the property `bmConn.MaxBufSize`, call this function to resize the content buffer
func (bmConn *BismuthConn) ResizeContentBuf() error { func (bmConn *BismuthConn) ResizeContentBuf() error {
if !bmConn.initDone { if !bmConn.initDone {
return fmt.Errorf("bmConn not initialized") return fmt.Errorf("bmConn not initialized")
@ -95,6 +106,7 @@ func (bmConn *BismuthConn) ResizeContentBuf() error {
return nil 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) { func (bmConn *BismuthConn) ReadFromBuffer(b []byte) (n int, err error) {
bmConn.lock.Lock() bmConn.lock.Lock()
defer bmConn.lock.Unlock() defer bmConn.lock.Unlock()
@ -126,6 +138,7 @@ func (bmConn *BismuthConn) ReadFromBuffer(b []byte) (n int, err error) {
return 0, nil 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) { func (bmConn *BismuthConn) ReadFromNetwork(b []byte) (n int, err error) {
bmConn.lock.Lock() bmConn.lock.Lock()
defer bmConn.lock.Unlock() 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 // - 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, // 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. // 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") return 0, fmt.Errorf("packet too large")
} else if bmConn.AllowNonstandardPacketSizeLimit && encryptedContentLength > uint32(bmConn.MaxBufSize) { } else if bmConn.AllowNonstandardPacketSizeLimit && encryptedContentLength > uint32(bmConn.MaxBufSize) {
return 0, fmt.Errorf("packet too large") return 0, fmt.Errorf("packet too large")
@ -187,6 +200,7 @@ func (bmConn *BismuthConn) ReadFromNetwork(b []byte) (n int, err error) {
return calcSize, nil 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) { func (bmConn *BismuthConn) Read(b []byte) (n int, err error) {
if !bmConn.initDone { if !bmConn.initDone {
return 0, fmt.Errorf("bmConn not initialized") 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 return bufferReadSize + networkReadSize, nil
} }
// Encrypts and sends off a message
func (bmConn *BismuthConn) Write(b []byte) (n int, err error) { func (bmConn *BismuthConn) Write(b []byte) (n int, err error) {
encryptedMessage, err := bmConn.encryptMessage(b) encryptedMessage, err := bmConn.encryptMessage(b)
@ -251,8 +266,10 @@ func (bmConn *BismuthConn) SetWriteDeadline(time time.Time) error {
return bmConn.PassedConn.SetWriteDeadline(time) 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 { type BismuthConnWrapped struct {
Bismuth *BismuthConn Bismuth *BismuthConn
} }

View file

@ -1,9 +1,13 @@
// Conversion libraries for 24 bit numbering instead of 32 bit numbering
package commons 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 { func Int24ToInt32(b []byte) uint32 {
return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16 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) { func Int32ToInt24(b []byte, int uint32) {
b[0] = uint8((int >> 16) & 0xff) b[0] = uint8((int >> 16) & 0xff)
b[1] = uint8((int >> 8) & 0xff) b[1] = uint8((int >> 8) & 0xff)

View file

@ -1,18 +1,33 @@
// Enums used internally
package commons package commons
// Commands
const ( const (
// SendPublicKey: Sending public key back and forth
SendPublicKey = iota SendPublicKey = iota
// SwitchToSymmetricKey: Sent by the client along with the symmetric key that is going to be used
SwitchToSymmetricKey SwitchToSymmetricKey
// ClientSendHost: Currently unimplemented.
// Client sends what host they are connecting to.
ClientSendHost ClientSendHost
// GetSigningServers: Currently unimplemented.
// Gets the signing servers trusting/signing the current server.
GetSigningServers GetSigningServers
// GetTrustedDomains: Currently unimplemented.
// Gets the domains that are supported by this certificate (should be cross-checked)
GetTrustedDomains GetTrustedDomains
// InitiateForwarding: Starts forwarding traffic over this protocol.
InitiateForwarding InitiateForwarding
) )
// Encryption algorithms
const ( const (
// Default and only encryption algorithm
XChaCha20Poly1305 = iota XChaCha20Poly1305 = iota
) )
// Unsigned integer limits
const ( const (
BitLimit24 = 16_777_215 BitLimit24 = 16_777_215
BitLimit16 = 65535 BitLimit16 = 65535

3
server/README.md Normal file
View file

@ -0,0 +1,3 @@
# Bismuth Server
This is the Bismuth server, which lets you speak the Bismuth protocol.

View file

@ -13,17 +13,22 @@ import (
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
) )
// Bismuth Server
type BismuthServer struct { type BismuthServer struct {
PublicKey *crypto.Key // Public key to use for transmission
PublicKey *crypto.Key
// Private key to use for transmission
PrivateKey *crypto.Key PrivateKey *crypto.Key
pgp *crypto.PGPHandle pgp *crypto.PGPHandle
// Algorithm to use for encryption (currently XChaCha20Poly1305 is the only option)
SymmetricEncryptionAlgorithm int SymmetricEncryptionAlgorithm int
// Servers that are signing this server. If none, this server becomes self-signed
// in the clients eyes
SigningServers []string SigningServers []string
// This is what's called after a successful handshake & connection. // Called after a successful handshake & connection.
HandleConnection func(conn net.Conn) error HandleConnection func(conn net.Conn) error
} }
@ -56,7 +61,7 @@ func (bismuth BismuthServer) decryptMessage(aead cipher.AEAD, encMsg []byte) ([]
return decryptedData, nil 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 { func (bismuth BismuthServer) HandleProxy(conn net.Conn) error {
serverState := "keyHandshake" 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) { func NewBismuthServer(pubKey string, privKey string, signServers []string, encryptionAlgo int, connHandler func(conn net.Conn) error) (*BismuthServer, error) {
publicKey, err := crypto.NewKeyFromArmored(pubKey) publicKey, err := crypto.NewKeyFromArmored(pubKey)

3
tests/README.md Normal file
View file

@ -0,0 +1,3 @@
# Test Suites
These are either test suites or support code for test suites for Bismuth's client and server code.