
क्रिप्टैनालिसिस गोल्फ: स्कीमों को तोड़ें और इसे Lean 4 में सिद्ध करें। प्रूफ-ऑफ-कॉन्सेप्ट बोर्ड।
एक बोर्ड जो क्रिप्टोविश्लेषण के परिणामों के लिए है जो सिद्ध हैं, चलाए नहीं गए।
एक चुनौती दो दुनियाओं और उस कथन को निर्धारित करती है जिसे आपको सिद्ध करना है। आप एक विरोधी और एक प्रमाण लिखते हैं कि वह उन्हें अलग करता है। जीत प्रमाण है: कुछ भी निष्पादित, नमूनाकृत, या पुनःचलाया नहीं जाता, और कोई मानव यह तय करने के लिए सबमिशन नहीं पढ़ता कि यह मायने रखता है या नहीं।
स्थिति: सत्यापनकर्ता चलता है। एक verify: issue को CI में चुनौती के पिन किए गए प्रकार के विरुद्ध विस्तारित किया जाता है और #print axioms के साथ ऑडिट किया जाता है; निर्णय एक टिप्पणी के रूप में वापस आता है। रिकॉर्ड अभी भी मैन्युअल रूप से scoring/ledger.json में जोड़े जाते हैं।
यह एक स्टैंडअलोन Lake प्रोजेक्ट है। यह गणित को एक git निर्भरता के रूप में लेता है जो एक रिवीज़न पर पिन की गई है, इसलिए एक बोर्ड मोड के एक निश्चित कथन के विरुद्ध सत्यापित करता है:
require RandomSystems from git
"https://github.com/MarcIlunga/random-systems.git" @ "08aec7a7"
बिल्ड समय एक कैश है, कोई समस्या नहीं: Mathlib के oleans अपने CDN से
lake exe cache get के माध्यम से आते हैं, और pinned-revision तथा challenge oleans का ~400 MB Actions कैश में मैनिफ़ेस्ट और हमारे अपने स्रोतों पर keyed रहता है।
| पथ | यह क्या है |
|---|---|
challenges/<name>/Challenge.lean | विश्वसनीय। उन सटीक प्रकारों को pin करता है जिन्हें एक सबमिशन को inhabit करना होता है। किसी भी अपलोड का हिस्सा नहीं। |
challenges/<name>/Cost.lean | फ़ॉर्म के नंबरों से प्रति सबमिशन प्लेटफ़ॉर्म-जनित। |
challenges/<name>/config.json | स्कोरिंग, par, अनुमत axioms, timeout। चैलेंज जोड़ने के लिए आप जो एकमात्र फ़ाइल संपादित करते हैं। |
challenges/<name>/Solve.template.lean | वह ढाँचा जिसे सबमिटर भरता है। |
tools/manifest.py | challenges/ से docs/data/manifest.json व्युत्पन्न करता है। |
tools/ledger.py | scoring/ledger.json से docs/data/ledger.json व्युत्पन्न करता है, रिकॉर्ड्स को स्कोर और सॉर्ट करता है। |
tools/record.py | एक सत्यापित सबमिशन को लेजर में जोड़ता है। proved न होने वाली किसी भी चीज़ को अस्वीकार करता है। |
tools/verify.py | एक सबमिशन सत्यापित करता है: चैलेंज id argv[1] के रूप में, बॉडी stdin पर, JSON निर्णय stdout पर। lean-golf के समान इंटरफ़ेस। |
tools/lint_challenges.py | प्रत्येक चैलेंज कॉन्फ़िग में वह सब होता है जो बोर्ड और verifier को चाहिए। |
tools/check_site.py | स्टैटिक साइट अपने स्वयं के जनित डेटा को लोड और रेंडर कर सकती है। |
scoring/ledger.json | रिकॉर्ड सेट। |
docs/ | GitHub Pages साइट। स्टैटिक; केवल दो जनित फ़ाइलों को पढ़ती है। |
examples/ | टेस्ट फ़िक्स्चर: एक सबमिशन जो सत्यापित होता है और कुछ नहीं जीतता, और उसका over-claiming जुड़वाँ। |
python3 tools/manifest.py # rewrite the manifest
python3 tools/manifest.py --check # fail if stale
python3 tools/ledger.py # rescore and sort the records
python3 tools/lint_challenges.py # configs are complete
python3 tools/check_site.py # the site can render what the tools generate
printf '%s' "$BODY" | python3 tools/verify.py spoc128 # one submission
verify.yml push और pull request पर चलता है: --check गेट्स, कॉन्फ़िग लिंट, साइट चेक, और ऐसे सबमिशन्स की एक बैटरी जिन्हें वेरिफ़ायर को स्वीकार करना चाहिए और अस्वीकार करना चाहिए — एक sorry, एक native_decide, एक अज्ञात चैलेंज, एक शून्य लाभ, एक ऐसा बॉडी जिसमें कोई Lean ब्लॉक नहीं है। workflow_dispatch बिना कोई issue खोले मांग पर एक सबमिशन का सत्यापन करता है।
submission.yml एक verify: issue को दो जॉब्स में संभालता है। पहला असुरक्षित इनपुट को पार्स करता है और कोई write स्कोप नहीं रखता; दूसरा उसका निर्णय डाउनलोड करता है और टिप्पणी पोस्ट करता है। यह विभाजन lean-golf का है और यही कारण है कि कोई issue बॉडी किसी टोकन तक नहीं पहुँच सकती।
एक सबमिशन एक ही डिक्लेरेशन होता है जो उस टाइप को इनहैबिट करता है जिसे चैलेंज पिन करता है:
-- Challenge.lean, trusted. The verifier builds its own copy.
def score : Golf.Score := ⟨budget, advNum, advDen⟩ -- from the submission form
structure Solution where
strategy : game.Param → PFunDDS.DDE game.Query game.Response
verdict : List (game.Query × Option game.Response) → Bool
wins : Golf.Attack.Wins (G := game) ⟨strategy, verdict⟩ score.budget score.advantage
-- Solve.lean, the upload.
def solution : Challenge.SpoC128.Solution where
strategy := …
verdict := …
wins := …
एक संरचना तीन ढीले नामों के बजाय, क्योंकि ढीले नाम आपस में टकराते हैं:
एक Solve.lean जो एक Challenge.lean को आयात करता है जो पहले से ही
strategy/verdict/attackWins घोषित करता है, "has already been declared" के साथ विफल हो जाता है।
सबमिशन को एक विश्वसनीय प्रकार का मान बनाने से टकराव हट जाता है और
सत्यापनकर्ता के प्रकार जाँच को एकल #check तक घटा देता है।
दो बातें पूरी होनी चाहिए, और इनमें से कोई भी भरोसे पर नहीं ली जाती।
संख्याएँ प्रस्ताव का हिस्सा हैं। प्लेटफ़ॉर्म उन्हें
Cost.lean में लिखता है, Challenge.lean उनसे score बनाता है, और score
Solution.wins के प्रकार के अंदर प्रकट होता है। कोई संख्या बदलने से यह बदल जाता है कि क्या सिद्ध करना है।
सबमिशन को हमारे प्रकार में निवास करना चाहिए। ऑडिट चरण विश्वसनीय चैलेंज को सबमिशन के साथ आयात करता है और विस्तारित करता है
example : Challenge.SpoC128.Solution := Solution.SpoC128.solution
तो जो सबमिशन अपना स्वयं का Challenge.SpoC128.Solution घोषित करता है, वह हमारे साथ टकराव करता है, और जो कोई अन्य प्रकार उपयोग करता है, वह typecheck विफल हो जाता है। इस चरण के बिना, एक सबमिशन जो चुनौती को कभी import ही नहीं करता, वह उसी नाम की एक तुच्छ संरचना परिभाषित कर सकता है और 0 बिट्स पर "सिद्ध" हो सकता है — वह छेद वास्तविक था, और दोनों हमले अब CI केस हैं।
query count को किसी अलग प्रमाण की आवश्यकता नहीं है। यह किसी तैयार adversary के बारे में दावा नहीं है; यह एक constructor argument है। Attack.distinguisher q वास्तव में DDD.ofDDE strategy q verdict है, जो निर्माण के अनुसार q पर truncated है, और Attack.silent_past सिद्ध करता है कि यह उससे आगे कुछ भी जारी नहीं करता। बजट को अधिक खर्च करने का कोई तरीका नहीं है, इसलिए जाँचने के लिए कुछ भी नहीं है।
budget, advNum और advDen सबमिशन फ़ॉर्म से आते हैं, और वे विश्वसनीय नहीं हैं। उन्हें होने की आवश्यकता भी नहीं है: प्लेटफ़ॉर्म उन्हें Cost.lean में प्रतिस्थापित करता है, Challenge.lean उनसे score बनाता है, और score Solution.wins के अंदर प्रकट होता है। इसलिए ये संख्याएँ प्रस्ताव का हिस्सा हैं, उसके बारे में कोई दावा नहीं।
वास्तविक SpoC-128 हमले के विरुद्ध जाँचा गया:
| दावा | परिणाम |
|---|---|
budget = 3, advantage 1 | संकलित होता है — ईमानदार दावा |
budget = 1, advantage 1 | विफल — Wins 1 1 एक भिन्न, अधिक मज़बूत प्रमेय है |
budget = 3, advantage 2 | विफल — कोई भी advantage 1 से अधिक नहीं हो सकता |
और कम बताने का कोई प्रोत्साहन नहीं है: score = log₂(budget / advantage^e), इसलिए छोटा दावा किया गया advantage या बड़ा दावा किया गया budget score बढ़ाता है। दोनों दिशाएँ बदतर हैं। एकमात्र उपाय वह सबसे सटीक दावा है जिसे आप वास्तव में सिद्ध कर सकते हैं।
चार और अस्वीकृतियाँ जो verifier बिना कुछ पढ़े कर देता है:
| धोखा | निर्णय |
|---|---|
| दावे को कमज़ोर करना | pinned Solution के विरुद्ध type mismatch |
| एक सुविधाजनक hypothesis जोड़ना | वही |
प्रमाण में sorry | sorryAx permitted_axioms में नहीं है |
native_decide / बढ़ाया गया maxHeartbeats | सीधे अस्वीकृत |
पहले से कुछ भी निर्धारित नहीं है। ऐसी योजना पर जिसका किसी ने अध्ययन नहीं किया, किस लागत पर कौन-सा advantage पहुँच योग्य है, यही शोध प्रश्न है, इसलिए कोई भी लक्ष्य एक अनुमान है — और बहुत ऊँचा निर्धारित लक्ष्य एक वास्तविक 2⁻³⁰ distinguisher को शून्य के रूप में स्कोर करता है। इसके बजाय बोर्ड परिणाम को मापता है:
score = log₂( budget / advantage^e )
प्रति इकाई लाभ के लिए क्वेरीज़; इसका लॉग आधार दो वह सुरक्षा स्तर बिट्स में है जिसे हमला खंडित करता है। कम होना बेहतर है। अपने लाभ को कम आँकना स्कोर को बढ़ाता है, इसलिए जितना साबित कर सकते हैं उससे कम दावा करने में कुछ भी पाने को नहीं है।