Sequentia · Prototype Live

Desktop-grade DSC signing,
now for mobile.

A generic, driverless Type-C dongle that brings certificate-based digital signing to any Android, iOS, or Windows app — native or WebView, any stack — with the plug-and-play experience users already know from desktop, fully CCA-compliant. The private key is generated and held inside an FPGA-based secure element — hardware-rooted, never exposed to host software.

Hardware Based

Secure Sign for mobiles

Hardware Secured
Private Key Protection
Tamper Resistant
Trusted Authentication
Secure Sign TRANSACTION APPROVED Digital Signature 83AF 9C21 7B6E 2D91 7F3A 8C10 5D2E 9A7B SIGNED
LIVE STATUS
{{ tickerItems[tickerIndex] }}
Flow Reference

SecureSign — DSC Signing Flow Reference

Seven stages of a signing operation, compared across today's hardware-token desktop DSC signing, today's software-only (soft-cert) DSC signing, PC test tools, the mobile app, the STM32F417 prototype, and the STM32U585 + FPGA production target.

Stage What Happens Today — Hardware Token (Desktop) Today — Software-Based DSC (No Hardware) Windows / Mac (PC Test) Mobile App Prototype (F417 + mbedTLS) Real Hardware (U585 + FPGA) Touches Controller?
0. Onboarding — key generation Device generates a key pair internally; only the public key ever leaves. Done once, at purchase, by the DSC vendor (eMudhra, Sify, Capricorn, etc.) — usually at a physical Registration Authority or via video-KYC, per CCA rules. Key pair generated on-token by the vendor's own provisioning tools before delivery. Key pair generated in software (e.g. OpenSSL, or the CA's own enrollment tool) and exported into a password-protected PKCS#12 (.pfx) file. Unlike a hardware token, the private key is exportable — it exists as a file, not confined to a chip. N/A for the six-step test below. SDK calls generateKeyPair() once during enrollment. Skipped — demo key pair pre-embedded in flash at build time. Real GENERATE_KEY_PAIR APDU; key pair generated inside the FPGA's secure key store. Yes (real HW)
N/A (prototype)
1. Plug in / enumerate OS recognizes the dongle as a standard USB smart-card reader. User plugs a USB-A token into a PC. Almost always requires installing that specific vendor's own proprietary driver/middleware first (ePass2003, Aladdin eToken, Watchdata, etc.) — a different installer per brand, and none of it works on mobile. This is the actual gap SecureSign is solving. Nothing to plug in — the .pfx file just needs to be present on disk (or copied/emailed between machines, which is itself a real security weakness this approach has and hardware tokens don't). Device Manager shows a generic "Smart card readers" entry automatically — standard usbccid.sys, no driver install. Android: USB Host API attach intent. iOS: needs MFi certification. STM32F417 enumerates as composite CDC + CCID. Same enumeration; crypto backend behind it doesn't change this step. Yes
2. Power on / get ATR Host asks "what card is this," device answers with its ATR. Handled invisibly by the vendor's PKCS#11 DLL/driver stack, hidden behind whatever signer utility (or historically, Java applet) the portal uses. N/A — no smart-card protocol involved. The signing app simply opens the .pfx file directly, using a standard crypto library (e.g. Windows CryptoAPI, OpenSSL). opensc-tool --atr / certutil -scinfo SDK/middleware issues the equivalent low-level open-session call. SC_Itf_IccPowerOn() returns a placeholder ATR (still not a real card's ATR — only enough to enumerate; the rest of the flow below no longer needs it to be more than that). Forwarded to FPGA over SPI, returns its real capability info. Yes
3. Read certificates Host reads the token's cert(s) via SELECT + READ BINARY APDUs. Signer utility/browser plugin calls the vendor's own PKCS#11 DLL to enumerate certs — works, but only with that vendor's software installed. The signing app reads the certificate bundled inside the .pfx file itself — a local file parse, not a device query. pkcs15-tool --list-certificates SDK's enumerateCertificates() → PKCS#11 C_FindObjects/C_GetAttributeValue Real SELECT + READ BINARY against a PKCS#15 file system in flash (EF(ODF)/AODF/PrKDF/CDF + the cert EF), with GET RESPONSE chaining since the demo cert (611 B DER) is larger than one CCID bulk message. Same wire protocol a real card uses, not a shortcut. Same APDUs; cert from secure NVM on the FPGA side. Yes
4. PIN verification User's PIN is checked against the device, never cached elsewhere. Desktop popup (native OS dialog or the signer utility's own window) asks for the token PIN, forwards it to the vendor stack. User enters a password/passphrase that decrypts the .pfx locally. Critically, this decrypts the private key into the application's own memory — unlike a hardware token, where the key never leaves the chip at all. pkcs11-tool --login --pin 1234 User enters PIN in-app → SDK's authenticate(pin)C_Login → VERIFY APDU Real VERIFY APDU (INS 0x20) against the demo PIN, with a 3-try RAM retry counter and the standard 63Cx "X tries left" / 6983 "blocked" status words — same host-visible behavior as a real card, just checked against a flash-embedded demo value instead of provisioned secret storage. PIN check happens inside FPGA's secure boundary, not the MCU. Yes — comparison happens on-controller
5. Sign document App hashes the document, sends only the hash; device signs it with the private key. Desktop signer utility (or historically a Java applet) hashes the document, sends it via vendor PKCS#11, gets the signature back, embeds it — same PAdES/XMLDSig standards as the new solution. The hash is signed using the private key now sitting in the app's RAM, via software crypto (e.g. OpenSSL) on the general-purpose PC. The actual signing operation happens outside any tamper-resistant hardware — this is the core difference from both the hardware token and SecureSign. pkcs11-tool --sign --mechanism ECDSA-SHA256 SDK's signHash()/signPdf()/signXml()C_Sign → PSO APDU Real PSO: COMPUTE DIGITAL SIGNATURE APDU (INS 0x2A, P1P2 9E9A), gated on PIN verification, signs the host-supplied hash via mbedtls_ecdsa_sign_det() (deterministic ECDSA / RFC 6979 — no RNG dependency) and returns raw r‖s — software crypto on the F417, not certified, prototype only. FPGA's ECC engine signs; key never leaves it; signature relayed back through STM32U585. Yes — core security-critical step
6. Verify signature Anyone checks the signature against the public certificate. Identical to the new solution — recipient verifies with the public certificate, independent of desktop vs. mobile vs. any vendor. Identical again — verification only ever needs the public certificate, regardless of how (or how insecurely) the private key was held during signing. openssl dgst -verify pub.pem -signature test.sig test.txt Optional in-app convenience, or left to the recipient. N/A — pure math. N/A — pure math, identical regardless of backend. No — device never touched again
Note on software-based DSC: it's simpler and cheaper (no hardware to buy or lose), but it fails the requirement that matters most — Security Req 2, "cryptographic operations executed on-token" — because the private key is exportable and briefly sits in general-purpose RAM during signing. This is exactly why CCA guidance favors hardware tokens for most Class 3 individual-signing use cases, and why SecureSign is built on the hardware-token model rather than a purely software one.
Observation: Stages 0, 2, 3, 5, and 6 are conceptually identical to what already works on desktop today. The real new engineering is stage 1 (driver-less, vendor-agnostic USB-C enumeration that works the same on a phone as a PC) and carrying stages 2–5 over that same USB-C link on Android/iOS instead of a Windows-only vendor DLL.
Prototype status (F417): stages 1, 3, 4, and 5 are now a real ISO 7816-4 APDU responder over CCID — SELECT, READ BINARY (with GET RESPONSE chaining), VERIFY, and PSO: COMPUTE DIGITAL SIGNATURE — backed by a PKCS#15 file system and a demo key/cert in flash, not shortcuts or stubs. Verified: builds clean, follows the CCID/ISO 7816 wire protocol correctly, deterministic-ECDSA signing needs no RNG (this MCU has none wired up). Not yet verified: the PKCS#15 ASN.1 encoding was hand-built against the spec without a live OpenSC rig to test against, so pkcs15-tool/pkcs11-tool recognizing the card on the first flash isn't guaranteed — the raw-APDU path (opensc-tool --send-apdu) is the reliable fallback for proving the signing flow end-to-end while that gets ironed out.

Complete Command Reference — Requirement Traceability

The seven stages above are the core happy-path flow. A real implementation needs the full command set below — every row maps to the specific requirement(s) it exists to satisfy, across all four sections of the spec. "—" means that category genuinely doesn't apply to that row, rather than forcing a stretch connection.

Key to the reference codes used below

Scope of Work (SW) — SW1: complete hardware–software integration stack (token driver, security layer, middleware, SDK, API). SW2: native SDKs + JS bridge, framework-agnostic. SW3: vendor-agnostic support, no single-manufacturer dependency. SW4: reference/demo apps incl. WebView. SW5: complete digital signature workflow (detection, cert enumeration, PIN, signing, embedding/verification).

Functional Requirements (FR) — FR1: detect/communicate with dongle. FR2: enumerate certs, PIN auth, key never leaves dongle. FR3: sign PDF/XML/text. FR4: simple API/SDK, any dev stack. FR5: verifiable signatures, timestamping, clear error handling.

Security & Compliance (Sec) — Sec1: CCA/IT Act 2000 conformance, X.509 certs from Indian CCA-licensed CAs. Sec2: crypto executed on-token, PIN/key never cached/logged/transmitted. Sec3: protection against replay/MITM/tampering, integrity verifiable by standard validators. Sec4: security architecture document (threat model, data flows, compliance mapping).

Deliverables (Del) — Del1: working solution (SDKs, WebView bridge, middleware, token driver layer, source code). Del2: demo apps + live demonstration. Del3: documentation (technical architecture, security architecture, integration guide, API reference, CCA compliance). Del4: concept note, presentation, screenshots, demo video.
#Command / FunctionLayerWhat It Does Scope of WorkFunctional Req.Security & ComplianceDeliverable
Session / Device Management
1SELECT / GET_DEVICE_INFODevice APDUIdentifies the dongle and its capabilities (the spec's "CTRL" identity step).SW5FR1Del1
2RESETDevice APDUPower-cycles the session without a full USB replug.SW1FR1, FR5Del1
Authentication
3VERIFY_PINDevice APDUThe spec's "PIN (authentication)" step — checked on-device, never leaves it.SW5FR2Sec2Del1
4CHANGE_PINDevice APDUUser-initiated PIN change, same on-device guarantee as verify.SW1FR2Sec2Del1
5UNBLOCK_PIN (PUK recovery)Device APDUResets a locked-out PIN using a recovery code, without extracting key material.SW1FR5 — "wrong PIN" named explicitlySec2Del1
6GET_PIN_RETRIESDevice APDUReports remaining PIN attempts before lockout.FR5Del1
Key Management
7GENERATE_KEY_PAIRDevice APDUThe spec's "PUF (root key) → ECDSA (key pair)" step — key pair generated on-device from the hardware root key.SW1FR2 — private key must never leave the dongleSec2 — crypto executed on-tokenDel1
8GET_PUBLIC_KEYDevice APDUExports only the public key — the spec's explicit "Only the public key leaves the device."SW5FR2Sec2Del1
9DELETE_KEYDevice APDUKey lifecycle/re-issuance support (lost device, re-enrollment).SW1Sec4 — feeds the security architecture documentDel3
Certificate Management
10IMPORT_CERTIFICATEDevice APDULoads the CA-signed X.509 certificate back onto the device after CA enrollment.SW5FR2Sec1 — X.509 certs from Indian CCA-licensed CAsDel1
11GET_CERTIFICATEDevice APDUReads a stored certificate.SW5FR2 — enumerate certificatesDel1
12GET_CERTIFICATE_CHAINDevice APDUReturns the full chain (intermediate + root CA) needed for independent verification.SW5FR5 — verifiable signaturesSec3 — integrity verifiable by standard validatorsDel1
13LIST_CERTIFICATESDevice APDUEnumerates multiple stored identities if the token supports more than one.SW5FR2 — allow user selectionDel1
Signing Operations
14SIGN_HASH (PSO: Compute Digital Signature)Device APDUThe core operation — signs a hash with the on-device private key; the key itself never crosses back out.SW5FR3, FR2Sec2 — private key stays on-tokenDel1
15GET_SIGNING_ALGORITHM_INFODevice APDUReports supported algorithms/curves for interoperability.FR5 — standards-compliantDel1
Timestamping
16REQUEST_TIMESTAMPHost-side (TSA call)Obtains an RFC 3161 timestamp from a Time Stamping Authority, embedded alongside the signature.FR5 — "timestamping support," named explicitlyDel1
Device Lifecycle & Security
17LOCK_DEVICE / FACTORY_RESETDevice APDUAdministrative wipe/lock, e.g. for a lost or compromised dongle.SW1Sec3 — protection against tamperingDel1
18GET_SECURITY_STATUS / tamper statusDevice APDUReports tamper flags if the hardware supports detection.SW1Sec3 — protection against tamperingDel3
19FIRMWARE_VERSION / secure-boot attestationDevice APDUReports firmware/attestation info for audit purposes.SW1Sec4 — feeds the security architecture documentDel3
Cross-Cutting
20Consistent SW1SW2 status-word taxonomyDevice APDU (all commands)Every command above returns through the same error-code system (no dongle, wrong PIN, expired certificate, etc.) — this is scope across all ~19 device commands, not a separate feature.SW5FR5 — clear error handling, named with these exact examplesDel3
Host / SDK Layer (not device commands)
21SDK wrapper functions (detectDongle(), enumerateCertificates(), authenticate(), sign(), ...)SDKThe simple, documented API app developers actually call — wraps every device command above.SW1, SW2FR4 — minimal integration codeDel1, Del3 — this is what the API reference documents
22WebView JavaScript bridgeSDK / JSExposes the same SDK surface to WebView pages via addJavascriptInterface (Android) / WKScriptMessageHandler (iOS).SW2 — "JavaScript bridge/interface... WebView applications"FR4 — framework-agnosticDel1, Del2 — exercised by the WebView demo
23PAdES / XMLDSig / raw-hash container buildingSDKWraps the raw signature from SIGN_HASH into a standards-compliant PDF, XML, or hash signature container.SW5FR3Del1
24Vendor-agnostic PKCS#15 generic clientMiddlewareReads any compliant token's standard object layout instead of a vendor-specific one.SW3 — "no dependency on a single token manufacturer"Del1
25Independent signature verificationHost-side (SDK or recipient's own tools)Confirms a signature against the public certificate — no device involved.SW5 — "signature embedding/verification"FR5Sec3 — verifiable by standard validators
Project-level items not tied to a single command: SW4 (reference/demo apps) and Del2 (demo apps + live demonstration) are satisfied by the assembled demo app itself — built from rows 21–24 above, not any one command. Del4 (concept note, presentation, screenshots, demo video) is purely submission packaging for the challenge portal, satisfied by presenting this documentation alongside a working prototype, not by firmware or SDK work.
Status & Roadmap

SecureSign — Status & Roadmap

Current state of the STM32F417 prototype scored against the ten-category, 100-mark evaluation rubric, what's still missing in each category, and the next steps to close the gaps. Reflects only what has been verified this session (live opensc-tool / PC-SC testing over the working CDC+CCID USB link) — not aspirational claims.

Rubric Scorecard
CategoryMarksStatusEvidence / Current StateGap / What's Needed
Technical Feasibility12Proven Full end-to-end flow works on real hardware: USB composite CDC+CCID enumerates, ATR completes, SELECT → VERIFY PIN → PSO: COMPUTE DIGITAL SIGNATURE all succeed against opensc-tool/PC-SC, returning a valid 64-byte P-256 signature (SW=9000) in under 700 ms. Certificate SELECT + READ BINARY (611 bytes) also verified, including USB packet chunking and CCID GET RESPONSE (61xx) handling.
Performance & Reliability10Working Full PIN-verify-and-sign round trip completes in well under a second. Boot is stable after fixing the bootloader stack-pointer validation, watchdog timing, and CCID multi-packet transfer bugs found this session — no resets observed across repeated testing. Signing currently executes synchronously inside the USB interrupt path. Fine at P-256 speeds on this prototype, but should move to a task context before scaling to slower operations.
Innovation & Originality10Partial Digital Signature Certificates remain bound to the desktop: every hardware token ships with its own vendor driver, and that dependency is precisely why DSC signing has never reached mobile. SecureSign removes it. One driverless, generic Type-C dongle integrates with any Android, iOS, or Windows application — native or WebView, regardless of stack — delivering the same plug-and-play signing experience users expect on desktop, fully compliant with CCA guidelines. Its onboarding sequence is rooted in hardware at every step: CTRL establishes device identity, PIN authenticates the user, the FPGA's PUF derives a unique hardware root key, and the ECDSA key pair is generated on-device directly from that root — the private key never leaves the silicon; only the public key does. This prototype validates that protocol on real USB hardware, end to end: driverless CCID enumeration, on-device PIN verification, and on-device signing, with an MCU and mbedTLS software cryptography standing in for the FPGA's dedicated crypto engines. The mobile-facing half of the innovation — Android/iOS SDK, WebView bridge, and the FPGA target itself (USB device controller, CCID/APDU parser, command dispatcher, SPI/I²C/GPIO masters, AES/SHA/ECC/RSA engines, TRNG, Key Manager, flash controller) — is not yet built. This board is a desktop-only proof of the protocol, not the cross-platform dongle itself.
Security Architecture15Partial On-device PIN gate and deterministic ECDSA (RFC 6979, mbedTLS) signing implemented and tested — the private key never leaves the device during signing. Demo private key is embedded in flash rather than generated/held in a secure element (this MCU has none). No written security-architecture document yet (threat model, data flows, key custody).
Compliance with CCA Guidelines15Partial PKCS#15 file structure (EF(DIR)/ODF/AODF/PrKDF/CDF/TokenInfo) implemented on-card and fully readable by standard tooling — pkcs15-tool --dump correctly lists the PIN, the private EC key (with correct usage/path/algorithm reference), the X.509 certificate, and declared algorithm support (CKM_ECDSA), after fixing eight firmware bugs this session. The private key is also fully visible via PKCS#11 (pkcs11-tool --list-objects), with matching CKA_ID against the certificate. PKCS#11 signing now works end-to-end through three independent real-world clients: pkcs11-tool --sign, a PyKCS11 Python application, and jSignPdf actually signing a PDF (CMS signature independently verified). That required two genuine OpenSC-side fixes (the generic driver never registered a signing mechanism at all — patched card-default.c; and pkcs15_prkey_can_do() blocked the combined SHA-256 mechanism Java-based callers request directly) plus one firmware fix (PrKDF fieldSize). See the Testing Guide tab, TC 2.4/2.5/3.2/3.3. Certificate is self-signed, not issued by a CCA-licensed CA. No GENERATE_KEY_PAIR, CHANGE_PIN, UNBLOCK_PIN, or GET_PIN_RETRIES — key/PIN are fixed demo values, not provisioned per requirements.
Interoperability Across Platforms10Partial Confirmed working on Windows via PC/SC and OpenSC, using the standard driverless CCID class. Not yet tested on Linux/macOS PC/SC. No mobile (Android/iOS) path exists yet.
User Experience8Minimal On-device LCD shows live status (PIN / cert / sign / last event) driven off a lock-free event queue fed from the USB ISR. No host-side application yet — testing today is command-line only via opensc-tool.
Ease of Integration8Not started No SDK or wrapper layer yet — every test so far talks raw APDUs directly. Need a thin host SDK (detect / verify PIN / sign / read cert) so integrators don't need PC/SC or APDU knowledge.
Scalability & Maintainability6Prototype-only Single hardcoded demo key and PIN, by design, for this proof-of-concept. Production is intended to move to different target hardware (STM32U585 + FPGA) rather than extend this prototype's key handling.
Deployment Readiness6Not started No packaged submission deliverables yet — demo app, documentation set, concept note, or walkthrough video.
Where we are today: 2 of 10 categories proven & working, 6 partial with a clear specific gap, 2 not yet started. The core signing flow — the part every other category depends on — is fully working end-to-end on real hardware, on the desktop side of the model. Remaining gaps are mostly about breadth (mobile/FPGA target, compliance commands, platforms, packaging) rather than whether the core idea works.

Next Panel — Roadmap

Ordered by what unblocks the most rubric categories per step of effort — command-set gaps first, since they're referenced by both Security Architecture and CCA Compliance, then integration, then platform breadth, then packaging.

#Next StepWhy It MattersCloses Gap In
1Verify PKCS#15 at the tooling levelRun pkcs15-tool / pkcs11-tool against the device, not just raw APDUs, to catch anything a real signing client would trip on that opensc-tool --send-apdu testing can't reveal.Technical Feasibility, Interoperability
2Implement the missing card commandsCHANGE_PIN, UNBLOCK_PIN, GET_PIN_RETRIES, GET_CERTIFICATE_CHAIN, and a timestamp request — the set most compliance and security gaps trace back to.Security Architecture, CCA Compliance
3Write the security architecture documentKey custody, PIN handling, threat model, and what's demo-only vs. production-real. Directly required, not just implied.Security Architecture, CCA Compliance, Deployment Readiness
4Build a minimal host SDK + demo appWraps detect/verify/sign/read-cert so the device can be integrated without APDU knowledge.Ease of Integration, User Experience
5Test on Linux/macOS, then scope mobileConfirm PC/SC behavior beyond Windows before committing to an Android/iOS SDK effort.Interoperability
6Plan the production hardware moveDefine what changes — secure key storage, CA-issued certificate, real GENERATE_KEY_PAIR on a secure element — when moving off this prototype MCU to STM32U585 + FPGA.Scalability & Maintainability, CCA Compliance
7Assemble submission deliverablesDemo app, documentation, concept note, and a recorded walkthrough of the working SELECT → VERIFY → SIGN flow.Deployment Readiness
Scope note: figures above reflect the STM32F417 prototype only, verified this session via direct opensc-tool and serial-log testing. This is not a production security claim — see the Flow Reference tab for how each stage compares against the intended production target (STM32U585 + FPGA) and today's hardware-token / software-DSC alternatives.
Testing Guide

SecureSign — Testing Guide

Test cases for the STM32F417 prototype dongle, organized in four levels — device/APDU, PKCS#15/PKCS#11, real application, and reliability. Run each level in order: a failure at Level 1 explains a failure at every level above it.

Level 1 — Device / Raw APDU (opensc-tool)
TC#ObjectiveCommandExpected ResultStatus
1.1Device enumerates as composite CDC + CCIDWindows Device Manager → Ports (COM) & LPT + Smart card readersOne CDC COM port and one "SEQUENTIA SecureSign Dongle" reader appear, no driver install promptVerified
1.2Reader is visible to PC/SCopensc-tool --list-readersReader listed by name, card presentVerified
1.3ATR retrievalopensc-tool --atr19-byte T=0 ATR returned, no SCARD_STATE_MUTEVerified
1.4SELECT by AID/FIDopensc-tool --send-apdu 00A4000C023F00SW=9000Verified
1.5READ BINARY on certificate EF, with chainingopensc-tool --send-apdu 00B0000000 (611-byte cert, exceeds one 64-byte CCID packet)Full DER certificate returned intact via GET RESPONSE (61xx) chaining; matches embedded demo cert byte-for-byteVerified
1.6VERIFY PIN — correct valueopensc-tool --send-apdu 002000010431323334SW=9000Verified
1.7VERIFY PIN — wrong value, retry counterSend VERIFY with an incorrect PIN onceSW=63C2 (2 tries remaining), no crash, correct PIN still works afterVerified — SW=63C2, correct PIN worked afterward (retries reset to 3)
1.8PIN lockout after repeated failuresSend VERIFY with wrong PIN three times in a rowSW=6983 on the 3rd attempt and on all further attempts, including with the correct PINVerified — 63C2 → 63C1 → 6983, then 6983 even with the correct PIN
1.9PSO: COMPUTE DIGITAL SIGNATUREopensc-tool --send-apdu 002A9E9A20<32-byte-hash>64-byte raw r‖s ECDSA signature, SW=9000, completes in <700 msVerified
1.10Signature validity (independent check)openssl dgst -verify pub.pem -signature test.sig test.hash against the r‖s from TC 1.9 (DER-wrapped)OpenSSL reports Verified OKVerified — openssl dgst -sha256 -verify pub.pem -signature sig.der msg.txt reports Verified OK
1.11Signing without prior PIN verify is rejectedPower-cycle card, send PSO:SIGN without VERIFY firstSW=6982 (security status not satisfied)Verified — SW=6982 on PSO:SIGN attempted immediately after a fresh power-on, before any VERIFY
1.12Malformed APDU handlingSend a truncated/incomplete APDUClean error status word (e.g. SW=6A86), no hang, device remains responsive to the next commandVerified — a raw APDU with an unsupported INS byte (00 FF 00 00) returns SW=6D00; SELECT sent immediately after still returns SW=9000 normally. A genuinely truncated (<4-byte) APDU can't reach the device at all — both opensc-tool and Windows' own T=0 SCardTransmit reject it locally first.
Level 1 full regression check (this session): all 12 test cases now pass, with zero regressions from this session's SELECT-handler firmware changes (FCI response, multi-component path SELECT) — confirmed by re-running every case fresh, including TC 1.7/1.8/1.10/1.11/1.12 which had never actually been run before. One documentation bug found and fixed along the way: the VERIFY PIN command shown here used P2=0x81, but the firmware's actual SS_PIN_REFERENCE is 0x01 — P2=0x81 is correctly rejected with SW=6A88; P2=0x01 is what actually works. The commands above now reflect the correct value.
Level 2 — PKCS#15 / PKCS#11 Tooling
TC#ObjectiveCommandExpected ResultStatus
2.1Generic PKCS#15 driver parses the on-card file systempkcs15-tool --dumpODF, AODF, PrKDF, CDF, and TokenInfo all parse without errorsVerified — all five files parse cleanly, including the private key entry (see note below for the full fix chain). pkcs15-tool --dump shows the PIN, "Private EC Key [SecureSignKey]" with correct usage/path/algo_refs, the certificate, and declared CKM_ECDSA support
2.2Certificate enumeration via OpenSCpkcs15-tool --list-certificatesDemo certificate listed with correct label/usageVerified — "SecureSignCert" listed with correct path/ID/serial
2.3PKCS#11 object listingpkcs11-tool --module opensc-pkcs11.dll --list-objectsPrivate key and certificate objects both listed, correct CKA_ID linkageVerified — Private Key Object (EC, usage=sign), Certificate Object, and its derived Public Key Object all list correctly with matching CKA_ID (0x45/"69") and correct prime256v1 params
2.4PKCS#11 loginpkcs11-tool --module opensc-pkcs11.dll --login --pin 1234 --testLogin succeeds, built-in self-test signs and verifies successfullyVerified — login succeeds and the self-test's signing step now completes (see TC 2.5 note: required an OpenSC source patch)
2.5PKCS#11 sign via mechanismpkcs11-tool --sign --mechanism ECDSA --login --pin 1234 -i data.bin -o data.sigSignature file produced, verifies against the public certificateVerified — a real ECDSA signature is produced and independently verifies with OpenSSL (openssl pkeyutl -verify -pubin → "Signature Verified Successfully"). Root cause was genuinely OpenSC-side (see note below): fixed by patching card-default.c to register the ECDSA capability, plus one more firmware fix (PrKDF fieldSize) surfaced once the OpenSC side no longer masked it
Level 2 fully resolved this session, including signing. Eight firmware bugs found and fixed in total, plus one genuine OpenSC-side gap that needed a source patch and rebuild. The private key decodes correctly, is fully visible through standard PKCS#11 tooling, and now actually signs — pkcs11-tool --sign produces a signature that independently verifies with OpenSSL.
Level 3 — Real Application
TC#ObjectiveMethodExpected ResultStatus
3.1A real PDF-signing application recognizes the tokenjSignPdf (free, open-source; Adobe Acrobat Pro is a paid product) configured with a PKCS#11 keystore pointing at opensc-pkcs11.dllDemo identity appears as a usable signing keyVerified — jSignPdf's key listing returns SecureSignCert as a usable key alias
3.2End-to-end PDF signingSign a PDF with jSignPdf using the SecureSignCert identityPAdES/CMS signature embedded; signature cryptographically verifiesVerified — a real PDF is signed and the embedded CMS signature independently verifies (ECDSA/SHA-256 over the signed attributes). Required an OpenSC-side fix (pkcs15_prkey_can_do()) since jSignPdf/Java request the combined CKM_ECDSA_SHA256 mechanism directly
3.3Programmatic sign/verify via PyKCS11Small Python script: load module, login(pin), sign(hash), verify against public certScript reports signature valid, matching TC 1.9/1.10 result through a real application-layer APIVerified — full script run against the real installed module. Load, login, and sign() with CKM_ECDSA all succeed, returning a signature that independently verifies with OpenSSL.
3.4OpenSSL engine signing (libp11)openssl dgst -engine pkcs11 -keyform engine -sign "pkcs11:object=...", -out out.sig file.txtSignature produced without any vendor-specific code, using only standard OpenSSL + PKCS#11 enginePartial — libp11's engine was built from source and correctly registers with OpenSSL, but its own module-load step hits a native-process DLL-loading quirk specific to this machine (the same file loads fine via Java/Python). Not a signing-capability gap — already proven by TC 2.5/3.2/3.3
Browser-based demo of TC 3.1/3.2: securesign_webapp/ (Flask) wraps the same jSignPdf signing path behind an actual web UI — upload a PDF, enter the PIN, download the signed result — closer to how a real DSC-signing portal is structured (local backend with token access, browser UI on top) than driving jSignPdf from the command line. PIN is passed to the signing process over stdin, never as a command-line argument or in any log. Confirmed working end-to-end, including the wrong-PIN error path and PIN-retry recovery.
Level 4 — Reliability & UX
TC#ObjectiveMethodExpected ResultStatus
4.1No unexpected resets under normal useRun TC 1.4 → 1.9 in a loop 20+ times, watch CDC logNo IWDG reset, no hang, consistent timing each passVerified (stable across repeated testing this session)
4.2Power-on without debugger attachedUnplug/replug the dongle directly (no ST-Link session)Enumerates and responds identically to the debugger-attached caseVerified (root cause of the earlier boot-hang was fixed)
4.3LCD status reflects live flow stateWatch LCD while running SELECT → VERIFY → SIGN from a hostStatus rows update per stage (Idle → Card ON → PIN OK → Sign OK) without flicker or garbled textVerified
4.4CDC debug log is readable and correctly taggedOpen the CDC COM port in a serial terminal during a test runEach line is [MODULE] Message. format, no garbled/binary outputVerified
Summary: Level 1 (device/APDU) is fully proven — this is the hard, security-critical part and it works.
Level 2 and Level 3 signing — now fully pass. TokenInfo, the PIN, the private key, and the X.509 certificate all parse correctly and are fully visible via standard OpenSC/PKCS#11 tooling. PKCS#11 signing (TC 2.4/2.5), a real Python application via PyKCS11 (TC 3.3), and a real PDF-signing application via jSignPdf (TC 3.2) all now produce ECDSA signatures independently verified outside OpenSC.
How to run Level 2/3 tests: requires OpenSC installed (already done — opensc-tool.exe confirmed working this session) and, for Level 3, jSignPdf (free) or a small PyKCS11 script pointed at opensc-pkcs11.dll (typically under C:\Program Files\OpenSC Project\OpenSC\pkcs11\). Adobe Acrobat Pro also works but is a paid product. See the Flow Reference tab for how each stage maps to the production (STM32U585 + FPGA) target. Setting up a different PC from scratch (install steps, packages, patched OpenSC download)? See the standalone setup guide.
Setup Guide

Testing Setup — From a Blank PC

Everything needed to set up a fresh Windows PC to test the SecureSign dongle, from a blank machine to running the Level 1–3 test cases in the Testing Guide tab. No firmware rebuild required — this is for testing an already-flashed device.

Why this isn't just "install OpenSC": the stock OpenSC installer's generic card driver never registers a signing mechanism for unrecognized cards (a real gap in OpenSC itself, not this device), and one PKCS#11-layer function is too strict about which mechanisms it accepts. Two small OpenSC-side source patches fix this — the patched DLLs are provided as a ready-to-use download below, so no build toolchain is needed unless you specifically want to rebuild them yourself.
Quick setup (recommended): Setup-SecureSign.ps1 automates Steps 1–7 below — installs OpenSC if missing, deploys the patched module, optionally registers the friendly device name, installs jSignPdf, checks Python/Flask, and downloads the web app. Safe to re-run (skips anything already done). Download it directly:
Setup-SecureSign.ps1
... or fetch and run it in one go, in an elevated PowerShell:
iwr https://securesigndongle.rioncore.com/downloads/Setup-SecureSign.ps1 -OutFile Setup-SecureSign.ps1
.\Setup-SecureSign.ps1
It self-elevates (UAC prompt) if not already running as Administrator. See Get-Help .\Setup-SecureSign.ps1 -Full for parameters (custom install path, skipping individual steps). Prefer doing it by hand, or want to understand each step first? Continue reading below.

Bookmark or share this page to come back to this guide: securesigndongle.rioncore.com/#setup

1. Hardware & OS requirements

ItemRequirement
PCWindows 10 or 11, 64-bit
DeviceSecureSign USB CCID dongle (STM32F417 prototype), already flashed with firmware
CableUSB cable matching the board's connector (no drivers needed — driverless composite CDC + CCID device)
Admin rightsRequired for installing OpenSC and replacing files under C:\Program Files\

2. Install and patch OpenSC (required for all levels)

  1. Download and install the official OpenSC Windows build (0.27.x, 64-bit MSI) from github.com/OpenSC/OpenSC/releases. Accept all defaults — it installs to C:\Program Files\OpenSC Project\OpenSC\.
  2. Plug in the SecureSign dongle. Windows should enumerate it with no driver prompt — check Device Manager for a COM port and a reader under "Smart card readers" (may show as "Unknown Smart Card" until Step 3 below — cosmetic only).
  3. Download the patched OpenSC module package:
    opensc_patch_package.zip (~7 MB)
    Contains two folders, tools\ and pkcs11\, mirroring the install layout.
  4. Close any running OpenSC processes (Task Manager → end opensc-notify.exe if present, it can lock the DLL).
  5. Copy the files over the installed ones, in an elevated PowerShell/cmd:
    copy /Y "<extracted>\tools\*"   "C:\Program Files\OpenSC Project\OpenSC\tools\"
    copy /Y "<extracted>\pkcs11\*"  "C:\Program Files\OpenSC Project\OpenSC\pkcs11\"
    This replaces opensc.dll, opensc-pkcs11.dll, and their libcrypto-3-x64.dll/zlib1.dll runtime dependencies with the patched build. It does not touch onepin-opensc-pkcs11.dll or any other card-specific tool.
  6. Verify the patch took effect:
    cd "C:\Program Files\OpenSC Project\OpenSC\tools"
    .\opensc-tool.exe --list-readers
    .\opensc-tool.exe --atr
    Should list the SecureSign reader and return a 19-byte ATR with no error.
PIN: the demo PIN is 1234 for every test below — a fixed demo value baked into this prototype's firmware, not something you set up.

3. (Optional) Friendly device name in Windows

Without this, Device Manager may show "Unknown Smart Card" instead of "SEQUENTIA SecureSign Dongle". Purely cosmetic — every test works either way.

  1. Get the device's ATR: opensc-tool --atr (from Step 2 above).
  2. In an elevated PowerShell, create the registry key (use your device's actual ATR bytes):
    New-Item -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SEQUENTIA SecureSign Dongle" -Force
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SEQUENTIA SecureSign Dongle" `
      -Name "ATR" -Value ([byte[]](0x3B,0x9F,0x11, ...rest of your ATR bytes...))
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\SEQUENTIA SecureSign Dongle" `
      -Name "ATRMask" -Value ([byte[]](0xFF,0xFF,0xFF, ...same length, all 0xFF...))
    Unplug and replug the device afterward.

4. Level 1 — Device / Raw APDU testing

No extra install needed beyond Step 2. From C:\Program Files\OpenSC Project\OpenSC\tools\:

.\opensc-tool.exe --list-readers
.\opensc-tool.exe --atr
.\opensc-tool.exe --send-apdu 00A4000C023F00
.\opensc-tool.exe --send-apdu 002000010431323334        REM VERIFY PIN 1234

Full command list and expected results: Testing Guide tab, Level 1 table (TC 1.1–1.12).

5. Level 2 — PKCS#15 / PKCS#11 tooling

Same directory, no extra install:

.\pkcs15-tool.exe --dump
.\pkcs15-tool.exe --list-certificates
.\pkcs11-tool.exe --module "C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll" --list-objects
.\pkcs11-tool.exe --module "C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll" --login --pin 1234 --test
.\pkcs11-tool.exe --module "C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll" ^
  --sign --mechanism ECDSA --login --pin 1234 -i data.bin -o data.sig

Full command list and expected results: Testing Guide tab, Level 2 table (TC 2.1–2.5).

6. Level 3 — Real application testing

6a. PDF signing via jSignPdf (free, no license needed)

  1. Download jsignpdf-3.1.0-windows-x64.zip from SourceForge (~130 MB — bundles its own Java runtime, so no separate JDK/JRE install is required). Extract it, e.g. to C:\jsignpdf\.
  2. Create C:\jsignpdf\config\pkcs11.cfg:
    name=SecureSign
    library=C:/Program Files/OpenSC Project/OpenSC/pkcs11/opensc-pkcs11.dll
    slotListIndex=0
  3. List the available signing key (confirms the token is recognized):
    $env:JSIGNPDF_CONFIG_DIR = "C:\jsignpdf\config"
    & "C:\jsignpdf\JSignPdf\JSignPdfC.exe" -kst PKCS11 -ksp 1234 -lk
    Should print SecureSignCert as an available key alias.
  4. Sign a PDF:
    & "C:\jsignpdf\JSignPdf\JSignPdfC.exe" -kst PKCS11 -ksp 1234 -ka SecureSignCert `
      -r "Test signature" -l "Test Bench" --overwrite -d "C:\jsignpdf" "C:\path\to\your.pdf"
    Produces your_signed.pdf with a real embedded PAdES/CMS signature from the device.

6b. Programmatic sign/verify via PyKCS11 (Python)

  1. Install Python 3.10+ from python.org (check "Add to PATH" during install).
  2. pip install PyKCS11 (prebuilt wheel, no compiler needed).
  3. Run:
    import PyKCS11
    
    pkcs11 = PyKCS11.PyKCS11Lib()
    pkcs11.load(r"C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll")
    slots = pkcs11.getSlotList(tokenPresent=True)
    session = pkcs11.openSession(slots[0], PyKCS11.CKF_SERIAL_SESSION | PyKCS11.CKF_RW_SESSION)
    session.login("1234")
    priv = session.findObjects([(PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY)])[0]
    sig = session.sign(priv, b"\x00" * 32, PyKCS11.Mechanism(PyKCS11.CKM_ECDSA, None))
    print("signature:", bytes(sig).hex())
    session.logout()
    session.closeSession()

7. Troubleshooting

SymptomCause / Fix
Device shows in Device Manager but opensc-tool --list-readers lists nothing Windows Smart Card service isn't running: Start-Service SCardSvr (elevated PowerShell)
CKR_MECHANISM_INVALID on pkcs11-tool --sign The patched DLLs weren't actually deployed — verify with Get-FileHash "C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll". If wrong, another process may be re-locking/reverting the file — close all OpenSC processes and retry the copy.
Copy to Program Files fails with "file in use" Get-Process | Where-Object {$_.ProcessName -match "opensc|pkcs11|pkcs15|scardsvr"}, stop any match, retry
jSignPdf: PKCS11 KeyStore not available $env:JSIGNPDF_CONFIG_DIR isn't set, or pkcs11.cfg isn't in that exact folder — the provider is registered automatically only from that specific location
jSignPdf: CKR_SLOT_ID_INVALID Device not detected right now — check opensc-tool --list-readers works first; replug the device if needed
PyKCS11: LoadLibrary() failed with error 127 Add the pkcs11 directory to PATH before loading, in Python before import PyKCS11: os.environ["PATH"] = r"C:\Program Files\OpenSC Project\OpenSC\pkcs11" + os.pathsep + os.environ["PATH"]
Rebuilding the patched OpenSC module from source: only needed if you don't have access to the package above, or want to modify the patches yourself. Full toolchain setup, exact source patches (card-default.c, framework-pkcs15.c), and Windows-specific build fixes are documented in the standalone setup guide's appendix.

8. Running the "Sign a PDF" tab (127.0.0.1:5001)

The Sign a PDF tab needs a small local service running on your own PC — the dongle can't be reached from a remote server, so this page is only the UI; the actual signing happens locally. To get it working, someone needs all of the following on their own PC:

  1. Windows 10/11 PC with admin rights.
  2. A physical SecureSign dongle, plugged into that PC via USB — this is the hard requirement; there's no way around needing the actual hardware.
  3. OpenSC installed + patched — the official installer, then overlay the patched DLLs (Step 2 above).
  4. jSignPdf installed — free download, self-contained (bundles its own Java runtime), just needs extracting + the small config file (Step 6a above).
  5. Python 3.10+ installed, then pip install flask.
  6. Download and extract the signing app:
    securesign_webapp.zip
  7. Run python app.py from that folder — this starts the local service on port 5001.
  8. Then open either http://127.0.0.1:5001 directly, or this website's Sign a PDF tab — both work identically, since the hosted tab just calls back to 127.0.0.1:5001 on that same machine.

Without step 7 (app.py actually running) and step 2 (the real dongle plugged in), nothing on that tab will work — it'll just show "local signing service not reachable."

Sign a PDF

Sign a PDF with SecureSign

A real, working PDF-signing demo, not just test tooling — upload a PDF, enter the device PIN, get back a genuinely signed document. Runs through a small local backend on your own PC, since the dongle can't be reached from a remote server; this page is just the UI.

This needs the local signing service running on your PC. The dongle only exists on your machine, so this page talks back to a small app running on http://127.0.0.1:5001 — start it with python app.py from the securesign_webapp folder before using the form below. See the Setup Guide tab if it's not installed yet.
Checking for the local signing service…

Sign a PDF

The document is signed on your own machine using the private key inside the connected SecureSign dongle — the key itself never leaves the device, and the file never leaves your machine either (this only talks to the local service on 127.0.0.1, not to this website's server).

Click to choose a file, or drag & drop
PDF only, up to 25 MB
Never sent anywhere — used only to unlock the key on the device for this signature.

Run Level 1 / Level 2 Test Commands

The same commands from the Testing Guide's Level 1 (raw APDU) and Level 2 (PKCS#15/PKCS#11) tables, run for real against the connected device, with the actual output shown below each one. Commands marked PIN use the PIN entered above.

1.2 — List readersopensc-tool --list-readers

        
1.3 — ATR retrievalopensc-tool --atr

        
1.4 — SELECT by AID/FIDopensc-tool --send-apdu 00A4000C023F00

        
1.5 — READ BINARY (certificate)opensc-tool --send-apdu 00A4080C06501550154331 --send-apdu 00B0000000

        
1.6 — VERIFY PINPINopensc-tool --send-apdu 0020000104<pin-hex>

        
2.1 — Dump PKCS#15 structurepkcs15-tool --dump

        
2.2 — List certificatespkcs15-tool --list-certificates

        
2.3 — List PKCS#11 objectspkcs11-tool --module opensc-pkcs11.dll --list-objects

        
2.4 — PKCS#11 login + self-testPINpkcs11-tool --login --pin <pin> --test

        
2.5 — Sign via PKCS#11 (ECDSA)PINpkcs11-tool --sign --mechanism ECDSA --login --pin <pin> -i data.bin -o data.sig

        
Requirement Spec

Requirement Specification — SecureSign Type-C DSC Dongle Mobile Signing Solution

Sequentia's original requirement document in full, reformatted for the site. This is the source of truth the Flow Reference, Status & Roadmap, and Testing Guide tabs are all built against.

1. Background & Objective

The SecureSign requirement is to solve a real-world gap: Digital Signature Certificate (DSC) signing today works only on desktops. The objective is the end-to-end development of a generic Type-C DSC dongle signing solution that integrates seamlessly with mobile apps and mobile WebView applications — irrespective of the app's development stack — on both Android and iOS, giving users a plug-and-play signing experience comparable to desktop DSC signing, fully conforming to CCA (Controller of Certifying Authorities) guidelines.

Onboarding / login flow: the host confirms the device identity, authenticates the user with a PIN, then has the device derive its hardware root key and generate the DSC key pair. Only the public key leaves the device.

CTRL (identity) → PIN (authentication) → PUF (root key) → ECDSA (key pair). Responses: pin_ok, key_ready, and the public key.

2. Scope of Work (Full Solution Build)

  • Design and develop the complete hardware–software integration stack: token driver integration, security layer, middleware, SDK, and API framework for Type-C DSC dongles.
  • Native SDKs for Android and iOS, plus a JavaScript bridge/interface enabling signing from mobile WebView applications, framework-agnostic (native, Flutter, React Native, hybrid web).
  • Vendor-agnostic support for commonly used CCA-approved Type-C DSC tokens (e.g., PKCS#11-compliant tokens); no dependency on a single token manufacturer.
  • Reference/demo mobile applications on Android and iOS demonstrating the full signing workflow, including a WebView demonstration.
  • Complete digital signature workflow: dongle detection, certificate enumeration and selection, PIN authentication, document signing (PDF, XML, text), and signature embedding/verification.

3. Functional Requirements

  • FR1: Detect and communicate with a Type-C DSC dongle on plug-in (USB-C OTG on Android; supported connectivity per Apple MFi/USB-C on iOS).
  • FR2: Enumerate certificates on the token, allow user selection, and authenticate via token PIN; private key must never leave the dongle.
  • FR3: Sign PDF (PAdES-compatible), XML (XMLDSig), and text/hash payloads invoked from a host mobile app or WebView page.
  • FR4: Expose a simple, documented API/SDK so any third-party app can integrate signing with minimal code, independent of its development stack.
  • FR5: Return verifiable, standards-compliant signatures with timestamping support and clear error handling (no dongle, wrong PIN, expired certificate, etc.).

4. Security & Compliance Requirements

  • Full conformance to CCA guidelines and IT Act, 2000 provisions for digital signatures; support X.509 certificates issued by Indian CCA-licensed CAs.
  • Cryptographic operations executed on-token; PIN and key material never cached, logged, or transmitted. Secure channel between app, middleware, and token.
  • Protection against replay, man-in-the-middle, and tampering; signed-data integrity verifiable by standard validators.
  • Security architecture document covering threat model, data flows, and compliance mapping (to be part of deliverables).

5. Deliverables

  • Working solution: Android SDK, iOS SDK, WebView bridge, middleware, and token driver integration layer — with source code (repository access to Sequentia).
  • Demo apps (Android & iOS) and live demonstration of the end-to-end signing workflow, including WebView flow.
  • Documentation: technical architecture document, security architecture overview, integration guide, API reference, and CCA compliance documentation.
  • Concept note, solution presentation, prototype screenshots, and demo video in formats required by the challenge portal (file-size limits apply).

6. System Architecture — Communication Stack

Every signing call travels down one fixed stack, from the application on the host to the secure element on the dongle. Nothing above the STM32 / FPGA line ever sees the private key — it terminates inside the secure element and only status words and signatures travel back up.

Application
PKCS#11 API
PKCS#11 Library (DLL / SO / DYLIB)
PC/SC Smart Card API
USB CCID Driver
USB Cable
STM32 / FPGA
APDU
Secure Element

On this prototype: the STM32F417 stands in for the STM32/FPGA layer, and mbedTLS software crypto stands in for the secure element — the stack above it (PKCS#11 → PC/SC → USB CCID) is real and unchanged from the production target.

7. How Digital Signing Actually Works (explainer, not part of the original requirement text)

The most common question about this whole system: "once a document is signed, does someone need the dongle plugged in to open or check it later?" No — and understanding why is the key to understanding the whole design.

Think of it like a padlock with two different keys, not one key that both locks and unlocks:

  • Key A (the private key) — can only be used to lock the padlock. This key lives inside the SecureSign dongle's secure element and physically never comes out of it, ever.
  • Key B (the public key) — can only be used to check that the padlock was locked correctly, and by which padlock. This key is not a secret — copies of it are handed out to everyone, and one travels inside every document the dongle signs, as part of the signer's X.509 certificate.

Signing a document (needs the dongle):

  1. The host sends the document's data to the dongle.
  2. Inside the dongle, Key A locks a cryptographic seal onto that data — Key A never leaves the secure element to do this.
  3. That seal, plus a copy of Key B (the certificate), gets embedded into the document before it's saved. The signed file now physically contains: the document + the seal + a copy of Key B.

Opening/verifying it later (does not need the dongle):

  1. The reader (e.g. Adobe Acrobat) finds the seal and the copy of Key B already embedded in the file — it doesn't ask the original signer or their dongle for anything.
  2. It uses that Key B to check: does this seal match this exact document, and was it produced by the Key A that pairs with this Key B? This is pure math, done in software — no hardware required, because Key B was never secret.

Why it can't be faked: only Key A can produce a seal that Key B accepts. Since Key A never leaves the dongle, nobody else can produce a valid seal — but anyone, anywhere, on any computer, can run the check, forever, using the Key B copy already sitting inside the file. That's the whole trick of public-key cryptography: the checking half is meant to be public and travels with the document; only the locking half needs to stay secret, which is exactly what the dongle's secure element is for.

Separate point — "trusted" vs. "unknown" signatures: whether a reader shows a green trusted checkmark or a yellow "unknown" icon depends on whether that reader already trusts who issued the certificate — e.g. this prototype's demo certificate is self-signed, not issued by a CCA-licensed CA, so readers will correctly show it as unverified/untrusted even though the underlying cryptographic signature is fully valid. That trust decision happens entirely on the verifier's side and has nothing to do with needing the signing device.