DSPIP Developer Documentation

Complete reference for implementing the Digital Signing of Physical Items Protocol. Version 1.0 | Internet-Draft: draft-midwestcyber-dspip-00

DSPIP is a cryptographic protocol for authenticating physical items using digitally signed QR codes. This specification focuses on the SHIP type for shipping and logistics, providing cryptographic authentication of packages with chain of custody tracking, privacy-preserving delivery, and anti-cloning protections through split-key labels.

Protocol Status

DSPIP is currently an Internet-Draft submitted to the IETF. The specification is stable for implementation.

Quick Start

1. Install the SDK

2. Generate a Key Pair

import { generateKeyPair } from '@dspip/core';

const { privateKey, publicKey } = generateKeyPair();

// privateKey: 32 bytes (hex encoded)
// publicKey: 33 bytes compressed (base64 for DNS)

3. Publish Your Public Key to DNS

warehouse._dspip.example.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1; p=YOUR_PUBLIC_KEY_BASE64"

4. Create a Signed QR Code

import { createSignedQR } from '@dspip/core';

const qrData = createSignedQR({
  privateKey: 'your-private-key',
  keyLocator: 'warehouse._dspip.example.com',
  payload: {
    type: 'SHIP',
    issuer: {
      organization: 'ACME Corp',
      address: { city: 'Omaha', state: 'NE', country: 'US' }
    },
    subject: {
      name: 'Bob Jones',
      address: { city: 'Lincoln', state: 'NE', country: 'US' }
    },
    itemId: 'TRACK-2025-000123',
    timestamp: Date.now(),
    typeData: {
      privacyMode: 'standard',
      parcelId: 'TRACK-2025-000123',
      carrier: 'ACME',
      service: 'Ground'
    }
  }
});

5. Verify a QR Code

import { verify } from '@dspip/core';

const result = await verify(qrData);

if (result.valid) {
  console.log('Signature verified!');
  console.log('Type:', result.type);           // "SHIP"
  console.log('Issuer:', result.payload.issuer);
  console.log('Item ID:', result.payload.itemId);
} else {
  console.error('Verification failed:', result.error);
}

Installation

JavaScript / TypeScript

npm install @dspip/core
# or
yarn add @dspip/core

Python

pip install dspip

Go

go get github.com/MidwestCyberLLC/dspip-go

Java (Maven)

<dependency>
  <groupId>io.dspip</groupId>
  <artifactId>dspip-java</artifactId>
  <version>1.0.0</version>
</dependency>

Rust

cargo add dspip
# or add to Cargo.toml:
[dependencies]
dspip = "1.0"

C / Embedded

git clone https://github.com/MidwestCyberLLC/dspip-c.git
cd dspip-c
mkdir build && cd build
cmake ..
make
sudo make install

Flutter / Dart

flutter pub add dspip
# or add to pubspec.yaml:
dependencies:
  dspip: ^1.0.0

From Source

git clone https://github.com/MidwestCyberLLC/dspip.git
cd dspip
npm install
npm run build

Digital Envelope Model

DSPIP follows a "digital envelope" paradigm for shipping labels. Understanding this model is key to implementing the protocol correctly.

Envelope Exterior (Public)

Like the address on a physical envelope, the following information is encoded but not encrypted. Anyone can read this data:

  • Issuer (sender) identity and address
  • Subject (recipient or last mile provider destination)
  • Tracking number (itemId)
  • Timestamp
  • Type (SHIP for shipping)

Signature (Authenticity Seal)

A cryptographic signature proves the label was created by the claimed sender and has not been tampered with. Similar to a shipping seal.

Private Contents (Encrypted)

For privacy modes, the actual recipient address and delivery instructions are encrypted for the last mile provider, like the contents of a sealed package that only the authorized recipient can access.

Design Philosophy

This model protects recipient privacy while maintaining package routability and authenticity verification. The last mile provider decrypts the final delivery address only when needed.

Protocol Flow

The DSPIP shipping protocol consists of these steps:

  1. Payload Creation
    Sender creates a JSON payload containing type (SHIP), issuer info, subject info, itemId, and timestamp.
  2. Privacy Mode Selection
    Choose privacy mode (standard, encrypted, or split-key) based on security requirements.
  3. Recipient Encryption (Privacy Modes)
    For encrypted/split-key modes, encrypt recipient info with last mile provider's public key using ECIES.
  4. Base64 Encoding
    Payload is Base64 encoded for consistent handling.
  5. Signing
    Sender signs protocol|version|type|keyLocator|encodedPayload using their private key (ECDSA or Ed25519 for split-key).
  6. QR Generation
    Complete 6-7 field structure is serialized with pipe delimiters and encoded as a QR code.
  7. Label Application
    QR code is printed on shipping label and attached to package.
  8. Transit Scanning
    Carriers scan QR code at each custody transfer point.
  9. DNS Lookup / Zone B Reveal
    Scanner queries DNS TXT record for public key (or reveals Zone B for split-key labels).
  10. Verification
    Scanner verifies signature and checks revocation status.
  11. Privacy Decryption
    Last mile provider decrypts recipient information using their ECIES private key.
  12. Delivery Confirmation
    Cryptographic proof of delivery recorded via challenge-response protocol.

Privacy Modes

DSPIP supports three privacy modes for shipping to accommodate different security requirements:

Mode
Standard
Traditional shipping with full recipient visibility
  • Full recipient address in cleartext
  • Standard ECDSA signature (secp256k1)
  • Business-to-business shipments
  • Transparency required scenarios
Mode
Encrypted
Privacy-preserving with encrypted recipient
  • Recipient encrypted with ECIES
  • Last mile provider decrypts at delivery
  • Consumer privacy protection
  • AES-256-GCM encryption
Mode
Split-Key
Maximum security with physical anti-cloning
  • Ed25519 signature from physical label
  • Private key under Zone A scratch-off
  • Public key under Zone B for verification
  • Prevents QR code duplication

Privacy Mode Selection

Use Case Recommended Mode Reason
Business shipments Standard Transparency, easy verification
Consumer deliveries Encrypted Protects recipient privacy
High-value packages Split-Key Prevents cloning attacks
International shipping Encrypted Privacy with customs compliance
Last Mile Provider

For encrypted and split-key modes, packages route to a "last mile provider" (e.g., local post office, corporate mailroom) who decrypts the actual recipient address for final delivery.

Payload Structure

The payload contains shipping information encoded as JSON. DSPIP uses issuer for the sender and subject for the recipient/delivery destination:

{
  "type": "SHIP",              // REQUIRED: Fixed for shipping
  "issuer": {                   // REQUIRED: Sender information
    "name": "string",           // Optional
    "organization": "string",   // Optional
    "address": {
      "street1": "string",      // Optional
      "street2": "string",      // Optional
      "city": "string",         // Optional
      "state": "string",        // Optional
      "postalCode": "string",   // Optional
      "country": "string"       // REQUIRED: ISO 3166-1 alpha-2
    },
    "email": "string",          // Optional
    "publicKeyId": "string"     // Optional
  },
  "subject": {                  // REQUIRED: Recipient/Provider
    "name": "string",           // Optional (may be encrypted)
    "organization": "string",   // Optional
    "lastMileProvider": "string", // For privacy modes
    "address": {                // Optional (may be encrypted)
      "street1": "string",
      "city": "string",
      "state": "string",
      "postalCode": "string",
      "country": "string"
    },
    "publicKeyId": "string"     // Optional
  },
  "itemId": "string",           // REQUIRED: Tracking number
  "timestamp": number,         // REQUIRED: Unix timestamp (ms)
  "message": "string",          // Optional: Public note
  "typeData": {}                // SHIP-specific fields (see below)
}

typeData Object (SHIP Type)

The typeData object contains shipping-specific information:

{
  "typeData": {
    "privacyMode": "standard|encrypted|split-key",
    "parcelId": "string",         // Tracking number
    "carrier": "string",          // Shipping company
    "service": "string",          // Express, ground, etc.
    "lastMileProvider": "string", // DNS key locator for privacy modes
    "encryptedRecipient": "string", // Base64 ECIES encrypted data
    "authenticationProfile": "string", // e.g., "PHYSICAL-SPLIT-KEY"
    "labelSerial": "string",     // For split-key labels
    "gs1": "string",              // Optional: GS1 identifier
    "edi": "string"               // Optional: EDI reference
  }
}

Required Fields

Field Type Description
type string Fixed to "SHIP" for shipping protocol
itemId string Unique tracking identifier for the package
timestamp number Unix timestamp in milliseconds
issuer.address.country string ISO 3166-1 alpha-2 country code
Extensibility

Organizations MAY include additional fields in typeData for compatibility with their systems. Scanners MUST ignore unknown fields to maintain forward compatibility.

QR Serialization

QR data MUST be serialized using pipe (|) delimiters:

DSPIP|<version>|<type>|<keyLocator>|<encodedPayload>|<signature>[|<privateMessage>]

Fields

Field Description Example
protocol Fixed string "DSPIP" DSPIP
version Semantic version 1.0
type Protocol type (SHIP for shipping) SHIP
keyLocator DNS path for public key warehouse._dspip.example.com
encodedPayload Base64-encoded JSON eyJ0eXBlIjoiU0hJUCIuLi4=
signature Hex-encoded ECDSA (DER) 3045022100...
privateMessage Optional ECIES-encrypted message (Base64-encoded ciphertext) IQLx4j2...kF9w== (ECIES compact format)

Examples

// Standard shipping (6 fields)
DSPIP|1.0|SHIP|warehouse._dspip.example.com|eyJ0eXBlIjoiU0hJUCJ9...|3045022...

// Privacy-mode shipping (6 fields)
DSPIP|1.0|SHIP|warehouse._dspip.example.com|eyJwcml2YWN5TW9kZSI6...|3045022...

// With ECIES-encrypted private message (7 fields)
DSPIP|1.0|SHIP|warehouse._dspip.example.com|eyJ0eXBlIjoiU0hJUCJ9...|3045022...|IQLx4j2Rk8...
Private Message Encryption

The privateMessage field must be encrypted using ECIES (Elliptic Curve Integrated Encryption Scheme) with the recipient's public key before Base64 encoding. Use eciesEncryptCompact() from the SDK to encrypt messages. The compact format contains: ephemeralPublicKey (33 bytes) || iv (12 bytes) || ciphertext || mac (16 bytes).

QR Code Requirements

Error Correction Level M (15% recovery) recommended
Version 40 or automatic selection
Encoding Mode Binary for optimal density
Max Capacity (Level M) 2,331 bytes
Validation Required

Implementations MUST validate that exactly 6 or 7 pipe-delimited fields are present, and that the type field equals "SHIP" for shipping protocol. The pipe delimiter was chosen for its low frequency in Base64 and domain names.

DNS Records

Key Locator Format

Key locators MUST follow this pattern:

<selector>._dspip.<domain>

Where:

  • selector: Unique identifier for the key (e.g., "warehouse-a", "omaha-main")
  • _dspip: Fixed protocol subdomain (underscore prefix per RFC 8552)
  • domain: Organization's domain name

Examples

// Warehouse/sender keys
warehouse._dspip.example.com
shipping._dspip.acmelogistics.com

// Last mile provider keys
omaha-main._dspip.usps.gov
receiving-lincoln._dspip.fedex.com

// Corporate mailroom keys
mailroom._dspip.acmecorp.com

DNS TXT Record Format

The DNS TXT record MUST contain semicolon-separated tag=value pairs:

v=DSPIP1; k=ec; c=secp256k1; p=<base64_public_key>; [optional tags]

Required Tags

v
string
Protocol version. MUST be "DSPIP1"
Required
k
string
Key type. MUST be "ec" (elliptic curve)
Required
c
string
Curve identifier. MUST be "secp256k1"
Required
p
string
Public key in Base64 (33 bytes compressed)
Required

Optional Tags

t
number
Key creation timestamp (Unix time)
exp
number
Signing expiration - key cannot create new signatures after this time
exp-v
number
Verification expiration - signatures become invalid after this time
s
string
Key status: "active" (default), "verify-only", or "revoked"
seq
number
Sequence number for key rotation tracking
n
string
Human-readable notes (percent-encoded)
types
string
Supported types (should include "SHIP")
auth
string
Authority level (enterprise, organization, government)
address
string
Physical address for last mile providers (see Address Field Format below)
coverage
string
Zip codes or regions served (comma-separated)
eth
string
Ethereum address for blockchain integration
chain
string
Blockchain network (e.g., "polygon", "ethereum")
rsig
string
Record signature - authenticates lifecycle metadata (see Record Signature below)

Record Signature (rsig)

The rsig field provides cryptographic authentication of lifecycle metadata within the DNS record itself. This protects against unauthorized modification of lifecycle fields (t, exp, exp-v, s, seq) by intermediate DNS resolvers or caches.

Threat Model

Without rsig, an attacker with access to an intermediate DNS resolver could modify lifecycle fields without invalidating QR code signatures:

  • Changing s=active to s=revoked to cause denial of service
  • Changing s=revoked to s=active to bypass revocation
  • Modifying seq to prevent key rotation from taking effect

Signable Content

The rsig signature covers a canonical string of lifecycle fields:

rsig_content = selector | t | exp | exp-v | s | seq

// Example:
warehouse|1703548800|1735084800|1766620800|active|1

Example Record with rsig

warehouse._dspip.example.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC;
  t=1703548800; exp=1735084800; exp-v=1766620800;
  s=active; seq=1; rsig=MEUCIQDx...base64...;
  n=Main%20Warehouse%20Key%202025"

Verification

When rsig is present, verifiers SHOULD:

  1. Extract the rsig value from the DNS record
  2. Reconstruct the signable content from lifecycle fields
  3. Compute SHA-256 hash of the signable content
  4. Verify the rsig signature using the public key from the p= field
  5. If verification fails, treat the record as untrusted
rsig vs DNSSEC

DNSSEC and rsig serve complementary purposes. DNSSEC proves the record came from the authoritative zone. rsig proves the signing key holder authorized the lifecycle values. For maximum security, use both DNSSEC and rsig. DNSSEC alone is sufficient if you control DNS infrastructure end-to-end.

Address Field Format

The address field specifies the physical location of signing facilities, primarily for last mile providers. This enables verifiers to display facility location on shipping labels for encrypted privacy mode, where the recipient address is hidden.

The address field uses a scheme prefix to indicate the encoding:

plus:
DEFAULT
Open Location Code / Plus Code. Compact, open standard, global coverage. RECOMMENDED for new deployments.
street:
string
Percent-encoded street address per RFC 3986. SHOULD include city and postal code.
geo:
coordinates
Geographic coordinates (latitude,longitude). MUST use decimal degrees with negative for South/West.
facility:
string
Named facility reference for organizations with published facility directories.

Note: If no scheme prefix is present, implementations MUST interpret the value as a Plus Code (plus: scheme).

Address Scheme Examples

// Plus Code (default scheme) - ~3m precision
address=plus:86HJW222+22
address=849VCWC8+R9              // implicit plus: scheme
address=plus:CWC8+R9,Omaha       // short code with locality

// Street address (percent-encoded)
address=street:1234%20Post%20Office%20Way%2C%20Omaha%2C%20NE%2068101

// Geographic coordinates
address=geo:41.2565,-95.9345

// Facility reference
address=facility:USPS-OMAHA-MAIN

Address Field Requirements

  • The address field is OPTIONAL for key records
  • Last mile providers SHOULD include address for encrypted mode label display
  • Implementations SHOULD prefer Plus Codes for new deployments
  • Verifiers MUST support all four schemes
  • When displaying addresses, verifiers MAY render Plus Codes as clickable links to mapping services

Example Records

// Shipping warehouse with lifecycle fields
warehouse._dspip.example.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC;
  t=1703548800; exp=1735084800; exp-v=1766620800; s=active; seq=1;
  types=SHIP; auth=enterprise; n=Main%20Warehouse"

// Last mile provider using Plus Code (RECOMMENDED)
omaha-main._dspip.usps.gov. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=BxkiXPQwFZfe80J86AOhMMVU1MWWK+xu9RVPMp/1oJYD;
  t=1703548800; exp=1735084800; exp-v=1766620800; s=active;
  types=SHIP; auth=government; address=86HJW222+22;
  coverage=68101,68102,68103,68104,68105"

// Last mile provider using street address scheme
nyc-hub._dspip.fedex.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=BxkiXPQwFZfe80J86AOhMMVU1MWWK+xu9RVPMp/1oJYD;
  types=SHIP; auth=enterprise;
  address=street:640%20W%2052nd%20St%2C%20New%20York;
  coverage=10019,10020"

// Corporate mailroom using geo coordinates
mailroom._dspip.acmecorp.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=CylmZCNxGafb91I86BPhNNVV2NXYL+yu8SVQNq/2pKZE;
  t=1703548800; exp=1735084800; s=active; seq=1;
  types=SHIP; auth=organization; address=geo:41.2565,-95.9345"

Key Generation

DSPIP uses secp256k1 for standard/encrypted modes and Ed25519 for split-key labels.

secp256k1 (Standard/Encrypted Mode)

Curve secp256k1 (SEC 2) - same as Bitcoin/Ethereum
Private Key Size 256 bits (32 bytes)
Public Key Size 264 bits (33 bytes compressed)
Signature Algorithm ECDSA with SHA-256

Ed25519 (Split-Key Mode)

Curve Curve25519
Private Key Size 256 bits (32 bytes) - Zone A
Public Key Size 256 bits (32 bytes) - Zone B
Signature Algorithm Ed25519

Code Example

import { generateKeyPair, deriveEthAddress } from '@dspip/core';

// Generate secp256k1 key pair for standard/encrypted mode
const { privateKey, publicKey, publicKeyBase64 } = generateKeyPair();

// Optional: derive Ethereum address for blockchain
const ethAddress = deriveEthAddress(publicKey);

console.log('Private Key:', privateKey);
console.log('Public Key (base64):', publicKeyBase64);

// For split-key labels, Ed25519 keys are generated during manufacturing
// and printed under scratch-off zones (Zone A: private, Zone B: public)
Key Compatibility

secp256k1 keys can be used for DSPIP signatures, Ethereum/Polygon transactions, and Bitcoin transactions. Ed25519 split-key labels provide physical anti-cloning through one-time-use keys.

Signature Creation

Standard signatures are created using ECDSA over the secp256k1 curve. Split-key labels use Ed25519.

Standard Mode (ECDSA)

  1. Construct Signable Content
    Concatenate with pipe delimiters: protocol|version|type|keyLocator|encodedPayload
  2. Calculate Hash
    Compute SHA-256 hash of the signable content
  3. Sign
    Sign the hash using ECDSA with the private key
  4. Encode
    Encode signature in DER format, then convert to hexadecimal string

Split-Key Mode (Ed25519)

  1. Reveal Zone A
    Sender scratches Zone A on label to reveal Ed25519 private key
  2. Construct Signable Content
    Same format: protocol|version|type|keyLocator|encodedPayload
  3. Sign
    Sign using Ed25519 with the revealed private key
  4. Destroy Key
    Private key is destroyed after signing (single-use)

What Gets Signed

The signature covers: protocol identifier, version, type, key locator, and encoded payload. It does NOT cover the signature field itself or the optional private message.

// Standard ECDSA signing
const signableContent = `${protocol}|${version}|${type}|${keyLocator}|${encodedPayload}`;
const hash = sha256(signableContent);
const signature = ecdsaSign(hash, privateKey);
const signatureHex = derEncode(signature).toString('hex');

// Split-key Ed25519 signing
const signableContent = `${protocol}|${version}|${type}|${keyLocator}|${encodedPayload}`;
const signature = ed25519Sign(signableContent, zoneAPrivateKey);
const signatureHex = signature.toString('hex');

Verification

Verification Algorithm

  1. Parse QR Data
    Split by pipe delimiter. Validate exactly 6 or 7 fields. Extract protocol, version, type, keyLocator, encodedPayload, signature, [privateMessage].
  2. Validate Protocol
    Protocol MUST equal "DSPIP". Version MUST be compatible (1.0). Type MUST equal "SHIP".
  3. Decode Payload
    Base64 decode encodedPayload. Parse JSON. Check privacyMode if present.
  4. DNS Lookup (Standard/Encrypted)
    Query DNS TXT record for keyLocator. Verify types includes "SHIP". Extract public key.
  5. Zone B Reveal (Split-Key Only)
    For split-key mode, recipient scratches Zone B to reveal Ed25519 public key.
  6. Verify Signature
    Reconstruct signable content: protocol|version|type|keyLocator|encodedPayload. Verify signature with public key.
  7. Revocation Check
    Query revocation list for itemId. Verify within 180-day window.
  8. Privacy Decryption (Last Mile Provider)
    If keyLocator matches verifier, decrypt encryptedRecipient using ECIES private key.

Code Example

import { verify } from '@dspip/core';

const result = await verify(qrData, {
  cacheEnabled: true,
  checkRevocation: true,
  recordToBlockchain: false
});

if (result.valid) {
  console.log('Valid!');
  console.log('Type:', result.type);  // "SHIP"
  console.log('Issuer:', result.payload.issuer);
  console.log('Item ID:', result.payload.itemId);
  console.log('Privacy Mode:', result.payload.typeData?.privacyMode);
} else {
  console.error('Error:', result.errorCode, result.errorMessage);
}

Error Conditions

Error Code Description Severity
INVALID_PROTOCOL Protocol not "DSPIP" Fatal
INVALID_TYPE Type not "SHIP" Fatal
PARSE_ERROR Cannot parse QR structure (wrong field count) Fatal
SIGNATURE_INVALID Signature verification failed Fatal
REVOKED Package has been revoked Fatal
DECRYPTION_FAILED Cannot decrypt recipient (for LMP) Fatal
Expired Keys

Implementations MUST accept signatures from expired keys for packages created before the expiration date. Check the payload timestamp against key expiration.

Selector Discovery

Organizations with multiple signing keys can publish a discovery record at the base _dspip.<domain> location. This allows verifiers to enumerate available selectors and understand key organization.

Discovery Record Format

_dspip.example.com. IN TXT "v=DSPIP1; selectors=warehouse,shipping,returns;
  delegate=geo:region._dspip.example.com"

Delegation Schemes

The delegate tag supports various selector organization strategies:

Scheme Pattern Use Case
list selectors=a,b,c Small number of static selectors
geo delegate=geo:{region}._dspip.example.com Geographic distribution (us-east, eu-west)
postal delegate=postal:{zip}._dspip.example.com Postal code based routing
region delegate=region:{state}._dspip.example.com State/province organization
service delegate=service:{type}._dspip.example.com Service level differentiation

Discovery Lookup Flow

  1. Query Base Record
    Query _dspip.<domain> to check for discovery record
  2. Parse Delegation
    If delegate tag present, determine scheme and template
  3. Resolve Selector
    Substitute variables in template to construct final key locator
  4. Fallback
    If no discovery record, fall back to direct <selector>._dspip.<domain> lookup

Example: Geographic Delegation

// Discovery record
_dspip.acmelogistics.com. IN TXT "v=DSPIP1; delegate=geo:{region}._dspip.acmelogistics.com"

// Regional records
us-east._dspip.acmelogistics.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1; p=..."
us-west._dspip.acmelogistics.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1; p=..."
eu-central._dspip.acmelogistics.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1; p=..."

Key Lifecycle Management

DSPIP supports comprehensive key lifecycle management through DNS record fields that indicate key status, creation time, and expiration policies.

Lifecycle States

State
Active
Key can be used for signing and verification
  • s=active (or omitted, default)
  • Current time < exp timestamp
  • Normal operation mode
State
Verify-Only
Key only valid for signature verification
  • s=verify-only
  • Current time < exp-v timestamp
  • Used during key rotation
State
Expired
Key is no longer valid for any purpose
  • Current time >= exp-v timestamp
  • Signatures should be rejected
  • Record may be removed

Lifecycle DNS Tags

t
number
Key creation timestamp (Unix time)
exp
number
Signing expiration timestamp - key cannot create new signatures after this time
exp-v
number
Verification expiration timestamp - signatures are invalid after this time
s
string
Key status: "active", "verify-only", or "revoked"
seq
number
Sequence number for key rotation tracking

Recommended Lifecycle Policy

Phase Duration Description
Active Signing 1 year Key can sign new packages
Verify-Only 1 year Key validates in-transit packages
Total Lifetime 2 years Covers full shipping lifecycle

Example: Full Lifecycle Record

warehouse._dspip.example.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC;
  t=1703548800; exp=1735084800; exp-v=1766620800;
  s=active; seq=1; types=SHIP; n=Main%20Warehouse"

Key Rotation Process

  1. Generate New Key
    Create new key pair with incremented sequence number
  2. Publish New Record
    Add new key record with s=active
  3. Transition Old Key
    Update old key to s=verify-only
  4. Update Signing Systems
    Configure systems to sign with new key
  5. Monitor & Retire
    After exp-v passes, old record may be removed
Verify-Only Transition

When transitioning a key to verify-only status, ensure all in-transit packages signed with that key will reach their destination before the exp-v timestamp. The recommended 1-year verify-only window accommodates international shipping delays.

Revocation

DSPIP supports three types of revocation records:

  • Key Revocation (_revoked-key) - Revoke compromised or retired signing keys
  • Item Revocation (_revoked) - Revoke individual packages (lost, stolen, etc.)
  • Bulk Revocation (revocation) - Pointer to bulk package revocation lists

Key Revocation (_revoked-key._dspip.<domain>)

When a signing key is compromised or must be retired, publish a key revocation record for fast propagation. This provides a dedicated revocation channel that operates independently of key record caching.

_revoked-key._dspip.example.com. IN TXT "v=DSPIP1; type=key-revocation;
  selector=warehouse; revoked=1703548900; reason=compromised;
  replacement=warehouse-v2"

Key Revocation Fields

selector
string
The selector of the revoked key (REQUIRED)
revoked
number
Unix timestamp when the key was revoked (REQUIRED)
reason
string
Reason: compromised, retired, superseded, suspended (REQUIRED)
replacement
string
Selector of the replacement key (OPTIONAL)

Additionally, update the key record with s=revoked for authoritative status:

warehouse._dspip.example.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC;
  s=revoked; types=SHIP"
Two-Step Key Revocation

For fastest propagation: (1) Publish _revoked-key record immediately, (2) Update key record with s=revoked. Verifiers MUST treat a key as revoked if EITHER indicates revocation.

Item Revocation (_revoked._dspip.<domain>)

Individual packages can be revoked without revoking the signing key:

_revoked._dspip.example.com. IN TXT "v=DSPIP1; type=item-revocation;
  itemId=TRACK-2025-000123; revoked=1703548900; reason=lost"

Item Revocation Reasons

  • lost - Package lost in transit
  • stolen - Package was stolen
  • damaged - Package damaged and reshipped
  • recalled - Package recalled by sender
  • fraud - Fraudulent QR code detected

Bulk Revocation (revocation._dspip.<domain>)

For high-volume scenarios, point to an external revocation list:

revocation._dspip.example.com. IN TXT "v=DSPIP1; type=revocation;
  url=https://example.com/dspip/revoked.json; format=json;
  updated=1703548800"

Revocation Freshness

To prevent stale revocation information, revocation records include freshness metadata:

ts
number
Timestamp when the revocation list was last updated (Unix time)
max-age
number
Maximum acceptable age in seconds before list is considered stale

Verification Priority

Verifiers MUST check revocation in this order:

  1. Check Key Revocation
    Query _revoked-key._dspip.<domain> - if selector appears, REJECT immediately
  2. Check Key Record Status
    If key record has s=revoked, REJECT
  3. Check Item Revocation
    Query _revoked._dspip.<domain> and/or bulk revocation list
  4. Proceed with Verification
    If no revocation found, continue with signature verification
Security Critical

Always check revocation status before accepting a package. Compromised keys or canceled shipments may be exploited if revocation checks are skipped. Use short TTLs (60-300 seconds) when caching key revocation records.

Caching Strategy

High-volume verification environments benefit from multi-tier caching to reduce DNS lookups and improve verification speed. This section describes recommended caching architectures for different deployment scales.

Three-Tier Architecture

Tier 1
Device Cache
Scanner-local caching for immediate lookups
  • Store 1,000-5,000 frequently used keys
  • LRU eviction policy
  • 5-minute TTL for active keys
  • Persist across restarts
Tier 2
Regional Hub
Distribution center or regional cache
  • Serve 10-50 devices per hub
  • 50,000+ key capacity
  • 15-minute TTL
  • Redis or similar in-memory store
Tier 3
Central Cache
Organization-wide authoritative cache
  • Single source of truth
  • Millions of keys capacity
  • Respect DNS TTL
  • Force refresh on revocation

Cache Invalidation

Event Action Propagation
Key revocation Immediate invalidation Push to all tiers
Key expiration Remove on access Lazy propagation
TTL expiry Background refresh Pull from upstream
New key Cache on first access Pull on demand

Offline Operation

When network connectivity is unavailable, devices should operate with cached data while indicating staleness to users:

// Staleness thresholds
const CACHE_WARNING_THRESHOLD = 3600;    // 1 hour - show warning
const CACHE_ERROR_THRESHOLD = 86400;      // 24 hours - show error
const CACHE_REJECT_THRESHOLD = 604800;   // 7 days - reject verification

function verifyWithCache(qrData, cache) {
  const cacheAge = Date.now() - cache.lastSync;
  const result = verify(qrData, { cache });

  if (cacheAge > CACHE_REJECT_THRESHOLD) {
    return { valid: false, error: 'CACHE_TOO_OLD' };
  }

  if (cacheAge > CACHE_ERROR_THRESHOLD) {
    result.warning = 'CACHE_STALE';
    result.flags = ['OFFLINE_MODE'];
  } else if (cacheAge > CACHE_WARNING_THRESHOLD) {
    result.warning = 'CACHE_AGING';
  }

  return result;
}

Performance Metrics

Expected cache hit rates for a well-tuned deployment:

Tier Hit Rate Latency
Device (L1) 70-80% < 1ms
Regional (L2) 15-25% 1-5ms
Central (L3) 3-8% 5-20ms
DNS (origin) 1-3% 20-100ms
Optimization Tip

Pre-warm caches with keys from expected shippers before shift start. A logistics hub receiving packages from 50 regular shippers can pre-fetch those keys during quiet periods, achieving 95%+ L1 hit rates during peak hours.

Blockchain Integration

DSPIP supports optional blockchain recording for immutable custody tracking. Since DSPIP uses secp256k1 keys, the same keys can sign both QR codes and blockchain transactions.

Enabling Blockchain

Add blockchain information to your DNS TXT record:

v=DSPIP1; k=ec; c=secp256k1; p=...;
  eth=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0;
  chain=polygon

Custody Event Structure

{
  "parcelId": "sha256(DSPIP-parcelId)",  // Hashed for privacy
  "custodian": "ethereum_address",
  "timestamp": "block.timestamp",
  "location": "geohash_or_facility_id",
  "previousCustodyHash": "bytes32"
}
Privacy Warning

Only hashed parcel IDs should be stored on public blockchains. Personal information MUST NOT be recorded on immutable ledgers.

Offline Verification

For environments without network connectivity, DSPIP supports offline verification using cached DNS record bundles.

Cache Bundle Format

{
  "version": "1.0",
  "generated": 1703548800,
  "expires": 1704153600,
  "records": {
    "warehouse._dspip.example.com": "v=DSPIP1; k=ec; ...",
    "emp-jsmith._dspip.example.com": "v=DSPIP1; k=ec; ..."
  },
  "signature": "<bundle_signature>"
}

Requirements

  • Cache bundles MUST be updated at least weekly
  • Bundles MUST be signed by organization's root key
  • Critical revocations SHOULD use out-of-band distribution

DNS Sharding

Large organizations with thousands of keys MAY implement DNS sharding to distribute records across multiple subdomains.

Root Discovery Record

_dspip.example.org. IN TXT "v=DSPIP1; mode=sharded;
  shards=_dspip01,_dspip02,_dspip03,_dspip04; count=4"

Shard Distribution Algorithm

  1. Calculate SHA-256 hash of selector
  2. Take hash modulo shard count
  3. Map to shard subdomain with zero-padded index

Lookup Procedure

  1. Query _dspip.<domain> for root record
  2. If mode=direct, key is at <selector>._dspip.<domain>
  3. If mode=sharded:
    1. Calculate shard from selector hash
    2. Query <selector>.<shard>.<domain>
  4. If not found in calculated shard, MAY query other shards

Error Handling

Error Codes

INVALID_PROTOCOL
Protocol field is not "DSPIP"
Fatal
PARSE_ERROR
Cannot parse QR structure (wrong number of fields)
Fatal
INVALID_PAYLOAD
Cannot decode or parse payload JSON
Fatal
MISSING_REQUIRED_FIELD
Required payload field is missing
Fatal
DNS_LOOKUP_FAILED
Cannot resolve key locator
Fatal
INVALID_DNS_RECORD
DNS record doesn't contain valid DSPIP key
Fatal
SIGNATURE_INVALID
Signature verification failed
Fatal
KEY_EXPIRED
Key has passed its expiration date
Warning
KEY_REVOKED
Key has been revoked
Fatal

Test Vectors

Warning: Test Keys Only

The private keys below are PUBLIC and MUST NOT be used for any production purpose. They are provided solely for verifying implementation correctness.

secp256k1 Key Pair (Standard/Encrypted Mode)

Private Key (hex)

e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35

Public Key Compressed (hex)

0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2

Public Key Base64 (for DNS)

AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC

Ed25519 Key Pair (Split-Key Mode)

Zone A Private Key (hex)

9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60

Zone B Public Key (hex)

d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a

Standard Mode Sample Payload

{
  "type": "SHIP",
  "issuer": {
    "organization": "ACME Logistics",
    "address": {
      "city": "Omaha",
      "state": "NE",
      "country": "US"
    }
  },
  "subject": {
    "name": "Bob Jones",
    "address": {
      "street1": "456 Main Street",
      "city": "Lincoln",
      "state": "NE",
      "postalCode": "68501",
      "country": "US"
    }
  },
  "itemId": "TRACK-2025-000123",
  "timestamp": 1703548800000,
  "typeData": {
    "privacyMode": "standard",
    "parcelId": "TRACK-2025-000123",
    "carrier": "ACME",
    "service": "Ground"
  }
}

DNS TXT Records

// Warehouse with lifecycle fields
warehouse._dspip.example.com. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC;
  t=1703548800; exp=1735084800; exp-v=1766620800; s=active; seq=1;
  types=SHIP; n=Main%20Warehouse"

// Last mile provider
omaha-main._dspip.usps.gov. IN TXT "v=DSPIP1; k=ec; c=secp256k1;
  p=AzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXC;
  t=1703548800; exp=1735084800; s=active; types=SHIP;
  coverage=68101,68102,68103,68104,68105"

Complete QR Data (6-field format)

DSPIP|1.0|SHIP|warehouse._dspip.example.com|eyJ0eXBlIjoiU0hJUCIsImlzc3VlciI6ey...}|304502203a8b4c9d2e1f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f022100f9e8d7c6b5a4938271605f4e3d2c1b0a9988776655443322110fedcba987654

Live Test DNS Record

Query this record to test your implementation:

dig TXT test._dspip.dspip.io

Implementation Checklist

Use this checklist to verify your implementation is complete:

Parsing

  • Parse QR data with exactly 6 or 7 pipe-delimited fields
  • Validate protocol field equals "DSPIP"
  • Validate version compatibility (1.0)
  • Validate type field equals "SHIP"

Payload Handling

  • Base64 decode the payload
  • Parse JSON payload
  • Validate required fields: type, itemId, timestamp, issuer.address.country
  • Check privacyMode in typeData if present
  • Ignore unknown fields for forward compatibility

DNS

  • Query DNS TXT record at key locator
  • Parse semicolon-delimited tags
  • Validate v=DSPIP1, k=ec, c=secp256k1
  • Verify types includes "SHIP"
  • Base64 decode public key (33 bytes)
  • Check key status (s tag) - reject if revoked
  • Check signing expiration (exp) for new signatures
  • Check verification expiration (exp-v) for verification
  • Implement caching based on TTL

Cryptography

  • Reconstruct signable content: protocol|version|type|keyLocator|encodedPayload
  • SHA-256 hash the signable content
  • Hex decode the signature
  • For standard/encrypted: Verify ECDSA signature using secp256k1
  • For split-key: Verify Ed25519 signature using Zone B public key
  • Check key lifecycle status (s, exp, exp-v tags)

Privacy Modes

  • Handle standard mode (cleartext recipient)
  • Handle encrypted mode (ECIES decryption for last mile provider)
  • Handle split-key mode (Zone B reveal for verification)

Validation

  • Verify test vectors produce expected results
  • Test against live DNS record at test._dspip.dspip.io
  • Handle all error conditions appropriately
  • Check revocation lists for itemId

Revocation Lists (Section 5.5)

DSPIP supports revocation of individual item IDs through signed revocation lists. This is used for lost, stolen, damaged, or recalled packages. Revocation lists include an optional Bloom filter for O(1) probabilistic lookup and automatic 180-day pruning of stale entries.

Revocation Entry Structure

{
  "itemId": "TRACK-2025-001",     // Item being revoked
  "timestamp": 1703548800,          // When revoked (Unix time)
  "reason": "lost"                  // lost | stolen | damaged | recalled | other
}

Signed Revocation List

{
  "version": "1.0",
  "issuer": "warehouse._dspip.acme.com",
  "timestamp": 1703548800,
  "expires": 1703635200,
  "entries": [...],
  "bloomFilter": {
    "bits": "base64-encoded-bitarray",
    "size": 10000,
    "hashCount": 7
  },
  "signature": "..."
}

Bloom Filter

For large revocation lists, a Bloom filter enables O(1) "definitely not revoked" checks. The optimal filter size depends on expected entries and desired false positive rate:

Expected Items False Positive Rate Optimal Size (bits) Hash Functions
1,000 1% 9,585 7
10,000 1% 95,851 7
100,000 0.1% 1,437,759 10

JavaScript Example

import {
  createRevocationList,
  checkRevocation,
  calculateBloomFilterSize
} from '@dspip/core';

// Create a signed revocation list with Bloom filter
const list = createRevocationList({
  issuer: 'warehouse._dspip.acme.com',
  privateKey: privateKeyHex,
  entries: [
    { itemId: 'TRACK-001', reason: 'lost' },
    { itemId: 'TRACK-002', reason: 'stolen' }
  ],
  includeBloomFilter: true
});

// Check if an item is revoked (O(1) with Bloom filter)
const result = checkRevocation('TRACK-001', list);
if (result.revoked) {
  console.log('Item revoked:', result.entry.reason);
}

// Calculate optimal Bloom filter parameters
const { size, hashCount } = calculateBloomFilterSize(10000, 0.01);

Automatic Pruning

Revocation entries older than 180 days (REVOCATION_MAX_AGE_SECONDS) should be automatically removed. This ensures lists don't grow indefinitely while maintaining sufficient history for in-transit packages.

// Prune old entries and re-sign
const prunedList = pruneRevocationList(list, privateKey);
console.log('Pruned entries:', list.entries.length - prunedList.entries.length);
Signature Verification

Always verify the revocation list signature before trusting its contents. An attacker could provide a forged list to prevent detection of stolen packages.

Delivery Confirmation (Section 7.4)

DSPIP provides cryptographic proof of delivery through a challenge-response protocol. This creates non-repudiable evidence that a package was delivered to the intended recipient.

Protocol Flow

  1. Carrier Creates Challenge
    At delivery, carrier generates a random nonce and creates a signed challenge with the item ID and timestamp.
  2. Recipient Signs Challenge
    Recipient signs the challenge hash with their private key, creating a response.
  3. Carrier Verifies and Creates Proof
    Carrier verifies the recipient's signature and creates a complete delivery proof.
  4. Proof Stored for Audit
    The proof can be stored on-chain or in traditional systems for later verification.

Challenge Structure

{
  "version": "1.0",
  "itemId": "TRACK-2025-001",
  "carrierKeyLocator": "driver._dspip.usps.gov",
  "nonce": "random-16-bytes-base64",
  "timestamp": 1703548800,
  "expires": 1703549100,            // 5 minutes validity
  "signature": "carrier-signature"
}

Delivery Proof

{
  "version": "1.0",
  "itemId": "TRACK-2025-001",
  "challenge": { ... },
  "response": {
    "challengeHash": "sha256-of-challenge",
    "recipientPubkey": "recipient-public-key",
    "timestamp": 1703548850,
    "signature": "recipient-signature"
  },
  "proofHash": "sha256-of-proof",
  "carrierSignature": "carrier-attestation",
  "valid": true
}

JavaScript Example

import {
  createDeliveryChallenge,
  respondToChallenge,
  verifyDeliveryResponse
} from '@dspip/core';

// Step 1: Carrier creates challenge
const challenge = createDeliveryChallenge({
  itemId: 'TRACK-2025-001',
  carrierKeyLocator: 'driver._dspip.usps.gov',
  carrierPrivateKey: carrierPrivKey,
  validitySeconds: 300
});

// Step 2: Recipient signs the challenge
const response = respondToChallenge({
  challenge,
  recipientPrivateKey: recipientPrivKey,
  metadata: {
    recipientName: 'John Doe',
    location: 'geo:41.2565,-95.9345'
  }
});

// Step 3: Carrier verifies and creates proof
const proof = verifyDeliveryResponse({
  challenge,
  response,
  carrierPrivateKey: carrierPrivKey
});

if (proof.valid) {
  console.log('Delivery confirmed! Proof:', proof.proofHash);
}

Multi-Party Attestation

For high-value deliveries, DSPIP supports multi-party attestation requiring signatures from multiple witnesses (carrier, recipient, third-party witness).

import {
  createMultiPartyAttestation,
  addAttestation,
  verifyMultiPartyAttestation
} from '@dspip/core';

// Create attestation requiring 3 signatures
const attestation = createMultiPartyAttestation(proof, 3);

// Add attestations from different parties
addAttestation(attestation, 'carrier._dspip.usps.gov', 'carrier', carrierPrivKey);
addAttestation(attestation, 'recipient', 'recipient', recipientPrivKey);
addAttestation(attestation, 'witness._dspip.acme.com', 'witness', witnessPrivKey);

console.log('Complete:', attestation.complete); // true when 3+ signatures

// Verify all attestations
const valid = verifyMultiPartyAttestation(attestation, publicKeyMap);

Compact Format for QR

Challenges can be serialized to a compact format for display as QR codes:

DSPIP-DC|1.0|TRACK-001|driver._dspip.usps.gov|1703548800|1703549100|nonce|signature
Non-Repudiation

Delivery proofs provide cryptographic evidence that cannot be forged. The recipient's signature on the challenge proves they received the package at the specified time and location.

DNSSEC Validation (Section 8)

DNSSEC provides cryptographic authentication for DNS responses, creating a chain of trust from the root zone down to the key locator record. When DNSSEC is enabled, verifiers can be confident the public key was published by the domain owner.

Chain of Trust

DNSSEC validation verifies a chain from the DNS root to the key record:

  1. Root Zone
    Trust anchor (hardcoded root keys) validates the root zone's DNSKEY.
  2. TLD Zone
    DS record in root points to TLD's DNSKEY. Validate TLD zone.
  3. Domain Zone
    DS record in TLD points to domain's DNSKEY. Validate domain zone.
  4. Key Record
    RRSIG on TXT record validates the DSPIP key data.

DNS-over-HTTPS (DoH)

DNSSEC validation typically uses DNS-over-HTTPS for secure transport. Common DoH providers:

Provider Endpoint
Cloudflare https://cloudflare-dns.com/dns-query
Google https://dns.google/dns-query
Quad9 https://dns.quad9.net/dns-query

JavaScript Example

import {
  validateDNSSEC,
  validateKeyLocatorDNSSEC,
  hasDNSSEC
} from '@dspip/core';

// Quick check: does domain have DNSSEC?
const enabled = await hasDNSSEC('cloudflare.com');
console.log('DNSSEC enabled:', enabled);

// Full validation for a key locator
const result = await validateKeyLocatorDNSSEC(
  'warehouse._dspip.example.com',
  { fullChainValidation: true }
);

console.log('Secure:', result.secure);
console.log('Chain:', result.chainOfTrust);
// { root: true, tld: true, domain: true, subdomain: true }

// Get human-readable recommendation
console.log('Recommendation:', result.recommendation);
// "DNSSEC validated - key lookup is cryptographically authenticated"

Validation Result

{
  "secure": true,
  "chainOfTrust": {
    "root": true,
    "tld": true,
    "domain": true,
    "subdomain": true
  },
  "algorithm": 13,          // ECDSAP256SHA256
  "keyTag": 12345,
  "validFrom": 1703548800,
  "validUntil": 1704153600,
  "recommendation": "DNSSEC validated..."
}

Key Tag Calculation

The key tag is a 16-bit identifier for DNSKEY records, calculated per RFC 4034:

import { calculateKeyTag } from '@dspip/core';

const keyTag = calculateKeyTag({
  flags: 257,        // KSK
  protocol: 3,
  algorithm: 13,     // ECDSAP256SHA256
  publicKey: 'base64-key'
});
console.log('Key tag:', keyTag);

Security Considerations

DNSSEC Enabled
High Assurance
Key lookup cryptographically authenticated
  • Chain of trust validated to root
  • RRSIG signatures verified
  • Recommended for production
DNSSEC Not Enabled
Standard Security
Relies on network security only
  • Use DoH for transport security
  • Display warning to users
  • Acceptable for low-value items
DNSSEC Adoption

Not all domains have DNSSEC enabled. When DNSSEC is not available, DSPIP still provides signature verification but without DNS authentication. Display appropriate warnings to users about the reduced assurance level.

Resources

Official Links

SDKs

SDK Feature Support

Feature JS Python Go Java Rust C Flutter
Key Generation Yes Yes Yes Yes Yes Yes Yes
Payload Creation Yes Yes Yes Yes Yes Yes Yes
Verification Yes Yes Yes Yes Yes Yes Yes
Revocation Lists Yes Yes Yes Yes Yes Yes Yes
Delivery Confirmation Yes Yes Yes Yes Yes Yes Yes
DNSSEC Validation Yes Yes Yes Yes Yes Yes Yes
QR Code Generation Yes Yes Yes Yes Yes Yes Yes

Standards References

Community