التحقق من إيصالات معاملات كتابة دفتر الأستاذ السري من Azure

يمثل إيصال معاملة كتابة دفتر الأستاذ السري من Azure إثبات Merkle المشفر على أن معاملة الكتابة المقابلة قد تم الالتزام بها عالميا من قبل شبكة CCF. يمكن لمستخدمي Azure Confidential Ledger الحصول على إيصال عبر معاملة كتابة ملتزمة في أي وقت للتحقق من تسجيل عملية الكتابة المقابلة بنجاح في دفتر الأستاذ غير القابل للتغيير.

لمزيد من المعلومات حول إيصالات معاملات كتابة دفتر الأستاذ السري من Azure، راجع المقالة المخصصة.

خطوات التحقق من الإيصال

يمكن التحقق من إيصال معاملة الكتابة باتباع مجموعة معينة من الخطوات الموضحة في الأقسام الفرعية التالية. يتم توضيح نفس الخطوات في وثائق CCF.

حساب عقدة طرفية

الخطوة الأولى هي حساب تجزئة SHA-256 للعقدة الطرفية في شجرة Merkle المقابلة للمعاملة الملتزم بها. تتكون عقدة طرفية من السلسلة مرتبة من الحقول التالية التي يمكن العثور عليها في إيصال دفتر الأستاذ السري Azure، ضمن leafComponents:

  1. writeSetDigest
  2. ملخص SHA-256 ل commitEvidence
  3. claimsDigest الحقول

يجب تسلسل هذه القيم كصفائف من وحدات البايت: يجب تحويل كل writeSetDigest من و claimsDigest من سلاسل الأرقام السداسية العشرية إلى صفائف من وحدات البايت؛ من ناحية أخرى، يمكن الحصول على تجزئة commitEvidence (كصفيف من البايت) عن طريق تطبيق دالة التجزئة SHA-256 على السلسلة المشفرة commitEvidence UTF-8.

وبالمثل، يمكن حساب ملخص تجزئة العقدة الطرفية عن طريق تطبيق دالة التجزئة SHA-256 على سلسلة نتائج البايت الناتجة.

حساب عقدة الجذر

الخطوة الثانية هي حساب تجزئة SHA-256 لجذر شجرة Merkle في وقت تنفيذ المعاملة. يتم الحساب عن طريق تسلسل وتجزئة نتيجة التكرار السابق (بدءا من تجزئة العقدة الطرفية المحسوبة في الخطوة السابقة) مع تجزئات العقد مرتبة المتوفرة proof في حقل الإيصال. proof يتم توفير القائمة كقوائم مرتبة ويجب تكرار عناصرها بالترتيب المحدد.

يجب إجراء السلسلة على تمثيل وحدات البايت فيما يتعلق بالترتيب النسبي المشار إليه في العناصر المقدمة proof في الحقل (إما left أو right).

  • إذا كان مفتاح العنصر الحالي في proof هو left، فيجب إلحاق نتيجة التكرار السابق بقيمة العنصر الحالي.
  • إذا كان مفتاح العنصر الحالي في proof هو right، فيجب إلحاق نتيجة التكرار السابق بقيمة العنصر الحالي.

بعد كل سلسلة، يجب تطبيق الدالة SHA-256 من أجل الحصول على الإدخال للتكرار التالي. تتبع هذه العملية الخطوات القياسية لحساب العقدة الجذر لبنية بيانات Merkle Tree نظرا للعقد المطلوبة للحساب.

التحقق من التوقيع عبر عقدة الجذر

الخطوة الثالثة هي التحقق من أن توقيع التشفير الذي تم إنتاجه عبر تجزئة العقدة الجذر صالح باستخدام شهادة عقدة التوقيع في الإيصال. تتبع عملية التحقق الخطوات القياسية للتحقق من التوقيع الرقمي للرسائل الموقعة باستخدام خوارزمية التوقيع الرقمي Elliptic Curve (ECDSA). وبشكل أكثر تحديدا، الخطوات هي:

  1. فك ترميز سلسلة signature base64 إلى صفيف من وحدات البايت.
  2. استخراج المفتاح العام ECDSA من شهادة certعقدة التوقيع .
  3. تحقق من أن التوقيع فوق جذر شجرة Merkle (المحسوبة باستخدام الإرشادات الموجودة في القسم الفرعي السابق) أصلي باستخدام المفتاح العام المستخرج من الخطوة السابقة. تتوافق هذه الخطوة بشكل فعال مع عملية التحقق من التوقيع الرقمي القياسية باستخدام ECDSA. هناك العديد من المكتبات بلغات البرمجة الأكثر شيوعا التي تسمح بالتحقق من توقيع ECDSA باستخدام شهادة مفتاح عام عبر بعض البيانات (على سبيل المثال، مكتبة التشفير ل Python).

التحقق من مصادقة شهادة عقدة التوقيع

بالإضافة إلى الخطوة السابقة، يلزم أيضا التحقق من أن شهادة عقدة التوقيع معتمدة (أي موقعة) بواسطة شهادة دفتر الأستاذ الحالية. لا تعتمد هذه الخطوة على الخطوات الثلاث السابقة الأخرى ويمكن تنفيذها بشكل مستقل عن الخطوات الأخرى.

من الممكن أن تكون هوية الخدمة الحالية التي أصدرت الإيصال مختلفة عن تلك التي أيدت عقدة التوقيع (على سبيل المثال، بسبب تجديد الشهادة). في هذه الحالة، يلزم التحقق من ثقة سلسلة الشهادات من شهادة عقدة التوقيع (أي cert الحقل في الإيصال) حتى المرجع المصدق الجذر الموثوق به (CA) (أي شهادة هوية الخدمة الحالية) من خلال هويات الخدمة السابقة الأخرى (أي serviceEndorsements حقل القائمة في الإيصال). serviceEndorsements يتم توفير القائمة كقوائم مرتبة من الأقدم إلى أحدث هوية الخدمة.

يجب التحقق من مصادقة الشهادة للسلسلة بأكملها وتتبع نفس عملية التحقق من التوقيع الرقمي الموضحة في القسم الفرعي السابق. هناك مكتبات تشفير مفتوحة المصدر شائعة (على سبيل المثال، OpenSSL) يمكن استخدامها عادة لتنفيذ خطوة مصادقة الشهادة.

التحقق من ملخص مطالبات التطبيق

كخطوة اختيارية، في حالة إرفاق مطالبات التطبيق بالإيصال، من الممكن حساب ملخص المطالبات من المطالبات المكشوفة (بعد خوارزمية معينة) والتحقق من أن الملخص يطابق الحمولة claimsDigest الواردة في الإيصال. لحساب الملخص من كائنات المطالبة المكشوفة، يلزم التكرار من خلال كل كائن مطالبة تطبيق في القائمة والتحقق من حقله kind .

إذا كان كائن المطالبة من النوع LedgerEntry، يجب استخراج معرف مجموعة دفتر الأستاذ (collectionId) ومحتويات (contents) المطالبة واستخدامها لحساب ملخصات HMAC الخاصة بهم باستخدام المفتاح السري (secretKey) المحدد في كائن المطالبة. ثم يتم تسلسل هذين الملخصين ويتم حساب تجزئة SHA-256 للسلسلة. ثم يتم تسلسل البروتوكول (protocol) وملخص بيانات المطالبة الناتجة ويتم حساب تجزئة SHA-256 أخرى من السلسلة للحصول على الملخص النهائي.

إذا كان كائن المطالبة من النوع ClaimDigest، يجب استخراج ملخص المطالبة (value) وتسلسله مع البروتوكول (protocol)، ويتم حساب تجزئة SHA-256 للسلسلة للحصول على الملخص النهائي.

بعد حساب كل ملخص مطالبة واحد، من الضروري سلسلة جميع الملخصات المحسوبة من كل كائن مطالبة تطبيق (بنفس الترتيب الذي يتم تقديمه في الإيصال). وينبغي بعد ذلك إلحاق السلسلة بعدد المطالبات التي تتم معالجتها. تنتج تجزئة SHA-256 للسلسلة السابقة ملخص المطالبات النهائي، والذي يجب أن يتطابق مع claimsDigest الحاضر في كائن الإيصال.

موارد إضافية

لمزيد من المعلومات حول محتوى إيصال معاملة كتابة دفتر الأستاذ السري Azure وشرح كل حقل، راجع المقالة المخصصة. تحتوي وثائق CCF أيضا على مزيد من المعلومات حول التحقق من الإيصالات والموارد الأخرى ذات الصلة في الروابط التالية:

التحقق من إيصالات معاملة الكتابة

أدوات التحقق من الإيصال

توفر مكتبة عميل دفتر الأستاذ السري Azure ل Python وظائف الأداة المساعدة للتحقق من إيصالات معاملة الكتابة واحسب ملخص المطالبات من قائمة مطالبات التطبيق. لمزيد من المعلومات حول كيفية استخدام Data Plane SDK والأدوات المساعدة الخاصة بالإيصال، راجع هذا القسم ونموذج التعليمات البرمجية هذا.

الإعداد والمتطلبات الأساسية

لأغراض مرجعية، نقدم نموذج التعليمات البرمجية في Python للتحقق بشكل كامل من إيصالات معاملات كتابة دفتر الأستاذ السري Azure باتباع الخطوات الموضحة في القسم السابق.

لتشغيل خوارزمية التحقق الكاملة، يلزم شهادة شبكة الخدمة الحالية وإيصال معاملة الكتابة من مورد Confidential Ledger قيد التشغيل. راجع هذه المقالة للحصول على تفاصيل حول كيفية إحضار إيصال معاملة الكتابة وشهادة الخدمة من مثيل Confidential Ledger.

معاينة التعليمات البرمجية

يمكن استخدام التعليمات البرمجية التالية لتهيئة الكائنات المطلوبة وتشغيل خوارزمية التحقق من الإيصال. يتم استخدام أداة مساعدة منفصلة (verify_receipt) لتشغيل خوارزمية التحقق الكاملة، وتقبل محتوى receipt الحقل في استجابة GET_RECEIPT كقاموس وشهادة الخدمة كسلسلة بسيطة. تطرح الدالة استثناء إذا كان الإيصال غير صالح أو إذا تمت مصادفة أي خطأ أثناء المعالجة.

من المفترض أنه يمكن تحميل كل من الإيصال وشهادة الخدمة من الملفات. تأكد من تحديث كل من الثوابت service_certificate_file_name و receipt_file_name بأسماء الملفات المعنية لشهادة الخدمة والإيصال الذي ترغب في التحقق من صحته.

import json 

# Constants
service_certificate_file_name = "<your-service-certificate-file>"
receipt_file_name = "<your-receipt-file>"

# Use the receipt and the service identity to verify the receipt content 
with open(service_certificate_file_name, "r") as service_certificate_file, open( 
    receipt_file_name, "r" 
) as receipt_file: 

    # Load relevant files content 
    receipt = json.loads(receipt_file.read())["receipt"] 
    service_certificate_cert = service_certificate_file.read() 

    try: 
        verify_receipt(receipt, service_certificate_cert) 
        print("Receipt verification succeeded") 

    except Exception as e: 
        print("Receipt verification failed") 

        # Raise caught exception to look at the error stack
        raise e 

نظرا لأن عملية التحقق تتطلب بعض البدائيات المشفرة والتجزئة، يتم استخدام المكتبات التالية لتسهيل الحساب.

  • مكتبة CCF Python: توفر الوحدة النمطية مجموعة من الأدوات للتحقق من الإيصال.
  • مكتبة تشفير Python: مكتبة مستخدمة على نطاق واسع تتضمن خوارزميات تشفير وبدائية مختلفة.
  • الوحدة النمطية hashlib، وهي جزء من مكتبة Python القياسية: وحدة توفر واجهة مشتركة لخوارزميات التجزئة الشائعة.
from ccf.receipt import verify, check_endorsements, root 
from cryptography.x509 import load_pem_x509_certificate, Certificate 
from hashlib import sha256 
from typing import Dict, List, Any 

داخل الدالة verify_receipt ، نتحقق من أن الإيصال المحدد صالح ويحتوي على جميع الحقول المطلوبة.

# Check that all the fields are present in the receipt 
assert "cert" in receipt 
assert "leafComponents" in receipt 
assert "claimsDigest" in receipt["leafComponents"] 
assert "commitEvidence" in receipt["leafComponents"] 
assert "writeSetDigest" in receipt["leafComponents"] 
assert "proof" in receipt 
assert "signature" in receipt 

نقوم بتهيئة المتغيرات التي سيتم استخدامها في بقية البرنامج.

# Set the variables 
node_cert_pem = receipt["cert"] 
claims_digest_hex = receipt["leafComponents"]["claimsDigest"] 
commit_evidence_str = receipt["leafComponents"]["commitEvidence"] 
write_set_digest_hex = receipt["leafComponents"]["writeSetDigest"] 
proof_list = receipt["proof"] 
service_endorsements_certs_pem = receipt.get("serviceEndorsements", [])
root_node_signature = receipt["signature"] 

يمكننا تحميل شهادات PEM لهوية الخدمة وعقدة التوقيع وشهادات المصادقة من هويات الخدمة السابقة باستخدام مكتبة التشفير.

# Load service and node PEM certificates 
service_cert = load_pem_x509_certificate(service_cert_pem.encode()) 
node_cert = load_pem_x509_certificate(node_cert_pem.encode()) 

# Load service endorsements PEM certificates 
service_endorsements_certs = [ 
    load_pem_x509_certificate(pem.encode()) 
    for pem in service_endorsements_certs_pem 
] 

الخطوة الأولى من عملية التحقق هي حساب ملخص العقدة الطرفية.

# Compute leaf of the Merkle Tree corresponding to our transaction 
leaf_node_hex = compute_leaf_node( 
    claims_digest_hex, commit_evidence_str, write_set_digest_hex 
)

compute_leaf_node تقبل الدالة كمعلمات مكونات طرفية للإيصال (claimsDigestو commitEvidenceو وwriteSetDigest) وترجع تجزئة العقدة الطرفية في شكل سداسي عشري.

كما هو مفصل سابقا، نحسب ملخص commitEvidence (باستخدام الدالة SHA-256 hashlib ). بعد ذلك، نقوم بتحويل كل من writeSetDigest و claimsDigest إلى صفائف من وحدات البايت. وأخيرا، نقوم بسلسلة الصفائف الثلاثة، ونلخص النتيجة باستخدام الدالة SHA256.

def compute_leaf_node( 
    claims_digest_hex: str, commit_evidence_str: str, write_set_digest_hex: str 
) -> str: 
    """Function to compute the leaf node associated to a transaction 
    given its claims digest, commit evidence, and write set digest.""" 

    # Digest commit evidence string 
    commit_evidence_digest = sha256(commit_evidence_str.encode()).digest() 

    # Convert write set digest to bytes 
    write_set_digest = bytes.fromhex(write_set_digest_hex) 

    # Convert claims digest to bytes 
    claims_digest = bytes.fromhex(claims_digest_hex) 

    # Create leaf node by hashing the concatenation of its three components 
    # as bytes objects in the following order: 
    # 1. write_set_digest 
    # 2. commit_evidence_digest 
    # 3. claims_digest 
    leaf_node_digest = sha256( 
        write_set_digest + commit_evidence_digest + claims_digest 
    ).digest() 

    # Convert the result into a string of hexadecimal digits 
    return leaf_node_digest.hex() 

بعد حساب ورقة، يمكننا حساب جذر شجرة Merkle.

# Compute root of the Merkle Tree 
root_node = root(leaf_node_hex, proof_list) 

نستخدم الدالة root المتوفرة كجزء من مكتبة CCF Python. تتسلسل الدالة على التوالي نتيجة التكرار السابق مع عنصر جديد من proof، وتهضم السلسلة، ثم تكرر الخطوة لكل عنصر في proof مع الملخص المحسوب مسبقا. يحتاج التسلسل إلى احترام ترتيب العقد في شجرة Merkle للتأكد من إعادة حساب الجذر بشكل صحيح.

def root(leaf: str, proof: List[dict]): 
    """ 
    Recompute root of Merkle tree from a leaf and a proof of the form: 
    [{"left": digest}, {"right": digest}, ...] 
    """ 

    current = bytes.fromhex(leaf) 

    for n in proof: 
        if "left" in n: 
            current = sha256(bytes.fromhex(n["left"]) + current).digest() 
        else: 
            current = sha256(current + bytes.fromhex(n["right"])).digest() 
    return current.hex() 

بعد حساب تجزئة عقدة الجذر، يمكننا التحقق من التوقيع المضمن في الإيصال عبر الجذر للتحقق من صحة التوقيع.

# Verify signature of the signing node over the root of the tree 
verify(root_node, root_node_signature, node_cert) 

وبالمثل، توفر مكتبة CCF دالة verify للقيام بهذا التحقق. نستخدم المفتاح العام ECDSA لشهادة عقدة التوقيع للتحقق من التوقيع فوق جذر الشجرة.

def verify(root: str, signature: str, cert: Certificate):
    """ 
    Verify signature over root of Merkle Tree 
    """ 

    sig = base64.b64decode(signature) 
    pk = cert.public_key() 
    assert isinstance(pk, ec.EllipticCurvePublicKey) 
    pk.verify( 
        sig, 
        bytes.fromhex(root), 
        ec.ECDSA(utils.Prehashed(hashes.SHA256())), 
    )

الخطوة الأخيرة للتحقق من الإيصال هي التحقق من صحة الشهادة التي تم استخدامها لتوقيع جذر شجرة Merkle.

# Verify node certificate is endorsed by the service certificates through endorsements 
check_endorsements(node_cert, service_cert, service_endorsements_certs) 

وبالمثل، يمكننا استخدام الأداة المساعدة check_endorsements CCF للتحقق من أن هوية الخدمة تدعم عقدة التوقيع. يمكن أن تتكون سلسلة الشهادات من شهادات الخدمة السابقة، لذلك يجب علينا التحقق من أن المصادقة يتم تطبيقها متعاضة إذا serviceEndorsements لم تكن قائمة فارغة.

def check_endorsement(endorsee: Certificate, endorser: Certificate): 
    """ 
    Check endorser has endorsed endorsee 
    """ 

    digest_algo = endorsee.signature_hash_algorithm 
    assert digest_algo 
    digester = hashes.Hash(digest_algo) 
    digester.update(endorsee.tbs_certificate_bytes) 
    digest = digester.finalize() 
    endorser_pk = endorser.public_key() 
    assert isinstance(endorser_pk, ec.EllipticCurvePublicKey) 
    endorser_pk.verify( 
        endorsee.signature, digest, ec.ECDSA(utils.Prehashed(digest_algo)) 
    ) 

def check_endorsements( 
    node_cert: Certificate, service_cert: Certificate, endorsements: List[Certificate] 
): 
    """ 
    Check a node certificate is endorsed by a service certificate, transitively through a list of endorsements. 
    """ 

    cert_i = node_cert 
    for endorsement in endorsements: 
        check_endorsement(cert_i, endorsement) 
        cert_i = endorsement 
    check_endorsement(cert_i, service_cert) 

كبديل، يمكننا أيضا التحقق من صحة الشهادة باستخدام مكتبة OpenSSL باستخدام طريقة مماثلة.

from OpenSSL.crypto import ( 
    X509, 
    X509Store, 
    X509StoreContext, 
)

def verify_openssl_certificate( 
    node_cert: Certificate, 
    service_cert: Certificate, 
    service_endorsements_certs: List[Certificate], 
) -> None: 
    """Verify that the given node certificate is a valid OpenSSL certificate through 
    the service certificate and a list of endorsements certificates.""" 

    store = X509Store() 

    # pyopenssl does not support X509_V_FLAG_NO_CHECK_TIME. For recovery of expired 
    # services and historical receipts, we want to ignore the validity time. 0x200000 
    # is the bitmask for this option in more recent versions of OpenSSL. 
    X509_V_FLAG_NO_CHECK_TIME = 0x200000 
    store.set_flags(X509_V_FLAG_NO_CHECK_TIME) 

    # Add service certificate to the X.509 store 
    store.add_cert(X509.from_cryptography(service_cert)) 

    # Prepare X.509 endorsement certificates 
    certs_chain = [X509.from_cryptography(cert) for cert in service_endorsements_certs] 

    # Prepare X.509 node certificate 
    node_cert_pem = X509.from_cryptography(node_cert) 

    # Create X.509 store context and verify its certificate 
    ctx = X509StoreContext(store, node_cert_pem, certs_chain) 
    ctx.verify_certificate() 

التعليمة البرمجية العينة

يتم توفير نموذج التعليمات البرمجية الكامل المستخدم في معاينة التعليمات البرمجية.

البرنامج الرئيسي

import json 

# Use the receipt and the service identity to verify the receipt content 
with open("network_certificate.pem", "r") as service_certificate_file, open( 
    "receipt.json", "r" 
) as receipt_file: 

    # Load relevant files content 
    receipt = json.loads(receipt_file.read())["receipt"]
    service_certificate_cert = service_certificate_file.read()

    try: 
        verify_receipt(receipt, service_certificate_cert) 
        print("Receipt verification succeeded") 

    except Exception as e: 
        print("Receipt verification failed") 

        # Raise caught exception to look at the error stack 
        raise e 

التحقق من الإيصال

from cryptography.x509 import load_pem_x509_certificate, Certificate 
from hashlib import sha256 
from typing import Dict, List, Any 

from OpenSSL.crypto import ( 
    X509, 
    X509Store, 
    X509StoreContext, 
) 

from ccf.receipt import root, verify, check_endorsements 

def verify_receipt(receipt: Dict[str, Any], service_cert_pem: str) -> None: 
    """Function to verify that a given write transaction receipt is valid based 
    on its content and the service certificate. 
    Throws an exception if the verification fails.""" 

    # Check that all the fields are present in the receipt 
    assert "cert" in receipt 
    assert "leafComponents" in receipt 
    assert "claimsDigest" in receipt["leafComponents"] 
    assert "commitEvidence" in receipt["leafComponents"] 
    assert "writeSetDigest" in receipt["leafComponents"] 
    assert "proof" in receipt 
    assert "signature" in receipt 

    # Set the variables 
    node_cert_pem = receipt["cert"] 
    claims_digest_hex = receipt["leafComponents"]["claimsDigest"] 
    commit_evidence_str = receipt["leafComponents"]["commitEvidence"] 

    write_set_digest_hex = receipt["leafComponents"]["writeSetDigest"] 
    proof_list = receipt["proof"] 
    service_endorsements_certs_pem = receipt.get("serviceEndorsements", [])
    root_node_signature = receipt["signature"] 

    # Load service and node PEM certificates
    service_cert = load_pem_x509_certificate(service_cert_pem.encode()) 
    node_cert = load_pem_x509_certificate(node_cert_pem.encode()) 

    # Load service endorsements PEM certificates
    service_endorsements_certs = [ 
        load_pem_x509_certificate(pem.encode()) 
        for pem in service_endorsements_certs_pem 
    ] 

    # Compute leaf of the Merkle Tree 
    leaf_node_hex = compute_leaf_node( 
        claims_digest_hex, commit_evidence_str, write_set_digest_hex 
    ) 

    # Compute root of the Merkle Tree
    root_node = root(leaf_node_hex, proof_list) 

    # Verify signature of the signing node over the root of the tree
    verify(root_node, root_node_signature, node_cert) 

    # Verify node certificate is endorsed by the service certificates through endorsements
    check_endorsements(node_cert, service_cert, service_endorsements_certs) 

    # Alternative: Verify node certificate is endorsed by the service certificates through endorsements 
    verify_openssl_certificate(node_cert, service_cert, service_endorsements_certs) 

def compute_leaf_node( 
    claims_digest_hex: str, commit_evidence_str: str, write_set_digest_hex: str 
) -> str: 
    """Function to compute the leaf node associated to a transaction 
    given its claims digest, commit evidence, and write set digest.""" 

    # Digest commit evidence string
    commit_evidence_digest = sha256(commit_evidence_str.encode()).digest() 

    # Convert write set digest to bytes
    write_set_digest = bytes.fromhex(write_set_digest_hex) 

    # Convert claims digest to bytes
    claims_digest = bytes.fromhex(claims_digest_hex) 

    # Create leaf node by hashing the concatenation of its three components 
    # as bytes objects in the following order: 
    # 1. write_set_digest 
    # 2. commit_evidence_digest 
    # 3. claims_digest 
    leaf_node_digest = sha256( 
        write_set_digest + commit_evidence_digest + claims_digest 
    ).digest() 

    # Convert the result into a string of hexadecimal digits 
    return leaf_node_digest.hex() 

def verify_openssl_certificate( 
    node_cert: Certificate, 
    service_cert: Certificate, 
    service_endorsements_certs: List[Certificate], 
) -> None: 
    """Verify that the given node certificate is a valid OpenSSL certificate through 
    the service certificate and a list of endorsements certificates.""" 

    store = X509Store() 

    # pyopenssl does not support X509_V_FLAG_NO_CHECK_TIME. For recovery of expired 
    # services and historical receipts, we want to ignore the validity time. 0x200000 
    # is the bitmask for this option in more recent versions of OpenSSL. 
    X509_V_FLAG_NO_CHECK_TIME = 0x200000 
    store.set_flags(X509_V_FLAG_NO_CHECK_TIME) 

    # Add service certificate to the X.509 store
    store.add_cert(X509.from_cryptography(service_cert)) 

    # Prepare X.509 endorsement certificates
    certs_chain = [X509.from_cryptography(cert) for cert in service_endorsements_certs] 

    # Prepare X.509 node certificate
    node_cert_pem = X509.from_cryptography(node_cert) 

    # Create X.509 store context and verify its certificate
    ctx = X509StoreContext(store, node_cert_pem, certs_chain) 
    ctx.verify_certificate() 

الخطوات التالية