تصدیق
ہر مکمل لفافے کی حفاظت دو آزاد ثبوت کرتے ہیں:
- سیل، PDF کے اندر: ایک عوامی ٹائم اسٹیمپ اتھارٹی سے PAdES دستاویزی ٹائم اسٹیمپ۔ Adobe Acrobat اور Reader اس کی جانچ کرتے ہیں اور دکھاتے ہیں کہ سیل ہونے کے بعد سے دستاویز میں کوئی تبدیلی نہیں ہوئی۔
- اینکر، PDF کے باہر: دستاویز کا فنگر پرنٹ صرف اضافے والے Merkle ٹری میں ایک لیف ہے، اور Bona Fide ہر ٹری روٹ پر Ed25519 سے دستخط کرتا ہے۔ کوئی بھی شائع شدہ پبلک کیز سے اس ثبوت کی جانچ کر سکتا ہے، نہ اکاؤنٹ کی ضرورت اور نہ کسی PDF ویور پر بھروسے کی۔
جب آخری وصول کنندہ دستخط کرتا ہے تو لفافہ مکمل ہو جاتا ہے (envelope.completed) اور سیلر کام سنبھال لیتا ہے:
- اصل دستاویز کے SHA-256 کا موازنہ اس SHA-256 سے کرتا ہے جو لفافہ بنتے وقت ریکارڈ ہوا تھا۔
- دستاویز کو فلیٹن کرتا ہے: فیلڈز کی ویلیوز (متن، چیک باکسز، بنائے گئے، ٹائپ کیے گئے یا اپ لوڈ کیے گئے دستخط) صفحے کے مواد میں بنا دی جاتی ہیں، اور ہر فارم فیلڈ، اینوٹیشن، JavaScript ایکشن اور ایمبیڈ کی گئی فائل ہٹا دی جاتی ہے۔
- تکمیل کا سرٹیفکیٹ منسلک کرتا ہے: لفافے کی id، اصل دستاویز کا SHA-256، ہر دستخط کنندہ اپنے اصل میں استعمال ہونے والے تصدیقی طریقے کے ساتھ (مثلاً ای میل لنک (… پر بھیجا گیا منفرد لنک) یا ای میل لنک + ایک بار کا کوڈ، دیکھیں دستخط کنندہ کی تصدیق)، ٹائم اسٹیمپس، IP ایڈریس اور آلہ، اور ایونٹس کی ٹائم لائن۔ ہر صفحے پر یہ فوٹر ہوتا ہے: Bona Fide Envelope ID … · Verify at bonafidesign.com/verify۔
- ایک عوامی ٹائم اسٹیمپ اتھارٹی سے PAdES دستاویزی ٹائم اسٹیمپ (
/DocTimeStamp،ETSI.RFC3161، SHA-256) شامل کرتا ہے (خودکار failover کے ساتھ کئی عوامی طور پر قابلِ اعتماد فراہم کنندگان)، اور TSA کی سرٹیفکیٹ چین کا تصدیقی ڈیٹا (DSS میں OCSP/CRL) ایمبیڈ کرتا ہے تاکہ یہ طویل مدت تک قابلِ تصدیق رہے (LTV)۔ - اپنے آؤٹ پٹ کی خود جانچ کرتا ہے، اسے محفوظ کرتا ہے، اور سیل شدہ SHA-256 (
sealedDocumentHash) اور اینکر لاگ کے لیے ایک لیف ریکارڈ کرتا ہے۔
پھر envelope.sealed فائر ہوتا ہے اور GET /v1/envelopes/{id}/document?version=sealed فائل واپس کرتا ہے۔ اگر تمام TSAs
دستیاب نہ ہوں تو سیلنگ دوبارہ کی جاتی ہے؛ اس دوران لفافہ seal: "pending" کے ساتھ completed رہتا ہے۔
اینکرنگ
سیکشن کا عنوان «اینکرنگ»ہر سیل شدہ لفافہ ایک لیف کا حصہ ڈالتا ہے، جو اس کا canonical JSON ہے (کیز ترتیب وار، کوئی خالی جگہ نہیں):
{ "completed_at": "2026-10-13T14:09:52.004Z", "envelope_id": "env_8Gm2kQ4rT1", "event_chain_head": "<head of the envelope's audit hash chain>", "original_document_sha256": "<SHA-256 of the document as sent>", "sealed_document_sha256": "<SHA-256 of the sealed PDF>"}ہر گھنٹے، پچھلے اینکر کے بعد ریکارڈ ہونے والے لیفس ایک Merkle ٹری بن جاتے ہیں (RFC 6962 / RFC 9162:
leaf = SHA-256(0x00 ‖ leaf JSON)، node = SHA-256(0x01 ‖ left ‖ right))، اور اس کا روٹ ایک دستخط شدہ
اینکر کے طور پر شائع ہوتا ہے:
{ "id": "…", "version": "bona-fide-anchor-v1", "sequence": 412, "root": "<hex Merkle root>", "previous_root": "<root of anchor 411>", "leaf_count": 37, "created_at": "2026-10-13T15:00:00.118Z", "key_id": "<first 16 hex of SHA-256(public key)>", "signature": "<base64url Ed25519 signature>", "signed_message": "bona-fide-anchor-v1|412|<root>|<previous_root>|2026-10-13T15:00:00.118Z|37"}Ed25519 دستخط bona-fide-anchor-v1|sequence|root|previous_root|created_at|leaf_count (UTF-8) کا احاطہ کرتا ہے۔
previous_root ہر اینکر کو اس سے پہلے والے اینکر سے جوڑتا ہے (پہلا اینکر 64 صفروں کی طرف اشارہ کرتا ہے)، اس لیے کسی اینکر کو
زنجیر توڑے بغیر نہ ہٹایا جا سکتا ہے نہ اس کی ترتیب بدلی جا سکتی ہے، اور اینکر ٹیبلز صرف اضافے والی ہیں۔
دستخطی کیز ان کی میعادِ جواز کے ساتھ شائع کی جاتی ہیں؛ ریٹائرڈ کیز فہرست میں رہتی ہیں تاکہ پرانے اینکرز قابلِ تصدیق رہیں:
{ "issuer": "Bona Fide", "anchor_format": "bona-fide-anchor-v1|sequence|root|previous_root|created_at|leaf_count (UTF-8, Ed25519; root = RFC 6962 SHA-256 Merkle tree)", "keys": [ { "key_id": "<16 hex>", "algorithm": "Ed25519", "public_key": "<base64url raw 32-byte key>", "public_key_pem": "-----BEGIN PUBLIC KEY-----…", "valid_from": "2026-10-14T00:00:00.000Z", "valid_until": null, "status": "active" } ]}دستاویز کی تصدیق
سیکشن کا عنوان «دستاویز کی تصدیق»سب سے تیز طریقہ عوامی صفحہ ہے: bonafidesign.com/verify۔ یہ PDF کا ہیش آپ کے براؤزر میں نکالتا ہے (فائل کبھی اپ لوڈ نہیں ہوتی)، ہیش تلاش کرتا ہے، اور ثبوت اور دستخط کی جانچ آپ کے آلے پر کرتا ہے۔ اینکر لاگ ہر اینکر کی فہرست دیتا ہے۔
یہی تلاش ایک عوامی API بھی ہے: کوئی کلید نہیں، کوئی بھی origin، فی IP ریٹ لمٹ۔ صرف SHA-256 بھیجیں:
curl https://api.bonafidesign.com/v1/public/verify \ -H "Content-Type: application/json" \ -d "{\"sha256\": \"$(shasum -a 256 signed.pdf | cut -d' ' -f1)\"}"{ "sha256": "…", "status": "verified", "results": [ { "status": "verified", "matched": "sealed_document", "envelope": { "id": "env_8Gm2kQ4rT1", "status": "completed", "completed_at": "2026-10-13T14:09:52.004Z", "sealed_at": "2026-10-13T14:10:31.442Z", "organization_name": "Acme Robotics, Inc.", "signer_count": 1, "signers": [{ "name": "Jane Doe", "email": "jane@acme.dev", "role": "client", "verification_method": "Email OTP", "signed_at": "…" }], "verification_methods": ["Email OTP"], "test_mode": false }, "leaf": { "envelope_id": "env_8Gm2kQ4rT1", "…": "…" }, "leaf_hash": "…", "proof": { "leaf_index": 12, "tree_size": 37, "hashes": ["…", "…"] }, "anchor": { "sequence": 412, "…": "…" }, "links": { "anchor": "…", "keys": "…/.well-known/bona-fide/keys.json" } } ]}status یہ ہو سکتا ہے: verified، pending_anchor (مکمل اور ریکارڈ شدہ، اگلے گھنٹہ وار اینکر کا انتظار) یا
not_found: کسی مکمل لفافے میں یہ عین فائل نہیں ہے، اس لیے یہ وہ دستاویز نہیں جس پر دستخط ہوئے تھے (یا اس کے بعد
اس میں تبدیلی کی گئی)۔ سیل شدہ فائل اور بھیجی گئی اصل دستاویز دونوں میچ ہوتی ہیں۔
ثبوت کی خود جانچ
سیکشن کا عنوان «ثبوت کی خود جانچ»API کی بات پر یقین نہ کریں: جواب کی جانچ کے لیے درکار ہر چیز جواب میں اور keys.json میں موجود ہے۔
- دستاویز: آپ کی فائل کا SHA-256،
leaf.sealed_document_sha256(یاoriginal_document_sha256) کے برابر ہے۔ - لیف:
SHA-256(0x00 ‖ canonical JSON of leaf)،leaf_hashکے برابر ہے۔ - شمولیت: لیف ہیش سے
proof.hashesپر چلنا (RFC 9162 §2.1.3.2،leaf_indexاورtree_size == anchor.leaf_countکے ساتھ)anchor.rootدیتا ہے۔ - کلید:
anchor.key_id،keys.jsonمیں موجود ہے، کلید کی id خام پبلک کلید کے SHA-256 کے پہلے 16 ہیکس حروف ہے، اورanchor.created_atکلید کی میعادِ جواز کے اندر ہے۔ - دستخط: Ed25519 دستخط
bona-fide-anchor-v1|sequence|root|previous_root|created_at|leaf_countپر درست ثابت ہوتا ہے۔
# pip install requests cryptographyimport base64, hashlib, json, sys, requestsfrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
API = "https://api.bonafidesign.com"b64url = lambda s: base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))sha = lambda b: hashlib.sha256(b).digest()
def root_from_proof(leaf: bytes, index: int, size: int, path: list[bytes]) -> bytes: fn, sn, r = index, size - 1, leaf for p in path: if sn == 0: raise ValueError("proof too long") if fn % 2 == 1 or fn == sn: r = sha(b"\x01" + p + r) while fn % 2 == 0 and fn != 0: fn, sn = fn // 2, sn // 2 else: r = sha(b"\x01" + r + p) fn, sn = fn // 2, sn // 2 if sn != 0: raise ValueError("proof too short") return r
digest = hashlib.sha256(open(sys.argv[1], "rb").read()).hexdigest()res = requests.post(f"{API}/v1/public/verify", json={"sha256": digest}).json()keys = {k["key_id"]: k for k in requests.get(f"{API}/.well-known/bona-fide/keys.json").json()["keys"]}
for m in res["results"]: leaf, proof, anchor = m["leaf"], m["proof"], m["anchor"] if not anchor: print("pending anchor"); continue assert digest in (leaf["sealed_document_sha256"], leaf["original_document_sha256"]) leaf_json = json.dumps(leaf, sort_keys=True, separators=(",", ":")).encode() leaf_hash = sha(b"\x00" + leaf_json) assert proof["tree_size"] == anchor["leaf_count"] root = root_from_proof(leaf_hash, proof["leaf_index"], proof["tree_size"], [bytes.fromhex(h) for h in proof["hashes"]]) assert root.hex() == anchor["root"], "not included under the anchor root" key = keys[anchor["key_id"]] raw = b64url(key["public_key"]) assert hashlib.sha256(raw).hexdigest()[:16] == anchor["key_id"] message = "|".join([anchor["version"], str(anchor["sequence"]), anchor["root"], anchor["previous_root"], anchor["created_at"], str(anchor["leaf_count"])]).encode() Ed25519PublicKey.from_public_bytes(raw).verify(b64url(anchor["signature"]), message) # raises if invalid print(f"VERIFIED {leaf['envelope_id']} in anchor #{anchor['sequence']}")لیف میں صرف ASCII کیز والی سٹرنگ ویلیوز ہیں، اس لیے json.dumps(…, sort_keys=True, separators=(",", ":")) سے
وہی canonical JSON بنتا ہے۔ کلید کے valid_from / valid_until کو بھی anchor["created_at"] سے ملا کر دیکھیں۔
حوالہ جاتی تصدیق کنندہ، @bona-fide/verify، isomorphic ہے (براؤزر اور Node، کوئی Bona Fide سیکرٹس نہیں) اور یہی
/verify صفحہ چلاتا ہے:
import { sha256Hex, verifyProof, type KeySet, type PublicVerifyResponse } from '@bona-fide/verify';import { readFile } from 'node:fs/promises';
const api = 'https://api.bonafidesign.com';const sha256 = sha256Hex(new Uint8Array(await readFile('signed.pdf')));const body = (await (await fetch(`${api}/v1/public/verify`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ sha256 }),})).json()) as PublicVerifyResponse;const keys = (await (await fetch(`${api}/.well-known/bona-fide/keys.json`)).json()) as KeySet;
for (const m of body.results) { if (!m.leaf || !m.proof || !m.anchor) continue; // pending_anchor const r = await verifyProof(m.leaf, m.proof, m.anchor, keys, { documentSha256: sha256, expectedLeafHash: m.leaf_hash ?? undefined }); console.log(r.valid ? 'VERIFIED' : 'INVALID', m.envelope.id, r.checks);}پیکج میں ایک کمانڈ لائن چیکر بھی ہے (bona-fide-verify <file.pdf | sha256> [--api URL] [--json]؛
Bona Fide ریپوزیٹری کے checkout سے: pnpm --filter @bona-fide/verify cli signed.pdf)۔ یہ فائل کا ہیش مقامی طور پر نکالتا ہے،
اوپر کے ہر مرحلے کی جانچ کرتا ہے اور تصدیق ہونے پر 0، نہ ملنے یا غلط ہونے پر 1، اور دستاویز کے
ابھی اینکر نہ ہونے پر 2 کے ساتھ ختم ہوتا ہے۔
keys.json کی PEM کلید سے کسی اینکر کے دستخط کی جانچ کریں (OpenSSL 3):
curl -s https://api.bonafidesign.com/v1/public/anchors/412 > anchor.jsoncurl -s https://api.bonafidesign.com/.well-known/bona-fide/keys.json > keys.json
printf %s "$(jq -r .signed_message anchor.json)" > message.txtjq -r .signature anchor.json | python3 -c 'import base64,sys; s=sys.stdin.read().strip(); sys.stdout.buffer.write(base64.urlsafe_b64decode(s + "=" * (-len(s) % 4)))' > signature.binKEY_ID=$(jq -r '.key_id' anchor.json)jq -r --arg k "$KEY_ID" '.keys[] | select(.key_id == $k) | .public_key_pem' keys.json > key.pem
openssl pkeyutl -verify -pubin -inkey key.pem -rawin -in message.txt -sigfile signature.bin# Signature Verified Successfullyاینکرز GET /v1/public/anchors?limit=20&before=<sequence> سے نئے سے پرانے کی ترتیب میں درج ہوتے ہیں (پیچھے کے صفحات کے لیے next_before بھیجیں)
اور GET /v1/public/anchors/{sequence} سے ایک ایک کر کے حاصل کیے جاتے ہیں۔