ขอขอบคุณทีมงาน Mach34 เป็นอย่างมากสำหรับเวอร์ชันแรกของ zkemail ใน Noir การแนะนำ บัญชี zkEmail เป็นบัญชีสัญญาอัจฉริยะที่อนุญาตให้ผู้ใช้ยืนยันธุรกรรมโดยการตอบกลับอีเมล บัญชีนี้ทำงานอย่างไร ข้อความอีเมลแต่ละข้อความได้รับการลงนามโดยผู้ให้บริการอีเมลของผู้ใช้ (เช่น iCloud, Gmail หรือ ProtonMail) บัญชีสัญญาอัจฉริยะของผู้ใช้จะตรวจสอบลายเซ็นบนเชนและดำเนินการธุรกรรมหากลายเซ็นนั้นถูกต้อง นอกเหนือจากการตรวจสอบลายเซ็นแล้ว การตรวจสอบลายเซ็นยังมีความสำคัญอีกด้วย อีเมลถูกส่งจากที่อยู่อีเมลของผู้ใช้ มันถูกส่งไปยังที่อยู่อีเมลของผู้ถ่ายทอดแล้ว อีเมลมีหัวเรื่องที่ถูกต้อง (ซึ่งรวมถึงคำอธิบายข้อมูลการโทรหรือการชำระเงิน) ตัวอย่าง อลิซส่งอีเมลต่อไปนี้ไปยังผู้ส่งต่อ: 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 */ สังเกตส่วนหัว ไหม มันคือลายเซ็นที่สร้างขึ้นโดยคีย์ส่วนตัวของ iCloud บทความที่เหลือจะอธิบายดังนี้: DKIM-Signature ผู้ให้บริการอีเมล์ลงนามอะไรบ้าง วิธีการรับคีย์สาธารณะของผู้ให้บริการอีเมล วิธีการตรวจสอบลายเซ็นใน Noir ลายเซ็น ลายเซ็น DKIM ถูกสร้างขึ้นโดยผู้ให้บริการอีเมลที่ลงนามส่วนหัวย่อยตามลำดับและรูปแบบมาตรฐานที่ถูกต้อง มาดูส่วนหัวลายเซ็น DKIM กัน: 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== ประกอบด้วยคู่คีย์-ค่าที่คั่นด้วยเครื่องหมายเซมิโคลอน ส่วนที่สำคัญได้แก่ และ มาแยกย่อยกัน: a= b= คืออัลกอริทึมที่ใช้ในการลงนามข้อความ ในกรณีนี้คือ a= rsa-sha256 คือลายเซ็นต์ในรหัสแบบ base64 b= ลงนามอะไรบ้างคะ? ส่วนหัวอีเมลจะถูกเรียงลำดับและกำหนดเป็นมาตรฐานก่อนลงนาม ลำดับของส่วนหัวจะระบุโดยพารามิเตอร์ ของส่วนหัว จากนั้นส่วนหัวจะถูกแปลงเป็นตัวพิมพ์เล็ก ลบช่องว่างพิเศษออก แล้วจึงเชื่อมด้วยอักขระ มาดูส่วนหัวก่อนกำหนดเป็นมาตรฐานกัน: 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 และหลังจากการกำหนดมาตรฐานแล้ว: 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= โปรดทราบว่าส่วนหัว จะรวมอยู่ในส่วนหัวที่เป็นมาตรฐานด้วย แต่ไม่มีค่าพารามิเตอร์ มีเพียงคีย์เท่านั้นที่รวมอยู่ DKIM-Signature b= เมื่อส่งอีเมลแล้ว iCloud.com จะลงนามในส่วนหัวที่กำหนดมาตรฐานด้วยคีย์ส่วนตัว จากนั้นลายเซ็นจะรวมอยู่ในส่วนหัว โดยเฉพาะในพารามิเตอร์ DKIM-Signature b= คีย์สาธารณะของผู้ให้บริการอีเมล์ 2 คำถาม: จะรับคีย์สาธารณะของผู้ให้บริการอีเมล์ได้อย่างไร? จะตรวจสอบลายเซ็นใน Noir ได้อย่างไร? หากคุณดูที่ส่วนหัว จากด้านบน คุณจะสังเกตเห็นพารามิเตอร์ 2 ตัว: และ ซึ่งก็คือ และตัวเลือก ของผู้ให้บริการอีเมล เราสามารถรับคีย์สาธารณะได้โดยสอบถามระเบียน DNS เฉพาะ: ในกรณีนี้ เราต้องสอบถาม มาสอบถามโดยใช้คำสั่ง : DKIM-Signature d=icloud.com s=1a1hai d s {s}._domainkey.{d} 1a1hai._domainkey.icloud.com dig dig +short TXT 1a1hai._domainkey.icloud.com เราได้รับการตอบสนองดังต่อไปนี้: "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1ZEfbkf4TbO2TDZI67WhJ6G8Dwk3SJyAbBlE/QKdyXFZB4HfEU7AcuZBzcXSJFE03DlmyOkUAmaaR8yFlwooHyaKRLIaT3epGlL5YGowyfItL" "ly2k0Jj0IOICRxWrB378b7qMeimE8KlH1UNaVpRTTi0XIYjIKAOpTlBmkM9a/3Rl4NWy8pLYApXD+WCkYxPcxoAAgaN8osqGTCJ5r+VHFU7Wm9xqq3MZmnfo" "0bzInF4UajCKjJAQa+HNuh95DWIYP/wV77/PxkEakOtzkbJMlFJiK/hMJ+HQUvTbtKW2s+t4uDK8DI16Rotsn6e0hS8xuXPmVte9ZzplD0fQgm2qwIDAQAB" ส่วนที่น่าสนใจคือพารามิเตอร์ โปรดทราบว่าพารามิเตอร์นี้ถูกแบ่งออกเป็นหลายบรรทัดเครื่องหมายคำพูด ดังนั้นเราต้องต่อกัน ผลลัพธ์คือ: p= MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1ZEfbkf4TbO2TDZI67WhJ6G8Dwk3SJyAbBlE/QKdyXFZB4HfEU7AcuZBzcXSJFE03DlmyOkUAmaaR8yFlwooHyaKRLIaT3epGlL5YGowyfItLly2k0Jj0IOICRxWrB378b7qMeimE8KlH1UNaVpRTTi0XIYjIKAOpTlBmkM9a/3Rl4NWy8pLYApXD+WCkYxPcxoAAgaN8osqGTCJ5r+VHFU7Wm9xqq3MZmnfo0bzInF4UajCKjJAQa+HNuh95DWIYP/wV77/PxkEakOtzkbJMlFJiK/hMJ+HQUvTbtKW2s+t4uDK8DI16Rotsn6e0hS8xuXPmVte9ZzplD0fQgm2qwIDAQAB นี่คือคีย์สาธารณะของผู้ให้บริการอีเมล ( ) ที่เข้ารหัสด้วยรหัส base64 เราจะใช้รหัสนี้ในภายหลังเพื่อยืนยันลายเซ็นใน Noir iCloud.com วงจรนัวร์ เนื่องจากข้อจำกัดของวงจร ZK เราจึงต้องใช้การใช้งาน RSA ที่ได้รับการปรับให้เหมาะสมโดยเฉพาะและตัวเลขขนาดใหญ่ที่จัดทำโดยทีม Noir: https://github.com/noir-lang/noir_rsa ติดตั้งใน Nargo.toml: [dependencies] noir_rsa = { tag = "v0.2", git = "https://github.com/noir-lang/noir_rsa" } ตอนนี้มาดูวงจร Noir เพื่อการตรวจสอบกัน: 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) } ฉันจะข้ามการนำเข้าและการกำหนดประเภทเพื่อความกระชับ (ใครจะสนใจล่ะ แค่คัดลอกแล้ววางมัน) มาดูพารามิเตอร์ กัน: verify_email : ส่วนหัวที่กำหนดมาตรฐานเป็นอาร์เรย์ของไบต์ มีความยาวคงที่และต้องเติม 0 ไว้ที่ท้าย headers : ความยาวจริงของส่วนหัว headers_len & : คีย์สาธารณะของผู้ให้บริการอีเมลที่เข้ารหัสโดยเฉพาะสำหรับ Noir pubkey_limbs pubkey_redc_limbs : ลายเซ็นของผู้ให้บริการอีเมลที่เข้ารหัสเฉพาะสำหรับ Noir signature ในฟังก์ชัน เราจะแปลงลายเซ็นและคีย์สาธารณะเป็นอินสแตนซ์จำนวนมากก่อน จากนั้นเราจะแฮชส่วนหัวมาตรฐานด้วย sha256 และตรวจสอบลายเซ็นด้วยคีย์สาธารณะและแฮช 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) หากการตรวจสอบสำเร็จ ฟังก์ชันจะส่งคืน หากไม่เช่นนั้นจะส่งคืน true false จากสตริงอีเมลดิบสู่อินพุตของ Noir ตอนนี้เรามีวงจร Noir แล้ว เราจะแปลงสตริงอีเมลแบบดิบให้เป็นอินพุตที่มีโครงสร้างสำหรับวงจรได้อย่างไร มาเริ่มกันที่ส่วนหัว ฉันจะใช้ เพื่อแยกวิเคราะห์อีเมลและ เพื่อแยกส่วนหัวที่เป็นมาตรฐานออกมา วิเคราะห์อีเมล: 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 }; } ตอนนี้ทดสอบมัน: 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)); เราจะได้ผลลัพธ์ดังต่อไปนี้: 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=" และแปลงเป็นไบต์: const canonicalHeadersBytes = Array.from( Buffer.from(canonicalHeaders, "utf-8"), ); console.log( "canonicalHeadersBytes:", "[", canonicalHeadersBytes.join(", "), "]", ); เอาท์พุต: 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 ] บันทึกเอาท์พุตเหล่านี้ไว้เพื่อใช้ในภายหลัง ตอนนี้มาแปลงคีย์สาธารณะและลายเซ็นของผู้ให้บริการอีเมลเป็น Noir big number limbs กัน มี เพียงไลบรารีเดียวเท่านั้น แต่คุณโชคดีเพราะฉันคอมไพล์เป็น wasm แล้วเผยแพร่ไปยัง npm มาติดตั้งกันเลย: ไลบรารี Rust อย่างเป็นทางการ npm i @shieldswap/email_account_utils_rs ยอมรับคีย์สาธารณะและลายเซ็นใน base64 และส่งคืนคีย์สาธารณะและลายเซ็นในรูปแบบ 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(",") + "]"); ตอนนี้เรามีข้อมูลที่จำเป็นทั้งหมดสำหรับการตรวจสอบลายเซ็นแล้ว มาเริ่มกันเลย ฉันจะคัดลอกและวางข้อมูลที่สร้างโดย JavaScript ลงในการทดสอบ Noir เพื่อความเรียบง่าย: #[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"); } และรันการทดสอบ: nargo test [zkemail] Running 1 test function [zkemail] Testing test_email_signature... ok [zkemail] 1 test passed นี่คือวิธีการตรวจสอบลายเซ็น DKIM ใน Noir แต่บัญชี zkEmail ไม่ได้มีแค่การตรวจสอบลายเซ็น DKIM เท่านั้น สุดท้ายแล้ว ใครๆ ก็สามารถส่งอีเมลและรับลายเซ็น DKIM ที่ถูกต้องได้ นอกจากนี้ เราต้องตรวจสอบว่าอีเมลถูกส่งจากที่อยู่อีเมลที่ถูกต้อง และอีเมลถูกส่งไปยังที่อยู่อีเมลที่ถูกต้อง รวมถึงตรวจสอบว่าหัวเรื่องอีเมลมีข้อมูลการโทรที่ถูกต้องหรือไม่ โปรดติดตามในตอนที่ 2 เร็วๆ นี้! ระหว่างนี้ให้ชำระเงิน: บัญชีอีเมลบน ShieldWallet: https://wallet.shieldswap.org/login/email พิสูจน์ว่าคุณได้รับอีเมลจากบุคคลที่ใช้ : Noir https://zkemail.shieldswap.org และแลกเปลี่ยนโทเค็นบางส่วนบน ShieldSwap (ทดสอบเครือข่าย โทเค็นไม่มีค่า): https://app.shieldswap.org