| (3 intermediate revisions by the same user not shown) | |||
| Line 2: | Line 2: | ||
Application is required to establish a WebSocket server where ChronoFly can send real time timing data. | Application is required to establish a WebSocket server where ChronoFly can send real time timing data. | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
==Protocol== | ==Protocol== | ||
| Line 119: | Line 83: | ||
"event": "connected", | "event": "connected", | ||
"id": "0", | "id": "0", | ||
| − | "version": "1.2.30" | + | "version": "1.2.30", |
| + | "unique" : "129a9a6d1ced6963", | ||
| + | "dn" : "O=Salkku#4" | ||
} | } | ||
} | } | ||
| Line 131: | Line 97: | ||
* event: event type | * event: event type | ||
| − | * id: event id (incremented by 1 and zeroed when | + | * id: event id (incremented by 1 and zeroed when websocket tcp stream opened, should always be 0 in this event) |
| − | * version: ChronoFly version | + | * version: ChronoFly version (also messaging API version) |
| + | * unique: 2-part unique value containing a hash generated during install time of ChronoFly and a hash generated during start of ChronoFly | ||
| + | * dn: domain name component of the certificate holding public key used in data signing (in field signature) | ||
===Keepalive PDU=== | ===Keepalive PDU=== | ||
| Line 145: | Line 113: | ||
"data": { | "data": { | ||
"event": "keepalive", | "event": "keepalive", | ||
| − | "id": " | + | "latency": "262", |
| + | "id": "1" | ||
} | } | ||
} | } | ||
| Line 157: | Line 126: | ||
* event: event type | * event: event type | ||
| − | * id: event id (incremented by 1 and zeroed when websocket tcp stream opened) | + | * latency: round-trip time of previous keepalive (from station to server and back to station, calculated by station) |
| + | * id: event id (incremented by 1 and zeroed when websocket tcp stream opened, should always be larger than 0 in this event) | ||
====Confirmation rules==== | ====Confirmation rules==== | ||
| Line 199: | Line 169: | ||
* message2: additional malfunction desctiption | * message2: additional malfunction desctiption | ||
* type: malfunction type | * type: malfunction type | ||
| + | |||
| + | ==WebSocket server example== | ||
| + | |||
| + | ===NodeJs=== | ||
| + | |||
| + | var ws = require("nodejs-websocket"); | ||
| + | var server = ws.createServer(function(conn) { | ||
| + | var station = ""; | ||
| + | conn.on("text", function(msg) { | ||
| + | try { | ||
| + | var dataObject = JSON.parse(msg); | ||
| + | station = dataObject.station; | ||
| + | |||
| + | // Do something with dataObject here, maybe persist to database etc. | ||
| + | |||
| + | if(dataObject.data.event != undefined) { | ||
| + | if(dataObject.data.event == "passing") { | ||
| + | conn.sendText("bumb-" + dataObject.data.id); | ||
| + | } else if(dataObject.data.event == "keepalive") { | ||
| + | conn.sendText("pong"); | ||
| + | } | ||
| + | } else { | ||
| + | console.log("Received invalid json WS PDU"); | ||
| + | } | ||
| + | } catch (e) { | ||
| + | console.log("Received non-json WS PDU of length " + msg.length); | ||
| + | } | ||
| + | }); | ||
| + | conn.on("connect", function() { | ||
| + | console.log("New station started publishing"); | ||
| + | }); | ||
| + | conn.on("close", function(code, reason) { | ||
| + | console.log("Station " + station + " not publishing anymore"); | ||
| + | }); | ||
| + | }).listen(8275); | ||
==PDU signature verification example== | ==PDU signature verification example== | ||
| Line 235: | Line 240: | ||
?> | ?> | ||
| − | |||
===OpenSSL/Bash=== | ===OpenSSL/Bash=== | ||
Latest revision as of 12:01, 19 February 2022
ChronoCloud 2 protocol enables other applications to aquire real time timing data from ChronoFly.
Application is required to establish a WebSocket server where ChronoFly can send real time timing data.
Contents
Protocol
Uses WebSocket Draft_6455 and TCP port 8275.
Passing PDU
Sent whenever passing (transponder or manual) is registered on ChronoFly.
{
"station": "ChronoFly-12345",
"competition": "166656",
"signature": "aW52YWxpZA==",
"sent": "2007-11-20T22:19:17.531+02:00",
"data": {
"event": "passing",
"id": "70",
"type": "Route-TC-Finish",
"round": "ET 4 TC 9 Huhdanoja A",
"time": "2007-11-20T22:19:17.532+02:00",
"tctime": "2007-11-20T22:19:00.000+02:00",
"transponder": "7",
"hits": "0",
"lap": "5"
}
}
Where
- station: ChronoFly station identifier
- competition: ChronoFly competition identifier
- signature: Data signature based on keys negotiated during ChronoFly registration (base64 encoded SHA1withRSA)
- sent: time this message was sent from ChronoFly (ISO 8601)
- event: event type
- id: event id (incremented by 1 and zeroed when round changed or tables cleared)
- type: station type selected currently at ChronoFly
- round: round name selected currently at ChronoFly
- time: passing time (ISO 8601)
- tctime: time check time (ISO 8601)
- transponder: passing transponder number
- hits: times same transponder has been seen during current passing event
- lap: times same transponder has been seen during selected round (incremented by 1)
Confirmation rules
Whenever server receives a PDU it is required to confirm it back to the sender (ChronoFly).
Response format is: bumb-<data.id>
Example confirmation response
bumb-4291
If PDU is not confirmed back to sender (ChronoFly) it will be re-sent to server during next event cycle.
Supported station types
- Route-Finish
- Route-Start
- Route-Pause
- TC-Time
- TC-Separate
- TC-Pause
- Route-TC-Finish
- Route-TC-Start
- Track-Finish
- Track-Sector
Connection PDU
Sent whenever ChronoFly connects to the WebSocket server.
{
"station": "ChronoFly-12345",
"competition": "166656",
"signature": "aW52YWxpZA==",
"sent": "2007-11-20T22:19:17.531+02:00",
"data": {
"event": "connected",
"id": "0",
"version": "1.2.30",
"unique" : "129a9a6d1ced6963",
"dn" : "O=Salkku#4"
}
}
Where
- station: ChronoFly station identifier
- competition: ChronoFly competition identifier
- signature: Data signature based on keys negotiated during ChronoFly registration (base64 encoded SHA1withRSA)
- sent: time this message was sent from ChronoFly (ISO 8601)
- event: event type
- id: event id (incremented by 1 and zeroed when websocket tcp stream opened, should always be 0 in this event)
- version: ChronoFly version (also messaging API version)
- unique: 2-part unique value containing a hash generated during install time of ChronoFly and a hash generated during start of ChronoFly
- dn: domain name component of the certificate holding public key used in data signing (in field signature)
Keepalive PDU
Sent every 30 seconds to the WebSocket server.
{
"station": "ChronoFly-12345",
"competition": "166656",
"signature": "aW52YWxpZA==",
"sent": "2007-11-20T22:19:17.531+02:00",
"data": {
"event": "keepalive",
"latency": "262",
"id": "1"
}
}
Where
- station: ChronoFly station identifier
- competition: ChronoFly competition identifier
- signature: Data signature based on keys negotiated during ChronoFly registration (base64 encoded SHA1withRSA)
- sent: time this message was sent from ChronoFly (ISO 8601)
- event: event type
- latency: round-trip time of previous keepalive (from station to server and back to station, calculated by station)
- id: event id (incremented by 1 and zeroed when websocket tcp stream opened, should always be larger than 0 in this event)
Confirmation rules
Whenever server receives a PDU it is required to confirm it back to the sender (ChronoFly).
Response format is: pong
Example confirmation response
pong
If PDU is not confirmed back to sender (ChronoFly) it will be re-sent to server during next event cycle.
Malfunction PDU
Sent every 30 seconds to the WebSocket server.
{
"station": "ChronoFly-12345",
"competition": "166656",
"signature": "aW52YWxpZA==",
"sent": "2007-11-20T22:19:17.531+02:00",
"data": {
"event": "malfunction",
"message": "java.net.SocketException: Connection timeout at com.enymind.drivers.MercuryComm.connect()",
"message2": "Unknown error",
"type": "1"
}
}
Where
- station: ChronoFly station identifier
- competition: ChronoFly competition identifier
- signature: Data signature based on keys negotiated during ChronoFly registration (base64 encoded SHA1withRSA)
- sent: time this message was sent from ChronoFly (ISO 8601)
- event: event type
- message: malfunction desctiption
- message2: additional malfunction desctiption
- type: malfunction type
WebSocket server example
NodeJs
var ws = require("nodejs-websocket");
var server = ws.createServer(function(conn) {
var station = "";
conn.on("text", function(msg) {
try {
var dataObject = JSON.parse(msg);
station = dataObject.station;
// Do something with dataObject here, maybe persist to database etc.
if(dataObject.data.event != undefined) {
if(dataObject.data.event == "passing") {
conn.sendText("bumb-" + dataObject.data.id);
} else if(dataObject.data.event == "keepalive") {
conn.sendText("pong");
}
} else {
console.log("Received invalid json WS PDU");
}
} catch (e) {
console.log("Received non-json WS PDU of length " + msg.length);
}
});
conn.on("connect", function() {
console.log("New station started publishing");
});
conn.on("close", function(code, reason) {
console.log("Station " + station + " not publishing anymore");
});
}).listen(8275);
PDU signature verification example
PHP
<?php
// ######### INSERT VALUES HERE ############
// Base64 encoded public key
$LOCAL_PUBLIC = 'VDKNzVNUmnL3qTDs';
// Base64 encoded signature from PDU
$SIGNATURE = 'BsChw6wRCFTGziTu3p3a';
// JSON PDU as-is sent by ChronoFly
// BUT replace signature value with value "aW52YWxpZA=="
$PDU = '{"station": "chronofly-2248717", "sent": "2017-12-30T21:16:36.295+02:00", "signature": "aW52YWxpZA=="}';
// ######### DO NOT EDIT BELLOW ############
// http://phpseclib.sourceforge.net
set_include_path( get_include_path() . PATH_SEPARATOR . 'phpseclib' );
include_once( 'Crypt/RSA.php' );
$rsa = new Crypt_RSA();
$rsa->loadKey( $LOCAL_PUBLIC );
$rsa->setSignatureMode( CRYPT_RSA_SIGNATURE_PKCS1 );
if( $rsa->verify( $PDU, base64_decode( $SIGNATURE ) ) )
die("OK verify\n");
else
die("ERR verify\n");
?>
OpenSSL/Bash
Note! Do NOT use openssl rsa or openssl rsautl for playing with signatures as these friends does
not obey PKCS#1-v1.5 standard (not encoding the hash in an ASN.1 sequence). Use openssl sha1 instead. For playing
with public keys you CAN still use these friends.
#!/bin/bash
############ INSERT VALUES HERE ############
# Base64 encoded public key
LOCAL_PUBLIC='VDKNzVNUmnL3qTDs'
# Base64 encoded signature from PDU
SIGNATURE='BsChw6wRCFTGziTu3p3a'
# JSON PDU as-is sent by ChronoFly
# BUT replace signature value with value "aW52YWxpZA=="
PDU='{"station": "chronofly-2248717", "sent": "2017-12-30T21:16:36.295+02:00", "signature": "aW52YWxpZA=="}'
############ DO NOT EDIT BELLOW ############
printf "$LOCAL_PUBLIC" | base64 -d > chrono.der
openssl rsa -inform der -outform pem -pubin -in chrono.der -out chrono.pem
printf "$SIGNATURE" | base64 -d > chrono.sig
printf "$PDU" > chrono.json
printf "////// HEXDUMP chrono.der ->\n"; xxd chrono.der
printf "////// HEXDUMP chrono.pem ->\n"; xxd chrono.pem
printf "////// HEXDUMP chrono.json ->\n"; xxd chrono.json
printf "////// HEXDUMP chrono.sig ->\n"; xxd chrono.sig
printf "////// LOCAL_PUBLIC ASN1 ->\n"; openssl asn1parse -in chrono.pem
printf "////// LOCAL_PUBLIC ASN1 IDX 18 ->\n"; openssl asn1parse -in chrono.pem -strparse 18
printf "////// LOCAL_PUBLIC KEY ->\n"; openssl rsa -pubin -inform PEM -text -noout -in chrono.pem
printf "////// VERIFICATION RESULT ->\n"; openssl sha1 -verify chrono.pem -signature chrono.sig chrono.json
rm chrono.der
rm chrono.pem
rm chrono.json
rm chrono.sig