Moitas grazas ao equipo Mach34 pola versión inicial de . zkemail en Noir Introdución Unha conta de zkEmail é unha conta de contrato intelixente que permite aos usuarios confirmar transaccións respondendo a un correo electrónico. Como funciona? Cada mensaxe de correo electrónico está asinada polo provedor de correo electrónico do usuario (por exemplo, iCloud, Gmail ou ProtonMail). A conta de contrato intelixente do usuario verifica a sinatura na cadea e executa a transacción se a sinatura é válida. Ademais da verificación da sinatura, tamén é importante verificalo correo electrónico foi enviado desde o enderezo de correo electrónico do usuario, enviouse ao enderezo de correo electrónico do relé e que o correo electrónico contén o asunto correcto (que inclúe a descrición da chamada ou do tx). Exemplo Alice envía o seguinte correo electrónico a un transmisor: from: alice@icloud.com to: relayer@shieldswap.org subject: Send 10 ETH to vitalik.eth DKIM-Signature: /* signature by icloud.com */ /* body is not relevant */ Observa a cabeceira ? É a sinatura creada pola clave privada de iCloud. O resto do artigo explicarase: DKIM-Signature Que é exactamente asinado por un provedor de correo electrónico Como obter a chave pública do provedor de correo electrónico Como verificar a sinatura en Noir Sinatura A sinatura DKIM é creada por un provedor de correo electrónico que asina un subconxunto de cabeceiras nunha orde correcta e formato canónico. Vexamos unha cabeceira DKIM-Signature: DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=1a1hai; t=1693038337; bh=7xQMDuoVVU4m0W0WRVSrVXMeGSIASsnucK9dJsrc+vU=; h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; b=EhLyVPpKD7d2/+h1nrnu+iEEBDfh6UWiAf9Y5UK+aPNLt3fAyEKw6Ic46v32NOcZD M/zhXWucN0FXNiS0pz/QVIEy8Bcdy7eBZA0QA1fp8x5x5SugDELSRobQNbkOjBg7Mx VXy7h4pKZMm/hKyhvMZXK4AX9fSoXZt4VGlAFymFNavfdAeKgg/SHXLds4lOPJV1wR 2E21g853iz5m/INq3uK6SQKzTnz/wDkdyiq90gC0tHQe8HpDRhPIqgL5KSEpuvUYmJ wjEOwwHqP6L3JfEeROOt6wyuB1ah7wgRvoABOJ81+qLYRn3bxF+y1BC+PwFd5yFWH5 Ry43lwp1/3+sA== Consta de pares clave-valor separados por punto e coma. As partes importantes son: e . Desglosámolo: a= b= é o algoritmo usado para asinar a mensaxe. Neste caso, é . a= rsa-sha256 é a propia sinatura na codificación base64. b= Que está asinado exactamente? As cabeceiras dos correos electrónicos ordénanse e canonízanse antes de asinalas. A orde das cabeceiras está especificada polo parámetro da cabeceira . A continuación, as cabeceiras convértense a minúsculas, elimínanse os espazos en branco adicionais e, a continuación, únense caracteres. Vexamos as cabeceiras antes da canonización: h= DKIM-Signature \r\n DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=1a1hai; t=1693038337; bh=7xQMDuoVVU4m0W0WRVSrVXMeGSIASsnucK9dJsrc+vU=; h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; b=EhLyVPpKD7d2/+h1nrnu+iEEBDfh6UWiAf9Y5UK+aPNLt3fAyEKw6Ic46v32NOcZD M/zhXWucN0FXNiS0pz/QVIEy8Bcdy7eBZA0QA1fp8x5x5SugDELSRobQNbkOjBg7Mx VXy7h4pKZMm/hKyhvMZXK4AX9fSoXZt4VGlAFymFNavfdAeKgg/SHXLds4lOPJV1wR 2E21g853iz5m/INq3uK6SQKzTnz/wDkdyiq90gC0tHQe8HpDRhPIqgL5KSEpuvUYmJ wjEOwwHqP6L3JfEeROOt6wyuB1ah7wgRvoABOJ81+qLYRn3bxF+y1BC+PwFd5yFWH5 Ry43lwp1/3+sA== from: runnier.leagues.0j@icloud.com Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.500.231\)) Subject: Hello Message-Id: <8F819D32-B6AC-489D-977F-438BBC4CAB27@me.com> Date: Sat, 26 Aug 2023 12:25:22 +0400 to: zkewtest@gmail.com E despois da canonización: from:runnier.leagues.0j@icloud.com\r\ncontent-type:text/plain; charset=us-ascii\r\nmime-version:1.0 (Mac OS X Mail 16.0 \\(3731.500.231\\))\r\nsubject:Hello\r\nmessage-id:<8F819D32-B6AC-489D-977F-438BBC4CAB27@me.com>\r\ndate:Sat, 26 Aug 2023 12:25:22 +0400\r\nto:zkewtest@gmail.com\r\ndkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=1a1hai; t=1693038337; bh=7xQMDuoVVU4m0W0WRVSrVXMeGSIASsnucK9dJsrc+vU=; h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; b= Teña en conta que a cabeceira tamén está incluída nas cabeceiras canonizadas, pero falta o valor do parámetro . Só se inclúe a chave. DKIM-Signature b= Ao enviar un correo electrónico, iCloud.com asinará as cabeceiras canonizadas coa súa clave privada. A sinatura inclúese entón na cabeceira . Concretamente no parámetro . DKIM-Signature b= Chave pública do provedor de correo electrónico 2 preguntas: Como obter a chave pública do provedor de correo electrónico? Como verificar a sinatura en Noir? Se miras a cabeceira desde arriba, notarás 2 parámetros: e . Estes son o e o do provedor de correo electrónico. Podemos obter a clave pública consultando o rexistro DNS específico: . Neste caso, temos que consultar . Consultámolo usando o comando : DKIM-Signature d=icloud.com s=1a1hai d s {s}._domainkey.{d} 1a1hai._domainkey.icloud.com dig dig +short TXT 1a1hai._domainkey.icloud.com Recibimos a seguinte resposta: "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1ZEfbkf4TbO2TDZI67WhJ6G8Dwk3SJyAbBlE/QKdyXFZB4HfEU7AcuZBzcXSJFE03DlmyOkUAmaaR8yFlwooHyaKRLIaT3epGlL5YGowyfItL" "ly2k0Jj0IOICRxWrB378b7qMeimE8KlH1UNaVpRTTi0XIYjIKAOpTlBmkM9a/3Rl4NWy8pLYApXD+WCkYxPcxoAAgaN8osqGTCJ5r+VHFU7Wm9xqq3MZmnfo" "0bzInF4UajCKjJAQa+HNuh95DWIYP/wV77/PxkEakOtzkbJMlFJiK/hMJ+HQUvTbtKW2s+t4uDK8DI16Rotsn6e0hS8xuXPmVte9ZzplD0fQgm2qwIDAQAB" A parte interesante é o parámetro . Teña en conta que está dividido en varias liñas de comiñas, polo que temos que concatenalas. O resultado é: p= MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1ZEfbkf4TbO2TDZI67WhJ6G8Dwk3SJyAbBlE/QKdyXFZB4HfEU7AcuZBzcXSJFE03DlmyOkUAmaaR8yFlwooHyaKRLIaT3epGlL5YGowyfItLly2k0Jj0IOICRxWrB378b7qMeimE8KlH1UNaVpRTTi0XIYjIKAOpTlBmkM9a/3Rl4NWy8pLYApXD+WCkYxPcxoAAgaN8osqGTCJ5r+VHFU7Wm9xqq3MZmnfo0bzInF4UajCKjJAQa+HNuh95DWIYP/wV77/PxkEakOtzkbJMlFJiK/hMJ+HQUvTbtKW2s+t4uDK8DI16Rotsn6e0hS8xuXPmVte9ZzplD0fQgm2qwIDAQAB Esta é a clave pública do provedor de correo electrónico ( ) codificada en base64. Usarémolo máis tarde para verificar unha sinatura en Noir. iCloud.com Circuito negro Debido ás limitacións dos circuítos ZK, temos que utilizar unha implementación especialmente optimizada de RSA e grandes números proporcionados polo equipo Noir: . https://github.com/noir-lang/noir_rsa Instalalo en Nargo.toml: [dependencies] noir_rsa = { tag = "v0.2", git = "https://github.com/noir-lang/noir_rsa" } Agora, vexamos o circuíto Noir para a verificación: use noir_rsa::bignum::BigNum; use noir_rsa::bignum::runtime_bignum::BigNumInstance; use noir_rsa::bignum::fields::Params2048; use noir_rsa::RSA; global PUBKEY_LIMBS_LEN: u32 = 18; global SIGNATURE_LIMBS_LEN: u32 = 18; type BN2048 = BigNum<18, Params2048>; type RSA2048 = RSA<BN2048, BigNumInstance<18, Params2048>, 256>; global rsa: RSA2048 = RSA {}; fn verify_email<let MAX_HEADERS_LEN: u32>( headers: [u8; MAX_HEADERS_LEN], headers_len: u32, pubkey_limbs: [Field; PUBKEY_LIMBS_LEN], pubkey_redc_limbs: [Field; PUBKEY_LIMBS_LEN], signature: [Field; SIGNATURE_LIMBS_LEN] ) -> bool { let signature: BN2048 = BigNum::from_array(signature); let pubkey: BigNumInstance<18, Params2048> = BigNumInstance::new(pubkey_limbs, pubkey_redc_limbs); let headers_hash = std::hash::sha256_var(headers, headers_len as u64); rsa.verify_sha256_pkcs1v15(pubkey, headers_hash, signature) } Omitirei as importacións e as definicións de tipo por motivos de brevidade (a quen lle importa de todos os xeitos, basta con copialo e pegalo). Vexamos os parámetros : verify_email : as cabeceiras canonizadas como unha matriz de bytes. Ten unha lonxitude fixa e ten que estar acolchado cun 0 ao final headers : a lonxitude real das cabeceiras headers_len & : a chave pública do provedor de correo electrónico codificada especificamente para Noir pubkey_limbs pubkey_redc_limbs : a sinatura do provedor de correo electrónico codificada especificamente para Noir signature No corpo da función, primeiro convertemos a sinatura e a chave pública en instancias de gran número. Despois difundimos as cabeceiras canonizadas con sha256 e verificamos a sinatura coa chave pública e o hash. let signature: BN2048 = BigNum::from_array(signature); let pubkey: BigNumInstance<18, Params2048> = BigNumInstance::new(pubkey_limbs, pubkey_redc_limbs); let headers_hash = std::hash::sha256_var(headers, headers_len as u64); rsa.verify_sha256_pkcs1v15(pubkey, headers_hash, signature) Se a verificación ten éxito, a función devolve , se non, devolve . true false Desde a cadea de correo electrónico en bruto ata as entradas de Noir Agora que temos o circuíto Noir, como convertemos unha cadea de correo electrónico en bruto ás entradas estruturadas para o circuíto? Imos comezar coas cabeceiras. Usarei para analizar o correo electrónico e para extraer as cabeceiras canonizadas. Analiza o correo electrónico: mailparser dkim import DKIM from "dkim"; import { simpleParser } from "mailparser"; async function canonicalizeHeaders(emailStr: string) { const email = await simpleParser(emailStr); const dkimHeader = email.headers.get("dkim-signature").params; const canonicalHeaders = DKIM.processHeader( email.headerLines.map((x) => x.line), dkimHeader.h.split(":").map((x) => x.trim()), "relaxed", ); const signatureBase64 = dkimHeader.b.replace(/\s/g, ""); return { canonicalHeaders, signatureBase64 }; } Agora, probalo: const { canonicalHeaders, signatureBase64 } = await canonicalizeHeaders( String.raw` DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=1a1hai; t=1693038337; bh=7xQMDuoVVU4m0W0WRVSrVXMeGSIASsnucK9dJsrc+vU=; h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; b=EhLyVPpKD7d2/+h1nrnu+iEEBDfh6UWiAf9Y5UK+aPNLt3fAyEKw6Ic46v32NOcZD M/zhXWucN0FXNiS0pz/QVIEy8Bcdy7eBZA0QA1fp8x5x5SugDELSRobQNbkOjBg7Mx VXy7h4pKZMm/hKyhvMZXK4AX9fSoXZt4VGlAFymFNavfdAeKgg/SHXLds4lOPJV1wR 2E21g853iz5m/INq3uK6SQKzTnz/wDkdyiq90gC0tHQe8HpDRhPIqgL5KSEpuvUYmJ wjEOwwHqP6L3JfEeROOt6wyuB1ah7wgRvoABOJ81+qLYRn3bxF+y1BC+PwFd5yFWH5 Ry43lwp1/3+sA== from: runnier.leagues.0j@icloud.com Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.500.231\)) Subject: Hello Message-Id: <8F819D32-B6AC-489D-977F-438BBC4CAB27@me.com> Date: Sat, 26 Aug 2023 12:25:22 +0400 to: zkewtest@gmail.com `.trim(), ); console.log("signature:", signatureBase64); console.log(); console.log("headers:", JSON.stringify(canonicalHeaders)); Obtemos a seguinte saída: signature: EhLyVPpKD7d2/+h1nrnu+iEEBDfh6UWiAf9Y5UK+aPNLt3fAyEKw6Ic46v32NOcZDM/zhXWucN0FXNiS0pz/QVIEy8Bcdy7eBZA0QA1fp8x5x5SugDELSRobQNbkOjBg7MxVXy7h4pKZMm/hKyhvMZXK4AX9fSoXZt4VGlAFymFNavfdAeKgg/SHXLds4lOPJV1wR2E21g853iz5m/INq3uK6SQKzTnz/wDkdyiq90gC0tHQe8HpDRhPIqgL5KSEpuvUYmJwjEOwwHqP6L3JfEeROOt6wyuB1ah7wgRvoABOJ81+qLYRn3bxF+y1BC+PwFd5yFWH5Ry43lwp1/3+sA== headers: "from:runnier.leagues.0j@icloud.com\r\ncontent-type:text/plain; charset=us-ascii\r\nmime-version:1.0 (Mac OS X Mail 16.0 \\(3731.500.231\\))\r\nsubject:Hello\r\nmessage-id:<8F819D32-B6AC-489D-977F-438BBC4CAB27@me.com>\r\ndate:Sat, 26 Aug 2023 12:25:22 +0400\r\nto:zkewtest@gmail.com\r\ndkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=1a1hai; t=1693038337; bh=7xQMDuoVVU4m0W0WRVSrVXMeGSIASsnucK9dJsrc+vU=; h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; b=" E convérteo en bytes: const canonicalHeadersBytes = Array.from( Buffer.from(canonicalHeaders, "utf-8"), ); console.log( "canonicalHeadersBytes:", "[", canonicalHeadersBytes.join(", "), "]", ); Saída: canonicalHeadersBytes: [ 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61 ] Garda estas saídas para máis tarde. Agora, imos converter a chave pública e a sinatura do provedor de correo electrónico en membros de gran número de Noir. Só hai dispoñible . Pero tes sorte porque o compilei en wasm e o publiquei en npm. Instalémolo: esta biblioteca oficial de Rust npm i @shieldswap/email_account_utils_rs Acepta a clave pública e a sinatura en base64 e devolve os membros da chave pública e os membros da sinatura en formato JSON: import init, { get_limbs } from "@shieldswap/email_account_utils_rs"; await init(); // initialize the wasm module // base64 encoded public key from email provider const publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1ZEfbkf4TbO2TDZI67WhJ6G8Dwk3SJyAbBlE/QKdyXFZB4HfEU7AcuZBzcXSJFE03DlmyOkUAmaaR8yFlwooHyaKRLIaT3epGlL5YGowyfItLly2k0Jj0IOICRxWrB378b7qMeimE8KlH1UNaVpRTTi0XIYjIKAOpTlBmkM9a/3Rl4NWy8pLYApXD+WCkYxPcxoAAgaN8osqGTCJ5r+VHFU7Wm9xqq3MZmnfo0bzInF4UajCKjJAQa+HNuh95DWIYP/wV77/PxkEakOtzkbJMlFJiK/hMJ+HQUvTbtKW2s+t4uDK8DI16Rotsn6e0hS8xuXPmVte9ZzplD0fQgm2qwIDAQAB"; const { public_key_limbs, public_key_redc_limbs, signature_limbs } = JSON.parse( get_limbs(publicKey, signatureBase64), ); console.log("public_key_limbs", "[" + public_key_limbs.join(",") + "]"); console.log( "public_key_redc_limbs", "[" + public_key_redc_limbs.join(",") + "]", ); console.log("signature_limbs", "[" + signature_limbs.join(",") + "]"); Agora, temos todos os datos necesarios para verificar a sinatura, así que imos facelo. Vou copiar e pegar os datos xerados por javascript nunha proba de Noir para simplificar: #[test] fn test_email_signature() { let headers = [ 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61 ]; let pubkey_limbs = [ 0xe5cf995b5ef59ce9943d1f4209b6ab, 0xe0caf03235e91a2db27e9ed214bcc6, 0xafe1309f87414bd36ed296dacfade2, 0xbeff3f19046a43adce46c932514988, 0x324041af8736e87de4358860fff057, 0xadcc6669dfa346f322717851a8c22a, 0x8b2a193089e6bf951c553b5a6f71aa, 0x0a570fe582918c4f731a0002068df2, 0x39419a433d6bfdd1978356cbca4b60, 0x550d695a514d38b45c862320a00ea5, 0x1c56ac1dfbf1beea31e8a613c2a51f, 0x6a30c9f22d2e5cb6934263d0838809, 0x0a281f268a44b21a4f77a91a52f960, 0x5134dc3966c8e91402669a47cc8597, 0x71590781df114ec072e641cdc5d224, 0xa1bc0f0937489c806c1944fd029dc9, 0x911f6e47f84db3b64c3648ebb5a127, 0xd5 ]; let pubkey_redc_limbs = [ 0xa48a824e4ebc7e0f1059f3ecfa57c4, 0x05c1db23f3c7d47ad7e7d7cfda5189, 0x79bb6bbbd8facf011f022fa9051aec, 0x24faa4cef474bed639362ea71f7a21, 0x1503aa50b77e24b030841a7d061581, 0x5bbf4e62805e1860a904c0f66a5fad, 0x5cbd24b72442d2ce647dd7d0a44368, 0x074a8839a4460c169dce7138efdaef, 0x0f06e09e3191b995b08e5b45182f65, 0x51fad4a89f8369fe10e5d4b6e149a1, 0xdc778b15982d11ebf7fe23b4e15f10, 0xa09ff3a4567077510c474e4ac0a21a, 0xb37e69e5dbb77167b73065e4c5ad6a, 0xecf4774e22e7fe3a38642186f7ae74, 0x16e72b5eb4c813a3b37998083aab81, 0xa48e7050aa8abedce5a45c16985376, 0xdd3285e53b322b221f7bcf4f8f8ad8, 0x0132 ]; let signature_limbs = [ 0x5779c85587e51cb8de5c29d7fdfeb0, 0xcd7ea8b6119f76f117ecb5042f8fc0, 0xeb7ac32b81d5a87bc2046fa0004e27, 0x62708c43b0c07a8fe8bdc97c479138, 0xc1e90d184f22a80be4a484a6ebd462, 0x39f3ff00e47728aaf74802d2d1d07b, 0x0f39de2cf99bf20dab7b8ae9240acd, 0xf4875cb76ce2538f255d70476136d6, 0xde151a5005ca614d6af7dd01e2a083, 0x6fe12b286f3195cae005fd7d2a1766, 0xd6e43a3060eccc555f2ee1e2929932, 0x0d5fa7cc79c794ae80310b491a1b40, 0x9cff415204cbc05c772ede05903440, 0xe7190ccff38575ae70dd055cd892d2, 0xf34bb777c0c842b0e88738eafdf634, 0x21040437e1e945a201ff58e542be68, 0x12f254fa4a0fb776ffe8759eb9eefa, 0x12 ]; let result = verify_email( headers, headers.len(), pubkey_limbs, pubkey_redc_limbs, signature_limbs ); assert(result, "email verification failed"); } E realiza a proba: nargo test [zkemail] Running 1 test function [zkemail] Testing test_email_signature... ok [zkemail] 1 test passed Así é como verificar unha sinatura DKIM en Noir. Pero unha conta de zkEmail ten máis que verificar sinaturas DKIM. Ao final, calquera pode enviar un correo electrónico e obter unha sinatura DKIM válida. Tamén temos que verificar que o correo electrónico foi enviado desde o enderezo correcto e que o correo electrónico foi enviado ao enderezo correcto. Así como que o asunto do correo electrónico conteña os datos de chamada correctos. Estade atentos, próximamente na segunda parte! Mentres tanto, compra: unha conta de correo electrónico en ShieldWallet: https://wallet.shieldswap.org/login/email proba que recibiches un correo electrónico de alguén que usa : Noir https://zkemail.shieldswap.org e intercambia algúns tokens en ShieldSwap (testnet, os tokens non valen para nada): https://app.shieldswap.org