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#include "util/panic.h"
30
31namespace proofs {
32
33// This class implements a circuit to verify a restricted JWT+KB2 token.
34// The restrictions are:
35// - The token must be in the format of header.payload.signature~kb
36// - The device key is included in the payload as:
37// "cnf":{"jwk":{"kty":"EC","crv":"P-256","x":"...","y":"..."}
38// - None of the attribute identifiers include the characters
39// {colon,quote,solidus}.
40// - All of the attributes are encoded as strings.
41// These restrictions follow from our reasoning for why substring comparison
42// suffices in place of parsing.
43template <class LogicCircuit, class Field, class EC, size_t SHABlocks>
44class JWT {
45 constexpr static size_t kMaxSHABlocks = SHABlocks;
46 using EltW = typename LogicCircuit::EltW;
47 using BitW = typename LogicCircuit::BitW;
48 using Nat = typename Field::N;
50 using EcdsaWitness = typename Ecdsa::Witness;
51 using v8 = typename LogicCircuit::v8;
52 using v256 = typename LogicCircuit::v256;
53 using Flatsha =
54 FlatSHA256Circuit<LogicCircuit,
56 using ShaBlockWitness = typename Flatsha::BlockWitness;
57 using sha_packed_v32 = typename Flatsha::packed_v32;
58 using vind = typename LogicCircuit::template bitvec<kJWTIndexBits>;
59
60 public:
62 v8 pattern[128];
63 v8 len;
64 void input(const LogicCircuit& lc) {
65 for (size_t i = 0; i < 128; ++i) {
66 pattern[i] = lc.template vinput<8>();
67 }
68 len = lc.template vinput<8>();
69 }
70 };
71
72 class Witness {
73 public:
74 EltW e_, dpkx_, dpky_;
75 EcdsaWitness jwt_sig_, kb_sig_;
76 v8 preimage_[64 * kMaxSHABlocks];
77 v256 e_bits_;
78 ShaBlockWitness sha_[kMaxSHABlocks];
79 v8 nb_; /* index of sha block that contains the real hash */
80 std::vector<vind> attr_ind_;
81 vind payload_ind_, payload_len_;
82
83 void input(QuadCircuit<Field>& Q, const LogicCircuit& lc, size_t na) {
84 e_ = Q.input();
85 dpkx_ = Q.input();
86 dpky_ = Q.input();
87 jwt_sig_.input(Q);
88 kb_sig_.input(Q);
89 for (size_t i = 0; i < 64 * kMaxSHABlocks; ++i) {
90 preimage_[i] = lc.template vinput<8>();
91 }
92 e_bits_ = lc.template vinput<256>();
93 for (size_t j = 0; j < kMaxSHABlocks; ++j) {
94 sha_[j].input(Q);
95 }
96 nb_ = lc.template vinput<8>();
97
98 for (size_t j = 0; j < na; ++j) {
99 attr_ind_.push_back(lc.template vinput<kJWTIndexBits>());
100 }
101 payload_ind_ = lc.template vinput<kJWTIndexBits>();
102 payload_len_ = lc.template vinput<kJWTIndexBits>();
103 }
104 };
105
106 explicit JWT(const LogicCircuit& lc, const EC& ec, const Nat& order)
107 : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc) {
108 check(1 << kJWTIndexBits > kMaxSHABlocks * 64 - 9,
109 "JWT index bits too small");
110 }
111
112 // The assert_jwt_attributes circuit verifies the following claims:
113 // 1. There exists a hash digest e and a signature (r,s) on e
114 // under the public key (pkX, pkY).
115 // 2. There exists a msg, and the hash of msg is equal to e.
116 // 3. The JWT message is decoded correctly from base64.
117 // 4. The decoded message is equal to the payload.header.
118 // 5. The header contains alg:ESP256. [TODO]
119 // 6. The attributes occur as <ID>":"<VALUE>" in the payload.body.
120 //
121 // Note that the soundness of (6) relies on assumptions about the format of
122 // the JWT. The issuer cannot add spaces, cannot escape quotes in the body,
123 // and the character : should only appear as a separator.
124 void assert_jwt_attributes(EltW pkX, EltW pkY,
125 EltW e2 /* hash of kb message */,
126 OpenedAttribute oa[/* NUM_ATTR */],
127 Witness& vw) const {
128 Ecdsa ecc(lc_, ec_, order_);
129
130 ecc.verify_signature3(pkX, pkY, vw.e_, vw.jwt_sig_);
131 ecc.verify_signature3(vw.dpkx_, vw.dpky_, e2, vw.kb_sig_);
132
133 sha_.assert_message_hash(kMaxSHABlocks, vw.nb_, vw.preimage_, vw.e_bits_,
134 vw.sha_);
135 lc_.vassert_is_bit(vw.e_bits_);
136
137 // Check that the e_bits_ match the EltW for e used in the signature.
138 auto twok = lc_.one();
139 auto est = lc_.konst(0);
140 for (size_t i = 0; i < 256; ++i) {
141 est = lc_.axpy(&est, twok, lc_.eval(vw.e_bits_[i]));
142 lc_.f_.add(twok, twok);
143 }
144 lc_.assert_eq(&est, vw.e_);
145
146 // Assert the attribute equality
147 const v8 zz = lc_.template vbit<8>(0); // cannot appear in strings
148 std::vector<v8> shift_buf(64 * kMaxSHABlocks);
149
150 // First shift the payload into the shift_buf.
151 r_.shift(vw.payload_ind_, 64 * (kMaxSHABlocks - 2), shift_buf.data(),
152 64 * kMaxSHABlocks, vw.preimage_, zz, 3);
153
154 // Decode the entire payload. A possible improvement is to decode just
155 // the portion necessary.
156 std::vector<v8> dec_buf(64 * kMaxSHABlocks);
157 Base64Decoder<LogicCircuit> b64(lc_);
158 b64.base64_rawurl_decode_len(shift_buf.data(), dec_buf.data(),
159 64 * (kMaxSHABlocks - 2), vw.payload_len_);
160
161 // For each attribute, shift the decoded payload so that the
162 // attribute is at the beginning of B. Verify the attribute id, the
163 // json separator, the attribute value, and the end quote.
164 for (size_t i = 0; i < vw.attr_ind_.size(); ++i) {
165 v8 B[128];
166
167 // Check that values of the attribute_id.
168 r_.shift(vw.attr_ind_[i], 128, B, dec_buf.size(), dec_buf.data(), zz, 3);
169 assert_string_eq(128, oa[i].len, B, oa[i].pattern);
170 }
171 }
172
173 void assert_string_eq(size_t max, const v8& len, const v8 got[/*max*/],
174 const v8 want[/*max*/]) const {
175 for (size_t j = 0; j < max; ++j) {
176 auto ll = lc_.vlt(j, len);
177 auto same = lc_.eq(8, got[j].data(), want[j].data());
178 lc_.assert_implies(&ll, same);
179 }
180 }
181
182 private:
183 const LogicCircuit& lc_;
184 const EC& ec_;
185 const Nat& order_;
186 Flatsha sha_;
187 Routing<LogicCircuit> r_;
188};
189
190} // namespace proofs
191
192#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_H_
Definition bit_plucker.h:86
Definition flatsha256_circuit.h:52
Definition jwt.h:72
Definition compiler.h:50
Definition verify_circuit.h:32
Definition jwt.h:61
Definition jwt_witness.h:37
Definition verify_circuit.h:41