Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
mdoc_1f_witness.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_MDOC_MDOC_1F_WITNESS_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_WITNESS_H_
17
18#include <cstddef>
19#include <cstdint>
20#include <cstring>
21#include <vector>
22
23#include "arrays/dense.h"
24#include "circuits/cbor_parser/cbor_witness.h"
25#include "circuits/ecdsa/verify_witness.h"
26#include "circuits/logic/bit_plucker_encoder.h"
27#include "circuits/mdoc/mdoc_1f_io.h"
28#include "circuits/mdoc/mdoc_constants.h"
29#include "circuits/mdoc/mdoc_witness.h"
30#include "circuits/mdoc/mdoc_zk.h"
31#include "circuits/sha/flatsha256_witness.h"
32#include "util/log.h"
33namespace proofs {
34
35template <typename EC, typename Field, class ScalarField>
36class mdoc_1f_witness {
37 using ECField = typename EC::Field;
38 using ECElt = typename ECField::Elt;
39 using ECNat = typename ECField::N;
40 using Elt = typename Field::Elt;
41 using Nat = typename Field::N;
42 using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
43 using CborWitness = CborWitness<Field>;
44
45 public:
46 const EC ec_;
47 Elt e_, e2_; /* Issuer signature values. */
48 Elt dpkx_, dpky_; /* device key */
49 EcdsaWitness ew_, dkw_;
50 uint8_t now_[kMdoc1DateLen]; /* CBOR-formatted time for expiry comparison. */
51
52 FlatSHA256Witness::BlockWitness bw_[kMdoc1MaxSHABlocks];
53 uint8_t signed_bytes_[kMdoc1MaxSHABlocks * 64];
54 uint8_t numb_; /* Number of the correct sha block. */
55 ParsedMdoc pm_;
56
57 size_t num_attr_;
58 std::vector<std::vector<uint8_t>> attr_bytes_;
59 std::vector<std::vector<FlatSHA256Witness::BlockWitness>> atw_;
60
61 std::vector<uint8_t> attr_n_; /* All attributes currently require 2 SHA. */
62 std::vector<CborIndex> attr_mso_; /* The cbor indices of the attributes. */
63 std::vector<AttrShift> attr_ei_;
64 std::vector<AttrShift> attr_ev_;
65
66 // Cbor parsing witnesses
67 std::vector<typename CborWitness::v8> incb_;
68 std::vector<typename CborWitness::position_witness> pwcb_;
69 typename CborWitness::global_witness gwcb_;
70
71 explicit mdoc_1f_witness(size_t num_attr, const EC& ec, const ScalarField& Fn)
72 : ec_(ec),
73 ew_(Fn, ec),
74 dkw_(Fn, ec),
75 num_attr_(num_attr),
76 attr_bytes_(num_attr_),
77 atw_(num_attr_),
78 attr_n_(num_attr_),
79 attr_mso_(num_attr_),
80 attr_ei_(num_attr_),
81 attr_ev_(num_attr_),
82 incb_(kMdoc1MaxMsoLen),
83 pwcb_(kMdoc1MaxMsoLen) {}
84
85 void fill_sha(DenseFiller<Field>& filler,
86 const FlatSHA256Witness::BlockWitness& bw) const {
88 for (size_t k = 0; k < 48; ++k) {
89 filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
90 }
91 for (size_t k = 0; k < 64; ++k) {
92 filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
93 filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
94 }
95 for (size_t k = 0; k < 8; ++k) {
96 filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
97 }
98 }
99
100 void fill_attr_shift(DenseFiller<Field>& df, const AttrShift& attr) const {
101 df.push_back(attr.offset, kMdoc1CborIndexBits, ec_.f_);
102 df.push_back(attr.len, kMdoc1CborIndexBits, ec_.f_);
103 }
104
105 // The cbor index that is computed by our witness maker is with reference
106 // to the beginning of the cbor string. However the convention for the cbor
107 // parser is to 0-pad from the left to fill the full cbor string buffer.
108 // As a result, all cbor indices need to be offset by the padding length.
109 void fill_cbor_index(DenseFiller<Field>& filler, const CborIndex& ind,
110 size_t padding_offset = 0) const {
111 filler.push_back(ind.k + padding_offset, kMdoc1CborIndexBits, ec_.f_);
112 filler.push_back(ind.v + padding_offset, kMdoc1CborIndexBits, ec_.f_);
113 filler.push_back(ind.ndx, kMdoc1CborIndexBits, ec_.f_);
114 }
115
116 void fill_witness(DenseFiller<Field>& filler, bool small = false) const {
117 filler.push_back(e_);
118 filler.push_back(dpkx_);
119 filler.push_back(dpky_);
120
121 ew_.fill_witness(filler);
122 dkw_.fill_witness(filler);
123
124 filler.push_back(numb_, 8, ec_.f_);
125 for (size_t i = kCose1PrefixLen; i < kMdoc1MaxSHABlocks * 64; ++i) {
126 filler.push_back(signed_bytes_[i], 8, ec_.f_);
127 }
128 for (size_t j = 0; j < kMdoc1MaxSHABlocks; j++) {
129 fill_sha(filler, bw_[j]);
130 }
131
132 size_t prepad = kMdoc1MaxMsoLen - pm_.t_mso_.len + 5;
133 filler.push_back(prepad, kMdoc1CborIndexBits, ec_.f_);
134 filler.push_back(pm_.t_mso_.len - 5, kMdoc1CborIndexBits, ec_.f_);
135 for (size_t i = 0; i < kMdoc1MaxMsoLen; ++i) {
136 filler.push_back(pwcb_[i].encoded_sel_header);
137 }
138 filler.push_back(gwcb_.invprod_decode);
139 filler.push_back(gwcb_.cc0);
140 filler.push_back(gwcb_.invprod_parse);
141
142 fill_cbor_index(filler, pm_.valid_, prepad);
143 fill_cbor_index(filler, pm_.valid_from_, prepad);
144 fill_cbor_index(filler, pm_.valid_until_, prepad);
145 fill_cbor_index(filler, pm_.dev_key_info_, prepad);
146 fill_cbor_index(filler, pm_.dev_key_, prepad);
147 fill_cbor_index(filler, pm_.dev_key_pkx_, prepad);
148 fill_cbor_index(filler, pm_.dev_key_pky_, prepad);
149 fill_cbor_index(filler, pm_.value_digests_, prepad);
150 fill_cbor_index(filler, pm_.org_, prepad);
151
152 // Fill all attribute witnesses.
153 for (size_t ai = 0; ai < num_attr_; ++ai) {
154 for (size_t i = 0; i < 2 * 64; ++i) {
155 filler.push_back(attr_bytes_[ai][i], 8, ec_.f_);
156 }
157 for (size_t j = 0; j < 2; j++) {
158 fill_sha(filler, atw_[ai][j]);
159 }
160
161 // In the case of attribute mso, push the value to avoid having to
162 // deal with 1- or 2- byte key length.
163 // fill_cbor_index(filler, pm_.value_digests_);
164 fill_cbor_index(filler, attr_mso_[ai], prepad);
165 fill_attr_shift(filler, attr_ei_[ai]);
166 fill_attr_shift(filler, attr_ev_[ai]);
167 }
168 }
169
170 bool compute_witness(Elt pkX, Elt pkY, const uint8_t mdoc[/* len */],
171 size_t len, const uint8_t transcript[/* tlen */],
172 size_t tlen, const uint8_t tnow[/*kMdoc1DateLen*/],
173 const RequestedAttribute attrs[], size_t attrs_len) {
174 if (!pm_.parse_device_response(len, mdoc)) {
175 return false;
176 }
177 if (pm_.t_mso_.len >= kMdoc1MaxSHABlocks * 64 - 9 - kCose1PrefixLen) {
178 log(ERROR, "tagged mso is too big: %zu", pm_.t_mso_.len);
179 return false;
180 }
181
182 Nat ne = nat_from_hash<Nat>(pm_.tagged_mso_bytes_.data(),
183 pm_.tagged_mso_bytes_.size());
184 e_ = ec_.f_.to_montgomery(ne);
185
186 // Parse (r,s).
187 const size_t l = pm_.sig_.len;
188 Nat nr = nat_from_be<Nat>(&mdoc[pm_.sig_.pos]);
189 Nat ns = nat_from_be<Nat>(&mdoc[pm_.sig_.pos + l / 2]);
190 ew_.compute_witness(pkX, pkY, ne, nr, ns);
191
192 Nat ne2 = compute_transcript_hash<Nat>(transcript, tlen, &pm_.doc_type_);
193 const size_t l2 = pm_.dksig_.len;
194 Nat nr2 = nat_from_be<Nat>(&mdoc[pm_.dksig_.pos]);
195 Nat ns2 = nat_from_be<Nat>(&mdoc[pm_.dksig_.pos + l2 / 2]);
196 size_t pmso = pm_.t_mso_.pos + 5; /* skip the tag */
197 dpkx_ = ec_.f_.to_montgomery(
198 nat_from_be<Nat>(&mdoc[pmso + pm_.dev_key_pkx_.pos]));
199 dpky_ = ec_.f_.to_montgomery(
200 nat_from_be<Nat>(&mdoc[pmso + pm_.dev_key_pky_.pos]));
201 e2_ = ec_.f_.to_montgomery(ne2);
202 dkw_.compute_witness(dpkx_, dpky_, ne2, nr2, ns2);
203
204 memcpy(now_, tnow, kMdoc1DateLen);
205 std::vector<uint8_t> buf;
206
207 buf.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
208 // Add 2-byte length
209 buf.push_back((pm_.t_mso_.len >> 8) & 0xff);
210 buf.push_back(pm_.t_mso_.len & 0xff);
211 for (size_t i = 0; i < pm_.t_mso_.len; ++i) {
212 buf.push_back(mdoc[pm_.t_mso_.pos + i]);
213 }
214
215 FlatSHA256Witness::transform_and_witness_message(
216 buf.size(), buf.data(), kMdoc1MaxSHABlocks, numb_, signed_bytes_, bw_);
217
218 // Cbor parsing.
219 // The input is expected to be pre-padded with zeros.
220 // The +5 corresponds to the D8 18 59 <len2> prefix.
221 size_t prepad = kMdoc1MaxMsoLen - pm_.t_mso_.len + 5;
222 // Pad with enough 0s.
223 buf.erase(buf.begin(), buf.begin() + kCose1PrefixLen + 2 + 5);
224 buf.insert(buf.begin(), prepad, 0);
225
226 CborWitness cw(ec_.f_);
227 cw.fill_witnesses(kMdoc1MaxMsoLen, pm_.t_mso_.len, buf.data(), incb_.data(),
228 pwcb_.data(), gwcb_);
229
230 // initialize variables
231 for (size_t i = 0; i < num_attr_; ++i) {
232 attr_bytes_[i].resize(128);
233 atw_[i].resize(2);
234 }
235
236 // Match the attributes with the witnesses from the deviceResponse.
237 for (size_t i = 0; i < num_attr_; ++i) {
238 bool found = false;
239 for (auto fa : pm_.attributes_) {
240 if (fa == attrs[i]) {
241 FlatSHA256Witness::transform_and_witness_message(
242 fa.tag_len, &fa.doc[fa.tag_ind], 2, attr_n_[i],
243 &attr_bytes_[i][0], &atw_[i][0]);
244 attr_mso_[i] = fa.mso;
245 attr_ei_[i].offset = fa.id_ind - fa.tag_ind;
246 attr_ei_[i].len = fa.witness_length(attrs[i]);
247 attr_ev_[i].offset = fa.val_ind - fa.tag_ind;
248 attr_ev_[i].len = fa.val_len;
249 found = true;
250 break;
251 }
252 }
253 if (!found) {
254 log(ERROR, "Could not find attribute %.*s", attrs[i].id_len,
255 attrs[i].id);
256 return false;
257 }
258 }
259 return true;
260 }
261};
262
263} // namespace proofs
264
265#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_WITNESS_H_
Definition bit_plucker_encoder.h:27
Definition dense.h:153
Definition mdoc_witness.h:91
Definition verify_witness.h:30
Definition mdoc_zk.h:37
Definition mdoc_witness.h:48
Definition mdoc_witness.h:43
Definition cbor_witness.h:47
Definition flatsha256_witness.h:27
Definition gf2_128.h:63