forked from MyNextID/eudi-zk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassert-is-equal.go
More file actions
310 lines (255 loc) · 10.4 KB
/
assert-is-equal.go
File metadata and controls
310 lines (255 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// Package assertisequal asserts equality of two byte arrays
package assertisequal
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/math/uints"
"github.com/mynextid/eudi-zk/circuits"
"github.com/mynextid/eudi-zk/zkcore"
)
// ========================================================================
// CIRCUIT CONSTANTS
// ========================================================================
// Circuit input sizes
const (
MaxBytes = 256
)
// ========================================================================
// CIRCUIT DEFINITION
// ========================================================================
// DescriptionLong is a long circuit description
const DescriptionLong = `
AssertBytesEqual defines a zero-knowledge circuit that proves two byte arrays
are equal without revealing the secret byte array.
Use case: Prove you know a secret value that matches a public value, without
revealing the secret itself.
`
// CBCircuit defines the circuit input parameters
type CBCircuit struct {
// ===== SECRET INPUTS (Private Witness) =====
// SecretBytes is the private input - the witness that we want to keep
// secret. This value is NOT revealed in the proof, but we prove we know it
SecretBytes [MaxBytes]uints.U8 `gnark:",secret"`
// ===== PUBLIC INPUTS (Visible to Verifier) =====
// PublicBytes is the public input - visible to everyone
// We prove that SecretBytes matches PublicBytes without revealing SecretBytes
PublicBytes [MaxBytes]uints.U8 `gnark:",public"`
}
// Define implements the circuit logic that will be converted into constraints
// This is the core of the zero-knowledge proof - what we're actually proving
//
// In this circuit, we prove: "I know SecretBytes such that SecretBytes ==
// PublicBytes"
func (c *CBCircuit) Define(api frontend.API) error {
// Assert that the secret bytes equal the public bytes
// This creates arithmetic constraints that enforce equality
// If the bytes don't match, proof generation will fail
zkcore.AssertIsEqualBytes(api, c.SecretBytes[:], c.PublicBytes[:])
return nil
}
// ========================================================================
// WITNESS INPUT STRUCTS
// ========================================================================
// WitnessInput holds raw inputs before conversion to circuit witness
type WitnessInput struct {
// Private inputs
SecretBytes string
// Public inputs
PublicBytes string
}
// Validate performs input validation
func (w *WitnessInput) Validate() error {
sBuf, err := base64.RawURLEncoding.DecodeString(w.SecretBytes)
if err != nil {
return fmt.Errorf("failed to decode the secret input: %v", err)
}
if len(sBuf) > MaxBytes {
return fmt.Errorf("secret input must be %d, got %d", MaxBytes, len(sBuf))
}
pBuf, err := base64.RawURLEncoding.DecodeString(w.PublicBytes)
if err != nil {
return fmt.Errorf("failed to decode the secret input: %v", err)
}
if len(pBuf) > MaxBytes {
return fmt.Errorf("secret input must be %d, got %d", MaxBytes, len(pBuf))
}
return nil
}
// CreateWitness creates a new witness
func (w *WitnessInput) CreateWitness() (frontend.Circuit, error) {
sBuf, err := base64.RawURLEncoding.DecodeString(w.SecretBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode the secret input: %v", err)
}
pBuf, err := base64.RawURLEncoding.DecodeString(w.PublicBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode the secret input: %v", err)
}
sBuf8, err := zkcore.BytesToU8ArrayWithPadding(sBuf, MaxBytes)
if err != nil {
return nil, err
}
pBuf8, err := zkcore.BytesToU8ArrayWithPadding(pBuf, MaxBytes)
if err != nil {
return nil, err
}
var sb, pb [MaxBytes]uints.U8
copy(sb[:], sBuf8)
copy(pb[:], pBuf8)
return &CBCircuit{
SecretBytes: sb,
PublicBytes: pb,
}, nil
}
// CreatePublicWitness creates only the public inputs for verification
func (w *WitnessInput) CreatePublicWitness() (frontend.Circuit, error) {
pBuf, err := base64.RawURLEncoding.DecodeString(w.PublicBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode the secret input: %v", err)
}
sBuf8, err := zkcore.BytesToU8ArrayWithPadding([]byte{}, MaxBytes)
if err != nil {
return nil, err
}
pBuf8, err := zkcore.BytesToU8ArrayWithPadding(pBuf, MaxBytes)
if err != nil {
return nil, err
}
var sb, pb [MaxBytes]uints.U8
copy(sb[:], sBuf8)
copy(pb[:], pBuf8)
return &CBCircuit{
SecretBytes: sb,
PublicBytes: pb,
}, nil
}
// ========================================================================
// API DATA MODELS - JSON structures for HTTP endpoints
// ========================================================================
// PrivateInput defines the JSON structure for private inputs sent via API
// This data is known only to the prover
type PrivateInput struct {
// Bytes contains base64url encoded bytes that remain secret
// This is the witness - the secret value we're proving we know
Bytes string `json:"bytes" description:"BASE64URL encoded byte array (secret witness)"`
}
// PublicInput defines the JSON structure for public inputs sent via API
// This data will be visible to anyone verifying the proof
type PublicInput struct {
// Bytes contains base64url encoded bytes that will be publicly visible
// Example: "SGVsbG8gV29ybGQ" for "Hello World"
Bytes string `json:"bytes" description:"BASE64URL encoded byte array (public)"`
}
// Constraints defines the circuit constraints
// cnf size constraint applies to the marshalled cnf JSON object
var Constraints = map[string]circuits.Constraints{
"bytes": {
Max: MaxBytes,
},
}
// ========================================================================
// PROVE ENDPOINT - POST /prove
// ========================================================================
// ProveRequest defines the request body for generating a zero-knowledge proof
// The prover sends both public and private inputs to generate the proof
type ProveRequest struct {
Public PublicInput `json:"public" description:"Public ZK circuit inputs (visible to all)"`
Private PrivateInput `json:"private" description:"Private ZK circuit inputs (secret witness)"`
}
// ProveResponse defines the response body containing the generated proof
type ProveResponse struct {
// Proof is the base64url encoded zero-knowledge proof
// This can be sent to anyone for verification without revealing the private input
Proof string `json:"proof" description:"BASE64URL encoded ZK proof"`
}
// ========================================================================
// VERIFY ENDPOINT - POST /verify
// ========================================================================
// VerifyRequest defines the request body for verifying a zero-knowledge proof
// The verifier only needs the public inputs and the proof (no private data)
type VerifyRequest struct {
Public PublicInput `json:"public" description:"Public ZK circuit inputs (must match prove request)"`
Proof string `json:"proof" description:"BASE64URL encoded ZK proof to verify"`
}
// VerifyResponse defines the response body from proof verification
type VerifyResponse struct {
// Success indicates if the verification process completed (not if proof is valid)
Success string `json:"success"`
// Valid indicates if the proof is mathematically valid
// true = the proof is correct, false = the proof is invalid or incorrect
Valid bool `json:"valid"`
// Message contains optional error or status information
Message *string `json:"message,omitempty"`
}
// ========================================================================
// INPUT PARSER - Converts API JSON to circuit format
// ========================================================================
// API implements the InputParser interface for this circuit
// It bridges the gap between HTTP JSON API and gnark circuit inputs
type API struct{}
// Parse converts JSON-encoded API inputs into a populated circuit instance
// This is called by the framework before proof generation
//
// Flow: JSON (API) → Decode base64url → Convert to U8 arrays → Circuit struct
func (api *API) Parse(publicInputJSON, privateInputJSON []byte) (frontend.Circuit, error) {
// Step 1: Parse JSON into Go structs
var publicInput PublicInput
var privateInput PrivateInput
// Unmarshal public input JSON
if err := json.Unmarshal(publicInputJSON, &publicInput); err != nil {
return nil, fmt.Errorf("failed to parse public input JSON: %w", err)
}
if privateInputJSON == nil {
w := WitnessInput{
PublicBytes: publicInput.Bytes,
}
return w.CreatePublicWitness()
}
// Unmarshal private input JSON
if err := json.Unmarshal(privateInputJSON, &privateInput); err != nil {
return nil, fmt.Errorf("failed to parse private input JSON: %w", err)
}
w := WitnessInput{
SecretBytes: privateInput.Bytes,
PublicBytes: publicInput.Bytes,
}
return w.CreateWitness()
}
// ========================================================================
// CIRCUIT REGISTRATION - Metadata for the framework
// ========================================================================
// Info contains all metadata needed to register this circuit with the
// framework
// This enables automatic API endpoint generation
var Info = &circuits.CircuitInfo{
// Name is the circuit identifier used in API routes
// e.g., GET /circuits/assert-is-equal, POST /prove/assert-is-equal
Name: "assert-is-equal",
// Description explains what this circuit proves in plain language
Description: "Proves equality between a secret byte array and a public byte array without revealing the secret. Both inputs are decoded from base64url before comparison.",
LongDescription: DescriptionLong,
// Version enables API versioning and backward compatibility
Version: 1,
// Circuit is a template instance with properly sized arrays
// BYTE_SIZE64 indicates this circuit handles up to 64 bytes
Circuit: &CBCircuit{
SecretBytes: [MaxBytes]uints.U8{},
PublicBytes: [MaxBytes]uints.U8{},
},
// InputParser converts JSON API requests into circuit inputs
InputParser: &API{},
// EndpointInfo defines API documentation for auto-generated docs
EndpointInfo: &circuits.EndpointInfo{
Constraints: Constraints,
Prove: circuits.Endpoints{
Request: circuits.CreateSchemaInfo("application/json", ProveRequest{}, nil),
Response: circuits.CreateSchemaInfo("application/json", ProveResponse{}, nil),
},
Verify: circuits.Endpoints{
Request: circuits.CreateSchemaInfo("application/json", VerifyRequest{}, nil),
Response: circuits.CreateSchemaInfo("application/json", VerifyResponse{}, nil),
},
},
}