Note
This draft extension is not yet implemented in any device or reader. Algorithm choice, key management and generator id conventions may still change.
Note
  • Signature covers the whole .bor archive, appended as a block after the zip End Of Central Directory (EOCD) rather than as a regular zip entry (Salem): avoids the ambiguity issues known from JAR signing v1 (duplicate entries, central directory not authoritative for byte order), and stays backward-compatible for readers unaware of the block - standard zip libraries already ignore bytes following the EOCD.

  • The public key is embedded directly in the signature block, PEM-encoded (Salem): the file stays self-verifiable cryptographically by anyone; whether a given key belongs to a trusted generator is a separate concern, kept internal to LIM and out of this spec.

  • algorithm is an explicit field rather than a fixed choice (Salem): lets a future algorithm (e.g. Ed25519) be added later without a new block format. Default assumed algorithm is SIGALG_RSA_PSS_SHA256; the embedded crypto library used by PocketLim devices may require falling back to SIGALG_RSA_PKCS1_SHA256 instead - to confirm with the firmware team before implementation.

  • Signing key for PocketLim devices is not settled (Salem): preference order is (1) a dedicated signing keypair generated and stored on the device, independent from its VPN/SSH key, (2) the VPN/SSH key used only to bootstrap/certify a dedicated key generated on-device, (3) the VPN/SSH key reused directly as a last resort. Mixing a network authentication key with a document-signing key couples two trust domains with unrelated lifecycles (VPN key rotation vs. a signed file’s lifetime) - to confirm with the firmware team.

  • <generator> is added alongside the existing <device>, not instead of it (Salem): <device> stays as-is for readers that only know it (e.g. azurite); a physical device fills in both, a software generator only fills in <generator>.

  • The generator id is self-chosen by whoever signs (a free-form reverse-DNS-style string, e.g. eu.lim.pocketlim), not centrally allocated by LIM (Salem): the format stays open to third-party signers - the real security identity is the embedded public key, id is only a human-readable label.

  • A .bor file has at most one generator and one signature - no signature chains (Salem): a re-export by another piece of software (e.g. an app re-processing a device’s data) produces a different file with its own identity and its own signature, deliberately not chained to the source file’s signature, since the re-export may carry modified values and must not inherit the source file’s non-modification guarantee.

  • Signature presence is never mandatory, on read or on write, even for a new homologated generator (Salem): purely best-effort, so existing unsigned PocketLim archives and any reader unaware of this extension keep working unmodified.

  • Signing/verifying trust (which key belongs to which homologated generator, key revocation) is not part of this draft - kept as an internal LIM mechanism, same treatment as device serial allocation.

Generator identity

<generator> is added under the root <description> element, alongside <device>. A physical device fills in both <device> and <generator type="GEN_DEVICE">, using the same identifier for <generator>’s `id as <device>’s `serial. A software generator fills in only <generator type="GEN_APPLICATION">.

Table 1. Generator properties
Property Description Type Required Example

type

Kind of generator, specified as an XML attribute (see below)

code (see below)

required

GEN_APPLICATION

id

Self-chosen identifier of the generator [1]

text

required

eu.lim.pocketlim

Table 2. Generator type codes
Generator type Description

GEN_DEVICE

Physical measurement device (also described by <device>)

GEN_APPLICATION

Software application

Example description.xml excerpt
  <device>
    <serial>50000</serial>
    <version>1.0</version>
    <build>20190104</build>
  </device>
  <generator type="GEN_DEVICE" id="50000"/>
Example description.xml excerpt (software generator, no physical device)
  <generator type="GEN_APPLICATION" id="eu.lim.example-app"/>

Signature block

The signature is not a zip entry: it is a block appended to the .bor file immediately after the zip End Of Central Directory (EOCD) record. A reader unaware of this extension parses the file as an ordinary zip archive; the EOCD it finds is the original archive’s EOCD, and the trailing bytes making up the block below are simply ignored by standard zip libraries.

The signature value is computed over every byte of the .bor file, from offset 0 up to (excluding) the first byte of this block - i.e. the complete, valid zip archive exactly as it would read without the block appended.

Example signature block
<?xml version="1.0" encoding="UTF-8"?>
<signature xmlns="http://www.lim.eu/bor-signature">
  <algorithm>SIGALG_RSA_PSS_SHA256</algorithm>
  <generator type="GEN_APPLICATION" id="eu.lim.example-app"/>
  <public_key encoding="pem">-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----</public_key>
  <value encoding="base64">...</value>
</signature>
Table 3. Signature block properties
Property Description Type Required

algorithm

Signature algorithm (see below)

code (see below)

required

generator

Generator that produced the file, same shape as <generator> in description.xml

structure

required

public_key

Signer’s public key, PEM-encoded

text

required

value

Signature value, base64-encoded

text

required

Table 4. Signature algorithm codes
Algorithm Description

SIGALG_RSA_PSS_SHA256

RSA-PSS signature, SHA-256 digest (RFC 8017) - default target

SIGALG_RSA_PKCS1_SHA256

RSASSA-PKCS1-v1_5 signature, SHA-256 digest - fallback for embedded crypto libraries without PSS support

Out of scope

This draft only specifies how a .bor file is signed and how a signature can be cryptographically checked against the public key it carries. Whether a given public key belongs to a legitimate, homologated generator - the trust/registry mechanism, and any key revocation policy - is an internal LIM concern and deliberately not documented here.


1. for GEN_DEVICE, should match device/serial; for GEN_APPLICATION, a free-form identifier, reverse-DNS style recommended (e.g. eu.lim.pocketlim) - not centrally allocated