À medida que avançamos em direção a uma infraestrutura de identidade digital (DID) mais centrada no usuário, uma das questões fundamentais que enfrentamos é a verificabilidade e a portabilidade de uma declaração. Em outras palavras, como posso provar independentemente que alguém disse algo sobre mim. Isso contrasta fortemente com o modelo de confiança existente, no qual a verificação de uma declaração exige interação direta entre o validador e o emissor da declaração (ou seja, para validar um certificado digital, eu verificaria o certificado no banco de dados do emissor).
Existem muitas soluções sendo construídas para alcançar isso tecnologicamente e a grande maioria delas envolveria alguma forma de criptografia de chave pública. Explicar a matemática complexa por trás dessa criptografia não está no escopo deste guia, mas é crucial apreciar as funcionalidades que ela permite:
- Um par de chaves é gerado onde uma das chaves é disponibilizada publicamente enquanto a outra chave é mantida privada.
- Quaisquer ações realizadas com a chave pública exigirão confirmação assinando com a chave privada. As transações resultam em um hash exclusivo com base nas entradas da transação.
- Devido à forma como o par de chaves foi gerado, o proprietário pode provar a propriedade da chave pública sem precisar divulgar a chave privada.
No contexto da Ethereum, isso significa que, como emissor, posso fazer uma reclamação sobre um assunto, assinando uma transação com minha carteira criptográfica, a mesma que uso para transferir Ethereum. Ao registrar este evento de assinatura em um registro de contrato inteligente, ethr-di-registry,
qualquer pessoa pode provar que a reivindicação foi emitida de minha conta, verificando a mensagem assinada neste registro descentralizado. Dada a acessibilidade e disponibilidade do contrato inteligente, isso significa que a validação requer apenas a propriedade da mensagem assinada, que pode ser facilmente armazenada no dispositivo pessoal do sujeito (ou seja, carteira de identidade). Com esse design, os usuários sempre têm controle total, pois a operação de seus dados/identidade digital não depende de terceiros.
Dado tal pano de fundo, este guia é uma introdução técnica sobre como isso pode ser alcançado usando o Metamask na Ethereum (Goerli testnet). Além dos conceitos específicos de identidade descentralizada, presume-se que você tenha alguma familiaridade básica com Express.js, Ethers.js e Metamask. Se você quiser uma atualização de como esses 3 são integrados, especialmente em relação à execução do Ethers.js no navegador, consulte:
Conectar Metamask com Ethers.js
A biblioteca Ethers.js nos permite interagir facilmente com a blockchain Ethereum usando Javascript. Como tal,…
O repositório do Github para este guia pode ser encontrado aqui:
GitHub - 0xKai27/did-jwt-metamask: Exemplo de implementação de emissão e verificação de Ethereum DIDs…
Este projeto é um exemplo de implementação de emissão e verificação de uma declaração sobre um assunto. É composto por 3 logicamente…
Principais Conceitos e Notas de Desenvolvimento
Antes de mergulhar no código, existem alguns objetos de dados importantes com os quais trabalharemos:
- DID: um identificador descentralizado único e global com recursos projetados para blockchains. Consiste em um método DID que define como os DIDs funcionam dentro de uma blockchain específica, bem como um identificador específico do método que é exclusivo dentro do namespace do método.
- Documento DID: um objeto JSON-LD que descreve as chaves públicas e os terminais de serviço necessários para inicializar interações criptograficamente verificáveis com o assunto em questão. Para obter mais informações sobre DIDs e documentos DID — DID: linha de partida da identidade descentralizada
- Registro de Dados Verificáveis: Sistema que facilita a criação, verificação, atualização e/ou desativação de DIDs e Documentos DID. Neste caso, estamos usando o contrato inteligente ethr-did-registry.
- Par de Chaves: um par de chaves públicas e privadas que representa uma conta Ethereum que permite a resolução pública de um endereço, bem como o controle privado sobre as ações da conta. Para mais informações: Livro Ethereum
- Token da Web JSON (JWT): define uma maneira compacta e independente de transmitir informações com segurança entre as partes como um objeto JSON. Consulte o site do JWT: https://jwt.io/introduction
- Reivindicação privada (específica do JWT): uma declaração personalizável que foi pré-acordada entre o produtor e o consumidor da declaração JWT. Usaremos isso como um exemplo de dados que podem ser incluídos em uma carga JWT. Você pode consultar a especificação JWT RFC7519 aqui.
Este é um ótimo recurso se você quiser se aprofundar nesses conceitos:
DIF FAQ
Você está aqui (ou deveria estar): https://identity.foundation/faq Esta é a Fundação de Identidade Descentralizada…
Para propriedades DID mais específicas, você pode consultar diretamente a especificação DID:
Identificadores Descentralizados (DIDs) v1.0
Identificadores descentralizados (DIDs) são um novo tipo de identificador que permite identidade digital verificável e descentralizada…
Notas de Desenvolvimento
Este guia é composto por 2 estágios logicamente separados ao emitir/verificar um Ethereum DID-JWT:
- Emissão de um JWT com uma reivindicação privada assinada pelo Emissor
- Validação do Documento DID do sujeito e da carga de pagamento JWT pela Audiencia
Cada um dos estágios acima é separado em sua própria página (aplicativo de emissor e público), que exige que você interaja com o JWT por meio de contas de função separadas na Metamask.
A intenção por trás da operação manual de cada estágio é fornecer uma visão mais detalhada das seções do fluxo de ponta a ponta. Observe que embora os diagramas de sequência indiquem salvar o JWT em um “dispositivo de assunto” (ou seja, carteira de identidade), optei por armazená-lo temporariamente no token de sessão para simplificar.
Assinatura e Emissão de JWTs
Primeiro precisamos preparar as reivindicações que estarão no JWT. Isso consiste na reivindicação privada, bem como nas partes da reivindicação:
- "iss": A declaração “iss” (emissor) identifica o principal que emitiu o \ JWT. Essa conta exigirá algum ETH de teste para assinar o JWT.
- "sub": A declaração “sub” (assunto) identifica o principal que é o \ sujeito do JWT. Esta conta exigirá algum ETH de teste para atualizar o documento DID on-chain.
- "aud": a declaração “aud” (público) identifica os destinatários aos quais o JWT será enviado.
Todos os endereços Ethereum terão que ser formatados em seu método DID equivalente. Como estamos usando o método DID:ethr:, nosso DID equivalente estará no seguinte formato: did:ethr:<chainId>:<ethAddress>
. Observe que o chainId
está sendo puxado com base na rede Metamask conectada. A biblioteca ethr-did fornece um wrapper útil em torno da conta conectada que nos permite interagir convenientemente com DIDs.
Você pode substituir facilmente os padrões para esses 3 campos por meio da interface do usuário ou alterando as variáveis relevantes no código (subjectAddress, audienceAddress, privateClaim
). Ao construir a mensagem JWT, você deve ver o objeto impresso no console do navegador:
Como usaremos uma conta Metamask para assinar a mensagem, primeiro precisamos criar um signatário delegado que será conectado à nossa conta do emissor. Isso é necessário porque “os provedores web3 não são capazes de assinar dados diretamente de uma maneira compatível com o JWT ES256K ou os algoritmos ES256K-R
(não registrados)” (Introdução Ethr-Did).Ao criar um signatário delegado, um novo objeto assertionMethod e método de verificação que vincula o emissor delegado será adicionado ao documento DID do emissor. Isso é o que permite que o JWT seja verificado posteriormente.
Podemos prosseguir e criar o delegado selecionando o botão “Criar Delegado” que acionará uma solicitação do Metamask para você confirmar a transação. Observe que você pode ter que alterar a taxa de gas sugerida no Metamask para que a transação seja minerada.
Depois que a transação for confirmada, você poderá ver a conta delegada gerada randomicamente e os DIDs:
A principal observação ao criar o delegado é que a função createSigningDelegate()
exposta por ethr-did
substitui o signatário do nosso objeto Issuer EthrDID
pelo do delegado:
Com o signatário delegado criado, podemos finalmente assinar a mensagem clicando no botão “Assinar JWT” que assina a mensagem com o DID do emissor atualizado.
O JWT assinado é uma concatenação em Base64 da entrada de assinatura (cabeçalho e carga útil), bem como a assinatura resultante, cada uma separada por um .
. Criticamente, dado que o JWT é autocontido, isso significa que as entradas do JWT podem ser decodificadas com base apenas na propriedade do JWT. Portanto, qualquer JWT que contenha informações pessoalmente identificáveis ou confidenciais nunca deve ser mantido na cadeia, pois ficará permanentemente visível para todos. Nesse caso, é recomendável que o JWT seja armazenado no dispositivo do Sujeito (ou seja, carteira de identidade) de onde pode ser recuperado com eficiência com base no consentimento do usuário. Os JWTs devem ser reemitidos quando necessário (ou seja, perda de dispositivo/DID).
Para completar, o documento DID do Emissor também é registrado no console do navegador para referência. Observe que a conta delegada que foi adicionada no método de verificação corresponde à entrada assertionMethod.
Código da seção:
- Visualizar: /views/issuer.ejs
- Lógica: /jwt/sign.ts
Validando o JWT
A biblioteca ethr-did
torna a verificação do JWT um processo extremamente simples, pois todas as complexidades são tratadas por trás de uma única função VerifyJWT()
. Conceitualmente, o JWT está sendo verificado ao:
- Decodificar o JWT
- Obter a chave pública de verificação e o controlador do JWT decodificado (
verificationMethod
) - Checar o propósito da verificação do JWT
- Resolver o Documento DID do controlador (ou seja, Emissor)
- Verificar o documento DID
assertionMethod
do emissor em relação ao JWT decodificado - Verificar a expiração do JWT e o
aud
válido
Como a biblioteca ethr-did
verifica o JWT em relação ao documento iss
DID, que foi atualizado no primeiro estágio (ou seja, issuerDelegateKp.signJWT()
), esse fluxo pode ser alcançado pelo validador apenas com o JWT sozinho. Em outras palavras,o JWT pode ser armazenado de forma privada fora da cadeia e fornecido somente quando um Sujeito solicita validação pela Audiência listada no JWT.
Para este fluxo, verifique se você está conectado à carteira de audiência na Metamask ou pode configurar o endereço da audiência através do formulário:
O ethr-did
vai comparar o DID do público configurado com a carga de pagamento JWT “aud” como parte das verificações de verificação. Para acionar as verificações, você pode clicar no botão “Validar JWT”:
A verificação também vai retornar o resultado, bem como a carga de pagamento totalmente decodificada, que é exibida na interface do usuário. O resultado completo pode ser visualizado no console:
Embora o valor verified
seja tudo o que é necessário para validar o JWT, a biblioteca ethr-did
também retorna detalhes adicionais sobre o JWT e o signatário para sua conveniência. Algumas coisas a serem observadas:
- O Documento DID devolvido é o mesmo DID Emissor da etapa anterior. Ou seja, qualquer pessoa que resolver o DID do emissor do
ethr-did-registry
receberá o mesmo resultado - O valor
issuer
é o equivalente DID do nosso endereço do emissor - O
payload
, com todas as nossas reivindicações, pode ser completamente decodificada com base no JWT - O objeto
signer
vincula o Emissor à conta do signatário delegado que foi criada
Código da seção:
- Visualizar: /views/audience.ejs
- Lógica: /jwt/verify.ts
Obrigado por ficar até o fim. Adoraria ouvir seus pensamentos/comentários, então deixe um comentário. estou ativo no twitter @Aw Kai Shin se você quiser receber petiscos mais digeríveis de informações relacionadas à criptografia ou visite meu site pessoal se você gostaria de meus serviços :)
Este artigo foi escrito por Aw Kai Shin e traduzido por Diogo Jorge. O artigo original pode ser encontrado aqui.
Latest comments (0)