Skip to content

Instantly share code, notes, and snippets.

@amcgregor
Created November 20, 2009 04:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amcgregor/239295 to your computer and use it in GitHub Desktop.
Save amcgregor/239295 to your computer and use it in GitHub Desktop.

This document is a draft. Please be aware that it may change at any time, and some of it is probably on crack, insecure, and may kick your dog.

Data Structure

The on-disk data structure is stored within a self-contained portable MongoDB instance.

Collection Type Description
hosts capped The cache of host IDs, address, and public key. Also includes the private and public keys you generate for each host.
Allows for identity verification during re-connection to recent hosts.
pow capped Proof-of-Work history to prevent re-use of work tokens.
identities normal Your collection of public identities and private keys.

Protocol Layers

TLS
Negotiated Middleware
Frame Chunking
Messaging
Application

Negotiated middleware would include features such as zlib or lzma compression, etc.

Frame Layer

The frame layer self-organizes available middleware between client and server and provides a rich, DoS attack-resistant, and asynchronous low-level protocol for bidirectional TCP communication between nodes.

Each frame may contain no more than 4 + 16 + 64 + 2^24 bytes (roughly 16MB, though few frames ever reach this size) with the following layout:

Length Type Name Notes
4 Bytes UInt32 Payload Size The 8 highest-order bits should be random and are ignored.
16 Bytes UUID Frame ID This is to allow for the deferred message received/denied/corrupt messages.
64 Bytes SHA-512 Payload Hash This ensures data integrity of the bencoded payload.
<2^24 Bytes Bencode Payload Potentially multi-part; each message is decoded and parsed separately.

The header signature is ignored (and should be padded with random data) during the key/feature negotiation phase of communication.

Middleware layers may add additional prefix headers (e.g. the “compressed or not” flag, anti-analysis pre/postfix padding) which are stripped before the frame layer handles the remaining packet.

Message Layer

Key / Feature Negotiation

A ‘hello’ packet is passed from server to client upon connection, and the client responds.

Server Hello

{
        'msg': 'system.hello',
        'pubkey': '...',
        'challenge': (20, UUID(...)),
        'requires': {
                'header.signature.sha': [256, 512]
            },
        'offers': {
                'frame.compression': ['gzip', 'lzma'],
                'frame.salting': []
            }
    }

The challenge is a 2-tuple/list defining the required bits and cleartext UUID.

Client Hello

This is sent in response if all is well.

{
        'msg': 'system.hello:ok',
        'pubkey': '...',
        'response': '...',
        'accept': {
                'header.signature.sha': 512,
                'frame.compression': 'lzma',
                'frame.salting': []
            }
    }

Client Olleh

If the client is unable to meet the requirements, or itself has unmet requirements, communication is halted with the client disconnecting after sending this message. The requires key indicates unmet requirements, the rejects key indicates server requirements that can not be met.

{
        'msg': 'system.hello:error',
        'requires': {
                'header.signature.md5': []
            },
        'rejects': {
                'header.signature.sha': [256, 512]
            }
    }

Application Layer

The application layer defines a rich object-oriented RPC mechanism that allows for the proxy of bencode-able objects, the execution of public remote methods, etc. Other application layer interfaces can be defined for more explicit control on server and client. Each side of the connection has full introspection capabilities. Proxied objects are given UUIDs. Common messages include:

RPC Object Construction

{
        'msg': 'rpc.construct',
        'cls': 'Foo',
        'args': [],
        'kwargs': {}
    }

Returns:

{
        'msg': 'rpc.construct:ok',
        'value': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'),
        'dict': {}
    }

The UUID is an object reference, with the actual object stored server-side. Client-side the object reference will proxy property and method requests across the network. Objects can optionally allow certain data to be cached locally, returned as dict.

RPC Method Call

{
        'msg': 'rpc.call',
        'oid': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'),
        'method': 'hello',
        'args': [],
        'kwargs': {}
    }

Returns:

{
        'msg': 'rpc.call:ok',
        'value': "Hello world!"
    }

Attribute Manipulation

Getters

{
        'msg': 'rpc.attribute.get',
        'oid': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'),
        'attribute': 'name'
    }

Returns:

{
        'msg': 'rpc.attribute.get:ok',
        'value': "world"
    }

Setters

{
        'msg': 'rpc.attribute.set',
        'oid': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'),
        'attribute': 'name',
        'value': 'Alice'
    }

Returns:

{
        'msg': 'rpc.attribute.set:ok'
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment