Niftyzk tutorials 3 – Edwards-curve Digital Signatures

Niftyzk از EdDSA که یک طرح امضای دیجیتال رمزنگاری عمومی است پشتیبانی می کند. این آموزش حاوی اطلاعاتی در مورد کد تولید شده است و برای درک همه چیز باید آموزش های قبلی را بخوانید.
بیایید با شروع کنیم niftyzk init
Setting up your current directory
? What project do you want to scaffold? EdDSA signature verification
? Choose the hashing algorithm to use: mimc7
? Do you wish to add public inputs to sign? (E.g: address,amount) yes
? Enter the name of the public inputs in a comma separated list (no numbers or special characters): address,amount
Generating circuits
Generating javascript
Done
بنابراین ما EdDSA را با mimc7 انتخاب کردیم و تعدادی ورودی به علامت، آدرس، مقدار اضافه کردیم
استفاده از هش MiMC7 به این معنی است که باید از کلیدی برای ایجاد هش استفاده کنیم که مخفی نگه داشته می شود. اما برای امضای EdDSA با استفاده از MiMC، از کلید استفاده نمی شود. این تصمیم برای عدم استفاده از کلید برای امضا توسط توسعه دهندگان circomlib گرفته شد، زیرا استفاده از کلید پیش فرض مشکلی نیست، داده های امضا شده یک هش عمومی است.
بیایید مدارهای تولید شده را ببینیم، می بینید که یک وجود دارد circuit.circom
فایل
pragma circom 2.0.0;
include "../node_modules/circomlib/circuits/eddsamimc.circom";
include "../node_modules/circomlib/circuits/mimc.circom";
template VerifySignature(){
signal input message;
signal input address;
signal input amount;
signal input k;
signal input Ax;
signal input Ay;
signal input S;
signal input R8x;
signal input R8y;
component eddsa = EdDSAMiMCVerifier();
component mimc7Hash = MultiMiMC7(3, 91);
mimc7Hash.k <== k;
mimc7Hash.in[0] <== message;
mimc7Hash.in[1] <== address;
mimc7Hash.in[2] <== amount;
eddsa.enabled <== 1;
eddsa.Ax <== Ax;
eddsa.Ay <== Ay;
eddsa.S <== S;
eddsa.R8x <== R8x;
eddsa.R8y <== R8y;
eddsa.M <== mimc7Hash.out;
}
component main {public [message,address,amount]} = VerifySignature();
بنابراین وابستگی های مورد نیاز را وارد می کنیم، از الگوریتم هش یکسانی برای امضاها و هش پیام استفاده می کنیم.
بنابراین ما یک message
ورودی، address
و amount
که اضافی بودند و این ورودیهای عمومی ما هستند که برای تأیید در زنجیره آشکار میشوند.
k
راز مورد استفاده برای هش کردن mimc است
Ax
،Ay
نقاطی هستند که برای کلید عمومی استفاده می شوند، S
، R8x
،R8y
پارامترهای امضا هستند که توسط تابع امضا برگردانده می شوند.
ما یک هش MiMC7 را با استفاده از آن محاسبه می کنیم k
و سپس فقط ورودی ها را به eddsa
سیگنال های ورودی الگو با مشخص کردن eddsa.enabled <== 1;
ما ادعا می کنیم که امضا باید برای هش معتبر باشد. حال اجازه دهید به برخی از جاوا اسکریپت نگاه کنیم
حساب ها با استفاده از EdDSA ایجاد می شوند:
/**
* The signature parameters used for verifying pedersen hash signed EdDSA
* @typedef {Object} Account
* @property {Buffer} prvKey - Private key buffer
* @property {Uint8Array[]} pubKey - Public key is a tuple of 32 bit Uint8Array
*/
/**
* @param {any} eddsa - The EdDSA object
* @returns {Account}
* Generate a new account which composes of a private and public key
*/
export function generateAccount(eddsa) {
const prvKey = rbytes();
const pubKey = eddsa.prv2pub(prvKey);
return {
prvKey,
pubKey
}
}
ما از 32 بایت برای حساب کاربری استفاده می کنیم rbytes()
یک بافر تصادفی برمی گرداند. استفاده می کند crypto.randomBytes(32)
کلیدهای عمومی یک سری Uint8Array هستند که هر کدام 32 بیت اندازه دارند.
/**
*
* @param {Array} pubKey - Used for computing an address
* @param {bigin | number} key -The key used for the mimc7 hash
* @returns
*/
export async function getAddressFromPubkey(pubKey, key) {
return mimc7(pubKey, key);
}
ما “آدرس ها” را از کلید عمومی با هش کردن آن محاسبه می کنیم. این آدرسها آدرسهای بلاک چین معتبر نیستند اما برای انتزاع حساب قابل استفاده هستند. شما می توانید آدرس ها را از یک کلید عمومی با استفاده از طرح ها و مسیرهای مشتق مختلف استخراج کنید. این به شما بستگی دارد که چه چیزی را انتخاب می کنید.
/**
* @typedef {Object} Message
* @property {string | bigint} message
* @property {string | bigint} address
* @property {string | bigint} amount
*/
/**
*
* @param {Message} data - The data content of the message. The hash of the data object will be signed
* @param key {number | bigint} - The key used for the mimc7 hash
* @returns {bigint} Returns a mimc7 hash
*/
export async function computeMessageHash(data, key) {
return await mimc7(
[
BigInt(data.message),
BigInt(data.address),
BigInt(data.amount)
], key)
}
هش پیام با استفاده از mimc7 محاسبه میشود، این همان هشسازی است که در داخل مدار برای تأیید انجام میشود.
/**
* @param {any} eddsa - the built EDDSA
* @param {bigint} messagehash - The poseidon hash of the message
* @param {Buffer} prvKey - The private key used to sign the message
* @returns {Signature} signature
*/
export function signMessage(eddsa, messageHash, prvKey) {
const signature = eddsa.signMiMC(prvKey, eddsa.F.e(messageHash));
const pubKey = eddsa.prv2pub(prvKey);
assert(eddsa.verifyMiMC(eddsa.F.e(messageHash), signature, pubKey))
return {
signature,
pubKey
}
}
/**
* @typedef {Object} Signature
* @property {Uint8Array[]} R8
* @property {bigint} S
* /
امضای پیام از تابع فوق استفاده می کند. شما یک شی eddsa، هش پیام محاسبه شده و کلید خصوصی ایجاد شده برای حساب شما را ارسال می کنید. می توانید نوع امضای تعریف شده با JsDoc را ببینید.
/**
* @typedef {Object} SignatureParameters
* @property {bigint} Ax
* @property {bigint} Ay
* @property {bigint} R8x
* @property {bigint} R8y
* @property {bigint} S
*/
/**
* @param {any} eddsa
* @param {Uint8Array[]} pubKey - The public key of the account
* @param {Signature} signature - The signature of the signed message
* @returns {SignatureParameters} - The signature parameters are prepared parameters, ready to use for the circuit
*/
export function getSignatureParameters(eddsa, pubKey, signature) {
return {
Ax: eddsa.F.toObject(pubKey[0]),
Ay: eddsa.F.toObject(pubKey[1]),
R8x: eddsa.F.toObject(signature.R8[0]),
R8y: eddsa.F.toObject(signature.R8[1]),
S: signature.S
}
}
حال برای استفاده از این امضا در circom، باید آن را تبدیل کنیم. بنابراین پارامترهای امضا را برای تأیید با تابع بالا دریافت می کنیم. /test/input.js
/**
* This is a test input, generated for the starting circuit.
* If you update the inputs, you need to update this function to match it.
*/
export async function getInput(){
await buildHashImplementation()
const eddsa = await getEDDSA();
const account = generateAccount(eddsa);
const key = 313; // just an example key for tests
const message = rbigint();
const address = rbigint()
const amount = rbigint()
const messageHash = await computeMessageHash({message,address,amount}, key)
const signedMessage = signMessage(eddsa, messageHash, account.prvKey);
const signatureParameters = getSignatureParameters(eddsa, account.pubKey, signedMessage.signature)
return {
Ax: signatureParameters.Ax,
Ay: signatureParameters.Ay,
S: signatureParameters.S,
R8x: signatureParameters.R8x,
R8y: signatureParameters.R8y,
k: key,
address,
amount,
message
}
}
و در بالا می توانید ببینید که چگونه ورودی مدارها را محاسبه می کنیم. برای بارگذاری مجدد داغ و تست ها استفاده می شود
اکنون به شما نشان خواهم داد که چگونه درختان مرکل با EdDSA کار می کنند
Setting up your current directory
? What project do you want to scaffold? EdDSA signature verification with Fixed Merkle Tree
? Choose the hashing algorithm to use: poseidon
? Do you wish to add public inputs to sign? (E.g: address,amount) no
بنابراین با استفاده از پروژه بازسازی شد niftyzk init
و پارامترهای مختلف را انتخاب کرد. و این همان است circuit.circom
اکنون فایل کنید:
pragma circom 2.0.0;
include "../node_modules/circomlib/circuits/eddsaposeidon.circom";
include "../node_modules/circomlib/circuits/poseidon.circom";
include "./merkletree.circom";
template VerifySignature(levels){
signal input message;
signal input root;
signal input pathIndices[levels];
signal input pathElements[levels];
//The parameters for the signature
//The Ax and Ay parameters are the public key, Ax = pubKey[0], Ay = pubKey[1]
signal input Ax;
signal input Ay;
signal input S;
signal input R8x;
signal input R8y;
component eddsa = EdDSAPoseidonVerifier();
component poseidon = Poseidon(1);
poseidon.inputs[0] <== message;
// Verify the signature on the message hash
eddsa.enabled <== 1;
eddsa.Ax <== Ax;
eddsa.Ay <== Ay;
eddsa.S <== S;
eddsa.R8x <== R8x;
eddsa.R8y <== R8y;
eddsa.M <== poseidon.out;
//We compute a public key by hashing Ax and Ay,
// this will be later used with the merkle tree
component pubKeyHasher = Poseidon(2);
pubKeyHasher.inputs[0] <== Ax;
pubKeyHasher.inputs[1] <== Ay;
//Verify the merkle tree
component tree = MerkleTreeChecker(levels);
for (var i = 0; i < levels; i++) {
tree.pathElements[i] <== pathElements[i];
tree.pathIndices[i] <== pathIndices[i];
}
tree.root <== root;
tree.leaf <== pubKeyHasher.out;
}
component main {public [message,root]} = VerifySignature(20);
بنابراین می توانید ببینید که تفاوت هایی وجود دارد. من این بار پارامترهای اضافی بیشتری برای امضا اضافه نکردم، بنابراین فقط دو ورودی عمومی وجود دارد، message
و root
.
بنابراین root
ریشه درخت مرکل است که می خواهیم بررسی کنیم که کلید عمومی که این پیام را امضا کرده است در داخل درخت وجود دارد. این یک روش کنترل دسترسی است، بنابراین ما می دانیم که امضا معتبر است و کلید عمومی امضاکننده مجاز است تأیید را انجام دهد زیرا کلید او در داخل درخت است.
آدرس را با استفاده از pubKeyHasher
از Axe و Ay، می توانید ببینید که دقیقاً همان کد جاوا اسکریپت را که قبلاً نشان دادم انجام می دهد. را MerkleTreeChecker
صحت را ادعا می کند و اگر برگ در ریشه نباشد شکست خواهد خورد.
pathIndices
و pathElements
اثبات merkle هستند. می توانید مدار درخت مرکل را در فایل کاوش کنید merkletree.circom
در آموزش قبلی توضیح دادم.
بنابراین برای ایجاد دستی درختان مرکل با استفاده از cli، می توانیم استفاده کنیم npm run new
که آدرس ها را تولید می کند و درختی را با استفاده از آنها به عنوان برگ ایجاد می کند.
CREATING A NEW MERKLE TREE
Enter how many accounts would you like to generate:
4
Generating accounts and addresses. Please wait!
Done
Generating merkle tree from addresses!
Done.Root is 21804813178957116268623645093306988559564490743632413729059072914509024611553
Serializing data.
Writing to file.
Done
کلیدهای خصوصی برای حساب های مربوطه در قسمت قرار می گیرند private
دایرکتوری در حالی که بقیه در قسمت قرار می گیرند public
دایرکتوری اگر شما انتخاب کردید MiMC7
یا MiMCSponge
سپس پارامتر کلید در dir خصوصی ذخیره می شود و برای تأیید مورد نیاز است، در غیر این صورت نیازی به استفاده از جزئیات خصوصی برای ایجاد یک اثبات merkle و تأیید آن ندارید.
پس بیایید فرار کنیم npm run proof
و ریشه و نشانی می خواهد و برهان مرکل را بیرون می اندازد
سپس npm run verify
یک ریشه و اثبات می خواهد و اگر همه چیز به درستی اجرا شود، اثبات معتبر خواهد بود.
میتوانید از EdDSA برای هویت، جمعبندی، مقیاسبندی، رأیگیری یا انتزاع حساب استفاده کنید. بستگی به خودت داره 🙂