In this article:
Data received from Myriota modules can be forwarded to one or more http(s) destinations. Data is sent as a http POST request with Content-Type: application/json. All data is accompanied by an reference (EndpointRef), timestamp, a unique identifier (UUID), and a digital signature that may be used to verify that the data originated from Myriota. Multiple messages may be batched into a single request.
Note that messages may be duplicated and/or arrive out of order.
POST request structure
Data is packaged into a structure with the following fields:
- EndpointRef: Unique Identifier of destination
- Timestamp: Unix Epoch time at which the HTTP POST request was generated
- Id: UUID identifying data
- Data: Message data
- Signature: PKCS1_15 signature of Timestamp, Id and Data fields
- CertificateUrl: URL of certificate containing the public key to authenticate Signature
The signature applies to the EndpointRef, Timestamp, Id and Data fields concatenated together with each value separated by a newline character. The public key used to verify the Signature may be obtained from the X509 certificate specified by CertificateUrl.
The CertificateUrl is included in the message body to facilitate periodic rotation of the underlying key. The certificate will always be hosted at security.myriota.com. The certificate itself contains a Subject field and the Subject field contains CN and O fields . The CN field must be security.myriota.com and the O field must by "Myriota Pty Ltd". Any data not meeting these criteria should be rejected.
Data payload
The Data field in the POST request is a JSON serialised structure with the following fields:
- Packets: Array of raw messages received from one or more modules
Elements within the Packets array have the following structure:
- Timestamp: Unix Epoch time (ms) at which the packet was captured by satellite
- TerminalId: Id of the module from which the packet was transmitted
- Value: The 20-byte message from the module that can be modified by the firmware. A further message generation timestamp can be included
The example below shows a http POST request body containing a single message from a module:
{
"EndpointRef": "N_HlfTNgRsqe:uyXKvYTmTAO5",
"Timestamp": 1563521870,
"Data": "{\"Packets\": [{\"Timestamp\": 1563521870359, \"TerminalId\": \"0001020304\", \"Value\": \"0001020304050607080910111213141516171819\"}]}",
"Id": "fe77e2c7-8e9c-40d0-8980-43720b9dab75",
"CertificateUrl": "https://security.myriota.com/data-13f7751f3c5df569a6c9c42a9ce73a8a.crt",
"Signature": "k2OIBppMRmBT520rUlIvMxNg+h9soJYBhQhOGSIWGdzkppdT1Po2GbFr7jbg..."
}
Signature Verification Example
The python fragment below contains example code showing how the Certificate and Signature may be used to validate the data.
import base64
import json
import requests
from OpenSSL import crypto
try:
# Python2
import urlparse
except ImportError:
# Python3
import urllib.parse as urlparse
def verify(rx_body):
# Verify CertificateUrl is hosted by Myriota
url = urlparse.urlparse(rx_body['CertificateUrl'])
assert url.scheme == 'https'
assert url.netloc == 'security.myriota.com'
# Get the certificate
response = requests.get(rx_body['CertificateUrl'])
assert response.status_code == 200
# import and verify the certificate
cert = crypto.load_certificate(crypto.FILETYPE_PEM, response.content)
cert_subj = cert.get_subject().get_components()
assert b'Myriota Pty Ltd' == next(cs[1] for cs in cert_subj if cs[0] == b'O')
assert b'security.myriota.com' == next(cs[1] for cs in cert_subj if cs[0] == b'CN')
# Reconstruct signed payload
data = '\n'.join([
rx_body['EndpointRef'],
str(rx_body['Timestamp']),
rx_body['Id'],
rx_body['Data']])
# verify signature - asserts on failure
crypto.verify(cert, base64.b64decode(rx_body['Signature']), data, "sha256")
The python fragment below shows how the verify function may be used to authenticate an example, signed update.
post_body = {
"EndpointRef": "N_HlfTNgRsqe:uyXKvYTmTAO5",
"Timestamp": 1563521870,
"Data": ("{\"Packets\": [{\"Timestamp\": 1563521870359, "
"\"TerminalId\": \"f74636ec549f9bde50cf765d2bcacbf9\", "
"\"Value\": \"0101010101010101010101010101010101010101\"}]}"),
"Id": "fe77e2c7-8e9c-40d0-8980-43720b9dab75",
"CertificateUrl":
"https://security.myriota.com/data-13f7751f3c5df569a6c9c42a9ce73a8a.crt",
"Signature": ("k2OIBppMRmBT520rUlIvMxNg+h9soJYBhQhOGSIWGdzkppdT1Po2GbFr7jbg"
"i/NZapTG92VQkeHiaq4N9AIyP0mmUIv0x+liIbproMV04ML5Y1svhrtwFQoaJTVZqwuZObuq"
"vFeXpqKGMfgHeet+B5EVJVKM/aqSy9lFpIP8ptX55jespbiLeQ6J2kmKyXZK72As8ryOW6D0"
"ASa1kAFsFcSoSIcS0eOkyuP+MKEHApKfzN9J/qPOobP45OfC14j83GdEdA1Fo/wTUwyefwRe"
"HVJlPKnsEtjqXefZdmqSytF7S/Pd0ypMtC+R4PW52rtQa0Doq63I38oDSvQ0EbawYA==")
}
verify(post_body)
# deserialise data now it's authenticated
data = json.loads(post_body['Data'])