Groth16 एल्गोरिथम एक prover को एक trusted setup में प्राप्त elliptic curve points पर एक quadratic arithmetic program की गणना करने में सक्षम बनाता है, जिसे एक verifier द्वारा जल्दी से चेक किया जा सकता है। यह जाली (forged) proofs को रोकने के लिए trusted setup से auxiliary elliptic curve points का उपयोग करता है।
हम elliptic curve group से संबंधित एक elliptic curve point को के रूप में और elliptic curve group से संबंधित एक elliptic curve point को के रूप में संदर्भित करते हैं। और के बीच एक pairing को के रूप में दर्शाया जाता है और यह में एक element उत्पन्न करता है। बोल्ड में दिए गए variables जैसे कि वेक्टर्स (vectors) हैं, अपर केस बोल्ड अक्षर जैसे कि मैट्रिसेस (matrices) हैं, और फील्ड एलिमेंट्स (field elements) (जिन्हें कभी-कभी अनौपचारिक रूप से “scalars” कहा जाता है) लोअर केस अक्षर हैं जैसे कि । सभी अंकगणितीय संचालन (arithmetic operations) एक finite field में होते हैं जिसकी characteristic elliptic curve group के order के बराबर होती है।
एक Arithmetic Circuit (ZK Circuit) दिए जाने पर, हम इसे एक Rank 1 Constraint System (R1CS) में परिवर्तित करते हैं, जिसमें मैट्रिसेस का आयाम (dimension) पंक्तियाँ (rows) और कॉलम (columns) होता है और एक witness vector होता है। फिर, हम मैट्रिसेस के कॉलम को मानों पर मानों के रूप में इंटरपोलेट (interpolate) करके R1CS को Quadratic Arithmetic Program (QAP) में बदल सकते हैं। चूँकि , , और में कॉलम हैं, इसलिए हमारे पास पॉलीनोमियल्स (polynomials) के तीन सेट होंगे:
इससे, हम एक Quadratic Arithmetic Program (QAP) का निर्माण कर सकते हैं:
जहाँ
और
यदि कोई तीसरा पक्ष powers of tau सेरेमनी (ceremony) के माध्यम से एक structured reference string (srs) बनाता है, तो prover QAP में sum terms ( terms) का मूल्यांकन एक छिपे हुए बिंदु पर कर सकता है। मान लें कि structured reference strings की गणना इस प्रकार की जाती है:
हम को inner product के माध्यम से एक structured reference string पर मूल्यांकित (evaluated) एक पॉलीनोमियल के रूप में संदर्भित करते हैं:
या srs के लिए:
उपरोक्त अभिव्यक्ति (expression) के लिए एक शॉर्टहैंड है, और यह एक elliptic curve point उत्पन्न करता है। इसका मतलब यह नहीं है कि prover को जानता है।
Prover निम्नलिखित गणना करके trusted setup पर अपने QAP का मूल्यांकन कर सकता है:
यदि QAP संतुलित (balanced) है, तो निम्नलिखित समीकरण लागू होता है:
उद्देश्य (Motivation)
केवल को प्रस्तुत करना यह साबित करने के लिए कोई ठोस तर्क नहीं है कि prover को जानता है जिससे कि QAP संतुलित हो।
Prover आसानी से , , मानों का आविष्कार कर सकता है जहाँ हो, और गणना कर सकता है
और उन्हें verifier के सामने elliptic curve points , , के रूप में प्रस्तुत कर सकता है।
इस प्रकार, verifier को यह पता नहीं चल पाता है कि क्या एक संतुष्ट (satisfied) QAP का परिणाम थे या नहीं।
हमें prover को ईमानदार रहने के लिए मजबूर करने की आवश्यकता है, वह भी बिना बहुत अधिक computational overhead जोड़े। इसे पूरा करने वाला पहला एल्गोरिथम “Pinocchio: Nearly Practical Verifiable Computation” था। यह इतना उपयोगी था कि ZCash ने अपने ब्लॉकचेन का पहला संस्करण इसी पर आधारित किया।
हालाँकि, Groth16 इसी काम को बहुत कम चरणों में पूरा करने में सक्षम था, और यह एल्गोरिथम आज भी व्यापक रूप से उपयोग में है, क्योंकि इसके बाद किसी भी एल्गोरिथम ने verification चरण के लिए इतना कुशल एल्गोरिथम तैयार नहीं किया है (यद्यपि अन्य एल्गोरिदम ने trusted setup को हटा दिया है या prover के लिए काम की मात्रा को काफी कम कर दिया है)।
2024 के लिए अपडेट: Cryptology में प्रकाशित एक पेपर जिसका काफी उत्साहपूर्ण शीर्षक “Polymath: Groth16 is not the limit” है, एक ऐसे एल्गोरिथम को प्रदर्शित करता है जिसमें Groth16 की तुलना में कम verifier चरणों की आवश्यकता होती है। हालाँकि, इस लेख को लिखे जाने के समय तक इस एल्गोरिथम का कोई ज्ञात कार्यान्वयन (implementation) नहीं है।
जालसाजी को रोकना भाग 1: और का परिचय
एक “unsolveable” (न सुलझाया जा सकने वाला) verification फॉर्मूला
मान लीजिए कि हम अपने verification फॉर्मूले को निम्नलिखित में अपडेट करते हैं:
ध्यान दें कि हम सुविधा के लिए समूह के लिए additive notation का उपयोग कर रहे हैं।
यहाँ, , का एक element है और इसका एक अज्ञात discrete logarithm है।
अब हम यह दिखाते हैं कि के discrete logarithm को जाने बिना, एक verifier के लिए इस समीकरण का हल प्रदान करना असंभव है।
अटैक 1: A और B की जालसाजी (Forging) करना और C प्राप्त करना
मान लीजिए कि prover और उत्पन्न करने के लिए बेतरतीब ढंग से (randomly) और का चयन करता है और एक ऐसा मान प्राप्त करने का प्रयास करता है जो verifier के फॉर्मूले के अनुकूल हो।
और के discrete logarithms को जानते हुए, दुर्भावनापूर्ण (malicious) prover निम्नलिखित करके को हल करने का प्रयास करता है:
अंतिम पंक्ति में prover को के discrete log के लिए हल करने की आवश्यकता होती है, इसलिए वे के लिए एक वैध (valid) discrete log की गणना नहीं कर सकते हैं।
अटैक 2: C की जालसाजी (Forging) करना और A और B प्राप्त करना
यहाँ prover एक यादृच्छिक (random) बिंदु चुनता है और की गणना करता है। चूँकि वे को जानते हैं, इसलिए वे और के एक ऐसे अनुकूल संयोजन को खोजने का प्रयास कर सकते हैं जहाँ:
इसके लिए prover को, दिए जाने पर, एक ऐसा और खोजना होगा जो पेयर (pair) होने पर उत्पन्न करे।
Discrete log समस्या के समान, हम अप्रमाणित क्रिप्टोग्राफ़िक मान्यताओं (cryptographic assumptions) पर भरोसा करते हैं कि यह गणना ( में एक element को और element में डिकम्पोज़ (decompose) करना) अव्यावहारिक (infeasible) है। इस मामले में, यह मान्यता कि हम को और में डिकम्पोज़ नहीं कर सकते, Bilinear Diffie-Hellman Assumption कहलाती है। इच्छुक पाठक Decisional Diffie-Hellman Assumption पर एक संबंधित चर्चा देख सकते हैं।
(अप्रमाणित का अर्थ अविश्वसनीय नहीं है। यदि आप इस मान्यता को सिद्ध करने या गलत साबित करने का कोई तरीका खोज सकते हैं, तो नाम और पैसा आपका इंतजार कर रहा है! व्यवहार में, को और में डिकम्पोज़ करने का कोई ज्ञात तरीका नहीं है और इसे कम्प्यूटेशनल रूप से अव्यावहारिक (computationally infeasible) माना जाता है।)
और का उपयोग कैसे किया जाता है
व्यवहार में, Groth16 टर्म का उपयोग नहीं करता है। इसके बजाय, trusted setup दो यादृच्छिक (random) scalars और उत्पन्न करता है और निम्नलिखित रूप में गणना किए गए elliptic curve points प्रकाशित करता है:
जिसे हमने के रूप में संदर्भित किया था, वह वास्तव में है।
Proving और verification फ़ॉर्मूलों को फिर से प्राप्त करना (Re-deriving)
Verification फॉर्मूले को “सुलझाने योग्य (solvable)” बनाने के लिए, हमें और को शामिल करने के लिए अपने QAP फॉर्मूले को बदलना होगा।
अब विचार करें कि क्या होगा यदि हम समीकरण के बाईं ओर और टर्म्स को जोड़ दें:
हम मूल QAP परिभाषा का उपयोग करके सबसे दाएँ टर्म्स को प्रतिस्थापित (substitute) कर सकते हैं:
अब हम निम्नलिखित परिभाषा के साथ एक “विस्तारित (expanded)” QAP पेश कर सकते हैं:
हम आगे क्या करने जा रहे हैं, इसकी एक झलक के रूप में: यदि हम को से और को से बदल दें, तो हमें पहले का अपडेटेड verification फॉर्मूला मिलता है:
जहाँ
Prover , , या को जाने बिना और की गणना कर सकता है। Structured reference string (powers of ) और elliptic curve points दिए जाने पर, prover और की गणना इस प्रकार करता है:
यहाँ, का मतलब यह नहीं है कि prover को जानता है। Prover के लिए की गणना करने हेतु structure reference string का उपयोग कर रहा है, और के लिए srs का उपयोग कर रहा है।
हालाँकि, वर्तमान में और को जाने बिना की गणना करना संभव नहीं है। Prover को के साथ और को के साथ पेयर (pair) नहीं कर सकता क्योंकि इससे एक बिंदु बन जाएगा, जबकि prover को के लिए एक बिंदु की आवश्यकता होती है।
इसके बजाय, trusted setup को विस्तारित QAP के समस्याग्रस्त टर्म के लिए पॉलीनोमियल्स की पूर्व-गणना (precompute) करने की आवश्यकता होती है।
कुछ बीजगणितीय हेरफेर (algebraic manipulation) के साथ, हम sum terms को एक एकल (single) sum में जोड़ते हैं:
और को फैक्टर आउट (factor out) करते हैं:
Trusted setup ऊपर दिए गए बॉक्स वाले टर्म से पर मूल्यांकित पॉलीनोमियल्स बना सकता है, और prover sum की गणना करने के लिए उसका उपयोग कर सकता है। सटीक विवरण अगले अनुभाग में दिखाए गए हैं।
अब तक के एल्गोरिथम का सारांश
Trusted setup के चरण
ठोस रूप में, trusted setup निम्नलिखित की गणना करता है:
Trusted setup प्रकाशित करता है:
Prover के चरण
Prover गणना करता है:
ध्यान दें कि हमने “समस्याग्रस्त (problematic)” पॉलीनोमियल
(जिसमें और शामिल थे) को निम्नलिखित से बदल दिया है:
Verifier के चरण
Verifier गणना करता है:
पब्लिक इनपुट्स (Public inputs) का समर्थन करना
अब तक का verifier फॉर्मूला पब्लिक इनपुट्स का समर्थन नहीं करता है, अर्थात् witness के एक हिस्से को सार्वजनिक (public) बनाना।
परंपरानुसार, witness के सार्वजनिक हिस्से वेक्टर के पहले एलिमेंट्स होते हैं। उन एलिमेंट्स को सार्वजनिक बनाने के लिए, prover बस उन्हें उजागर (reveal) करता है:
Verifier को यह जांचने के लिए कि वे मान वास्तव में उपयोग किए गए थे, verifier को कुछ ऐसी गणनाएँ करनी होंगी जो मूल रूप से prover कर रहा था।
विशेष रूप से, prover गणना करता है:
ध्यान दें कि केवल की गणना बदली है – prover केवल और टर्म्स का उपयोग से तक करता है।
Verifier sum के पहले टर्म्स की गणना करता है:
और verification समीकरण है:
भाग 2: या के साथ पब्लिक इनपुट्स को प्राइवेट इनपुट्स से अलग करना
के लिए का दुरुपयोग करके proofs की जालसाजी करना
ऊपर दिए गए समीकरण में यह मान्यता है कि prover की गणना करने के लिए केवल से तक का उपयोग कर रहा है, लेकिन किसी बेईमान (dishonest) prover को की गणना करने के लिए से का उपयोग करने से कोई नहीं रोकता, जिससे एक जाली proof बन सकता है।
उदाहरण के लिए, यहाँ हमारा वर्तमान verification समीकरण है:
यदि हम C टर्म को अंदर से विस्तार (expand) करें, तो हमें निम्नलिखित मिलता है:
उदाहरण के लिए मान लीजिए कि और है। उस मामले में, witness का सार्वजनिक (public) हिस्सा है और निजी (private) हिस्सा है।
अंतिम समीकरण इस प्रकार होगा:
हालाँकि, prover को सार्वजनिक witness का एक वैध (valid) हिस्सा [1,2,0] बनाने और शून्य किए गए सार्वजनिक हिस्से को गणना के निजी हिस्से में ले जाने से कोई नहीं रोकता, जैसा कि नीचे दिया गया है:
ऊपर दिया गया समीकरण वैध (valid) है, लेकिन यह आवश्यक नहीं है कि witness मूल constraints को संतुष्ट करता हो।
इसलिए, हमें prover को की गणना के भाग के रूप में से का उपयोग करने से रोकना होगा।
और/या का परिचय
उपरोक्त समस्या से बचने के लिए, trusted setup एक नया scalar : या पेश करता है ताकि से तक को से से अलग करने के लिए बाध्य किया जा सके। ऐसा करने के लिए, trusted setup निजी टर्म्स (जो बनाते हैं) को से और/या सार्वजनिक टर्म्स (जो बनाते हैं, वह sum जिसकी verifier गणना करता है) को से विभाजित (divide) करता है (अर्थात् modular inverse से गुणा करता है)।
चूँकि टर्म में सन्निहित (embedded) है, इसलिए उन टर्म्स को भी से विभाजित करने की आवश्यकता है। यदि और दोनों में से किसी एक का अज्ञात discrete logarithm है, तो पहले वर्णित जालसाजी के साथ-साथ अन्य संभावित तरीकों से बचा जा सकता है। इस विधि का उपयोग Zcash के Sapling-आधारित trusted setups में किया गया था जहाँ को केवल पर छोड़ दिया जाता है और को अभी भी बाद के trusted setup चरणों में से एक यादृच्छिक (random) मान में अपडेट किया जाता है।
Trusted setup प्रकाशित करता है:
Prover के चरण पहले के समान ही हैं:
और verifier के चरणों में अब denominators को रद्द (cancel out) करने के लिए और/या द्वारा pairing शामिल है:
भाग 3: True zero knowledge लागू करना: r और s
हमारी योजना (scheme) अभी पूरी तरह से zero knowledge नहीं है। यदि कोई हमलावर हमारे witness vector का अनुमान लगाने में सक्षम है (जो कि संभव है यदि वैध (valid) इनपुट की केवल एक छोटी श्रृंखला हो, उदा. privileged addresses से गुप्त मतदान), तो वे अपने बनाए गए proof की तुलना मूल proof से करके सत्यापित कर सकते हैं कि उनका अनुमान सही है।
एक साधारण उदाहरण के रूप में, मान लें कि हमारा दावा है कि और दोनों या तो हैं या हैं। संबंधित arithmetic circuit इस प्रकार होगा:
हमलावर को यह पता लगाने के लिए केवल चार संयोजनों (combinations) का अनुमान लगाने की आवश्यकता है कि witness क्या है। अर्थात्, वे एक witness का अनुमान लगाते हैं, एक proof उत्पन्न करते हैं, और देखते हैं कि क्या उनका उत्तर मूल proof से मेल खाता है।
अनुमान लगाने से रोकने के लिए, prover को अपने proof में “salt” (यादच्छिक डेटा) मिलाना होगा, और salt को समायोजित करने के लिए verification समीकरण को संशोधित करने की आवश्यकता होगी।
Prover दो यादृच्छिक (random) field elements और को सैंपल करता है और उन्हें और में जोड़ता है ताकि witness का अनुमान न लगाया जा सके – एक हमलावर को witness और salts तथा दोनों का अनुमान लगाना होगा:
अंतिम verification फॉर्मूला प्राप्त करने के लिए, आइए अस्थायी रूप से इस बात को अनदेखा कर दें कि हम ग्रीक अक्षर वाले टर्म्स के discrete logs को नहीं जानते हैं, और verification समीकरण के बाएँ पक्ष (left-hand-side) की गणना करें:
टर्म्स का विस्तार (expanding) करने पर हमें मिलता है:
हम के लिए मूल टर्म्स का चयन कर सकते हैं:
और नए टर्म्स को दाईं ओर छोड़ते हुए, उन्हें बाईं ओर मिला सकते हैं:
हम और के संदर्भ में लिखने के लिए रेखांकित (underlined) टर्म्स को निम्नानुसार पुनर्व्यवस्थित करते हैं। हम को में भी विभाजित (split) करते हैं:
और टर्म्स को एक साथ समूहित (Group) करें:
और को फैक्टर आउट (Factor out) करें:
और को प्रतिस्थापित (Substitute) करें:
तो हमारा अंतिम समीकरण है:
अब हम इसे सार्वजनिक (public) और निजी (private) हिस्सों में विभाजित करते हैं:
हम चाहते हैं कि सार्वजनिक हिस्सा और निजी हिस्सा क्रमशः और द्वारा अलग किए जाएं:
कुछ टर्म्स के लिए रद्द (cancel) हो जाता है:
अब हम इस समीकरण को verifier और prover के हिस्सों में अलग करते हैं। बॉक्स वाले टर्म्स verifier का हिस्सा हैं, और underbrace वाले टर्म्स वे हैं जो prover प्रदान करता है:
Groth16 Proof एल्गोरिथम
अब हम शुरू से अंत तक (end-to-end) Groth16 एल्गोरिथम दिखाने के लिए तैयार हैं। Trusted setup और verification चरण पिछले उदाहरण से अपरिवर्तित रहते हैं जहाँ हमने और को शामिल किया था। और को शामिल करने के लिए केवल prover की गणना बदलती है।
Trusted Setup
Trusted setup प्रकाशित करता है:
Prover के चरण
Prover के पास एक witness है और वह यादृच्छिक (random) scalars और उत्पन्न करता है।
Prover प्रकाशित करता है।
Verifier के चरण
Verifier जांच करता है:
Solidity में Groth16 का सत्यापन (Verifying)
इस बिंदु पर, आपके पास Solidity में proof verification कोड को समझने के लिए पर्याप्त ज्ञान है। यहाँ Tornado Cash का proof verification कोड दिया गया है। पाठक को source code को ध्यान से पढ़ने के लिए प्रोत्साहित किया जाता है। यदि पाठक Solidity असेंबली प्रोग्रामिंग के साथ सहज है, तो इस source code को समझना मुश्किल नहीं होगा क्योंकि वेरिएबल नाम इस लेख में दिए गए नामों के अनुरूप हैं।
सहज रूप में (Intuitively), हमलावर और को से गुणा कर रहा है, और pairing में खुद को रद्द (cancel) कर देता है।
अतः, यदि verification फॉर्मूला इसे स्वीकार करता है:
तो यह इसे भी स्वीकार करेगा:
इस हमले से बचाव का वर्णन अगले अनुभाग में किया गया है।
आप इस लेख में इस हमले का एक proof of concept देख सकते हैं।
Prover एक ही witness के लिए असीमित संख्या में proofs बना सकता है
यह अपने आप में कोई “सुरक्षा मुद्दा” नहीं है – Zero Knowledge प्राप्त करने के लिए यह आवश्यक है। हालाँकि, एप्लिकेशन को यह ट्रैक करने के लिए एक तंत्र (mechanism) की आवश्यकता होती है कि कौन से तथ्य पहले ही साबित हो चुके हैं और इसे प्राप्त करने के लिए proof की विशिष्टता (uniqueness) पर निर्भर नहीं रहा जा सकता है।
RareSkills के साथ और जानें
इस तरह की सामग्री को निःशुल्क प्रकाशित करने की हमारी क्षमता हमारे छात्रों के निरंतर समर्थन पर निर्भर करती है। हमारे Zero Knowledge Bootcamp, Web3 Bootcamps के लिए साइन अप करने, या RareTalent पर नौकरी पाने पर विचार करें।