Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
mac_circuit.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_CIRCUIT_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_CIRCUIT_H_
17
18// Implements a message authentication code in GF 2^k for a 256-bit message.
19// The mac key is additively sampled by both the prover and verifier to ensure
20// soundness and zk.
21//
22// The mac is defined as (a_pi+a_v)*x_i = mac_i where (x_1,x_2) are 128-bit
23// portions of the hidden message. The verifier need only contribute one
24// a_v for all MACs verified in the circuit. The prover needs to commit to
25// separate a_p{i} for each portion of each message.
26//
27// The property that we need from this primitive is as follows:
28// Assume the prover has committed to a_pi and x, i.e., fix a_pi, x.
29// The probability over the verifier's random a_v that mac(x) = mac_(y)
30// if x != y is at most 2^{-128}.
31
32#include <algorithm>
33#include <cstddef>
34
35#include "circuits/compiler/compiler.h"
36#include "circuits/logic/logic.h"
37#include "gf2k/gf2_128.h"
38
39namespace proofs {
40
41static constexpr size_t kMACPluckerBits = 2u;
42
43// MAC: implements a MAC in GF 2^k for a 256-bit message by simulating
44// the arithmetic of the GF 2^k field. This implementation commits both
45// the prover's a_p key as well as the bits of the message. This allows
46// the MAC computation and the equality of the purported message to be verified
47// in parallel to reduce depth.
48template <class Logic, class BitPlucker>
49class MAC {
50 public:
51 using Field = typename Logic::Field;
52 using Elt = typename Field::Elt;
53 using EltW = typename Logic::EltW;
54 using Nat = typename Field::N;
55 using v8 = typename Logic::v8;
56 using v128 = typename Logic::v128;
57 using v256 = typename Logic::v256;
58 using packed_v128 = typename BitPlucker::packed_v128;
59 using packed_v256 = typename BitPlucker::packed_v256;
60
61 BitPlucker bp_;
62
63 class Witness {
64 public:
65 packed_v128 aa_[2];
66 packed_v256 xx_; // The value to be checked
67
68 template <typename T>
69 static T packed_input(QuadCircuit<typename Logic::Field>& Q) {
70 T r;
71 for (size_t i = 0; i < r.size(); ++i) {
72 r[i] = Q.input();
73 }
74 return r;
75 }
76
77 void input(const Logic& LC, QuadCircuit<typename Logic::Field>& Q) {
78 aa_[0] = packed_input<packed_v128>(Q);
79 aa_[1] = packed_input<packed_v128>(Q);
80 xx_ = packed_input<packed_v256>(Q);
81 }
82 };
83
84 explicit MAC(const Logic& lc) : bp_(lc), lc_(lc) {}
85
86 // Verifies a mac on the Field element value against the key (a_p + a_v).
87 // This method can only be called when the field is at least 256 bits, e.g.,
88 // with F_p256. In other cases, the caller should use a verify_mac method
89 // that takes the message in bit-wise form. Additionally, the order parameter
90 // is used to ensure that the message does not overflow the field.
91 void verify_mac(EltW msg, const v128 mac[/*2*/], const v128& av,
92 const Witness& vw, Nat order) const {
93 check(Field::kBits >= 256, "Field::kBits < 256");
94 v128 msg2[2];
95 unpack_msg(msg2, msg, order, vw);
96 assert_mac(mac, av, msg2, vw);
97 }
98
99 private:
100 // Checks mac[i] = (a_p + a_v)*xi[i] for i=0..1.
101 void assert_mac(const v128 mac[/*2*/], const v128& av, const v128 xi[/*2*/],
102 const Witness& vw) const {
103 v128 mv;
104 for (size_t i = 0; i < 2; ++i) {
105 v128 ap = bp_.template unpack<v128, packed_v128>(vw.aa_[i]);
106 v128 key = lc_.vxor(&av, ap);
107 lc_.gf2_128_mul(mv, key, xi[i]);
108 lc_.vassert_eq(&mac[i], mv);
109 }
110 }
111
112 void unpack_msg(v128 msg[/*2*/], EltW msgw, Nat order,
113 const Witness& vw) const {
114 v256 x = bp_.template unpack<v256, packed_v256>(vw.xx_);
115 std::copy(x.begin(), x.begin() + 128, msg[0].begin());
116 std::copy(x.begin() + 128, x.end(), msg[1].begin());
117
118 // Ensure that the incoming message does not overflow the field.
119 v256 bits_n;
120 for (size_t i = 0; i < 256; ++i) {
121 bits_n[i] = lc_.bit(order.bit(i));
122 }
123 lc_.assert1(lc_.vlt(&x, bits_n));
124
125 // Verify that the message bits in the witness correspond to msg.
126 EltW te = lc_.konst(lc_.zero());
127 Elt twok = lc_.one();
128 for (size_t i = 0; i < 256; ++i) {
129 te = lc_.axpy(&te, twok, lc_.eval(x[i]));
130 lc_.f_.add(twok, twok);
131 }
132 lc_.assert_eq(&te, msgw);
133 }
134
135 const Logic& lc_;
136};
137
138// Same MAC computation for native GF2_128 field.
139template <class Backend, class BitPlucker>
140class MACGF2 {
141 public:
142 using Elt = typename Logic<GF2_128<>, Backend>::Elt;
143 using EltW = typename Logic<GF2_128<>, Backend>::EltW;
144 using BitW = typename Logic<GF2_128<>, Backend>::BitW;
145
146 // In this specialization, 128 bits are stored in a native EltW.
147 using v128 = EltW;
148
149 // Message input types v8, v256 are still encoded bit-wise.
150 using v8 = typename Logic<GF2_128<>, Backend>::v8;
151 using v256 = typename Logic<GF2_128<>, Backend>::v256;
152
153 explicit MACGF2(const Logic<GF2_128<>, Backend>& lc)
154 : lc_(lc) {}
155 class Witness {
156 public:
157 EltW aa_[2];
158
159 void input(const Logic<GF2_128<>, Backend>& lc,
161 aa_[0] = Q.input();
162 aa_[1] = Q.input();
163 }
164 };
165
166 // Verify a mac on the 256-bit message msg.
167 void verify_mac(const EltW mac[/*2*/], const EltW& av, const v256& msg,
168 const Witness& vw) const {
169 // Check that mac[i] = (a_p + a_v)*mm[i] for i=0..1.
170 for (size_t i = 0; i < 2; ++i) {
171 EltW mm = pack(&msg[i * 128]);
172 EltW key = lc_.add(&av, vw.aa_[i]);
173 EltW got = lc_.mul(&key, mm);
174 lc_.assert_eq(&mac[i], got);
175 }
176 }
177
178 private:
179 // Pack a 128-bit message into a GF(2^128) field element.
180 EltW pack(const BitW msg[/*128*/]) const {
181 Elt alpha = lc_.f_.x();
182 Elt xi = lc_.f_.one();
183 EltW m = lc_.konst(0);
184 for (size_t i = 0; i < 128; ++i) {
185 m = lc_.axpy(&m, xi, lc_.eval(msg[i]));
186 xi = lc_.mulf(xi, alpha);
187 }
188 return m;
189 }
190
191 const Logic<GF2_128<>, Backend>& lc_;
192};
193
194} // namespace proofs
195
196#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_CIRCUIT_H_
Definition bit_plucker.h:86
Definition gf2_128.h:35
Definition logic.h:38
Definition mac_circuit.h:155
Definition mac_circuit.h:63
Definition nat.h:60
Definition compiler.h:50
Definition gf2_128.h:63