DEV Community

Antonio Jose Socorro Marin
Antonio Jose Socorro Marin

Posted on

Designing a Secure Digital Receipt Protocol (DRP) with Derived Identities, AES-GCM & Ed25519 Signatures

Author: Antonio José Socorro Marín
Year: 2025
*Copyright © 2025 Antonio José Socorro Marín

Note: The code and architecture presented here are for educational purposes only.
They illustrate how a Digital Receipt Protocol (DRP) could be designed with modern cryptography, wallet-controlled identity, and compliance alignment.
This is not a production-ready implementation.

🧩 1. Introduction

Today’s digital receipts suffer from fragmentation: merchants, issuers, acquirers, and card networks follow different schemas, formats, and privacy models.

A Digital Receipt Protocol (DRP) aims to introduce:

A unified,

Consent-driven,

End-to-end encrypted,

Verifiable,

User-controlled

standard for line-item receipt portability.

This article proposes a DRP Capsule architecture, including:

Derived identities

AES-GCM encryption

Ed25519 integrity protection

Strong alignment with NIST SP 800-171 Rev.3 and PCI DSS v4.0

A full Python reference implementation

🔐 2. Architectural Principles
2.1 Wallet-centric control

Users hold a long-term root secret.
From this, the wallet derives:

A pseudonymous identity for each merchant

A unique symmetric key for encrypting each receipt

This ensures unlinkability, privacy, and cryptographic isolation.

2.2 DRP Capsule Structure

A DRP Capsule consists of:

Header

Public metadata

No sensitive data

Integrity-protected

Ciphertext

AES-256-GCM encrypted claims

Signature

Ed25519 issuer signature over a deterministic digest

2.3 Regulatory Alignment

The implementation includes comments mapping cryptographic operations to:

NIST 800-171 Rev.3 controls:

SC-13 (Cryptographic Protection)

SC-28 (Data at Rest)

IA-5 (Authenticator/Secret Management)

AU-2/AU-3 (Auditability)

PCI DSS v4.0 controls:

Req.3 (Protect Stored PAN Data)

Req.4 (Encrypt Transmission)

Req.6.4.3 (Secure Crypto Design)

🧪 3. Full Python Implementation
(WITH NIST & PCI DSS COMMENTS — EDUCATIONAL ONLY)
🔽 Copy-and-paste ready code (complete)

-- coding: utf-8 --

"""
Digital Receipt Protocol (DRP) - Educational Reference Example
Author : Antonio José Socorro Marín
Year : 2025
Copyright (c) 2025, Antonio José Socorro Marín

This implementation is for EDUCATIONAL PURPOSES ONLY.

Compliance-oriented inline notes reference:

  • NIST SP 800-171 Rev.3 (SC-13, SC-28, IA-5, AU-2, AU-3)
  • PCI DSS v4.0 (Req.3, Req.4, Req.6.4.3)

Goal:
Demonstrate a DRP Capsule architecture using:

  • Derived Identities
  • AES-256-GCM
  • Ed25519 issuer signatures """

---------------------------------------------------------------------------

Standard Libraries

---------------------------------------------------------------------------

from dataclasses import dataclass, asdict
from typing import List
import json
import base64
import os
import time

---------------------------------------------------------------------------

Cryptographic Primitives (NIST/PCI aligned)

---------------------------------------------------------------------------

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

AES-GCM -> NIST SC-13 (Cryptographic Protection)

AES-256 -> PCI DSS Req.4 (Strong Encryption)

from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey, Ed25519PublicKey
)

Ed25519 signatures -> NIST SC-16, PCI Req.6.4.3

from cryptography.hazmat.primitives.kdf.hkdf import HKDF

HKDF -> NIST IA-5 (Secret Derivation), PCI Req.3.6

from cryptography.hazmat.primitives import hashes, serialization

---------------------------------------------------------------------------

Data Models

---------------------------------------------------------------------------

@dataclass
class LineItem:
"""Encrypted under AES-GCM. Sensitive transaction metadata."""
sku: str
description: str
quantity: int
unit_price: float
tax_rate: float

@dataclass
class ReceiptClaims:
"""
Encrypted receipt payload:
Must meet NIST SC-28 and PCI DSS Req.3 data-protection requirements.
"""
merchant_id: str
terminal_id: str
country_code: str
currency: str
total_amount: float
tax_amount: float
timestamp_utc: int
card_network: str
network_profile: str
pan_token: str # Tokenized PAN only — PCI DSS Req.3
auth_code: str
line_items: List[LineItem]

@dataclass
class DRPHeader:
"""Non-sensitive metadata that will be integrity-protected."""
drp_version: str
capsule_id: str
issuer_id: str
schema_id: str
country_code: str

@dataclass
class DRPCapsule:
"""
DRP Capsule = Header + Ciphertext + Signature
Signature meets NIST SC-16 & PCI DSS integrity requirements.
"""
header: DRPHeader
ciphertext: str
nonce: str
issuer_signature: str

---------------------------------------------------------------------------

Derived Identity Wallet

---------------------------------------------------------------------------

class DerivedIdentityWallet:
"""
Root secret → Derived keys & pseudonyms
Supports NIST IA-5 and privacy-by-design identity separation.
"""

def __init__(self, root_secret: bytes):
    self._root_secret = root_secret

def derive_user_key(self, merchant_context: str) -> bytes:
    """AES-256 key derivation using HKDF."""
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=None,
        info=merchant_context.encode(),
    )
    return hkdf.derive(self._root_secret)

def derive_user_pseudonym(self, merchant_context: str) -> str:
    """Privacy-preserving pseudonymous ID."""
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=None,
        info=(merchant_context + "|pseudonym").encode(),
    )
    raw = hkdf.derive(self._root_secret)

    digest = hashes.Hash(hashes.SHA256())
    digest.update(raw)
    return base64.urlsafe_b64encode(digest.finalize()).decode()
Enter fullscreen mode Exit fullscreen mode

---------------------------------------------------------------------------

Crypto Utilities

---------------------------------------------------------------------------

def encrypt_receipt_claims(claims: ReceiptClaims, key: bytes):
"""AES-256-GCM encryption (NIST SC-13, PCI Req.3/4)."""
aesgcm = AESGCM(key)
data = json.dumps(asdict(claims), separators=(",", ":")).encode()
nonce = os.urandom(12) # PCI-compliant nonce entropy
ciphertext = aesgcm.encrypt(nonce, data, None)
return nonce, ciphertext

def generate_issuer_keypair() -> Ed25519PrivateKey:
"""Issuer keypair (NIST SC-12, PCI Req.3.6)."""
return Ed25519PrivateKey.generate()

def export_public_key_pem(pk: Ed25519PublicKey) -> str:
"""PEM export for distribution (PCI Req.3.6.1)."""
return pk.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
).decode()

def sign_capsule_digest(priv: Ed25519PrivateKey, header, nonce_b64, ciphertext_b64):
"""Deterministic capsule digest signature."""
capsule = json.dumps(
{"header": asdict(header), "nonce": nonce_b64, "ciphertext": ciphertext_b64},
separators=(",", ":")
).encode()

h = hashes.Hash(hashes.SHA256())
h.update(capsule)
digest = h.finalize()

return priv.sign(digest)
Enter fullscreen mode Exit fullscreen mode

def verify_drp_capsule(capsule: DRPCapsule, pub: Ed25519PublicKey):
"""Ed25519 verification (NIST SI-7, PCI DSS Req.10)."""
data = json.dumps(
{"header": asdict(capsule.header), "nonce": capsule.nonce,
"ciphertext": capsule.ciphertext},
separators=(",", ":"),
).encode()

h = hashes.Hash(hashes.SHA256())
h.update(data)
digest = h.finalize()

sig = base64.b64decode(capsule.issuer_signature)
try:
    pub.verify(sig, digest)
    return True
except Exception:
    return False
Enter fullscreen mode Exit fullscreen mode

def decrypt_drp_capsule(capsule: DRPCapsule, key: bytes) -> ReceiptClaims:
"""AES-GCM decryption (NIST SC-28, PCI Req.3.4)."""
aesgcm = AESGCM(key)
nonce = base64.b64decode(capsule.nonce)
ct = base64.b64decode(capsule.ciphertext)
data = aesgcm.decrypt(nonce, ct, None)
raw = json.loads(data.decode())

items = [LineItem(**it) for it in raw["line_items"]]
return ReceiptClaims(**{**raw, "line_items": items})
Enter fullscreen mode Exit fullscreen mode

---------------------------------------------------------------------------

Issue Capsule

---------------------------------------------------------------------------

def issue_drp_capsule(claims, wallet, issuer_priv):
"""DRP Capsule issuance aligns with NIST SC-13 + PCI Req.4."""
ctx = f"{claims.merchant_id}|{claims.network_profile}"
key = wallet.derive_user_key(ctx)

nonce, ciphertext = encrypt_receipt_claims(claims, key)

nonce_b64 = base64.b64encode(nonce).decode()
ct_b64 = base64.b64encode(ciphertext).decode()

header = DRPHeader(
    drp_version="1.0.0",
    capsule_id=base64.b32encode(os.urandom(10)).decode().rstrip("="),
    issuer_id=claims.merchant_id,
    schema_id="DRP-RECEIPT-V1",
    country_code=claims.country_code,
)

sig = sign_capsule_digest(issuer_priv, header, nonce_b64, ct_b64)

return DRPCapsule(
    header=header,
    ciphertext=ct_b64,
    nonce=nonce_b64,
    issuer_signature=base64.b64encode(sig).decode(),
)
Enter fullscreen mode Exit fullscreen mode

---------------------------------------------------------------------------

Demo Example

---------------------------------------------------------------------------

if name == "main":
root = os.urandom(32)
wallet = DerivedIdentityWallet(root)

issuer_priv = generate_issuer_keypair()
issuer_pub = issuer_priv.public_key()

items = [
    LineItem("SKU1", "Coffee 1kg", 1, 15.99, 0.07),
    LineItem("SKU2", "Cup", 2, 8.50, 0.07),
]

subtotal = sum(i.unit_price * i.quantity for i in items)
tax = sum(i.unit_price * i.quantity * i.tax_rate for i in items)

claims = ReceiptClaims(
    merchant_id="MRC-001",
    terminal_id="POS-01",
    country_code="US",
    currency="USD",
    total_amount=round(subtotal + tax, 2),
    tax_amount=round(tax, 2),
    timestamp_utc=int(time.time()),
    card_network="VISA",
    network_profile="VISA-US-2025",
    pan_token="tok_visa_4242",
    auth_code="AUTH123",
    line_items=items,
)

capsule = issue_drp_capsule(claims, wallet, issuer_priv)

print("VALID SIGNATURE:", verify_drp_capsule(capsule, issuer_pub))

key = wallet.derive_user_key("MRC-001|VISA-US-2025")
recovered = decrypt_drp_capsule(capsule, key)

print(json.dumps(asdict(recovered), indent=2))
Enter fullscreen mode Exit fullscreen mode

DIAGRAM 1 — DRP CAPSULE ARCHITECTURE
┌───────────────────────────────────────────────────────────────┐
│ DRP CAPSULE │
├───────────────────────────────────────────────────────────────┤
│ 1. HEADER (Public, Non-Sensitive Metadata) │
│ - drp_version │
│ - capsule_id │
│ - issuer_id │
│ - schema_id │
│ - country_code │
│ │
Integrity-protected via Ed25519 signature
├───────────────────────────────────────────────────────────────┤
│ 2. ENCRYPTED CLAIMS (AES-256-GCM) │
│ - merchant_id │
│ - terminal_id │
│ - timestamp_utc │
│ - totals (amount, tax) │
│ - card_network / network_profile │
│ - tokenized PAN (DPAN) │
│ - authorization code │
│ - line items │
│ │
Confidentiality + Integrity (AEAD)
├───────────────────────────────────────────────────────────────┤
│ 3. SIGNATURE (Ed25519) │
│ Signature over: SHA-256( canonical(header + nonce + ciphertext) )│
│ │
Issuer authentication & non-repudiation
└───────────────────────────────────────────────────────────────┘

⭐ DIAGRAM 2 — DERIVED IDENTITY WALLET (KEY HIERARCHY)
USER WALLET (SECURE HARDWARE)
─────────────────────────────

                     Root Secret (32 bytes)
                 (Never leaves secure enclave)
                               │
                               ▼
                HKDF (merchant_context = 
          "merchant_id | network_profile")
                               │
 ┌─────────────────────────────┼─────────────────────────────┐
 │                             │                             │
 ▼                             ▼                             ▼
Enter fullscreen mode Exit fullscreen mode

Derived User Key Derived Pseudonym Derived Identifier (optional)
(AES-256 symmetric key) (privacy-preserving ID) (future DRP)
│ │
▼ ▼
Used for AES-GCM Encryption Used to avoid cross-merchant
of receipt claims correlation of identity

⭐ DIAGRAM 3 — ISSUANCE FLOW (MERCHANT → USER WALLET)
┌──────────────────────────────┐
│ Merchant / POS System │
└───────────────┬──────────────┘

│ 1. Build Receipt Claims (JSON)

┌──────────────────────────────┐
│ Receipt Claims │
└───────────────┬──────────────┘

│ 2. Wallet Derives User Key (HKDF)

user_wallet.derive_user_key()


3. AES-256-GCM Encryption


Ciphertext + GCM Nonce


4. Construct DRP Header (non-sensitive)


5. Sign Capsule Digest (Ed25519)


┌──────────────────────────────┐
│ DRP CAPSULE │
└──────────────────────────────┘

⭐ DIAGRAM 4 — VERIFICATION & DECRYPTION FLOW
┌──────────────────────────────┐
│ DRP CAPSULE │
└──────────────┬───────────────┘
│ Contains:
│ - header
│ - nonce
│ - ciphertext
│ - signature

1. Canonical JSON Representation


2. Compute SHA-256 Digest


3. Verify Ed25519 Signature

Is signature valid?

┌──────┴────────┐
│ │
Yes No
│ │
▼ ▼

  1. Wallet Derives Key Reject Capsule via HKDF │ ▼
  2. AES-GCM Decryption │ ▼
  3. Reconstruct Receipt Claims │ ▼ ┌──────────────────────────────┐ │ Decrypted Receipt (Plain) │ └──────────────────────────────┘

Top comments (0)