مواد پر جائیں

تصدیق

ہر مکمل لفافے کی حفاظت دو آزاد ثبوت کرتے ہیں:

  1. سیل، PDF کے اندر: ایک عوامی ٹائم اسٹیمپ اتھارٹی سے PAdES دستاویزی ٹائم اسٹیمپ۔ Adobe Acrobat اور Reader اس کی جانچ کرتے ہیں اور دکھاتے ہیں کہ سیل ہونے کے بعد سے دستاویز میں کوئی تبدیلی نہیں ہوئی۔
  2. اینکر، PDF کے باہر: دستاویز کا فنگر پرنٹ صرف اضافے والے Merkle ٹری میں ایک لیف ہے، اور Bona Fide ہر ٹری روٹ پر Ed25519 سے دستخط کرتا ہے۔ کوئی بھی شائع شدہ پبلک کیز سے اس ثبوت کی جانچ کر سکتا ہے، نہ اکاؤنٹ کی ضرورت اور نہ کسی PDF ویور پر بھروسے کی۔

جب آخری وصول کنندہ دستخط کرتا ہے تو لفافہ مکمل ہو جاتا ہے (envelope.completed) اور سیلر کام سنبھال لیتا ہے:

  1. اصل دستاویز کے SHA-256 کا موازنہ اس SHA-256 سے کرتا ہے جو لفافہ بنتے وقت ریکارڈ ہوا تھا۔
  2. دستاویز کو فلیٹن کرتا ہے: فیلڈز کی ویلیوز (متن، چیک باکسز، بنائے گئے، ٹائپ کیے گئے یا اپ لوڈ کیے گئے دستخط) صفحے کے مواد میں بنا دی جاتی ہیں، اور ہر فارم فیلڈ، اینوٹیشن، JavaScript ایکشن اور ایمبیڈ کی گئی فائل ہٹا دی جاتی ہے۔
  3. تکمیل کا سرٹیفکیٹ منسلک کرتا ہے: لفافے کی id، اصل دستاویز کا SHA-256، ہر دستخط کنندہ اپنے اصل میں استعمال ہونے والے تصدیقی طریقے کے ساتھ (مثلاً ای میل لنک (… پر بھیجا گیا منفرد لنک) یا ای میل لنک + ایک بار کا کوڈ، دیکھیں دستخط کنندہ کی تصدیق)، ٹائم اسٹیمپس، IP ایڈریس اور آلہ، اور ایونٹس کی ٹائم لائن۔ ہر صفحے پر یہ فوٹر ہوتا ہے: Bona Fide Envelope ID … · Verify at bonafidesign.com/verify۔
  4. ایک عوامی ٹائم اسٹیمپ اتھارٹی سے PAdES دستاویزی ٹائم اسٹیمپ (/DocTimeStamp، ETSI.RFC3161، SHA-256) شامل کرتا ہے (خودکار failover کے ساتھ کئی عوامی طور پر قابلِ اعتماد فراہم کنندگان)، اور TSA کی سرٹیفکیٹ چین کا تصدیقی ڈیٹا (DSS میں OCSP/CRL) ایمبیڈ کرتا ہے تاکہ یہ طویل مدت تک قابلِ تصدیق رہے (LTV)۔
  5. اپنے آؤٹ پٹ کی خود جانچ کرتا ہے، اسے محفوظ کرتا ہے، اور سیل شدہ 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))، اور اس کا روٹ ایک دستخط شدہ اینکر کے طور پر شائع ہوتا ہے:

GET /v1/public/anchors/latest
{
"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 صفروں کی طرف اشارہ کرتا ہے)، اس لیے کسی اینکر کو زنجیر توڑے بغیر نہ ہٹایا جا سکتا ہے نہ اس کی ترتیب بدلی جا سکتی ہے، اور اینکر ٹیبلز صرف اضافے والی ہیں۔

دستخطی کیز ان کی میعادِ جواز کے ساتھ شائع کی جاتی ہیں؛ ریٹائرڈ کیز فہرست میں رہتی ہیں تاکہ پرانے اینکرز قابلِ تصدیق رہیں:

GET /.well-known/bona-fide/keys.json
{
"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 بھیجیں:

Terminal window
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 میں موجود ہے۔

  1. دستاویز: آپ کی فائل کا SHA-256، leaf.sealed_document_sha256 (یا original_document_sha256) کے برابر ہے۔
  2. لیف: SHA-256(0x00 ‖ canonical JSON of leaf)، leaf_hash کے برابر ہے۔
  3. شمولیت: لیف ہیش سے proof.hashes پر چلنا (RFC 9162 §2.1.3.2، leaf_index اور tree_size == anchor.leaf_count کے ساتھ) anchor.root دیتا ہے۔
  4. کلید: anchor.key_id، keys.json میں موجود ہے، کلید کی id خام پبلک کلید کے SHA-256 کے پہلے 16 ہیکس حروف ہے، اور anchor.created_at کلید کی میعادِ جواز کے اندر ہے۔
  5. دستخط: Ed25519 دستخط bona-fide-anchor-v1|sequence|root|previous_root|created_at|leaf_count پر درست ثابت ہوتا ہے۔
# pip install requests cryptography
import base64, hashlib, json, sys, requests
from 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"] سے ملا کر دیکھیں۔

اینکرز GET /v1/public/anchors?limit=20&before=<sequence> سے نئے سے پرانے کی ترتیب میں درج ہوتے ہیں (پیچھے کے صفحات کے لیے next_before بھیجیں) اور GET /v1/public/anchors/{sequence} سے ایک ایک کر کے حاصل کیے جاتے ہیں۔