Azure recibos de gravação de transações do livro-razão confidencial

Para garantir a integridade das transações, o Azure Confidential Ledger usa uma estrutura de dados de árvore de Merkle para registrar o hash de todos os blocos de transações anexados ao ledger imutável. Depois que uma transação de gravação é confirmada, os usuários do Azure Confidential Ledger podem obter uma prova criptográfica de Merkle, ou recibo, relativa à entrada gerada em um Azure Confidential Ledger para verificar se a operação de gravação foi salva corretamente. Um recibo de transação de gravação é a prova de que o sistema confirmou a transação correspondente e pode ser usado para verificar se a entrada foi efetivamente acrescentada ao Razão.

Mais detalhes sobre como uma árvore de Merkle é usada no Azure Confidential Ledger podem ser encontrados na documentação do CCF.

Obter recibos de transação de gravação

Configuração e pré-requisitos

Os usuários do Azure Confidential Ledger podem obter um recibo para uma transação específica usando a biblioteca de cliente do Azure Confidential Ledger. O exemplo a seguir mostra como obter um recibo de gravação com a biblioteca cliente para Python, mas as etapas são as mesmas com qualquer outro SDK compatível com o Azure Confidential Ledger.

Pressupomos que um recurso do Azure Confidential Ledger já tenha sido criado usando a biblioteca de gerenciamento do Azure Confidential Ledger. Se você ainda não possuir um recurso de ledger, crie um usando as instruções a seguir.

Passo a passo do código

Começamos configurando as importações para nosso 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 seguir, estão os valores constantes usados para configurar o cliente do Azure Confidential Ledger. Certifique-se de atualizar a constante ledger_name com o nome exclusivo do recurso Azure Confidential Ledger.

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

Autenticamos usando a classe DefaultAzureCredential.

# Setup authentication 
credential = DefaultAzureCredential() 

Em seguida, obtemos e salvamos o certificado do serviço Azure Confidential Ledger usando o cliente de Certificados da URL de Identidade do Azure Confidential Ledger. O certificado de serviço é um certificado de chave pública de identidade de rede usado como raiz de confiança para autenticação de servidor TLS . Em outras palavras, é usado como CA (Autoridade de Certificação) para estabelecer uma conexão TLS com qualquer um dos nós na rede 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"]) 

Em seguida, podemos usar nossas credenciais, o certificado de rede obtido e nossa URL exclusiva do razão para criar um cliente do Azure Confidential Ledger.

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

Usando o cliente do Azure Confidential Ledger, podemos executar qualquer operação com suporte em uma instância do Azure Confidential Ledger. Por exemplo, podemos acrescentar uma nova entrada ao Razão e aguardar a confirmação da transação de gravação correspondente.

# 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() 

Depois que a transação for confirmada, podemos usar o cliente para obter um recibo referente à entrada acrescentada ao livro-razão na etapa anterior usando a respectiva ID da transação.

# 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 exemplo

O código de exemplo completo usado no passo a passo do código é fornecido.

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 = "<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)) 

Gravar conteúdo do recibo de transação

Aqui está um exemplo de payload de resposta JSON retornado por uma instância do Azure Confidential Ledger ao chamar o ponto de extremidade 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"
}

A resposta JSON contém os campos a seguir no nível raiz.

  • receipt: contém os valores que podem ser usados para verificar a validade do recibo da transação de escrita correspondente.

  • estado: o status da resposta JSON retornada. A seguir estão os valores possíveis permitidos:

    • Ready: o recibo retornado na resposta está disponível
    • Loading: o recibo ainda não está disponível para ser recuperado e a solicitação deve ser repetida
  • transactionId: o ID da transação associado ao recibo da transação de escrita.

O receipt campo contém os campos a seguir.

  • cert: string com o certificado de chave pública PEM do nó CCF que assinou a transação de escrita. O certificado de identidade de serviço deve sempre endossar o certificado do nó de assinatura. Veja também mais detalhes sobre como as transações são regularmente assinadas e como as transações assinadas são acrescentadas ao livro-razão no CCF no link a seguir.

  • nodeId: string hexadecimal representando o hash SHA-256 da chave pública do nó CCF de assinatura.

  • leafComponents: os componentes do hash do nó folha na Árvore de Merkle que estão associados à transação especificada. Uma Árvore Merkle é uma estrutura de dados de árvore que registra o hash de cada transação e garante a integridade do registro. Para obter mais informações sobre como uma Árvore Merkle é usada no CCF, consulte a documentação do CCF relacionada.

  • proof: lista de pares chave-valor representando os hashes dos nós da Árvore Merkle que, quando combinados com o hash do nó folha correspondente à transação em questão, permitem o recálculo do hash raiz da árvore. Graças às propriedades de uma Árvore Merkle, é possível recalcular o hash raiz da árvore apenas para um subconjunto de nós. Os elementos nesta lista estão na forma de pares chave-valor: as chaves indicam a posição relativa em relação ao nó pai na árvore em um determinado nível; os valores são os resumos de hash SHA-256 do nó fornecido, como cadeias de caracteres hexadecimais.

  • serviceEndorsements: lista de cadeias de caracteres de certificados codificados em PEM que representam certificados de identidades de serviço anteriores. É possível que a identidade de serviço que endossou o nó de assinatura não seja igual à que emitiu o recibo. Por exemplo, o certificado do serviço é renovado após uma recuperação de desastres do Azure Confidential Ledger. A lista de certificados de serviços anteriores permite que os auditores construam a cadeia de confiança desde o nó de assinatura do CCF até o certificado de serviço atual.

  • signature: String Base64 representando a assinatura da raiz da Árvore Merkle na transação em questão, pelo nó CCF de assinatura.

O leafComponents campo contém os campos a seguir.

  • claimsDigest: sequência hexadecimal que representa o resumo criptográfico SHA-256 da declaração do aplicativo anexada pelo aplicativo do Azure Confidential Ledger no momento em que a transação foi executada. Para anexar reivindicações do aplicativo, consulte a seção reivindicações do aplicativo.

  • commitEvidence: uma string única produzida por transação, derivada do ID da transação e dos segredos do ledger. Para obter mais informações sobre as evidências de confirmação, consulte a documentação do CCF relacionada.

  • writeSetDigest: cadeia de caracteres hexadecimal que representa o resumo de hash SHA-256 do repositórioKey-Value, que contém todas as chaves e valores gravados no momento em que a transação foi concluída. Para obter mais informações sobre o conjunto de gravação, consulte a documentação do CCF relacionada.

Reivindicações de aplicativo

Os aplicativos do Azure Confidential Ledger podem anexar dados arbitrários, chamados de declarações do aplicativo, a transações de gravação. Essas declarações representam as ações executadas durante uma operação de gravação. Quando anexado a uma transação, o resumo SHA-256 do objeto de declarações é incluído no razão e confirmado como parte da transação de gravação. A inclusão da declaração na transação de gravação garante que o resumo da declaração esteja conectado e não possa ser adulterado.

Posteriormente, as declarações de aplicativo podem ser reveladas em seu formato sem formatação na carga de recibo correspondente à mesma transação em que foram adicionadas. As declarações expostas permitem que os usuários recomputem o mesmo resumo de declarações que foi anexado e conectado pelo razão durante a transação. O resumo de declarações pode ser usado como parte do processo de verificação de recibo de transação de gravação, fornecendo uma maneira off-line para que os usuários verifiquem totalmente a autenticidade das reivindicações registradas.

Atualmente, há suporte para declarações de aplicativo na versão 2023-01-18-previewprévia da API.

Gravar o conteúdo do recibo da transação com declarações do aplicativo

Aqui está um exemplo de uma carga útil de resposta JSON retornada por uma instância do Azure Confidential Ledger que registrou declarações do aplicativo, ao chamar o ponto de extremidade 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"
}

Em comparação com o exemplo de recibo mostrado na seção anterior, a resposta JSON contém outro campo applicationClaims que representa a lista de declarações de aplicativo registradas pelo razão durante a transação de gravação. Cada objeto dentro da applicationClaims lista contém os campos a seguir.

  • tipo: representa o tipo de declaração do aplicativo. O valor indica como analisar o objeto de declaração de aplicativo para o tipo fornecido.

  • ledgerEntry: representa uma solicitação de aplicação derivada de dados de entrada da razão. A declaração conteria os dados registrados pelo aplicativo durante uma transação de gravação (por exemplo, a ID da coleção e o conteúdo fornecido pelo usuário) e as informações necessárias para calcular o resumo correspondente ao único objeto de declaração.

  • digest: representa uma declaração de aplicativo em forma digerida. Esse objeto de declaração conteria o resumo pré-compilado pelo aplicativo e o protocolo usado para a computação.

O ledgerEntry campo contém os campos a seguir.

  • protocolo: ele representa o protocolo a ser usado para calcular o resumo de uma declaração dos dados de declaração fornecidos.

  • collectionId: o identificador da coleção escrita durante a transação correspondente de escrita.

  • contents: o conteúdo da razão gravado durante a transação de gravação correspondente.

  • secretKey: uma chave secreta codificada em base64. Essa chave deve ser usada no algoritmo HMAC com os valores fornecidos na declaração do aplicativo para obter o resumo da declaração.

O digest campo contém os campos a seguir.

  • protocolo: ele representa o protocolo usado para calcular o resumo da declaração fornecida.

  • valor: o resumo da declaração do aplicativo, na forma hexadecimal. Esse valor teria que ser hash com o valor protocol para calcular o resumo completo da declaração do aplicativo.

Mais recursos

Para obter mais informações sobre recibos de transação de gravação e como o CCF garante a integridade de cada transação, consulte os seguintes links:

Próximas Etapas