Specified algorithms

CBOT contains a number of algorithms that must match within each client and implementation.

ID

ID is a 24 bit long number which is encoded in base64-like format to 4 characters. IDs are used to inject these numbers so that the length of the characters is always known.

Encoding Algorithm

Encoding algorithm is described as follows:

function numberToId(id: number): string {
  const FC = 32; // Character offset
  const part1 = ((id >> 18) & 63) + FC;
  const part2 = ((id >> 12) & 63) + FC;
  const part3 = ((id >> 6) & 63) + FC;
  const part4 = (id & 63) + FC;
  return String.fromCharCode(part1, part2, part3, part4);
}

Decoding Algorithm

Decoding algorithm is described as follows:

function idToNumber(str: string): number {
  const FC = 32; // Character offset
  if (str.length !== 4) {
    throw new Error("Invalid Id-length (4 bytes required)");
  }
  const part1 = str.charCodeAt(0) - FC;
  const part2 = str.charCodeAt(1) - FC;
  const part3 = str.charCodeAt(2) - FC;
  const part4 = str.charCodeAt(3) - FC;
  return (part1 << 18) | (part2 << 12) | (part3 << 6) | part4;
}

Key-ID Mapping

Key-ID -mapping is used to map arbitrary non-newline string into an ID so that the string does not need to be repeated or included in the message at all. This mapping is mainly used for transporting property names within the message but other purposes may exist in the future (for instance enumerated values).

By convention ID-generation is reserved as:

Protocol does not force a specific order how IDs are allocated but in practice:

Checksum Calculation

When Key-ID -pairs are reserved statically both sender and receiver must agree on how they are mapped. For this an 8-byte long checksum is calculated. The checksum itself is simple and does not require cryptographic security. It is mainly used as a quick sanity check.

Calculation Algorithm


type KeyId = {
  id: string,
  key: string
};

function calculateChecksum(protocolVersion: string, keyIds: KeyId[]): string {
  let chk = 0x12345678;

  const s = protocolVersion + ':' + [...keyIds]
    .sort((v1, v2) => v1.key.localeCompare(v2.key))
    .map(ki => `${ki.key}_${ki.id}`)
    .join(',');

  const length = s.length;

  for (let i = 0; i < length; i++) {
    chk += (s.charCodeAt(i) * (i + 1));
  }

  return (chk & 0xffffffff).toString(16);
}