Note
This draft extension is not yet implemented in any device or reader. It builds on revision 2026-10-22 of the main specification. The envelope format and key management may still change.
Note
  • The signature goes in the ZIP archive comment, not in a ZIP entry: no JAR v1 style ambiguity (duplicate entries, central directory order), and the file stays a valid ZIP archive.

  • Existing .bor files often have LIM as their ZIP comment. The envelope is added after it.

  • The public key is embedded in the envelope (Salem), so anyone can check the signature.

  • No algorithm field: BORSIG1 fixes the algorithm and is part of the signed bytes. Another algorithm means BORSIG2.

  • PocketLim devices first sign with their existing VPN key (RSA 2048, one per device). A dedicated signing key is the target, so that the VPN key and the signing key can be revoked separately.

  • The generator is not repeated in the envelope: it is the generator element of the signed description.xml. The public key, not the generator id, identifies who signed.

  • One signature per file, no chain (Salem). A re-exported file gets its own signature, with no link to the source file.

  • Signing is optional, even for approved generators (Salem). A signature can be removed from a file, so a reader that requires one has to reject unsigned files itself.

  • Open: is the order of the envelope lines (BORSIG1, key:, sig:) fixed?

  • Open: which state when the key is in the trust list but description.xml has no generator?

  • Open: must a reader reject an envelope whose last line has no line feed?

Signature envelope

The envelope is ASCII text with LF line endings, at the end of the ZIP archive comment.

  • It starts at the first BORSIG<n> line.

  • Text before it, such as LIM, is kept. If that text does not end with a line feed, the writer adds one before the envelope.

  • Every envelope line, including the last one, ends with a line feed.

  • A file has at most one envelope.

Example archive comment
LIM
BORSIG1
key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
sig: Zm9vYmFy...
Table 1. Envelope lines
Line Description Type Required

BORSIG1

Version, fixes the profile below

text

required

key:

Public key, base64 DER SubjectPublicKeyInfo

text

required

sig:

Signature, base64

text

required

Signed bytes

The signature covers the archive as if its comment were empty, prefixed by the version line. In order:

  1. BORSIG1 followed by a line feed;

  2. the archive from offset 0 up to, but not including, the EOCD comment length field (the first 20 bytes of the EOCD are included);

  3. two zero bytes (empty comment length).

The archive comment is not signed: neither the envelope nor the text before it.

ZIP64 archives are signed the same way: the classic EOCD is still the last record.

Profile BORSIG1

Table 2. BORSIG1 parameters
Parameter Value Note

Key type

RSA

At least 2048 bits. A smaller key, or a key that is not RSA, makes the signature invalid.

Signature scheme

RSASSA-PSS (RFC 8017, section 8.1)

Hash

SHA-256

The input of the signature operation is the signed bytes, not a digest of them.

Mask generation

MGF1 with SHA-256

Salt length

32 bytes

A signature made with another salt length is invalid.

Public key

DER SubjectPublicKeyInfo with the rsaEncryption algorithm identifier, base64

Signature

Raw RSA output, base64

As long as the key modulus: 256 bytes for a 2048-bit key.

Base64

RFC 4648, section 4, with padding

No line breaks.

Example with openssl (informative)
# signed bytes (file.bor has no envelope yet)
eocd=$(grep -obUaP 'PK\x05\x06' file.bor | tail -n 1 | cut -d: -f1)
{ printf 'BORSIG1\n'; head -c $((eocd + 20)) file.bor; printf '\0\0'; } > signed-bytes.bin

# sign, then verify
opts="-sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -sigopt rsa_mgf1_md:sha256"
openssl dgst -sha256 -sign key.pem $opts -out sig.bin signed-bytes.bin
openssl dgst -sha256 -verify pub.pem $opts -signature sig.bin signed-bytes.bin

# values of the key: and sig: lines
openssl pkey -in key.pem -pubout -outform DER | base64 -w0
base64 -w0 sig.bin

Verification result

A reader that supports this extension reports one of these states:

Table 3. Verification states
State Meaning

absent

No line of the form BORSIG<n> in the archive comment

unsupported

The envelope version is not supported by the reader, for example BORSIG2

invalid

The envelope is malformed, or the signature does not match the signed bytes

valid, unknown key

The signature matches, but the key is not in the reader’s trust list

valid, recognized generator

The signature matches, and the reader’s trust list associates the key with the generator declared in description.xml

Compatibility

ZIP readers find the EOCD from the end of the file and read the comment with it. The comment holds up to 65535 bytes; the envelope is about 0.8 KB with an RSA 2048 key. Python zipfile, rubyzip 0.9.4 and fflate 0.8 unzipSync open a signed file and return the comment unchanged.

Any change to the signed bytes invalidates the signature. A tool that rewrites the archive without copying the comment removes the signature.

Out of scope

Generator approval, key ownership, trust lists and key revocation are internal to LIM and not covered by this draft.