Recibos de transacciones de escritura de Azure confidential ledger

Para aplicar garantías de integridad en transacciones, una instancia de Azure confidential ledger usa una estructura de datos de árbol de Merkle para registrar el hash de todos los bloques de transacciones que se anexan al libro de contabilidad inmutable. Después de confirmar una transacción de escritura, los usuarios de Azure confidential ledger pueden obtener una prueba criptográfica de Merkle, o recibo, a través de la entrada generada en un libro de contabilidad de confidential ledger para comprobar que la operación de escritura se guardó correctamente. Un recibo de transacción de escritura es una prueba de que el sistema ha confirmado la transacción correspondiente y se puede usar para comprobar que la entrada se ha anexado efectivamente al libro de contabilidad.

En la documentación de CCF puede encontrar más detalles sobre cómo se usa un árbol de Merkle en un libro de contabilidad confidencial.

Obtención de recibos de transacción de escritura

Configuración y requisitos previos

Los usuarios de Azure confidential ledger pueden obtener un recibo de una transacción específica mediante la biblioteca cliente de Azure confidential ledger. En el ejemplo siguiente se muestra cómo obtener un recibo de escritura mediante la biblioteca cliente para Python, pero los pasos son los mismos con cualquier otro SDK compatible para Azure confidential ledger.

Se supone que ya se ha creado un recurso de confidential ledger mediante la biblioteca de administración de Azure confidential ledger. Si aún no tiene un recurso de libro de contabilidad existente, cree uno mediante las instrucciones siguientes.

Tutorial de código

Empezamos configurando las importaciones para nuestro programa de Python.

import json 

# Import the Azure authentication library 
from azure.identity import DefaultAzureCredential 

# Import the Confidential Ledger Data Plane SDK 
from azure.confidentialledger import ConfidentialLedgerClient 
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient 

A continuación se muestran los valores constantes que se usan para configurar el cliente de Azure confidential ledger. Asegúrese de actualizar la constante ledger_name con el nombre único del recurso de confidential ledger.

# Constants for our program 
ledger_name = "<your-unique-ledger-name>" 
identity_url = "https://identity.confidential-ledger.core.azure.com" 
ledger_url = "https://" + ledger_name + ".confidential-ledger.azure.com" 

Realizamos la autenticación mediante la clase DefaultAzureCredential.

# Setup authentication 
credential = DefaultAzureCredential() 

Después, obtenemos y guardamos el certificado de servicio de confidential ledger mediante el cliente de certificados de la dirección URL de la identidad de confidential ledger. El certificado de servicio es un certificado de clave pública de identidad de red que se usa como raíz de confianza para la autenticación del servidor TLS. En otras palabras, se usa como entidad de certificación (CA) para establecer una conexión TLS con cualquiera de los nodos de la red CCF.

# Create a Certificate client and use it to 
# get the service identity for our ledger 
identity_client = ConfidentialLedgerCertificateClient(identity_url) 
network_identity = identity_client.get_ledger_identity( 
     ledger_id=ledger_name 
)

# Save network certificate into a file for later use 
ledger_tls_cert_file_name = "network_certificate.pem" 

with open(ledger_tls_cert_file_name, "w") as cert_file: 
    cert_file.write(network_identity["ledgerTlsCertificate"]) 

Después, podemos usar nuestras credenciales, el certificado de red capturado y nuestra dirección URL única del libro de contabilidad para crear un cliente de confidential ledger.

# Create Confidential Ledger client 
ledger_client = ConfidentialLedgerClient( 
     endpoint=ledger_url,  
     credential=credential, 
     ledger_certificate_path=ledger_tls_cert_file_name 
) 

Con el cliente de confidential ledger, podemos ejecutar cualquier operación compatible en una instancia de Azure confidential ledger. Por ejemplo, podemos anexar una nueva entrada al libro de contabilidad y esperar a que se confirme la transacción de escritura correspondiente.

# The method begin_create_ledger_entry returns a poller that  
# we can use to wait for the transaction to be committed 
create_entry_poller = ledger_client.begin_create_ledger_entry( 
    {"contents": "Hello World!"} 
)

create_entry_result = create_entry_poller.result() 

Una vez confirmada la transacción, podemos usar el cliente para obtener un recibo sobre la entrada anexada al libro de contabilidad en el paso anterior mediante el id. de transacción correspondiente.

# The method begin_get_receipt returns a poller that  
# we can use to wait for the receipt to be available by the system 
get_receipt_poller = ledger_client.begin_get_receipt( 
    create_entry_result["transactionId"] 
)

get_receipt_result = get_receipt_poller.result() 

Código de ejemplo

Se proporciona el ejemplo de código completo utilizado en el tutorial de código.

import json 

# Import the Azure authentication library 
from azure.identity import DefaultAzureCredential 

# Import the Confidential Ledger Data Plane SDK 
from azure.confidentialledger import ConfidentialLedgerClient 
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient 

from receipt_verification import verify_receipt 

# Constants 
ledger_name = "<your-unique-ledger-name>" 
identity_url = "https://identity.confidential-ledger.core.azure.com" 
ledger_url = "https://" + ledger_name + ".confidential-ledger.azure.com" 

# Setup authentication 
credential = DefaultAzureCredential() 

# Create Ledger Certificate client and use it to 
# retrieve the service identity for our ledger 
identity_client = ConfidentialLedgerCertificateClient(identity_url) 
network_identity = identity_client.get_ledger_identity(ledger_id=ledger_name) 

# Save network certificate into a file for later use 
ledger_tls_cert_file_name = "network_certificate.pem" 

with open(ledger_tls_cert_file_name, "w") as cert_file: 
    cert_file.write(network_identity["ledgerTlsCertificate"]) 

# Create Confidential Ledger client 
ledger_client = ConfidentialLedgerClient( 
    endpoint=ledger_url, 
    credential=credential, 
    ledger_certificate_path=ledger_tls_cert_file_name, 
) 

# The method begin_create_ledger_entry returns a poller that 
# we can use to wait for the transaction to be committed 
create_entry_poller = ledger_client.begin_create_ledger_entry( 
    {"contents": "Hello World!"} 
) 
create_entry_result = create_entry_poller.result() 

# The method begin_get_receipt returns a poller that 
# we can use to wait for the receipt to be available by the system 
get_receipt_poller = ledger_client.begin_get_receipt( 
    create_entry_result["transactionId"] 
) 
get_receipt_result = get_receipt_poller.result() 

# Save fetched receipt into a file
with open("receipt.json", "w") as receipt_file: 
    receipt_file.write(json.dumps(get_receipt_result, sort_keys=True, indent=2)) 

Escritura de contenido de recibo de transacción

Este es un ejemplo de una carga de respuesta JSON que devuelve una instancia de Azure confidential ledger al llamar al punto de conexión GET_RECEIPT.

{
    "receipt": {
        "cert": "-----BEGIN CERTIFICATE-----\nMIIB0jCCAXmgAwIBAgIQPxdrEtGY+SggPHETin1XNzAKBggqhkjOPQQDAjAWMRQw\nEgYDVQQDDAtDQ0YgTmV0d29yazAeFw0yMjA3MjAxMzUzMDFaFw0yMjEwMTgxMzUz\nMDBaMBMxETAPBgNVBAMMCENDRiBOb2RlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\nQgAEWy81dFeEZ79gVJnfHiPKjZ54fZvDcFlntFwJN8Wf6RZa3PaV5EzwAKHNfojj\noXT4xNkJjURBN7q+1iE/vvc+rqOBqzCBqDAJBgNVHRMEAjAAMB0GA1UdDgQWBBQS\nwl7Hx2VkkznJNkVZUbZy+TOR/jAfBgNVHSMEGDAWgBTrz538MGI/SdV8k8EiJl5z\nfl3mBTBbBgNVHREEVDBShwQK8EBegjNhcGljY2lvbmUtdGVzdC1sZWRnZXIuY29u\nZmlkZW50aWFsLWxlZGdlci5henVyZS5jb22CFWFwaWNjaW9uZS10ZXN0LWxlZGdl\ncjAKBggqhkjOPQQDAgNHADBEAiAsGawDcYcH/KzF2iK9Ldx/yABUoYSNti2Cyxum\n9RRNKAIgPB/XGh/FQS3nmZLExgBVXkDYdghQu/NCY/hHjQ9AvWg=\n-----END CERTIFICATE-----\n",
        "leafComponents": {
            "claimsDigest": "0000000000000000000000000000000000000000000000000000000000000000",
            "commitEvidence": "ce:2.40:f36ffe2930ec95d50ebaaec26e2bec56835abd051019eb270f538ab0744712a4",
            "writeSetDigest": "8452624d10bdd79c408c0f062a1917aa96711ea062c508c745469636ae1460be"
        },
        "nodeId": "70e995887e3e6b73c80bc44f9fbb6e66b9f644acaddbc9c0483cfc17d77af24f",
        "proof": [
            {
                "left": "b78230f9abb27b9b803a9cae4e4cec647a3be1000fc2241038867792d59d4bc1"
            },
            {
                "left": "a2835d4505b8b6b25a0c06a9c8e96a5204533ceac1edf2b3e0e4dece78fbaf35"
            }
        ],
        "signature": "MEUCIQCjtMqk7wOtUTgqlHlCfWRqAco+38roVdUcRv7a1G6pBwIgWKpCSdBmhzgEdwguUW/Cj/Z5bAOA8YHSoLe8KzrlqK8="
    },
    "state": "Ready",
    "transactionId": "2.40"
}

La respuesta JSON contiene los siguientes campos en el nivel raíz.

  • receipt: contiene los valores que se pueden usar para comprobar la validez del recibo de la transacción de escritura correspondiente.

  • state: el estado de la respuesta JSON devuelta. Los posibles valores permitidos son los siguientes:

    • Ready: el recibo devuelto en la respuesta está disponible.
    • Loading: el recibo aún no está disponible para ser recuperado y la solicitud debe reintentarse
  • transactionId: el id. de transacción asociado al recibo de transacción de escritura.

El campo receipt contiene los campos siguientes.

  • cert: cadena con el certificado de clave pública PEM del nodo CCF que firmó la transacción de escritura. El certificado de identidad de servicio siempre debería aprobar el certificado del nodo firmante. Vea también más detalles sobre cómo las transacciones se firman periódicamente y cómo se anexan las transacciones de firma al libro de contabilidad en CCF en el vínculo siguiente.

  • nodeId: cadena hexadecimal que representa el código hash SHA-256 de la clave pública del nodo CCF de firma.

  • leafComponents: los componentes del hash del nodo hoja en el árbol de Merkle que están asociados a la transacción especificada. Un árbol de Merkle es una estructura de datos de árbol que registra el hash de cada transacción y garantiza la integridad del libro de contabilidad. Para obtener más información sobre cómo se usa un árbol de Merkle en CCF, vea la documentación de CCF relacionada.

  • proof: lista de pares clave-valor que representan los hashes de nodos del árbol de Merkle que, combinados con el hash del nodo hoja correspondiente a la transacción especificada, permiten el recálculo del hash raíz del árbol. Gracias a las propiedades de un árbol de Merkle, es posible volver a calcular el hash raíz del árbol solo en un subconjunto de nodos. Los elementos de esta lista están en formato de pares clave-valor: las claves indican la posición relativa con respecto al nodo primario del árbol en un determinado nivel y los valores son los resúmenes hash SHA-256 del nodo especificado, como cadenas hexadecimales.

  • serviceEndorsements: lista de cadenas de certificados con codificación PEM que representan certificados de identidades de servicio anteriores. Es posible que la identidad de servicio que ha aprobado el nodo de firma no sea la misma que la que ha emitido el recibo. Por ejemplo, el certificado de servicio se renueva después de una recuperación ante desastres de un libro de contabilidad de confidential Ledger. La lista de certificados de servicio anteriores permite a los auditores crear la cadena de confianza desde el nodo de firma CCF al certificado de servicio actual.

  • signature: cadena Base64 que representa la firma de la raíz del árbol de Merkle en la transacción especificada, mediante el nodo CCF de firma.

El campo leafComponents contiene los campos siguientes.

  • claimsDigest: cadena hexadecimal que representa el código hash SHA-256 de la notificación de aplicación que adjunta la aplicación de confidential ledger en el momento en que se ejecutó la transacción. Actualmente, las notificaciones de aplicación no son compatibles, ya que la aplicación de confidential ledger no adjunta ninguna notificación al ejecutar una transacción de escritura.

  • commitEvidence: una cadena única generada por transacción, derivada del id. de transacción y los secretos del libro de contabilidad. Para obtener más información sobre la evidencia de confirmación, vea la documentación de CCF relacionada.

  • writeSetDigest: cadena hexadecimal que representa la síntesis hash SHA-256 del almacén de pares clave-valor, que contiene todas las claves y valores escritos en el momento en que se completó la transacción. Para obtener más información sobre el establecimiento de la escritura, vea la documentación de CCF relacionada.

Notificaciones de la aplicación

Las aplicaciones de Azure confidential Ledger pueden adjuntar datos arbitrarios, denominados notificaciones de aplicación, para escribir transacciones. Estas notificaciones representan las acciones ejecutadas durante una operación de escritura. Cuando se adjunte a una transacción, el resumen SHA-256 del objeto de notificaciones se incluirá en el libro de contabilidad y se confirmará como parte de la transacción de escritura. La inclusión de la notificación en la transacción de escritura garantiza que el resumen de la notificación está firmado y no se puede alterar.

Después, las notificaciones de aplicación se podrán revelar en su formato sin formato en la carga de recepción correspondiente a la misma transacción en la que se agregaron. Las notificaciones expuestas permiten a los usuarios volver a calcular el mismo hash de notificaciones adjunto y firmado en su lugar por el libro de contabilidad durante la transacción. El resumen de notificaciones se puede usar como parte del proceso de comprobación de recibo de transacción de escritura, lo que proporciona una forma sin conexión para que los usuarios comprueben la autenticidad total de las notificaciones registradas.

Actualmente, las notificaciones de aplicación se admiten en la versión preliminar de la API 2023-01-18-preview.

Escribir contenido de recibo de transacciones con notificaciones de aplicaciones

Este es un ejemplo de una carga de respuesta JSON que devuelve una instancia de Azure confidential ledger que registró las notificaciones de la aplicación al llamar al punto de conexión GET_RECEIPT.

{
  "applicationClaims": [
    {
      "kind": "LedgerEntry",
      "ledgerEntry": {
        "collectionId": "subledger:0",
        "contents": "Hello world",
        "protocol": "LedgerEntryV1",
        "secretKey": "Jde/VvaIfyrjQ/B19P+UJCBwmcrgN7sERStoyHnYO0M="
      }
    }
  ],
  "receipt": {
    "cert": "-----BEGIN CERTIFICATE-----\nMIIBxTCCAUygAwIBAgIRAMR89lUNeIghDUfpyHi3QzIwCgYIKoZIzj0EAwMwFjEU\nMBIGA1UEAwwLQ0NGIE5ldHdvcmswHhcNMjMwNDI1MTgxNDE5WhcNMjMwNzI0MTgx\nNDE4WjATMREwDwYDVQQDDAhDQ0YgTm9kZTB2MBAGByqGSM49AgEGBSuBBAAiA2IA\nBB1DiBUBr9/qapmvAIPm1o3o3LRViSOkfFVI4oPrw3SodLlousHrLz+HIe+BqHoj\n4nBjt0KAS2C0Av6Q+Xg5Po6GCu99GQSoSfajGqmjy3j3bwjsGJi5wHh1pNbPmMm/\nTqNhMF8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUCPaDohOGjVgQ2Lb8Pmubg7Y5\nDJAwHwYDVR0jBBgwFoAU25KejcEmXDNnKvSLUwW/CQZIVq4wDwYDVR0RBAgwBocE\nfwAAATAKBggqhkjOPQQDAwNnADBkAjA8Ci9myzieoLoIy+7mUswVEjUG3wrEXtxA\nDRmt2PK9bTDo2m3aJ4nCQJtCWQRUlN0CMCMOsXL4NnfsSxaG5CwAVkDwLBUPv7Zy\nLfSh2oZ3Wn4FTxL0UfnJeFOz/CkDUtJI1A==\n-----END CERTIFICATE-----\n",
    "leafComponents": {
      "claimsDigest": "d08d8764437d09b2d4d07d52293cddaf40f44a3ea2176a0528819a80002df9f6",
      "commitEvidence": "ce:2.13:850a25da46643fa41392750b6ca03c7c7d117c27ae14e3322873de6322aa7cd3",
      "writeSetDigest": "6637eddb8741ab54cc8a44725be67fd9be390e605f0537e5a278703860ace035"
    },
    "nodeId": "0db9a22e9301d1167a2a81596fa234642ad24bc742451a415b8d653af056795c",
    "proof": [
      {
        "left": "bcce25aa51854bd15257cfb0c81edc568a5a5fa3b81e7106c125649db93ff599"
      },
      {
        "left": "cc82daa27e76b7525a1f37ed7379bb80f6aab99f2b36e2e06c750dd9393cd51b"
      },
      {
        "left": "c53a15cbcc97e30ce748c0f44516ac3440e3e9cc19db0852f3aa3a3d5554dfae"
      }
    ],
    "signature": "MGYCMQClZXVAFn+vflIIikwMz64YZGoH71DKnfMr3LXkQ0lhljSsvDrmtmi/oWwOsqy28PsCMQCMe4n9aXXK4R+vY0SIfRWSCCfaADD6teclFCkVNK4317ep+5ENM/5T/vDJf3V4IvI="
  },
  "state": "Ready",
  "transactionId": "2.13"
}

En comparación con el ejemplo de recibo que se muestra en la sección anterior, la respuesta JSON contiene otro campo applicationClaims que representa la lista de notificaciones de aplicación registradas por el libro de contabilidad durante la transacción de escritura. Cada objeto de la lista applicationClaims contiene los campos siguientes.

  • kind: representa el tipo de notificación de aplicación. El valor indica cómo analizar el objeto de notificación de aplicación para el tipo proporcionado.

  • ledgerEntry: representa una notificación de aplicación derivada de los datos de entrada del libro de contabilidad. La notificación contendría los datos registrados por la aplicación durante una transacción de escritura (por ejemplo, el identificador de colección y el contenido proporcionado por el usuario) y la información necesaria para procesar el hash correspondiente al objeto de notificación único.

  • digest: representa una notificación de aplicación en forma sintetizada. Este objeto de notificación contendría la síntesis del mensaje procesado previamente por la aplicación y el protocolo que se usa para el cálculo.

El campo ledgerEntry contiene los campos siguientes.

  • protocol: representa el protocolo que se usará para calcular el la síntesis del mensaje de una notificación a partir de los datos de notificación especificados.

  • collectionId: identificador de la colección escrita durante la transacción de escritura correspondiente.

  • contents: contenido del libro de contabilidad escrito durante la transacción de escritura correspondiente.

  • secretKey: clave secreta codificada en base64. Esta clave se usará en el algoritmo HMAC con los valores proporcionados en la notificación de aplicación para obtener la síntesis del mensaje de notificación.

El campo digest contiene los campos siguientes.

  • protocol: representa el protocolo utilizado para procesar la síntesis del mensaje de la notificación especificada.

  • value: síntesis del mensaje de la notificación de aplicación, en formato hexadecimal. Este valor tendría que aplicar un hash con el valor protocol para calcular la síntesis del mensaje completo de la notificación de la aplicación.

Más recursos

Para obtener más información sobre la escritura de recibos de transacciones y cómo CCF garantiza la integridad de cada transacción, vea los vínculos siguientes:

Pasos siguientes