Destination API

Introduction

Data received from Myriota terminals can be forwarded to a pre-registered http(s) endpoint. Data is sent as a http POST request with Content-Type: application/json. All data is accompanied by an endpoint reference, timestamp, a unique identifier (UUID), and a digital signature that may be used to verify that the data originated from Myriota. Multiple packets may be batched into a single request.

POST request structure

Data is packaged into a structure with the following fields:

  • EndpointRef: Unique Identifier of endpoint
  • Timestamp: UTC timestamp when data was issued
  • Id: UUID identifying data
  • Data: Terminal data
  • Signature: PKCS1_15 signature of Timestamp, Id and Data fields
  • CertificiateUrl: 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 packets received from one or more terminals

Elements within the Packets array have the following structure:

  • Timestamp: UTC timestamp (ms) at which the packet was transmitted
  • TerminalId: Id of the terminal from which the packet was transmitted
  • Value: The packet data, serialised as a hexadecimal string

The example below shows a http POST request body containing a single message from a terminal:

{
  "EndpointRef": "mY7769yqTWKp:JS2iz1T9S52D",
  "Timestamp": 1526630211,
  "Data": "{\"Packets\": [{\"Timestamp\": 1526626779028,\
    \"TerminalId\": \"48a6b2fbc33f902d19970b58802c4518\",\
    \"Value\": \"8136fd5acc1384f4cbeba657cccccccccccccccccc\"}]}",
  "Id": "71b21edf-e385-4ff1-91db-0ed7de432496",
  "CertificateUrl":
    "https://security.myriota.com/data-081550a4ac7b450ba4f710d30454d907.crt",
  "Signature": "Qk3/qgUCGVeMkeTBmNhMM63Ys2t54GThfNAEcGY2fae+V80hZXLJHzuM3pV..."
}

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": "mY7769yqTWKp:JS2iz1T9S52D",
  "Timestamp": 1526630211,
  "Data": ("{\"Packets\": [{\"Timestamp\": 1526626779028, "
    "\"TerminalId\": \"48a6b2fbc33f902d19970b58802c4518\", "
    "\"Value\": \"8136fd5acc1384f4cbeba657cccccccccccccccccc\"}]}"),
  "Id": "71b21edf-e385-4ff1-91db-0ed7de432496",
  "CertificateUrl":
    "https://security.myriota.com/data-081550a4ac7b450ba4f710d30454d907.crt",
  "Signature": ("Qk3/qgUCGVeMkeTBmNhMM63Ys2t54GThfNAEcGY2fae+V80hZXLJHzuM3pVnTK"
    "kiemTMOZu+K69EsboHkIQXVfVpHvkEYK8mhW0ChMs2yvqDQxvXmzqdtSft7GtJO/C8ZA7IaKKq"
    "eFr17HD5E6bR/zo8JXAg2cZp8eMd7b0xbmSIGFlpfGGm9fHniFxXTLFw8qKvPWza99Mss69MpD"
    "0TFQC0+NS5C+G9ZGkIlhsOX3g55ApO2nv1asOh8Q9UpDZUtnubIIJ73nYfapcvC/+VTY/QXBls"
    "iYjhjdnQQz3CehmrrwqEz9N6Q8ZEqgs36Q0tc+cKPGKMHorgfnu/bUpoAQ==")
}
verify(post_body)

# deserialise data now it's authenticated
data = json.loads(post_body['Data'])