Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
jwt.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_JWT_JWT_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_H_
17
18#include <cstddef>
19#include <cstdint>
20#include <vector>
21
22#include "circuits/base64/decode.h"
23#include "circuits/compiler/compiler.h"
24#include "circuits/ecdsa/verify_circuit.h"
25#include "circuits/jwt/jwt_constants.h"
26#include "circuits/logic/bit_plucker.h"
27#include "circuits/logic/routing.h"
28#include "circuits/sha/flatsha256_circuit.h"
29
30namespace proofs {
31
32template <class LogicCircuit, class Field, class EC>
33class JWT {
34 using EltW = typename LogicCircuit::EltW;
35 using BitW = typename LogicCircuit::BitW;
36 using Nat = typename Field::N;
38 using EcdsaWitness = typename Ecdsa::Witness;
39 using v8 = typename LogicCircuit::v8;
40 using v256 = typename LogicCircuit::v256;
41 using Flatsha =
42 FlatSHA256Circuit<LogicCircuit,
44 using ShaBlockWitness = typename Flatsha::BlockWitness;
45 using sha_packed_v32 = typename Flatsha::packed_v32;
46 using vind = typename LogicCircuit::template bitvec<kJWTIndexBits>;
47
48 public:
50 v8 attr[32]; /* 32b representing attribute name in be. */
51 v8 v1[64]; /* 64b of attribute value */
52 };
53
54 class Witness {
55 public:
56 EltW r_, s_, e_;
57 EcdsaWitness jwt_sig_;
58 v8 preimage_[64 * kMaxJWTSHABlocks];
59 v256 e_bits_;
60 ShaBlockWitness sha_[kMaxJWTSHABlocks];
61 v8 nb_; /* index of sha block that contains the real hash */
62 std::vector<vind> attr_ind_;
63 std::vector<v8> attr_id_len_;
64 std::vector<v8> attr_value_len_;
65 vind payload_ind_, payload_len_;
66
67 void input(QuadCircuit<Field>& Q, const LogicCircuit& lc, size_t na) {
68 r_ = Q.input();
69 s_ = Q.input();
70 e_ = Q.input();
71 jwt_sig_.input(Q);
72 for (size_t i = 0; i < 64 * kMaxJWTSHABlocks; ++i) {
73 preimage_[i] = lc.template vinput<8>();
74 }
75 e_bits_ = lc.template vinput<256>();
76 for (size_t j = 0; j < kMaxJWTSHABlocks; ++j) {
77 sha_[j].input(Q);
78 }
79 nb_ = lc.template vinput<8>();
80
81 for (size_t j = 0; j < na; ++j) {
82 attr_ind_.push_back(lc.template vinput<kJWTIndexBits>());
83 attr_id_len_.push_back(lc.template vinput<8>());
84 attr_value_len_.push_back(lc.template vinput<8>());
85 }
86 payload_ind_ = lc.template vinput<kJWTIndexBits>();
87 payload_len_ = lc.template vinput<kJWTIndexBits>();
88 }
89 };
90
91 explicit JWT(const LogicCircuit& lc, const EC& ec, const Nat& order)
92 : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc) {}
93
94 // The assert_jwt_attributes circuit verifies the following claims:
95 // 1. There exists a hash digest e and a signature (r,s) on e
96 // under the public key (pkX, pkY).
97 // 2. There exists a msg, and the hash of msg is equal to e.
98 // 3. The JWT message is decoded correctly from base64.
99 // 4. The decoded message is equal to the payload.header.
100 // 5. The header contains alg:ESP256. [TODO]
101 // 6. The attributes occur as <ID>":"<VALUE>" in the payload.body.
102 //
103 // Note that the soundness of (6) relies on assumptions about the format of
104 // the JWT. The issuer cannot add spaces, cannot escape quotes in the body,
105 // and the character : should only appear as a separator.
106 void assert_jwt_attributes(EltW pkX, EltW pkY,
107 OpenedAttribute oa[/* NUM_ATTR */],
108 Witness& vw) const {
109 Ecdsa ecc(lc_, ec_, order_);
110
111 ecc.verify_signature3(pkX, pkY, vw.e_, vw.jwt_sig_);
112
113 sha_.assert_message_hash(kMaxJWTSHABlocks, vw.nb_, vw.preimage_, vw.e_bits_,
114 vw.sha_);
115 lc_.vassert_is_bit(vw.e_bits_);
116
117 // Check that the e_bits_ match the EltW for e used in the signature.
118 auto twok = lc_.one();
119 auto est = lc_.konst(0);
120 for (size_t i = 0; i < 256; ++i) {
121 est = lc_.axpy(&est, twok, lc_.eval(vw.e_bits_[i]));
122 lc_.f_.add(twok, twok);
123 }
124 lc_.assert_eq(&est, vw.e_);
125
126 // Assert the attribute equality
127 const v8 zz = lc_.template vbit<8>(0); // cannot appear in strings
128 std::vector<v8> shift_buf(64 * kMaxJWTSHABlocks);
129
130 // First shift the payload into the shift_buf.
131 r_.shift(vw.payload_ind_, 64 * (kMaxJWTSHABlocks - 2), shift_buf.data(),
132 64 * kMaxJWTSHABlocks, vw.preimage_, zz, 3);
133
134 // Decode the entire payload. A possible improvement is to decode just
135 // the portion necessary.
136 std::vector<v8> dec_buf(64 * kMaxJWTSHABlocks);
137 Base64Decoder<LogicCircuit> b64(lc_);
138 b64.base64_rawurl_decode_len(shift_buf.data(), dec_buf.data(),
139 64 * (kMaxJWTSHABlocks - 2), vw.payload_len_);
140
141 // For each attribute, shift the decoded payload so that the
142 // attribute is at the beginning of B. Verify the attribute id, the
143 // json separator, the attribute value, and the end quote.
144 for (size_t i = 0; i < vw.attr_ind_.size(); ++i) {
145 v8 B[32 + 3 + 64 + 1];
146
147 // Check that values of the attribute_id.
148 r_.shift(vw.attr_ind_[i], 100, B, dec_buf.size(), dec_buf.data(), zz, 3);
149 assert_string_eq(32, vw.attr_id_len_[i], B, oa[i].attr);
150
151 r_.shift(vw.attr_id_len_[i], 100, B, 100, B, zz, 3);
152 uint8_t sep[3] = {'"', ':', '"'};
153 for (size_t j = 0; j < 3; ++j) {
154 auto want_j = lc_.template vbit<8>(sep[j]);
155 lc_.vassert_eq(&B[j], want_j);
156 }
157
158 auto three = lc_.template vbit<2>(3);
159 r_.shift(three, 100, B, 100, B, zz, 3);
160
161 assert_string_eq(64, vw.attr_value_len_[i], B, oa[i].v1);
162
163 r_.shift(vw.attr_value_len_[i], 100, B, 100, B, zz, 3);
164
165 auto end_quote = lc_.template vbit<8>('"');
166 lc_.vassert_eq(&B[0], end_quote);
167 }
168 }
169
170 void assert_string_eq(size_t max, const v8& len, const v8 got[/*max*/],
171 const v8 want[/*max*/]) const {
172 for (size_t j = 0; j < max; ++j) {
173 auto ll = lc_.vlt(j, len);
174 auto same = lc_.eq(8, got[j].data(), want[j].data());
175 lc_.assert_implies(&ll, same);
176 }
177 }
178
179 private:
180 const LogicCircuit& lc_;
181 const EC& ec_;
182 const Nat& order_;
183 Flatsha sha_;
184 Routing<LogicCircuit> r_;
185};
186
187} // namespace proofs
188
189#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_H_
Definition bit_plucker.h:86
Definition flatsha256_circuit.h:52
Definition jwt.h:54
Definition compiler.h:50
Definition verify_circuit.h:32
Definition jwt.h:49
Definition jwt_witness.h:36
Definition verify_circuit.h:41