chore: Adds lots of documentation.
This commit is contained in:
parent
514ccee264
commit
8eb9bb3cee
11 changed files with 101 additions and 14 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
bismuthd
|
21
README.md
21
README.md
|
@ -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
3
client/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Bismuth Client
|
||||||
|
|
||||||
|
This is a wrapper around connections which lets you speak the Bismuth protocol.
|
|
@ -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
4
commons/README.md
Normal 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`.
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
3
server/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Bismuth Server
|
||||||
|
|
||||||
|
This is the Bismuth server, which lets you speak the Bismuth protocol.
|
|
@ -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
3
tests/README.md
Normal 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.
|
Loading…
Add table
Reference in a new issue